The gtp pktinfo structure is unnecessary and needs a lot of code to
manage it. Remove it. Also, add per pdp port configuration for transmit.

Signed-off-by: Tom Herbert <t...@quantonium.net>
---
 drivers/net/gtp.c        | 177 +++++++++++++++++++++--------------------------
 include/uapi/linux/gtp.h |   1 +
 2 files changed, 80 insertions(+), 98 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index bbb08f8849d3..44844eba8df2 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -54,6 +54,7 @@ struct pdp_ctx {
        } u;
        u8                      gtp_version;
        u8                      hlen;
+       __be16                  gtp_port;
        u16                     af;
 
        struct in_addr          ms_addr_ip4;
@@ -420,73 +421,36 @@ static inline void gtp1_push_header(struct sk_buff *skb, 
struct pdp_ctx *pctx)
         */
 }
 
-struct gtp_pktinfo {
-       struct sock             *sk;
-       struct iphdr            *iph;
-       struct flowi4           fl4;
-       struct rtable           *rt;
-       struct pdp_ctx          *pctx;
-       struct net_device       *dev;
-       __be16                  gtph_port;
-};
-
-static void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo)
+static void gtp_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
 {
-       switch (pktinfo->pctx->gtp_version) {
+       switch (pctx->gtp_version) {
        case GTP_V0:
-               pktinfo->gtph_port = htons(GTP0_PORT);
-               gtp0_push_header(skb, pktinfo->pctx);
+               gtp0_push_header(skb, pctx);
                break;
        case GTP_V1:
-               pktinfo->gtph_port = htons(GTP1U_PORT);
-               gtp1_push_header(skb, pktinfo->pctx);
+               gtp1_push_header(skb, pctx);
                break;
        }
 }
 
-static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
-                                       struct sock *sk, struct iphdr *iph,
-                                       struct pdp_ctx *pctx, struct rtable *rt,
-                                       struct flowi4 *fl4,
-                                       struct net_device *dev)
-{
-       pktinfo->sk     = sk;
-       pktinfo->iph    = iph;
-       pktinfo->pctx   = pctx;
-       pktinfo->rt     = rt;
-       pktinfo->fl4    = *fl4;
-       pktinfo->dev    = dev;
-}
-
-static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
-                            struct gtp_pktinfo *pktinfo)
+static int gtp_xmit(struct sk_buff *skb, struct net_device *dev,
+                   struct pdp_ctx *pctx)
 {
-       struct gtp_dev *gtp = netdev_priv(dev);
-       struct pdp_ctx *pctx;
+       struct iphdr *inner_iph = NULL;
+       struct sock *sk = pctx->sk;
+       __be32 saddr = inet_sk(sk)->inet_saddr;
        struct rtable *rt;
-       struct flowi4 fl4;
-       struct iphdr *iph;
-       struct sock *sk;
-       __be32 saddr;
+       int err = 0;
 
-       /* Read the IP destination address and resolve the PDP context.
-        * Prepend PDP header with TEI/TID from PDP ctx.
-        */
-       iph = ip_hdr(skb);
-       if (gtp->role == GTP_ROLE_SGSN)
-               pctx = ipv4_pdp_find(gtp, iph->saddr);
-       else
-               pctx = ipv4_pdp_find(gtp, iph->daddr);
+       if (skb->protocol == ETH_P_IP)
+               inner_iph = ip_hdr(skb);
 
-       if (!pctx) {
-               netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
-                          &iph->daddr);
-               return -ENOENT;
-       }
-       netdev_dbg(dev, "found PDP context %p\n", pctx);
+       /* Ensure there is sufficient headroom. */
+       err = skb_cow_head(skb, dev->needed_headroom);
+       if (unlikely(err))
+               goto out_err;
 
-       sk = pctx->sk;
-       saddr = inet_sk(sk)->inet_saddr;
+       skb_reset_inner_headers(skb);
 
        /* Source address returned by route lookup is ignored since
         * we get the address from a socket.
@@ -494,81 +458,89 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct 
net_device *dev,
        rt = ip_tunnel_get_route(dev, skb, sk->sk_protocol,
                                 sk->sk_bound_dev_if, RT_CONN_FLAGS(sk),
                                 pctx->peer_addr_ip4.s_addr, &saddr,
-                                pktinfo->gtph_port, pktinfo->gtph_port,
+                                pctx->gtp_port, pctx->gtp_port,
                                 &pctx->dst_cache, NULL);
 
        if (IS_ERR(rt)) {
-               if (rt == ERR_PTR(-ELOOP)) {
-                       netdev_dbg(dev, "circular route to SSGN %pI4\n",
-                                  &pctx->peer_addr_ip4.s_addr);
-                       dev->stats.collisions++;
-                       goto err_rt;
-               } else {
-                       netdev_dbg(dev, "no route to SSGN %pI4\n",
-                                  &pctx->peer_addr_ip4.s_addr);
-                       dev->stats.tx_carrier_errors++;
-                       goto err;
-               }
+               err = PTR_ERR(rt);
+               goto out_err;
        }
 
        skb_dst_drop(skb);
 
-       gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev);
-       gtp_push_header(skb, pktinfo);
+       gtp_push_header(skb, pctx);
+
+       if (inner_iph)
+               __iptunnel_update_pmtu(dev, skb, &rt->dst,
+                                      !!inner_iph->frag_off,
+                                      inner_iph, pctx->hlen,
+                                      pctx->peer_addr_ip4.s_addr);
 
-       __iptunnel_update_pmtu(dev, skb, &rt->dst, !!iph->frag_off, iph,
-                              pctx->hlen, pctx->peer_addr_ip4.s_addr);
+       udp_tunnel_xmit_skb(rt, sk, skb, saddr,
+                           pctx->peer_addr_ip4.s_addr,
+                           0, ip4_dst_hoplimit(&rt->dst), 0,
+                           pctx->gtp_port, pctx->gtp_port,
+                           false, false);
+
+       netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
+                  &saddr, &pctx->peer_addr_ip4.s_addr);
 
        return 0;
-err_rt:
-       ip_rt_put(rt);
-err:
-       return -EBADMSG;
+
+out_err:
+       if (err == -ELOOP)
+               dev->stats.collisions++;
+       else
+               dev->stats.tx_carrier_errors++;
+
+       return err;
 }
 
 static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        unsigned int proto = ntohs(skb->protocol);
-       struct gtp_pktinfo pktinfo;
+       struct gtp_dev *gtp = netdev_priv(dev);
+       struct pdp_ctx *pctx;
        int err;
 
-       /* Ensure there is sufficient headroom. */
-       if (skb_cow_head(skb, dev->needed_headroom))
-               goto tx_err;
-
-       skb_reset_inner_headers(skb);
-
        /* PDP context lookups in gtp_build_skb_*() need rcu read-side lock. */
        rcu_read_lock();
        switch (proto) {
-       case ETH_P_IP:
-               err = gtp_build_skb_ip4(skb, dev, &pktinfo);
+       case ETH_P_IP: {
+               struct iphdr *iph = ip_hdr(skb);
+
+               if (gtp->role == GTP_ROLE_SGSN)
+                       pctx = ipv4_pdp_find(gtp, iph->saddr);
+               else
+                       pctx = ipv4_pdp_find(gtp, iph->daddr);
+
+               if (!pctx) {
+                       netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
+                                  &iph->daddr);
+                       err = -ENOENT;
+                       goto tx_err;
+               }
+
                break;
+       }
        default:
                err = -EOPNOTSUPP;
-               break;
+               goto tx_err;
        }
-       rcu_read_unlock();
+
+       netdev_dbg(dev, "found PDP context %p\n", pctx);
+
+       err = gtp_xmit(skb, dev, pctx);
 
        if (err < 0)
                goto tx_err;
 
-       switch (proto) {
-       case ETH_P_IP:
-               netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n",
-                          &pktinfo.iph->saddr, &pktinfo.iph->daddr);
-               udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
-                                   pktinfo.fl4.saddr, pktinfo.fl4.daddr,
-                                   pktinfo.iph->tos,
-                                   ip4_dst_hoplimit(&pktinfo.rt->dst),
-                                   0,
-                                   pktinfo.gtph_port, pktinfo.gtph_port,
-                                   true, false);
-               break;
-       }
+       rcu_read_unlock();
 
        return NETDEV_TX_OK;
+
 tx_err:
+       rcu_read_unlock();
        dev->stats.tx_errors++;
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
@@ -874,6 +846,8 @@ static struct gtp_dev *gtp_find_dev(struct net *src_net, 
struct nlattr *nla[])
 
 static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
+       __be16 default_port = 0;
+
        pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);
        pctx->af = AF_INET;
        pctx->peer_addr_ip4.s_addr =
@@ -890,15 +864,22 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct 
genl_info *info)
                pctx->u.v0.tid = nla_get_u64(info->attrs[GTPA_TID]);
                pctx->u.v0.flow = nla_get_u16(info->attrs[GTPA_FLOW]);
                pctx->hlen = sizeof(struct udphdr) + sizeof(struct gtp0_header);
+               default_port = htons(GTP0_PORT);
                break;
        case GTP_V1:
                pctx->u.v1.i_tei = nla_get_u32(info->attrs[GTPA_I_TEI]);
                pctx->u.v1.o_tei = nla_get_u32(info->attrs[GTPA_O_TEI]);
                pctx->hlen = sizeof(struct udphdr) + sizeof(struct gtp1_header);
+               default_port = htons(GTP1U_PORT);
                break;
        default:
                break;
        }
+
+       if (info->attrs[GTPA_PORT])
+               pctx->gtp_port = nla_get_u16(info->attrs[GTPA_PORT]);
+       else
+               pctx->gtp_port = default_port;
 }
 
 static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,
diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h
index 57d1edb8efd9..b2283a5c6d7f 100644
--- a/include/uapi/linux/gtp.h
+++ b/include/uapi/linux/gtp.h
@@ -27,6 +27,7 @@ enum gtp_attrs {
        GTPA_I_TEI,     /* for GTPv1 only */
        GTPA_O_TEI,     /* for GTPv1 only */
        GTPA_PAD,
+       GTPA_PORT,
        __GTPA_MAX,
 };
 #define GTPA_MAX (__GTPA_MAX + 1)
-- 
2.11.0

Reply via email to