pts rule name              description
 ---- ---------------------- --------------------------------------------------
  2.7 DNS_FROM_AHBL_RHSBL    RBL: Envelope sender listed in dnsbl.ahbl.org
 -0.0 SPF_PASS               SPF: sender matches SPF record
 -1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%
                             [score: 0.0000]
Status: RO
X-Status: F
Content-Length: 5442

Signed-off-by: Andreas Schultz <[email protected]>
---
 gtp.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 125 insertions(+), 27 deletions(-)

diff --git a/gtp.c b/gtp.c
index 457587e..48e98d8 100644
--- a/gtp.c
+++ b/gtp.c
@@ -452,6 +452,21 @@ ip4_route_output_gtp(struct net *net, struct flowi4 *fl4,
        return ip_route_output_key(net, fl4);
 }

+static struct dst_entry *
+ip6_route_output_gtp(struct net *net, struct flowi6 *fl6,
+                    const struct sock *sk,
+                    struct in6_addr *daddr)
+{
+       memset(fl6, 0, sizeof(*fl6));
+       fl6->flowi6_oif = sk->sk_bound_dev_if;
+       fl6->daddr = *daddr;
+       fl6->saddr = inet6_sk(sk)->saddr;
+       fl6->flowi6_tos = RT_CONN_FLAGS(sk);
+       fl6->flowi6_proto = sk->sk_protocol;
+
+       return ip6_route_output(net, NULL, fl6);
+}
+
 static inline void
 gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
 {
@@ -524,8 +539,12 @@ struct gtp_pktinfo {
        };
        union {
                struct flowi4   fl4;
+               struct flowi6   fl6;
+       };
+       union {
+               struct rtable           *rt;
+               struct dst_entry        *ndst;
        };
-       struct rtable           *rt;
        struct pdp_ctx          *pctx;
        struct net_device       *dev;
 };
@@ -634,11 +653,101 @@ err:
        return -EBADMSG;
 }

+static inline void
+gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo, struct sock *sk,
+                    struct ipv6hdr *ip6h,
+                    struct pdp_ctx *pctx, struct dst_entry *ndst,
+                    struct flowi6 *fl6, struct net_device *dev)
+{
+       pktinfo->sk     = sk;
+       pktinfo->ip6h   = ip6h;
+       pktinfo->pctx   = pctx;
+       pktinfo->ndst   = ndst;
+       pktinfo->fl6    = *fl6;
+       pktinfo->dev    = dev;
+}
+
 static int gtp_ip6_prepare_xmit(struct sk_buff *skb, struct net_device *dev,
                                struct gtp_pktinfo *pktinfo)
 {
-       /* TODO IPV6 support */
+       struct gtp_instance *gti = netdev_priv(dev);
+       struct sock *sk;
+       struct ipv6hdr *ipv6h;
+       struct pdp_ctx *pctx;
+       struct dst_entry *ndst;
+       struct flowi6 fl6;
+       int mtu;
+
+       /* Read the IP destination address and resolve the PDP context.
+        * Prepend PDP header with TEI/TID from PDP ctx.
+        */
+       ipv6h = ipv6_hdr(skb);
+       pctx = ipv6_pdp_find(gti, &ipv6h->daddr);
+       if (!pctx) {
+               netdev_dbg(dev, "no PDP ctx found for this packet, skip\n");
+               return -ENOENT;
+       }
+       netdev_dbg(dev, "found PDP context %p\n", pctx);
+
+       /* Obtain route for the new encapsulated GTP packet */
+       switch (pctx->gtp_version) {
+       case GTP_V0:
+               sk = gti->sock0->sk;
+               break;
+       case GTP_V1:
+               sk = gti->sock1u->sk;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       ndst = ip6_route_output_gtp(sock_net(sk), &fl6,
+                                   gti->sock0->sk,
+                                   &pctx->sgsn_addr.ip6);
+       if (IS_ERR(ndst)) {
+               netdev_dbg(dev, "no route to SSGN %pI6\n",
+                          &pctx->sgsn_addr.ip6.s6_addr);
+               dev->stats.tx_carrier_errors++;
+               goto err;
+       }
+
+       /* There is a routing loop */
+       if (ndst->dev == dev) {
+               netdev_dbg(dev, "circular route to SSGN %pI6\n",
+                          &pctx->sgsn_addr.ip6);
+               dev->stats.collisions++;
+               goto err_dst;
+       }
+
+       skb_dst_drop(skb);
+
+       mtu = dst_mtu(ndst) - dev->hard_header_len -
+               sizeof(struct ipv6hdr) - sizeof(struct udphdr);
+       switch (pctx->gtp_version) {
+       case GTP_V0:
+               mtu -= sizeof(struct gtp0_header);
+               break;
+       case GTP_V1:
+               mtu -= sizeof(struct gtp1_header);
+               break;
+       }
+       ndst->ops->update_pmtu(ndst, NULL, skb, mtu);
+
+       if (!skb_is_gso(skb) && skb->len > mtu) {
+               netdev_dbg(dev, "packet too big, fragmentation needed\n");
+               memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+               icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+                         htonl(mtu));
+               goto err_dst;
+       }
+
+       gtp_set_pktinfo_ipv6(pktinfo, sk, ipv6h, pctx, ndst, &fl6, dev);
+
        return 0;
+err_dst:
+       dst_release(ndst);
+err:
+       return -EBADMSG;
 }

 static inline int
@@ -657,15 +766,23 @@ gtp_udp_tunnel_xmit(struct sk_buff *skb, __be16 port,
 }

 static inline int
-gtp_ip6tunnel_xmit(struct sk_buff *skb, struct gtp_pktinfo *pktinfo)
+gtp_ip6tunnel_xmit(struct sk_buff *skb, __be16 port,
+                  struct gtp_pktinfo *pktinfo)
 {
-       /* TODO IPV6 support */
+       netdev_dbg(pktinfo->dev, "gtp -> IP src: %pI6 dst: %pI6\n",
+                  &pktinfo->ip6h->saddr, &pktinfo->ip6h->daddr);
+
+       return udp_tunnel6_xmit_skb(pktinfo->ndst, pktinfo->sk, skb,
+                                   pktinfo->ndst->dev,
+                                   &pktinfo->fl6.saddr,
+                                   &pktinfo->fl6.daddr,
+                                   0,
+                                   ip6_dst_hoplimit(pktinfo->ndst),
+                                   port, port, false);
 }

 static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct udphdr *uh;
-       unsigned int payload_len;
        struct gtp_pktinfo pktinfo;
        unsigned int proto = ntohs(skb->protocol);
        int gtph_len, err = -EINVAL;
@@ -713,28 +830,9 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, 
struct net_device *dev)
        case ETH_P_IP:
                err = gtp_udp_tunnel_xmit(skb, gtph_port, &pktinfo);
                break;
-       case ETH_P_IPV6:
-               /* Annotate length of the encapsulated packet */
-               payload_len = skb->len;
-
-               /* Push down and install the UDP header. */
-               skb_push(skb, sizeof(struct udphdr));
-               skb_reset_transport_header(skb);
-
-               uh = udp_hdr(skb);
-
-               uh->source = uh->dest = gtph_port;
-               uh->len = htons(sizeof(struct udphdr) + payload_len + gtph_len);
-               uh->check = 0;

-               netdev_dbg(dev, "gtp -> UDP src: %u dst: %u (len %u)\n",
-                          ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len));
-
-               nf_reset(skb);
-
-               netdev_dbg(dev, "Good, now packet leaving from GGSN to SGSN\n");
-
-               err = gtp_ip6tunnel_xmit(skb, &pktinfo);
+       case ETH_P_IPV6:
+               err = gtp_ip6tunnel_xmit(skb, gtph_port, &pktinfo);
                break;
        }

-- 
2.7.4

Reply via email to