gtp: Allow to create GTP device without FDs
authorWojciech Drewek <wojciech.drewek@intel.com>
Fri, 4 Mar 2022 16:40:42 +0000 (17:40 +0100)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Fri, 11 Mar 2022 16:27:16 +0000 (08:27 -0800)
Currently, when the user wants to create GTP device, he has to
provide file handles to the sockets created in userspace (IFLA_GTP_FD0,
IFLA_GTP_FD1). This behaviour is not ideal, considering the option of
adding support for GTP device creation through ip link. Ip link
application is not a good place to create such sockets.

This patch allows to create GTP device without providing
IFLA_GTP_FD0 and IFLA_GTP_FD1 arguments. If the user sets
IFLA_GTP_CREATE_SOCKETS attribute, then GTP module takes care
of creating UDP sockets by itself. Sockets are created with the
commonly known UDP ports used for GTP protocol (GTP0_PORT and
GTP1U_PORT). In this case we don't have to provide encap_destroy
because no extra deinitialization is needed, everything is covered
by udp_tunnel_sock_release.

Note: GTP instance created with only this change applied, does
not handle GTP Echo Requests. This is implemented in the following
patch.

Signed-off-by: Wojciech Drewek <wojciech.drewek@intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/gtp.c
include/uapi/linux/if_link.h

index bf087171bcf04c859f915b65b8427789886a7b56..25d8521897b3acd041ca06a3554018564aed3283 100644 (file)
@@ -66,8 +66,10 @@ struct gtp_dev {
 
        struct sock             *sk0;
        struct sock             *sk1u;
+       u8                      sk_created;
 
        struct net_device       *dev;
+       struct net              *net;
 
        unsigned int            role;
        unsigned int            hash_size;
@@ -320,8 +322,16 @@ static void gtp_encap_disable_sock(struct sock *sk)
 
 static void gtp_encap_disable(struct gtp_dev *gtp)
 {
-       gtp_encap_disable_sock(gtp->sk0);
-       gtp_encap_disable_sock(gtp->sk1u);
+       if (gtp->sk_created) {
+               udp_tunnel_sock_release(gtp->sk0->sk_socket);
+               udp_tunnel_sock_release(gtp->sk1u->sk_socket);
+               gtp->sk_created = false;
+               gtp->sk0 = NULL;
+               gtp->sk1u = NULL;
+       } else {
+               gtp_encap_disable_sock(gtp->sk0);
+               gtp_encap_disable_sock(gtp->sk1u);
+       }
 }
 
 /* UDP encapsulation receive handler. See net/ipv4/udp.c.
@@ -656,17 +666,69 @@ static void gtp_destructor(struct net_device *dev)
        kfree(gtp->tid_hash);
 }
 
+static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp)
+{
+       struct udp_tunnel_sock_cfg tuncfg = {};
+       struct udp_port_cfg udp_conf = {
+               .local_ip.s_addr        = htonl(INADDR_ANY),
+               .family                 = AF_INET,
+       };
+       struct net *net = gtp->net;
+       struct socket *sock;
+       int err;
+
+       if (type == UDP_ENCAP_GTP0)
+               udp_conf.local_udp_port = htons(GTP0_PORT);
+       else if (type == UDP_ENCAP_GTP1U)
+               udp_conf.local_udp_port = htons(GTP1U_PORT);
+       else
+               return ERR_PTR(-EINVAL);
+
+       err = udp_sock_create(net, &udp_conf, &sock);
+       if (err)
+               return ERR_PTR(err);
+
+       tuncfg.sk_user_data = gtp;
+       tuncfg.encap_type = type;
+       tuncfg.encap_rcv = gtp_encap_recv;
+       tuncfg.encap_destroy = NULL;
+
+       setup_udp_tunnel_sock(net, sock, &tuncfg);
+
+       return sock->sk;
+}
+
+static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[])
+{
+       struct sock *sk1u = NULL;
+       struct sock *sk0 = NULL;
+
+       sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp);
+       if (IS_ERR(sk0))
+               return PTR_ERR(sk0);
+
+       sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp);
+       if (IS_ERR(sk1u)) {
+               udp_tunnel_sock_release(sk0->sk_socket);
+               return PTR_ERR(sk1u);
+       }
+
+       gtp->sk_created = true;
+       gtp->sk0 = sk0;
+       gtp->sk1u = sk1u;
+
+       return 0;
+}
+
 static int gtp_newlink(struct net *src_net, struct net_device *dev,
                       struct nlattr *tb[], struct nlattr *data[],
                       struct netlink_ext_ack *extack)
 {
+       unsigned int role = GTP_ROLE_GGSN;
        struct gtp_dev *gtp;
        struct gtp_net *gn;
        int hashsize, err;
 
-       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
-               return -EINVAL;
-
        gtp = netdev_priv(dev);
 
        if (!data[IFLA_GTP_PDP_HASHSIZE]) {
@@ -677,11 +739,23 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
                        hashsize = 1024;
        }
 
+       if (data[IFLA_GTP_ROLE]) {
+               role = nla_get_u32(data[IFLA_GTP_ROLE]);
+               if (role > GTP_ROLE_SGSN)
+                       return -EINVAL;
+       }
+       gtp->role = role;
+
+       gtp->net = src_net;
+
        err = gtp_hashtable_new(gtp, hashsize);
        if (err < 0)
                return err;
 
-       err = gtp_encap_enable(gtp, data);
+       if (data[IFLA_GTP_CREATE_SOCKETS])
+               err = gtp_create_sockets(gtp, data);
+       else
+               err = gtp_encap_enable(gtp, data);
        if (err < 0)
                goto out_hashtable;
 
@@ -726,6 +800,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_CREATE_SOCKETS]       = { .type = NLA_U8 },
 };
 
 static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -848,7 +923,9 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
 {
        struct sock *sk1u = NULL;
        struct sock *sk0 = NULL;
-       unsigned int role = GTP_ROLE_GGSN;
+
+       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
+               return -EINVAL;
 
        if (data[IFLA_GTP_FD0]) {
                u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]);
@@ -868,18 +945,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
                }
        }
 
-       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);
-                       return -EINVAL;
-               }
-       }
-
        gtp->sk0 = sk0;
        gtp->sk1u = sk1u;
-       gtp->role = role;
 
        return 0;
 }
index ddca20357e7e89b5f204b3117ff3838735535470..ebd2aa3ef80950f9df54734d19c9f59147860af6 100644 (file)
@@ -887,6 +887,7 @@ enum {
        IFLA_GTP_FD1,
        IFLA_GTP_PDP_HASHSIZE,
        IFLA_GTP_ROLE,
+       IFLA_GTP_CREATE_SOCKETS,
        __IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)