From: Pravin B Shelar <pbshe...@fb.com>

This adds the Netlink interface necessary to set up flow based tunnels.

Signed-off-by: Jonas Bonn <jo...@norrbonn.se>
---
 drivers/net/gtp.c            | 139 +++++++++++++++++++++++++++--------
 include/uapi/linux/if_link.h |   1 +
 2 files changed, 111 insertions(+), 29 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index bbce2671de2d..a4fff0f1e174 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -407,6 +407,11 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
 {
        gtp_encap_disable_sock(gtp->sk0);
        gtp_encap_disable_sock(gtp->sk1u);
+       if (gtp->collect_md_sock) {
+               udp_tunnel_sock_release(gtp->collect_md_sock);
+               gtp->collect_md_sock = NULL;
+               netdev_dbg(gtp->dev, "GTP socket released.\n");
+       }
 }
 
 /* UDP encapsulation receive handler. See net/ipv4/udp.c.
@@ -904,6 +909,19 @@ static const struct net_device_ops gtp_netdev_ops = {
        .ndo_get_stats64        = dev_get_tstats64,
 };
 
+static struct gtp_dev *gtp_find_flow_based_dev(struct net *net)
+{
+       struct gtp_net *gn = net_generic(net, gtp_net_id);
+       struct gtp_dev *gtp;
+
+       list_for_each_entry(gtp, &gn->gtp_dev_list, list) {
+               if (gtp->collect_md)
+                       return gtp;
+       }
+
+       return NULL;
+}
+
 static const struct device_type gtp_type = {
        .name = "gtp",
 };
@@ -938,7 +956,7 @@ static void gtp_link_setup(struct net_device *dev)
 }
 
 static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]);
+static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, 
struct nlattr *data[]);
 
 static void gtp_destructor(struct net_device *dev)
 {
@@ -956,11 +974,24 @@ static int gtp_newlink(struct net *src_net, struct 
net_device *dev,
        struct gtp_net *gn;
        int hashsize, err;
 
-       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
+       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1] &&
+           !data[IFLA_GTP_COLLECT_METADATA])
                return -EINVAL;
 
        gtp = netdev_priv(dev);
 
+       if (data[IFLA_GTP_COLLECT_METADATA]) {
+               if (data[IFLA_GTP_FD0]) {
+                       netdev_dbg(dev, "LWT device does not support setting v0 
socket");
+                       return -EINVAL;
+               }
+               if (gtp_find_flow_based_dev(src_net)) {
+                       netdev_dbg(dev, "LWT device already exist");
+                       return -EBUSY;
+               }
+               gtp->collect_md = true;
+       }
+
        if (!data[IFLA_GTP_PDP_HASHSIZE]) {
                hashsize = 1024;
        } else {
@@ -973,7 +1004,7 @@ static int gtp_newlink(struct net *src_net, struct 
net_device *dev,
        if (err < 0)
                return err;
 
-       err = gtp_encap_enable(gtp, data);
+       err = gtp_encap_enable(gtp, dev, data);
        if (err < 0)
                goto out_hashtable;
 
@@ -987,7 +1018,7 @@ static int gtp_newlink(struct net *src_net, struct 
net_device *dev,
        list_add_rcu(&gtp->list, &gn->gtp_dev_list);
        dev->priv_destructor = gtp_destructor;
 
-       netdev_dbg(dev, "registered new GTP interface\n");
+       netdev_dbg(dev, "registered new GTP interface %s\n", dev->name);
 
        return 0;
 
@@ -1018,6 +1049,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 
1] = {
        [IFLA_GTP_FD1]                  = { .type = NLA_U32 },
        [IFLA_GTP_PDP_HASHSIZE]         = { .type = NLA_U32 },
        [IFLA_GTP_ROLE]                 = { .type = NLA_U32 },
+       [IFLA_GTP_COLLECT_METADATA]     = { .type = NLA_FLAG },
 };
 
 static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1044,6 +1076,9 @@ static int gtp_fill_info(struct sk_buff *skb, const 
struct net_device *dev)
        if (nla_put_u32(skb, IFLA_GTP_ROLE, gtp->role))
                goto nla_put_failure;
 
+       if (gtp->collect_md && nla_put_flag(skb, IFLA_GTP_COLLECT_METADATA))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
@@ -1089,35 +1124,24 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int 
hsize)
        return -ENOMEM;
 }
 
-static struct sock *gtp_encap_enable_socket(int fd, int type,
-                                           struct gtp_dev *gtp)
+static int __gtp_encap_enable_socket(struct socket *sock, int type,
+                                    struct gtp_dev *gtp)
 {
        struct udp_tunnel_sock_cfg tuncfg = {NULL};
-       struct socket *sock;
        struct sock *sk;
-       int err;
-
-       pr_debug("enable gtp on %d, %d\n", fd, type);
-
-       sock = sockfd_lookup(fd, &err);
-       if (!sock) {
-               pr_debug("gtp socket fd=%d not found\n", fd);
-               return NULL;
-       }
 
        sk = sock->sk;
        if (sk->sk_protocol != IPPROTO_UDP ||
            sk->sk_type != SOCK_DGRAM ||
            (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) {
-               pr_debug("socket fd=%d not UDP\n", fd);
-               sk = ERR_PTR(-EINVAL);
-               goto out_sock;
+               pr_debug("socket not UDP\n");
+               return -EINVAL;
        }
 
        lock_sock(sk);
        if (sk->sk_user_data) {
-               sk = ERR_PTR(-EBUSY);
-               goto out_rel_sock;
+               release_sock(sock->sk);
+               return -EBUSY;
        }
 
        sock_hold(sk);
@@ -1130,15 +1154,58 @@ static struct sock *gtp_encap_enable_socket(int fd, int 
type,
        tuncfg.gro_complete = gtp_gro_complete;
 
        setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg);
-
-out_rel_sock:
        release_sock(sock->sk);
-out_sock:
+       return 0;
+}
+
+static struct sock *gtp_encap_enable_socket(int fd, int type,
+                                           struct gtp_dev *gtp)
+{
+       struct socket *sock;
+       int err;
+
+       pr_debug("enable gtp on %d, %d\n", fd, type);
+
+       sock = sockfd_lookup(fd, &err);
+       if (!sock) {
+               pr_debug("gtp socket fd=%d not found\n", fd);
+               return NULL;
+       }
+       err =  __gtp_encap_enable_socket(sock, type, gtp);
        sockfd_put(sock);
-       return sk;
+       if (err)
+               return ERR_PTR(err);
+
+       return sock->sk;
+}
+
+static struct socket *gtp_create_gtp_socket(struct gtp_dev *gtp, struct 
net_device *dev)
+{
+       struct udp_port_cfg udp_conf;
+       struct socket *sock;
+       int err;
+
+       memset(&udp_conf, 0, sizeof(udp_conf));
+       udp_conf.family = AF_INET;
+       udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
+       udp_conf.local_udp_port = htons(GTP1U_PORT);
+
+       err = udp_sock_create(dev_net(dev), &udp_conf, &sock);
+       if (err < 0) {
+               pr_debug("create gtp sock failed: %d\n", err);
+               return ERR_PTR(err);
+       }
+       err = __gtp_encap_enable_socket(sock, UDP_ENCAP_GTP1U, gtp);
+       if (err) {
+               pr_debug("enable gtp sock encap failed: %d\n", err);
+               udp_tunnel_sock_release(sock);
+               return ERR_PTR(err);
+       }
+       pr_debug("create gtp sock done\n");
+       return sock;
 }
 
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
+static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, 
struct nlattr *data[])
 {
        struct sock *sk1u = NULL;
        struct sock *sk0 = NULL;
@@ -1162,11 +1229,25 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct 
nlattr *data[])
                }
        }
 
+       if (data[IFLA_GTP_COLLECT_METADATA]) {
+               struct socket *sock;
+
+               if (!sk1u) {
+                       sock = gtp_create_gtp_socket(gtp, dev);
+                       if (IS_ERR(sock))
+                               return PTR_ERR(sock);
+
+                       gtp->collect_md_sock = sock;
+                       sk1u = sock->sk;
+               } else {
+                       gtp->collect_md_sock = NULL;
+               }
+       }
+
        if (data[IFLA_GTP_ROLE]) {
                role = nla_get_u32(data[IFLA_GTP_ROLE]);
                if (role > GTP_ROLE_SGSN) {
-                       gtp_encap_disable_sock(sk0);
-                       gtp_encap_disable_sock(sk1u);
+                       gtp_encap_disable(gtp);
                        return -EINVAL;
                }
        }
@@ -1725,7 +1806,7 @@ static int __init gtp_init(void)
        if (err < 0)
                goto unreg_genl_family;
 
-       pr_info("GTP module loaded (pdp ctx size %zd bytes)\n",
+       pr_info("GTP module loaded (pdp ctx size %zd bytes) with tnl-md 
support\n",
                sizeof(struct pdp_ctx));
        return 0;
 
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 82708c6db432..2bd0d8bbcdb2 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -809,6 +809,7 @@ enum {
        IFLA_GTP_FD1,
        IFLA_GTP_PDP_HASHSIZE,
        IFLA_GTP_ROLE,
+       IFLA_GTP_COLLECT_METADATA,
        __IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
-- 
2.27.0

Reply via email to