From 5747406dbb4a594cb2eeb8ad19f529877530f268 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 23 Feb 2014 21:55:42 +0100 Subject: [PATCH] gtp: create socket from userspace and pass them as configuration openggsn already sets up the UDP sockets that we need for the control and user planes of GTP. Since we cannot bind two UDP sockets (one from userspace and another from the kernel) to the same port, change the current code to pass the socket descriptors that has been allocated by openggsn (or whatever daemon which uses the GTP kernel infrastructure). Two new attributes are added to set up the tunnel device: IFLA_GTP_FD0 (for GTP0) and IFLA_GTP_FD1 (for GTP1u), which specify the UDP socket created from userspace. Thus, the GTP kernel code only takes care of enabling the kernel UDP encapsulation routine. --- gtp.c | 136 +++++++++++++------------------ gtp_nl.h | 3 +- libgtnl/include/libgtpnl/gtpnl.h | 2 +- libgtnl/include/linux/gtp_nl.h | 3 +- libgtnl/src/gtp-rtnl.c | 5 +- 5 files changed, 64 insertions(+), 85 deletions(-) diff --git a/gtp.c b/gtp.c index f57bbc8..1820725 100644 --- a/gtp.c +++ b/gtp.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -58,14 +60,9 @@ struct pdp_ctx { struct gtp_instance { struct list_head list; - /* address for local UDP socket */ - struct sockaddr_in gtp0_addr; - struct sockaddr_in gtp1u_addr; - /* the socket */ struct socket *sock0; struct socket *sock1u; - struct work_struct sock_work; struct net_device *dev; struct net_device *real_dev; @@ -405,19 +402,16 @@ static int gtp_dev_init(struct net_device *dev) if (!dev->tstats) return -ENOMEM; - /* create the socket outside of rtnl to avoid a possible deadlock */ - queue_work(gtp_wq, >i->sock_work); - return 0; } -static void gtp_destroy_bind_sock(struct gtp_instance *gti); +static void gtp_encap_disable(struct gtp_instance *gti); static void gtp_dev_uninit(struct net_device *dev) { struct gtp_instance *gti = netdev_priv(dev); - gtp_destroy_bind_sock(gti); + gtp_encap_disable(gti); free_percpu(dev->tstats); } @@ -726,36 +720,22 @@ static const struct net_device_ops gtp_netdev_ops = { .ndo_start_xmit = gtp_dev_xmit, }; -static int gtp_create_bind_sock(struct gtp_instance *gti); - -/* Scheduled at device creation to bind to a socket */ -static void gtp_sock_work(struct work_struct *work) -{ - struct gtp_instance *gti = - container_of(work, struct gtp_instance, sock_work); - - gtp_create_bind_sock(gti); -} - static void gtp_link_setup(struct net_device *dev) { - struct gtp_instance *gti = netdev_priv(dev); - dev->netdev_ops = >p_netdev_ops; dev->destructor = free_netdev; - - INIT_WORK(>i->sock_work, gtp_sock_work); } static int gtp_hashtable_new(struct gtp_instance *gti, int hsize); static void gtp_hashtable_free(struct gtp_instance *gti); +static int gtp_encap_enable(struct gtp_instance *gti, int fd_gtp0, int fd_gtp1); static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net_device *real_dev; struct gtp_instance *gti; - int hashsize, err; + int hashsize, err, fd0, fd1; pr_info("gtp_newlink\n"); @@ -776,9 +756,10 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gti = netdev_priv(dev); gti->real_dev = real_dev; - gti->gtp0_addr.sin_addr.s_addr = - gti->gtp1u_addr.sin_addr.s_addr = - nla_get_u32(data[IFLA_GTP_LOCAL_ADDR_IPV4]); + fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + + gtp_encap_enable(gti, fd0, fd1); if (!data[IFLA_GTP_HASHSIZE]) hashsize = 1024; @@ -808,6 +789,7 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head) { struct gtp_instance *gti = netdev_priv(dev); + gtp_encap_disable(gti); gtp_hashtable_free(gti); dev_put(gti->real_dev); list_del_rcu(>i->list); @@ -815,13 +797,14 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head) } static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = { - [IFLA_GTP_LOCAL_ADDR_IPV4] = { .type = NLA_U32 }, + [IFLA_GTP_FD0] = { .type = NLA_U32 }, + [IFLA_GTP_FD1] = { .type = NLA_U32 }, [IFLA_GTP_HASHSIZE] = { .type = NLA_U32 }, }; static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) { - if (!data || !data[IFLA_GTP_LOCAL_ADDR_IPV4]) + if (!data || !data[IFLA_GTP_FD0] || !data[IFLA_GTP_FD1]) return -EINVAL; return 0; @@ -829,17 +812,14 @@ static int gtp_validate(struct nlattr *tb[], struct nlattr *data[]) static size_t gtp_get_size(const struct net_device *dev) { - return nla_total_size(sizeof(__u32)) + /* IFLA_GTP_LOCAL_ADDR_IPV4 */ - nla_total_size(sizeof(__u32)); /* IFLA_GTP_HASHSIZE */ + return nla_total_size(sizeof(__u32)); /* IFLA_GTP_HASHSIZE */ } static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct gtp_instance *gti = netdev_priv(dev); - if (nla_put_u32(skb, IFLA_GTP_LOCAL_ADDR_IPV4, - gti->gtp0_addr.sin_addr.s_addr) || - nla_put_u32(skb, IFLA_GTP_HASHSIZE, gti->hash_size)) + if (nla_put_u32(skb, IFLA_GTP_HASHSIZE, gti->hash_size)) goto nla_put_failure; return 0; @@ -902,23 +882,39 @@ static void gtp_hashtable_free(struct gtp_instance *gti) kfree(gti->tid_hash); } -static int gtp_create_bind_sock(struct gtp_instance *gti) +static int gtp_encap_enable(struct gtp_instance *gti, int fd_gtp0, int fd_gtp1) { - int rc; - struct sockaddr_in sin; + int err; + struct socket *sock0, *sock1u; struct sock *sk; - /* Create and bind the socket for GTP0 */ - rc = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, >i->sock0); - if (rc < 0) - goto out; + sock0 = sockfd_lookup(fd_gtp0, &err); + if (sock0 == NULL) { + pr_err("socket fd=%d not found (gtp0)\n", fd_gtp0); + return -ENOENT; + } - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(GTP0_PORT); - rc = kernel_bind(gti->sock0, (struct sockaddr *) &sin, sizeof(sin)); - if (rc < 0) - goto out; + if (sock0->sk->sk_protocol != IPPROTO_UDP) { + pr_err("socket fd=%d not UDP\n", fd_gtp0); + err = -EINVAL; + goto err1; + } + + sock1u = sockfd_lookup(fd_gtp1, &err); + if (sock1u == NULL) { + pr_err("socket fd=%d not found (gtp1u)\n", fd_gtp1); + err = -ENOENT; + goto err1; + } + + if (sock1u->sk->sk_protocol != IPPROTO_UDP) { + pr_err("socket fd=%d not UDP\n", fd_gtp1); + err = -EINVAL; + goto err2; + } + + gti->sock0 = sock0; + gti->sock1u = sock1u; sk = gti->sock0->sk; udp_sk(sk)->encap_type = UDP_ENCAP_GTP0; @@ -926,47 +922,27 @@ static int gtp_create_bind_sock(struct gtp_instance *gti) sk->sk_user_data = gti; udp_encap_enable(); - /* Create and bind the socket for GTP1 user-plane */ - rc = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, >i->sock1u); - if (rc < 0) - goto out_free0; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(GTP1U_PORT); - rc = kernel_bind(gti->sock1u, (struct sockaddr *) &sin, sizeof(sin)); - if (rc < 0) - goto out_free1; - sk = gti->sock1u->sk; udp_sk(sk)->encap_type = UDP_ENCAP_GTP1U; udp_sk(sk)->encap_rcv = gtp_udp_encap_recv; sk->sk_user_data = gti; - pr_info("socket successfully binded\n"); + pr_info("encapsulation socket enabled\n"); return 0; - -out_free1: - kernel_sock_shutdown(gti->sock1u, SHUT_RDWR); - sk_release_kernel(gti->sock1u->sk); -out_free0: - kernel_sock_shutdown(gti->sock0, SHUT_RDWR); - sk_release_kernel(gti->sock0->sk); -out: - return rc; +err1: + sockfd_put(sock0); +err2: + sockfd_put(sock1u); + return err; } -static void gtp_destroy_bind_sock(struct gtp_instance *gti) +static void gtp_encap_disable(struct gtp_instance *gti) { - if (gti->sock1u) { - kernel_sock_shutdown(gti->sock1u, SHUT_RDWR); - sk_release_kernel(gti->sock1u->sk); - } - if (gti->sock0) { - kernel_sock_shutdown(gti->sock0, SHUT_RDWR); - sk_release_kernel(gti->sock0->sk); - } + if (gti->sock1u) + sockfd_put(gti->sock1u); + if (gti->sock0) + sockfd_put(gti->sock0); } static struct net_device *gtp_find_dev(int ifindex) diff --git a/gtp_nl.h b/gtp_nl.h index 968824c..ff45fba 100644 --- a/gtp_nl.h +++ b/gtp_nl.h @@ -3,7 +3,8 @@ enum { IFLA_GTP_UNSPEC, - IFLA_GTP_LOCAL_ADDR_IPV4, + IFLA_GTP_FD0, + IFLA_GTP_FD1, IFLA_GTP_HASHSIZE, __IFLA_GTP_MAX, }; diff --git a/libgtnl/include/libgtpnl/gtpnl.h b/libgtnl/include/libgtpnl/gtpnl.h index 3c2c624..74fb8cf 100644 --- a/libgtnl/include/libgtpnl/gtpnl.h +++ b/libgtnl/include/libgtpnl/gtpnl.h @@ -14,7 +14,7 @@ int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq, void *data); int genl_lookup_family(struct mnl_socket *nl, const char *family); -int gtp_dev_create(const char *ifname); +int gtp_dev_create(const char *ifname, int fd0, int fd1); int gtp_dev_destroy(const char *ifname); struct gtp_tunnel; diff --git a/libgtnl/include/linux/gtp_nl.h b/libgtnl/include/linux/gtp_nl.h index 968824c..ff45fba 100644 --- a/libgtnl/include/linux/gtp_nl.h +++ b/libgtnl/include/linux/gtp_nl.h @@ -3,7 +3,8 @@ enum { IFLA_GTP_UNSPEC, - IFLA_GTP_LOCAL_ADDR_IPV4, + IFLA_GTP_FD0, + IFLA_GTP_FD1, IFLA_GTP_HASHSIZE, __IFLA_GTP_MAX, }; diff --git a/libgtnl/src/gtp-rtnl.c b/libgtnl/src/gtp-rtnl.c index 48dbdf0..d5af6fc 100644 --- a/libgtnl/src/gtp-rtnl.c +++ b/libgtnl/src/gtp-rtnl.c @@ -72,7 +72,7 @@ err: return -1; } -int gtp_dev_create(const char *ifname) +int gtp_dev_create(const char *ifname, int fd0, int fd1) { char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; @@ -90,7 +90,8 @@ int gtp_dev_create(const char *ifname) nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO); mnl_attr_put_str(nlh, IFLA_INFO_KIND, "gtp"); nest2 = mnl_attr_nest_start(nlh, IFLA_INFO_DATA); - mnl_attr_put_u32(nlh, IFLA_GTP_LOCAL_ADDR_IPV4, 0); + mnl_attr_put_u32(nlh, IFLA_GTP_FD0, fd0); + mnl_attr_put_u32(nlh, IFLA_GTP_FD1, fd1); mnl_attr_put_u32(nlh, IFLA_GTP_HASHSIZE, 131072); mnl_attr_nest_end(nlh, nest2); mnl_attr_nest_end(nlh, nest);