From laforge at gnumonks.org Mon Feb 13 14:23:21 2017 From: laforge at gnumonks.org (Harald Welte) Date: Mon, 13 Feb 2017 15:23:21 +0100 Subject: [PATCH 1/1] gtp: support SGSN-side tunnels In-Reply-To: <1384904207.69904.1486986729381.JavaMail.zimbra@tpip.net> References: <20170203091231.10142-1-jonas@southpole.se> <1107124975.65949.1486977919253.JavaMail.zimbra@tpip.net> <20170213111640.GA1458@salvia> <1384904207.69904.1486986729381.JavaMail.zimbra@tpip.net> Message-ID: <20170213142321.vkas3kdsc43xs35v@nataraja> Hi Andreas, Pablo, Jonas, I think that the SGSN/GGSN role flag (or whatever it may end up being called) logically belongs in the gtp-device at this point, and will in the future belong to the UDP/GTP-socket (with Andreas' proposed changes). Having it per-pdp-context indeed seems odd and just provide a way to create broken configurations (and increase the memory use per pdp context, of which you have many more than netdevs or gtp-sockets). -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From aschultz at tpip.net Mon Feb 13 15:36:21 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:21 +0100 Subject: [PATCH net-next v3 5/8] gtp: consolidate gtp socket rx path In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-6-aschultz@tpip.net> Add network device to gtp context in preparation for splitting the TEID from the network device. Use this to rework the socker rx path. Move the common RX part of v0 and v1 into a helper. Also move the final rx part into that helper as well. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index f340e20..e21b663 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,8 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct net_device *dev; + atomic_t tx_seq; struct rcu_head rcu_head; }; @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, + bool xnet) +{ + struct pcpu_sw_netstats *stats; + + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + pr_debug("No PDP ctx for this MS\n"); + return 1; + } + + /* Get rid of the GTP + UDP headers. */ + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + return -1; + + pr_debug("forwarding packet from GGSN to uplink\n"); + + /* Now that the UDP and the GTP header have been removed, set up the + * new network header. This is required by the upper layer to + * calculate the transport header. + */ + skb_reset_network_header(skb); + + skb->dev = pctx->dev; + + stats = this_cpu_ptr(pctx->dev->tstats); + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + netif_rx(skb); + return 0; +} + /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, bool xnet) @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static void gtp_encap_destroy(struct sock *sk) @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) */ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { - struct pcpu_sw_netstats *stats; struct gtp_dev *gtp; + int ret = 0; bool xnet; - int ret; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) switch (ret) { case 1: netdev_dbg(gtp->dev, "pass up to the process\n"); - return 1; + break; case 0: - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); break; case -1: netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); kfree_skb(skb); - return 0; + ret = 0; + break; } - /* Now that the UDP and the GTP header have been removed, set up the - * new network header. This is required by the upper layer to - * calculate the transport header. - */ - skb_reset_network_header(skb); - - skb->dev = gtp->dev; - - stats = this_cpu_ptr(gtp->dev->tstats); - u64_stats_update_begin(&stats->syncp); - stats->rx_packets++; - stats->rx_bytes += skb->len; - u64_stats_update_end(&stats->syncp); - - netif_rx(skb); - - return 0; + return ret; } static int gtp_dev_init(struct net_device *dev) @@ -953,6 +960,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:16 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:16 +0100 Subject: [PATCH net-next v3 0/8] gtp: misc improvements Message-ID: <20170213153624.14170-1-aschultz@tpip.net> Hi Pablo, This is v3 of the GTP improvements. This series lays the groundwork for removing the socket references from the GTP netdevice by removing duplicate code and simplifying the logic on some code paths. It slighly changes the GTP genl API by making the socket parameters optional (though one of them is still required). The removal of the socket references will break the 1:1 releation between GTP netdevice and GTP socket that prevents us to support multiple VRFs with overlaping IP addresse spaces attached to the same GTP-U entity (needed for multi APN support). Pablo found a socket hold problem in v2. In order to solve that I had to switch the socket references from the struct socket to the internal struct sock. This should have no functionl impact, but we can now hang on to the reference without blocking user space from closing the GTP socket. There was also some length off-list conversation about why the netdevice to socket relation needs to be changed at all. Harald did already agree to this but Pablo had some questions. I've tried to address that with a bit of dokumentation for the GTP module. Hopefully this will explain the need for the change sufficiently. This series does have conflicts with the SGSN side tunnel work done by Jonas Bonn. The unification of the tunnel Rx code touches the same places he changed. v2->v3: * add documentation to explain the goal of all these changes * incorporate review comments * switch from struct socket to struct sock Regards Andreas -- Andreas Schultz (8): gtp: add documentation gtp: switch from struct socket to struct sock for the GTP sockets gtp: make GTP sockets in gtp_newlink optional gtp: merge gtp_get_net and gtp_genl_find_dev gtp: consolidate gtp socket rx path gtp: unify genl_find_pdp and prepare for per socket lookup gtp: consolidate pdp context destruction into helper gtp: add socket to pdp context Documentation/networking/gtp.txt | 112 ++++++++ drivers/net/gtp.c | 548 +++++++++++++++++++-------------------- 2 files changed, 386 insertions(+), 274 deletions(-) create mode 100644 Documentation/networking/gtp.txt -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:19 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:19 +0100 Subject: [PATCH net-next v3 3/8] gtp: make GTP sockets in gtp_newlink optional In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-4-aschultz@tpip.net> Having both GTPv0-U and GTPv1-U is not always desirable. Fallback from GTPv1-U to GTPv0-U was depreciated from 3GPP Rel-8 onwards. Post Rel-8 implementation are discuraged from listening on the v0 port (see 3GPP TS 29.281, Sect. 1). A future change will completely decouple the sockets from the network device. Till then, at least one of the sockets needs to be specified (either v0 or v1), the other is optional. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 149 +++++++++++++++++++++++++++++------------------------- 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a8ce8c7..fe96d21 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -259,30 +259,30 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); } -static void gtp_encap_disable(struct gtp_dev *gtp) -{ - if (gtp->sk0) { - udp_sk(gtp->sk0)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk0, NULL); - sock_put(gtp->sk0); - } - if (gtp->sk1u) { - udp_sk(gtp->sk1u)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk1u, NULL); - sock_put(gtp->sk1u); - } - - gtp->sk0 = NULL; - gtp->sk1u = NULL; -} - static void gtp_encap_destroy(struct sock *sk) { struct gtp_dev *gtp; gtp = rcu_dereference_sk_user_data(sk); - if (gtp) - gtp_encap_disable(gtp); + if (gtp) { + udp_sk(sk)->encap_type = 0; + rcu_assign_sk_user_data(sk, NULL); + sock_put(sk); + } +} + +static void gtp_encap_disable_sock(struct sock *sk) +{ + if (!sk) + return; + + gtp_encap_destroy(sk); +} + +static void gtp_encap_disable(struct gtp_dev *gtp) +{ + gtp_encap_disable_sock(gtp->sk0); + gtp_encap_disable_sock(gtp->sk1u); } /* UDP encapsulation receive handler. See net/ipv4/udp.c. @@ -642,27 +642,24 @@ static void gtp_link_setup(struct net_device *dev) static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); static void gtp_hashtable_free(struct gtp_dev *gtp); -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1); +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); +static void gtp_encap_disable(struct gtp_dev *gtp); static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - int hashsize, err, fd0, fd1; struct gtp_dev *gtp; 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]) return -EINVAL; gtp = netdev_priv(dev); - fd0 = nla_get_u32(data[IFLA_GTP_FD0]); - fd1 = nla_get_u32(data[IFLA_GTP_FD1]); - - err = gtp_encap_enable(dev, gtp, fd0, fd1); + err = gtp_encap_enable(gtp, data); if (err < 0) - goto out_err; + return err; if (!data[IFLA_GTP_PDP_HASHSIZE]) hashsize = 1024; @@ -690,7 +687,6 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gtp_hashtable_free(gtp); out_encap: gtp_encap_disable(gtp); -out_err: return err; } @@ -805,63 +801,76 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) kfree(gtp->tid_hash); } -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1) +static struct sock *gtp_encap_enable_socket(int fd, int type, + struct gtp_dev *gtp) { struct udp_tunnel_sock_cfg tuncfg = {NULL}; - struct socket *sock0, *sock1u; + struct socket *sock; + struct sock *sk; int err; - netdev_dbg(dev, "enable gtp on %d, %d\n", fd_gtp0, fd_gtp1); + pr_debug("enable gtp on %d, %d\n", fd, type); - sock0 = sockfd_lookup(fd_gtp0, &err); - if (sock0 == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp0)\n", fd_gtp0); - return -ENOENT; + sock = sockfd_lookup(fd, &err); + if (!sock) { + pr_debug("gtp socket fd=%d not found\n", fd); + return NULL; } - if (sock0->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp0); - err = -EINVAL; - goto err1; + if (sock->sk->sk_protocol != IPPROTO_UDP) { + pr_debug("socket fd=%d not UDP\n", fd); + sk = ERR_PTR(-EINVAL); + goto out_sock; } - sock1u = sockfd_lookup(fd_gtp1, &err); - if (sock1u == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp1u)\n", fd_gtp1); - err = -ENOENT; - goto err1; + if (rcu_dereference_sk_user_data(sock->sk)) { + sk = ERR_PTR(-EBUSY); + goto out_sock; } - if (sock1u->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp1); - err = -EINVAL; - goto err2; - } - - netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - - sock_hold(sock0->sk); - gtp->sk0 = sock0->sk; - sock_hold(sock1u->sk); - gtp->sk1u = sock1u->sk; + sk = sock->sk; + sock_hold(sk); tuncfg.sk_user_data = gtp; + tuncfg.encap_type = type; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; - tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); - - tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); - - err = 0; -err2: - sockfd_put(sock1u); -err1: - sockfd_put(sock0); - return err; + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); + +out_sock: + sockfd_put(sock); + return sk; +} + +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) +{ + struct sock *sk1u = NULL; + struct sock *sk0 = NULL; + + if (data[IFLA_GTP_FD0]) { + u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + + sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); + if (IS_ERR(sk0)) + return PTR_ERR(sk0); + } + + if (data[IFLA_GTP_FD1]) { + u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + + sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); + if (IS_ERR(sk1u)) { + if (sk0) + gtp_encap_disable_sock(sk0); + return PTR_ERR(sk1u); + } + } + + gtp->sk0 = sk0; + gtp->sk1u = sk1u; + + return 0; } static struct net_device *gtp_find_dev(struct net *net, int ifindex) -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:23 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:23 +0100 Subject: [PATCH net-next v3 7/8] gtp: consolidate pdp context destruction into helper In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-8-aschultz@tpip.net> Consolidate duplicate code into helper. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index d3c384e..62f598b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -86,6 +86,8 @@ struct gtp_net { static u32 gtp_h_initval; +static void pdp_context_delete(struct pdp_ctx *pctx); + static inline u32 gtp0_hashfn(u64 tid) { u32 *tid32 = (u32 *) &tid; @@ -781,13 +783,10 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) struct pdp_ctx *pctx; int i; - for (i = 0; i < gtp->hash_size; i++) { - hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); - } - } + for (i = 0; i < gtp->hash_size; i++) + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) + pdp_context_delete(pctx); + synchronize_rcu(); kfree(gtp->addr_hash); kfree(gtp->tid_hash); @@ -997,6 +996,13 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_delete(struct pdp_ctx *pctx) +{ + hlist_del_rcu(&pctx->hlist_tid); + hlist_del_rcu(&pctx->hlist_addr); + kfree_rcu(pctx, rcu_head); +} + static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { struct gtp_dev *gtp; @@ -1105,9 +1111,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + pdp_context_delete(pctx); out_unlock: rcu_read_unlock(); -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:18 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:18 +0100 Subject: [PATCH net-next v3 2/8] gtp: switch from struct socket to struct sock for the GTP sockets In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-3-aschultz@tpip.net> After enabling the UDP encapsulation, only the sk member is used. Holding the socket would prevent user space from closing the socket, but holding a reference to the sk member does not have the same effect. This change will make it simpler to later detach the sockets from the netdevice. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index bda0c64..a8ce8c7 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -66,8 +66,8 @@ struct pdp_ctx { struct gtp_dev { struct list_head list; - struct socket *sock0; - struct socket *sock1u; + struct sock *sk0; + struct sock *sk1u; struct net_device *dev; @@ -261,17 +261,19 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, static void gtp_encap_disable(struct gtp_dev *gtp) { - if (gtp->sock0 && gtp->sock0->sk) { - udp_sk(gtp->sock0->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock0->sk, NULL); + if (gtp->sk0) { + udp_sk(gtp->sk0)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk0, NULL); + sock_put(gtp->sk0); } - if (gtp->sock1u && gtp->sock1u->sk) { - udp_sk(gtp->sock1u->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock1u->sk, NULL); + if (gtp->sk1u) { + udp_sk(gtp->sk1u)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk1u, NULL); + sock_put(gtp->sk1u); } - gtp->sock0 = NULL; - gtp->sock1u = NULL; + gtp->sk0 = NULL; + gtp->sk1u = NULL; } static void gtp_encap_destroy(struct sock *sk) @@ -484,14 +486,14 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, switch (pctx->gtp_version) { case GTP_V0: - if (gtp->sock0) - sk = gtp->sock0->sk; + if (gtp->sk0) + sk = gtp->sk0; else sk = NULL; break; case GTP_V1: - if (gtp->sock1u) - sk = gtp->sock1u->sk; + if (gtp->sk1u) + sk = gtp->sk1u; else sk = NULL; break; @@ -504,7 +506,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, return -ENOENT; } - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk, + rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", @@ -839,18 +841,20 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - gtp->sock0 = sock0; - gtp->sock1u = sock1u; + sock_hold(sock0->sk); + gtp->sk0 = sock0->sk; + sock_hold(sock1u->sk); + gtp->sk1u = sock1u->sk; tuncfg.sk_user_data = gtp; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sock0->sk), gtp->sock0, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sock1u->sk), gtp->sock1u, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); err = 0; err2: -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:20 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:20 +0100 Subject: [PATCH net-next v3 4/8] gtp: merge gtp_get_net and gtp_genl_find_dev In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-5-aschultz@tpip.net> Both function are always used together with the final goal to get the gtp_dev. This simplifies the code by merging them together. The netdevice lookup is changed to use the regular dev_get_by_index. The gtp netdevice list is now only used to find the PDP contexts for imcomming packets. It can be completely eliminated Once the TEID hash is moved into the GTP socket. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 147 ++++++++++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 77 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index fe96d21..f340e20 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -745,21 +745,6 @@ static struct rtnl_link_ops gtp_link_ops __read_mostly = { .fill_info = gtp_fill_info, }; -static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[]) -{ - struct net *net; - - /* Examine the link attributes and figure out which network namespace - * we are talking about. - */ - if (tb[GTPA_NET_NS_FD]) - net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD])); - else - net = get_net(src_net); - - return net; -} - static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) { int i; @@ -873,16 +858,31 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) return 0; } -static struct net_device *gtp_find_dev(struct net *net, int ifindex) +static struct gtp_dev *gtp_genl_find_dev(struct net *src_net, + struct nlattr *tb[]) { - struct gtp_net *gn = net_generic(net, gtp_net_id); - struct gtp_dev *gtp; - - list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { - if (ifindex == gtp->dev->ifindex) - return gtp->dev; - } - return NULL; + struct gtp_dev *gtp = NULL; + struct net_device *dev; + struct net *net; + + /* Examine the link attributes and figure out which network namespace + * we are talking about. + */ + if (tb[GTPA_NET_NS_FD]) + net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD])); + else + net = get_net(src_net); + + if (IS_ERR(net)) + return NULL; + + /* Check if there's an existing gtpX device to configure */ + dev = dev_get_by_index_rcu(net, nla_get_u32(tb[GTPA_LINK])); + if (dev->netdev_ops == >p_netdev_ops) + gtp = netdev_priv(dev); + + put_net(net); + return gtp; } static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) @@ -912,9 +912,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) { - struct gtp_dev *gtp = netdev_priv(dev); + struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; struct pdp_ctx *pctx; bool found = false; @@ -991,8 +991,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; - struct net *net; + struct gtp_dev *gtp; + int err; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || @@ -1016,77 +1016,79 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - return ipv4_pdp_add(dev, info); + err = ipv4_pdp_add(gtp, info); + +out_unlock: + rcu_read_unlock(); + return err; } static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; struct pdp_ctx *pctx; struct gtp_dev *gtp; - struct net *net; + int err = 0; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK]) return -EINVAL; - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - - gtp = netdev_priv(dev); switch (nla_get_u32(info->attrs[GTPA_VERSION])) { case GTP_V0: - if (!info->attrs[GTPA_TID]) - return -EINVAL; + if (!info->attrs[GTPA_TID]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); break; case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) - return -EINVAL; + if (!info->attrs[GTPA_I_TEI]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); break; default: - return -EINVAL; + err = -EINVAL; + goto out_unlock; } - if (pctx == NULL) - return -ENOENT; + if (!pctx) { + err = -ENOENT; + goto out_unlock; + } if (pctx->gtp_version == GTP_V0) - netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); kfree_rcu(pctx, rcu_head); - return 0; +out_unlock: + rcu_read_unlock(); + return err; } static struct genl_family gtp_genl_family; @@ -1130,11 +1132,9 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; - struct net_device *dev; struct sk_buff *skb2; struct gtp_dev *gtp; u32 gtp_version; - struct net *net; int err; if (!info->attrs[GTPA_VERSION] || @@ -1150,21 +1150,14 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); - - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; - } - put_net(net); - - gtp = netdev_priv(dev); - rcu_read_lock(); + + gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto err_unlock; + } + if (gtp_version == GTP_V0 && info->attrs[GTPA_TID]) { u64 tid = nla_get_u64(info->attrs[GTPA_TID]); -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:22 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:22 +0100 Subject: [PATCH net-next v3 6/8] gtp: unify genl_find_pdp and prepare for per socket lookup In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-7-aschultz@tpip.net> This unifies duplicate code into a helper. It also prepares the groundwork to add a lookup version that uses the socket to find attache pdp contexts. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 124 +++++++++++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 71 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index e21b663..d3c384e 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1039,55 +1039,70 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return err; } +static struct pdp_ctx *gtp_genl_find_pdp_by_link(struct sk_buff *skb, + struct genl_info *info) +{ + struct gtp_dev *gtp; + + gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) + return ERR_PTR(-ENODEV); + + if (info->attrs[GTPA_MS_ADDRESS]) { + __be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); + + return ipv4_pdp_find(gtp, ip); + } else if (info->attrs[GTPA_VERSION]) { + u32 gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); + + if (gtp_version == GTP_V0 && info->attrs[GTPA_TID]) + return gtp0_pdp_find(gtp, nla_get_u64( + info->attrs[GTPA_TID])); + else if (gtp_version == GTP_V1 && info->attrs[GTPA_I_TEI]) + return gtp1_pdp_find(gtp, nla_get_u32( + info->attrs[GTPA_I_TEI])); + } + + return ERR_PTR(-EINVAL); +} + +static struct pdp_ctx *gtp_genl_find_pdp(struct sk_buff *skb, + struct genl_info *info) +{ + struct pdp_ctx *pctx; + + if (info->attrs[GTPA_LINK]) + pctx = gtp_genl_find_pdp_by_link(skb, info); + else + pctx = ERR_PTR(-EINVAL); + + if (!pctx) + pctx = ERR_PTR(-ENOENT); + + return pctx; +} + static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx; - struct gtp_dev *gtp; int err = 0; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) + if (!info->attrs[GTPA_VERSION]) return -EINVAL; rcu_read_lock(); - gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto out_unlock; - } - - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { - case GTP_V0: - if (!info->attrs[GTPA_TID]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); - break; - case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); - break; - - default: - err = -EINVAL; - goto out_unlock; - } - - if (!pctx) { - err = -ENOENT; + pctx = gtp_genl_find_pdp(skb, info); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto out_unlock; } if (pctx->gtp_version == GTP_V0) - netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); @@ -1141,49 +1156,16 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; struct sk_buff *skb2; - struct gtp_dev *gtp; - u32 gtp_version; int err; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) + if (!info->attrs[GTPA_VERSION]) return -EINVAL; - gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - switch (gtp_version) { - case GTP_V0: - case GTP_V1: - break; - default: - return -EINVAL; - } - rcu_read_lock(); - gtp = gtp_genl_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto err_unlock; - } - - if (gtp_version == GTP_V0 && - info->attrs[GTPA_TID]) { - u64 tid = nla_get_u64(info->attrs[GTPA_TID]); - - pctx = gtp0_pdp_find(gtp, tid); - } else if (gtp_version == GTP_V1 && - info->attrs[GTPA_I_TEI]) { - u32 tid = nla_get_u32(info->attrs[GTPA_I_TEI]); - - pctx = gtp1_pdp_find(gtp, tid); - } else if (info->attrs[GTPA_MS_ADDRESS]) { - __be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); - - pctx = ipv4_pdp_find(gtp, ip); - } - - if (pctx == NULL) { - err = -ENOENT; + pctx = gtp_genl_find_pdp(skb, info); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto err_unlock; } -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:17 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:17 +0100 Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-2-aschultz@tpip.net> The GTP-U implementation in not only driven by 3GPP TS 29.281, but also by the requirements of the 3GPP network entities that use it. This document tries to explain and clarify some design decision and their origins. Signed-off-by: Andreas Schultz --- Documentation/networking/gtp.txt | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Documentation/networking/gtp.txt diff --git a/Documentation/networking/gtp.txt b/Documentation/networking/gtp.txt new file mode 100644 index 0000000..cefd983 --- /dev/null +++ b/Documentation/networking/gtp.txt @@ -0,0 +1,112 @@ +Note: this document contain some forward looking statements and does + not (yet) reflect the implementation. This is done to motivate + and explain some of the changes to come. + + This note will be removed once the implementation matches this + document. + +General Packet Radio System (GPRS) Tunnelling Protocol User Plane (GTP-U) +========================================================================= + +The GTP-U protocol is a tunnelling protocol used in public land mobile networks +[PLMN]. It is always use together with a user space control instance implementing +a 3GPP network entity (e.g. GGSN, SGSN, PDN-GW, S-GW). + +The protocol is specified for version 0 in [GSM 09.60] and for version 1 in +[3GPP TS 29.281]. However, the functionality defined in those documents has +always to be taken in relation of the functional 3GPP entity that is using it. + +The rest of document is focusing on version 1 of the protocol. GTPv1 to GTPv0 +interworking and the use of GTPv0-U in general has be depreciated from 3GPP Rel8 +onward [3GPP TS 29.060]. + +Local GTP-U entity and tunnel identification +-------------------------------------------- + +GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152 for +GTPv1-U and 3386 for GTPv0-U. + +There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW instance) +per IP address. Tunnel Endpoint Identifier (TEID) are unique per GTP-U entity. + +A specific tunnel is only defined by the destination entity. Since the +destination port is constant, only the destination IP and TEID define +a tunnel. The source IP and Port have no meaning for the tunnel. + +Therefore: + + * when sending, the remote entity is defined by the remote IP and the tunnel + endpoint id. The source IP and port have no meaning and can be changed + at any time. + + * when receiving the local entity is defined by the local destination IP + and the tunnel endpoint id. The source IP and port have no meaning and can + change at any time. + +[3GPP TS 29.281] Section 4.3.0 defines this so: + +> The TEID in the GTP-U header is used to de-multiplex traffic incoming from +> remote tunnel endpoints so that it is delivered to the User plane entities +> in a way that allows multiplexing of different users, different packet +> protocols and different QoS levels. Therefore no two remote GTP-U endpoints +> shall send traffic to a GTP-U protocol entity using the same TEID value except +> for data forwarding as part of mobility procedures. + +The definition above only defines that two remote GTP-U endpoints *should not* +send to the same TEID, it *does not* forbid or exclude such a scenario. In +fact, the mentioned mobility procedures make it necessary that the GTP-U entity +accepts traffic for TEID's from multiple or unknown peers. + +Therefor the receiving side only identifies tunnels based on TEID's, not based +on the source IP. + +APN vs. Network Device +---------------------- + +The GTP-U driver creates a Linux network device for each Gi/SGi interface. + +[3GPP TS 29.281] calls the Gi/SGi reference point an interface. This may lead +to the impression that the GGSN/P-GW can have only one such interface. + +Correct is that the Gi/SGi reference point defines the interworking between +the 3GPP packet domain (PDN) based on GTP-U tunnel and IP based networks. + +There is no provision in any of the 3GPP documents that limits the number of +Gi/SGi interfaces implemented by a GGSN/P-GW. + +[3GPP TS 29.061] Section 11.3 makes it clear that the selection of a specific +Gi/SGi interfaces is made through the Access Point Name (APN): + +> 2. each private network manages its own addressing. In general this will +> result in different private networks having overlapping address ranges. +> A logically separate connection (e.g. an IP in IP tunnel or layer 2 +> virtual circuit) is used between the GGSN/P-GW and each private network. +> In this case the IP address alone is not necessarily unique. The pair +> of values, Access Point Name (APN) and IPv4 address and/or IPv6 +> prefixes, is unique. + +In order to support the overlapping address range use case, each APN is mapped +to a separate Gi/SGi interface (network device). + +NOTE: The Access Point Name is purely a control plane (GTP-C) concept. At the + GTP-U level, only Tunnel Endpoint Identifiers are present in GTP-U packets + and network devices are known. Therefore for a given UE the mapping in + IP to PDN network is: + + * network device + MS IP -> Peer IP + Peer TEID, + + and from PDN to IP network: + + * local GTP-U IP + TEID -> network device + + Furthermore, before a received T-PDU is injected into the network device + the MS IP is checked against the IP recorded in PDP context. + +References: +----------- + +[PLMN] https://en.wikipedia.org/wiki/Public_land_mobile_network +[3GPP TS 29.060] http://www.etsi.org/deliver/etsi_ts/129000_129099/129060/13.06.00_60/ts_129060v130600p.pdf +[3GPP TS 29.061] http://www.etsi.org/deliver/etsi_ts/129000_129099/129061/13.06.00_60/ts_129061v130600p.pdf +[3GPP TS 29.281] http://www.etsi.org/deliver/etsi_ts/129200_129299/129281/13.02.00_60/ts_129281v130200p.pdf +[GSM 09.60] http://www.etsi.org/deliver/etsi_ts/101300_101399/101347/06.13.00_60/ts_101347v061300p.pdf -- 2.10.2 From aschultz at tpip.net Mon Feb 13 15:36:24 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 13 Feb 2017 16:36:24 +0100 Subject: [PATCH net-next v3 8/8] gtp: add socket to pdp context In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <20170213153624.14170-9-aschultz@tpip.net> Having the socket present in context simplifies the sending logic. It also fixes the invalid assumption that we have to use the same sending socket for all client IP's on a specific gtp interface. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 94 +++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 62f598b..45d6f7e 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,7 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct sock *sk; struct net_device *dev; atomic_t tx_seq; @@ -179,8 +180,7 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } -static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, - bool xnet) +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen) { struct pcpu_sw_netstats *stats; @@ -190,7 +190,8 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* Get rid of the GTP + UDP headers. */ - if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, + !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) return -1; pr_debug("forwarding packet from GGSN to uplink\n"); @@ -214,8 +215,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ -static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp0_header); @@ -239,11 +239,10 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } -static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp1_header); @@ -282,7 +281,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } static void gtp_encap_destroy(struct sock *sk) @@ -318,7 +317,6 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct gtp_dev *gtp; int ret = 0; - bool xnet; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -326,16 +324,14 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); - xnet = !net_eq(sock_net(sk), dev_net(gtp->dev)); - switch (udp_sk(sk)->encap_type) { case UDP_ENCAP_GTP0: netdev_dbg(gtp->dev, "received GTP0 packet\n"); - ret = gtp0_udp_encap_recv(gtp, skb, xnet); + ret = gtp0_udp_encap_recv(gtp, skb); break; case UDP_ENCAP_GTP1U: netdev_dbg(gtp->dev, "received GTP1U packet\n"); - ret = gtp1u_udp_encap_recv(gtp, skb, xnet); + ret = gtp1u_udp_encap_recv(gtp, skb); break; default: ret = -1; /* Shouldn't happen. */ @@ -378,8 +374,9 @@ static void gtp_dev_uninit(struct net_device *dev) free_percpu(dev->tstats); } -static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, - const struct sock *sk, __be32 daddr) +static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = sk->sk_bound_dev_if; @@ -388,7 +385,7 @@ static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, fl4->flowi4_tos = RT_CONN_FLAGS(sk); fl4->flowi4_proto = sk->sk_protocol; - return ip_route_output_key(net, fl4); + return ip_route_output_key(sock_net(sk), fl4); } static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) @@ -477,7 +474,6 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; struct flowi4 fl4; struct iphdr *iph; - struct sock *sk; __be16 df; int mtu; @@ -493,30 +489,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, } netdev_dbg(dev, "found PDP context %p\n", pctx); - switch (pctx->gtp_version) { - case GTP_V0: - if (gtp->sk0) - sk = gtp->sk0; - else - sk = NULL; - break; - case GTP_V1: - if (gtp->sk1u) - sk = gtp->sk1u; - else - sk = NULL; - break; - default: - return -ENOENT; - } - - if (!sk) { - netdev_dbg(dev, "no userspace socket is available, skip\n"); - return -ENOENT; - } - - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, - pctx->sgsn_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", &pctx->sgsn_addr_ip4.s_addr); @@ -561,7 +534,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, goto err_rt; } - gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev); + gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev); gtp_push_header(skb, pktinfo); return 0; @@ -918,7 +891,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, + struct genl_info *info) { struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; @@ -959,6 +933,8 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + sock_hold(sk); + pctx->sk = sk; pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); @@ -996,16 +972,26 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_free(struct rcu_head *head) +{ + struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head); + + sock_put(pctx->sk); + kfree(pctx); +} + static void pdp_context_delete(struct pdp_ctx *pctx) { hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + call_rcu(&pctx->rcu_head, pdp_context_free); } static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { + unsigned int version; struct gtp_dev *gtp; + struct sock *sk; int err; if (!info->attrs[GTPA_VERSION] || @@ -1014,7 +1000,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) !info->attrs[GTPA_MS_ADDRESS]) return -EINVAL; - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + version = nla_get_u32(info->attrs[GTPA_VERSION]); + + switch (version) { case GTP_V0: if (!info->attrs[GTPA_TID] || !info->attrs[GTPA_FLOW]) @@ -1038,7 +1026,19 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } - err = ipv4_pdp_add(gtp, info); + if (version == GTP_V0) + sk = gtp->sk0; + else if (version == GTP_V1) + sk = gtp->sk1u; + else + sk = NULL; + + if (!sk) { + err = -ENODEV; + goto out_unlock; + } + + err = ipv4_pdp_add(gtp, sk, info); out_unlock: rcu_read_unlock(); -- 2.10.2 From davem at davemloft.net Tue Feb 14 17:48:15 2017 From: davem at davemloft.net (David Miller) Date: Tue, 14 Feb 2017 12:48:15 -0500 (EST) Subject: [PATCH net-next v3 2/8] gtp: switch from struct socket to struct sock for the GTP sockets In-Reply-To: <20170213153624.14170-3-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-3-aschultz@tpip.net> Message-ID: <20170214.124815.217549517821684819.davem@davemloft.net> From: Andreas Schultz Date: Mon, 13 Feb 2017 16:36:18 +0100 > + if (gtp->sk0) { > + udp_sk(gtp->sk0)->encap_type = 0; > + rcu_assign_sk_user_data(gtp->sk0, NULL); > + sock_put(gtp->sk0); > } This does "sock_put(NULL);" because you are assigning gtp->sk0 to NULL before the sock_put() call. So you are leaking the socket, at best. You need to load the socket pointer into a local variable in order to do this correctly. From aschultz at tpip.net Wed Feb 15 07:04:56 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Wed, 15 Feb 2017 08:04:56 +0100 (CET) Subject: [PATCH net-next v3 2/8] gtp: switch from struct socket to struct sock for the GTP sockets In-Reply-To: <20170214.124815.217549517821684819.davem@davemloft.net> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-3-aschultz@tpip.net> <20170214.124815.217549517821684819.davem@davemloft.net> Message-ID: <1372457526.113429.1487142296624.JavaMail.zimbra@tpip.net> ----- On Feb 14, 2017, at 6:48 PM, David S. Miller davem at davemloft.net wrote: > From: Andreas Schultz > Date: Mon, 13 Feb 2017 16:36:18 +0100 > >> + if (gtp->sk0) { >> + udp_sk(gtp->sk0)->encap_type = 0; >> + rcu_assign_sk_user_data(gtp->sk0, NULL); >> + sock_put(gtp->sk0); >> } > > This does "sock_put(NULL);" because you are assigning gtp->sk0 to > NULL before the sock_put() call. So you are leaking the socket, > at best. I don't understand how this should happen. If I where to use rcu_assign_pointer, then yes, but rcu_assign_sk_user_data does assign to the sk_user_data member of struct sock and not to the argument itself. Andreas From davem at davemloft.net Wed Feb 15 16:07:26 2017 From: davem at davemloft.net (David Miller) Date: Wed, 15 Feb 2017 11:07:26 -0500 (EST) Subject: [PATCH net-next v3 2/8] gtp: switch from struct socket to struct sock for the GTP sockets In-Reply-To: <1372457526.113429.1487142296624.JavaMail.zimbra@tpip.net> References: <20170213153624.14170-3-aschultz@tpip.net> <20170214.124815.217549517821684819.davem@davemloft.net> <1372457526.113429.1487142296624.JavaMail.zimbra@tpip.net> Message-ID: <20170215.110726.1948359807046911436.davem@davemloft.net> From: Andreas Schultz Date: Wed, 15 Feb 2017 08:04:56 +0100 (CET) > ----- On Feb 14, 2017, at 6:48 PM, David S. Miller davem at davemloft.net wrote: > >> From: Andreas Schultz >> Date: Mon, 13 Feb 2017 16:36:18 +0100 >> >>> + if (gtp->sk0) { >>> + udp_sk(gtp->sk0)->encap_type = 0; >>> + rcu_assign_sk_user_data(gtp->sk0, NULL); >>> + sock_put(gtp->sk0); >>> } >> >> This does "sock_put(NULL);" because you are assigning gtp->sk0 to >> NULL before the sock_put() call. So you are leaking the socket, >> at best. > > I don't understand how this should happen. If I where to use rcu_assign_pointer, > then yes, but rcu_assign_sk_user_data does assign to the sk_user_data member > of struct sock and not to the argument itself. You are right, I misread the assignment. From aschultz at tpip.net Thu Feb 16 21:38:00 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Thu, 16 Feb 2017 22:38:00 +0100 (CET) Subject: [PATCH net-next v3 0/8] gtp: misc improvements In-Reply-To: <20170213153624.14170-1-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> Message-ID: <2065178262.135524.1487281080405.JavaMail.zimbra@tpip.net> Hi Pablo, Harald, Do you have some time to look at the patches? Andreas ----- On Feb 13, 2017, at 4:36 PM, Andreas Schultz aschultz at tpip.net wrote: > Hi Pablo, > > This is v3 of the GTP improvements. > > This series lays the groundwork for removing the socket references from > the GTP netdevice by removing duplicate code and simplifying the logic on > some code paths. > > It slighly changes the GTP genl API by making the socket parameters optional > (though one of them is still required). > > The removal of the socket references will break the 1:1 releation between > GTP netdevice and GTP socket that prevents us to support multiple VRFs with > overlaping IP addresse spaces attached to the same GTP-U entity (needed for > multi APN support). > > Pablo found a socket hold problem in v2. In order to solve that I had to > switch the socket references from the struct socket to the internal > struct sock. This should have no functionl impact, but we can now hang > on to the reference without blocking user space from closing the GTP socket. > > There was also some length off-list conversation about why the netdevice to > socket relation needs to be changed at all. Harald did already agree to this > but Pablo had some questions. I've tried to address that with a bit of > dokumentation for the GTP module. Hopefully this will explain the need for > the change sufficiently. > > This series does have conflicts with the SGSN side tunnel work done by > Jonas Bonn. The unification of the tunnel Rx code touches the same places > he changed. > > v2->v3: > * add documentation to explain the goal of all these changes > * incorporate review comments > * switch from struct socket to struct sock > > Regards > Andreas > > -- > Andreas Schultz (8): > gtp: add documentation > gtp: switch from struct socket to struct sock for the GTP sockets > gtp: make GTP sockets in gtp_newlink optional > gtp: merge gtp_get_net and gtp_genl_find_dev > gtp: consolidate gtp socket rx path > gtp: unify genl_find_pdp and prepare for per socket lookup > gtp: consolidate pdp context destruction into helper > gtp: add socket to pdp context > > Documentation/networking/gtp.txt | 112 ++++++++ > drivers/net/gtp.c | 548 +++++++++++++++++++-------------------- > 2 files changed, 386 insertions(+), 274 deletions(-) > create mode 100644 Documentation/networking/gtp.txt > > -- > 2.10.2 From laforge at gnumonks.org Thu Feb 16 22:08:01 2017 From: laforge at gnumonks.org (Harald Welte) Date: Thu, 16 Feb 2017 23:08:01 +0100 Subject: RFC: unit tests for kernel GTP module Message-ID: <20170216220801.o3fajacj4sx3iere@nataraja> Dear GTP-interested folks, I would love to somehow get towards some degree of unit testing (or even "continuous integration") for teh kernel GTP code. We currently have the original code in the kernel, we had some recent small fixes and now are getting more patches into place. With relatively few active users out there (and probably none of them in production yet), it's particularly easy to introduce regressions while working on the code. Also, having tested new code even against a test set with limited covrage could help to get more confidence in new patches and thus get them merged sooner. Using tools like sgsnemu of OpenGGSN and the command line tools included in libgtpnl, it should be possibel to cook up some scripts for testing. Even the most basic set of tests would be an improvement over what we have now. One could also think about pcap replay to test with hand-crafted or real-world packets from other GTP implementations. As much as I'd like to put something like this into place myself, I don't think I will be able to work much on this in the near future. The GTP module at this point is a pure hobby and contrary to some years ago while I started it, I don't have any contract work in the GTP area at this point, so other projects currently unfortunately get more attention. So in case somebody among the GTP-interested parties (Travelping, OAI, ...) would want to do something in terms of testing, I'd be more than happy if somebody would step ahead. Otherwise it's all just vapourware going to end up on my ever-growing TODO list :/ Also, if netdev folks have some ideas/pointers about possible frameworks/tools for this kind of testing [it must exist for at least some other kernel networking code?]: Please let me know. I'd be interested to have a look if there's something that can be used as a basis (starting network namespaces, sending/transmitting packets, test case startup/teardown, ...) My "old school" approach would have been to start one or multiple user-mode-linux kernels (those that are to be tested), and then have scripts that set up a gtp socket and gtp tunnels via the libgtp command line tools, and throw packets at that. But I'm sure there must be quite powerful frameworks for that kind of testing in the 21st century? How do other tunneling implementations handle this? Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From idosch at idosch.org Fri Feb 17 10:22:22 2017 From: idosch at idosch.org (Ido Schimmel) Date: Fri, 17 Feb 2017 12:22:22 +0200 Subject: RFC: unit tests for kernel GTP module In-Reply-To: <20170216220801.o3fajacj4sx3iere@nataraja> References: <20170216220801.o3fajacj4sx3iere@nataraja> Message-ID: <20170217102222.GA5566@splinter.mtl.com> Hi, On Thu, Feb 16, 2017 at 11:08:01PM +0100, Harald Welte wrote: > Also, if netdev folks have some ideas/pointers about possible > frameworks/tools for this kind of testing [it must exist for at least > some other kernel networking code?]: Please let me know. I'd be > interested to have a look if there's something that can be used as a > basis (starting network namespaces, sending/transmitting packets, test > case startup/teardown, ...) I'm not sure it meets all your needs, but we use LNST [1] to test our code. You can see the tests (recipes) here [2]. You can create network namespaces, send / receive packets, run arbitrary shell commands and use existing test modules (e.g., ping, netperf) which return pass / fail. Adding Jiri and Ondrej, who is currently in the process of refactoring the code and also familiar with more LNST use cases, such as those employed by Redhat. 1. https://github.com/jpirko/lnst 2. https://github.com/jpirko/lnst/tree/master/recipes/ From aschultz at tpip.net Fri Feb 17 15:02:59 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Fri, 17 Feb 2017 16:02:59 +0100 (CET) Subject: RFC: unit tests for kernel GTP module In-Reply-To: <20170216220801.o3fajacj4sx3iere@nataraja> References: <20170216220801.o3fajacj4sx3iere@nataraja> Message-ID: <1507789126.143172.1487343779235.JavaMail.zimbra@tpip.net> Hi, ----- On Feb 16, 2017, at 11:08 PM, laforge laforge at gnumonks.org wrote: > Dear GTP-interested folks, > > I would love to somehow get towards some degree of unit testing (or even > "continuous integration") for teh kernel GTP code. > > We currently have the original code in the kernel, we had some recent > small fixes and now are getting more patches into place. With > relatively few active users out there (and probably none of them in > production yet), it's particularly easy to introduce regressions while > working on the code. Also, having tested new code even against a > test set with limited covrage could help to get more confidence in new > patches and thus get them merged sooner. We do run the current kernel code in production. The version I have been push patches for will be deployed into prod soonish. Automated testing is something we are also looking at. Currently, we have cooperation going on with a vendor of a an RAN and EPC protocol testing solution. The goal is to have a automated testing setup for our GGSN/PDN-GW OpenSource project. That setup will also exercise the kernel parts. The test suite is proprietary, so we can only share the results but not the test setup itself. > Using tools like sgsnemu of OpenGGSN and the command line tools included > in libgtpnl, it should be possibel to cook up some scripts for testing. > Even the most basic set of tests would be an improvement over what we > have now. One could also think about pcap replay to test with > hand-crafted or real-world packets from other GTP implementations. We more or less removed static GTP tunnels. The tools in libgtpnl only work when an application is keeping the GTP socket alive. Andreas > As much as I'd like to put something like this into place myself, I > don't think I will be able to work much on this in the near future. The > GTP module at this point is a pure hobby and contrary to some years ago > while I started it, I don't have any contract work in the GTP area at > this point, so other projects currently unfortunately get more > attention. > > So in case somebody among the GTP-interested parties (Travelping, OAI, > ...) would want to do something in terms of testing, I'd be more than > happy if somebody would step ahead. Otherwise it's all just vapourware > going to end up on my ever-growing TODO list :/ > > Also, if netdev folks have some ideas/pointers about possible > frameworks/tools for this kind of testing [it must exist for at least > some other kernel networking code?]: Please let me know. I'd be > interested to have a look if there's something that can be used as a > basis (starting network namespaces, sending/transmitting packets, test > case startup/teardown, ...) > > My "old school" approach would have been to start one or multiple > user-mode-linux kernels (those that are to be tested), and then have > scripts that set up a gtp socket and gtp tunnels via the libgtp command > line tools, and throw packets at that. But I'm sure there must be > quite powerful frameworks for that kind of testing in the 21st century? > How do other tunneling implementations handle this? > > Regards, > Harald > -- > - Harald Welte http://laforge.gnumonks.org/ > ============================================================================ > "Privacy in residential applications is a desirable marketing option." > (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Fri Feb 17 18:03:16 2017 From: laforge at gnumonks.org (Harald Welte) Date: Fri, 17 Feb 2017 19:03:16 +0100 Subject: RFC: unit tests for kernel GTP module In-Reply-To: <1507789126.143172.1487343779235.JavaMail.zimbra@tpip.net> References: <20170216220801.o3fajacj4sx3iere@nataraja> <1507789126.143172.1487343779235.JavaMail.zimbra@tpip.net> Message-ID: <20170217180316.ujzsbnb2s2ee44iw@nataraja> Hi Andreas, On Fri, Feb 17, 2017 at 04:02:59PM +0100, Andreas Schultz wrote: > The test suite is proprietary, so we can only share the results but > not the test setup itself. it would be great to have some CI setup where both current stable as well as a development branch of the code is tested, and reports published regularly. Not sure how realistic that is. > We more or less removed static GTP tunnels. The tools in libgtpnl > only work when an application is keeping the GTP socket alive. Well, then those tools need to be adapted accordingly. That's what I also indicated in other mails: Changes in the kernel GTP code should always be followed-up with correspondign changes in libgtpnl and associated tools. I mean, the tool could just open the socket and then continue to run until terminated explicitly... I think there's a lot of value in some very low-level tools for testing and experimentation, without the complexity of configuring + running an Erlang GGSN/P-GW with all its dependencies. -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From anooptomar at fb.com Sat Feb 18 00:03:37 2017 From: anooptomar at fb.com (Anoop Singh Tomar) Date: Sat, 18 Feb 2017 00:03:37 +0000 Subject: osmocom Kernel GTPU module - Support for incoming DL packets for idle mode users in S-GW /SGSN Message-ID: Hi , Posting question related to capability to implement user plane (GTP tunnel) for S1-U and Iu-PS interface between S-GW & eNB and SGSN & RNC respectively via osmocom kernel GTP-U module. At present it seems if TE-ID is not valid packet is dropped in kernel GTP-U module. So in order to use this for S1-U/Iu-PS interface it is required to add support for following use-case: * Down link packet arrival in S-GW /SGSN for UE which is in idle mode - Buffer the packet and trigger paging to move the UE to connected. a. When UE moves to idle mode - S1-U or IuPS bearer is released. In this case IP address and TE-ID of eNB/RNC becomes invalid. b. DL Packet is received for the idle mode UE in S-GW/SGSN - In this case , kernel GTP-U module should deliver packet to user-space application (SGW/osmoSGSN) so that these packets can be buffered in user space and paging can be initiated. Question - Is there any plan to support above in near-future in osmocom kernel GTP-U module or larger question would be to plan to enable usage of kernel GTP-U module in SGSN and SGW towards RAN ? Thanks, Anoop -------------- next part -------------- An HTML attachment was scrubbed... URL: From laforge at gnumonks.org Sat Feb 18 11:58:16 2017 From: laforge at gnumonks.org (Harald Welte) Date: Sat, 18 Feb 2017 12:58:16 +0100 Subject: osmocom Kernel GTPU module - Support for incoming DL packets for idle mode users in S-GW /SGSN In-Reply-To: References: Message-ID: <20170218115816.pva326dlyhvlrjsw@nataraja> Hi Anoop, On Sat, Feb 18, 2017 at 12:03:37AM +0000, Anoop Singh Tomar wrote: > Posting question related to capability to implement user plane (GTP > tunnel) for S1-U and Iu-PS interface between S-GW & eNB and SGSN & RNC > respectively via osmocom kernel GTP-U module. The current kernel GTP-U module has been designed explicitly for the use case of a GGSN/P-GW. This was what the original requirement was, and where Pablo and I received some funding from a customer to implement it. The requirements for a SGSN/S-GW are completely different, and I don't think the current module is any good match for this. Please see the discussion starting from http://marc.info/?l=linux-netdev&m=148611438811696&w=2 for the rationale about this, specifically my post at http://marc.info/?l=linux-netdev&m=148638986910316&w=2 If you think otherwise, please let me know. I'd be happy to undeerstand why. > At present it seems if TE-ID is not valid packet is dropped in kernel > GTP-U module. This is a bug/mis-feature that has been fixed already and the patch is currently in net-next, waiting to be merged into the next linux kernel merge window: https://git.kernel.org/cgit/linux/kernel/git/davem/net-next.git/commit/drivers/net/gtp.c?id=1796a81de4fbd3a93290de61e443ea4153ad28d4 > Question - Is there any plan to support above in near-future in > osmocom kernel GTP-U module or larger question would be to plan to > enable usage of kernel GTP-U module in SGSN and SGW towards RAN ? There are no such plans, at least not from Pablo and my side. I don't know about Andreas, but I think he is also primarily focussing on various (more complex) P-GW use cases, rather than S-GW. The module was developed to fulfill certain requirements under a customer contract. Neither Pablo or I currently have any customers who have above requirement, so there's no related agenda. I would love to work on the kernel GTP code, but I cannot afford to do so in my limited spare time (which is already occupied by way too many projects getting too little attention), sorry. In general, I don't think the current GTP "tunnel endpoint" module fits the S-GW/SGSN use case at all (see my linked post above). The S-GW/SGSN is not a node visible in Layer3 of the user-IP payload, it must not terminate the user IP tunnel. A completely different architecture should be used for the SGSN/S-GW use case, where it is all about forwarding encapsulated packets between MME and P-GW without decapsulating the inner (user) IP layer. Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From aschultz at tpip.net Sun Feb 19 14:38:11 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Sun, 19 Feb 2017 15:38:11 +0100 (CET) Subject: osmocom Kernel GTPU module - Support for incoming DL packets for idle mode users in S-GW /SGSN In-Reply-To: References: Message-ID: <1134568422.152327.1487515091352.JavaMail.zimbra@tpip.net> Hi Anoop, ----- On Feb 18, 2017, at 1:03 AM, Anoop Singh Tomar wrote: > Hi , > Posting question related to capability to implement user plane (GTP tunnel) for > S1-U and Iu-PS interface between S-GW & eNB and SGSN & RNC respectively via > osmocom kernel GTP-U module. > At present it seems if TE-ID is not valid packet is dropped in kernel GTP-U > module. So in order to use this for S1-U/Iu-PS interface it is required to add > support for following use-case: > ? Down link packet arrival in S-GW /SGSN for UE which is in idle mode ? Buffer > the packet and trigger paging to move the UE to connected. > a. When UE moves to idle mode ? S1-U or IuPS bearer is released. In this case IP > address and TE-ID of eNB/RNC becomes invalid. > b. DL Packet is received for the idle mode UE in S-GW/SGSN - In this case , > kernel GTP-U module should deliver packet to user-space application > (SGW/osmoSGSN) so that these packets can be buffered in user space and paging > can be initiated. > Question ? Is there any plan to support above in near-future in osmocom kernel > GTP-U module or larger question would be to plan to enable usage of kernel > GTP-U module in SGSN and SGW towards RAN ? We (Travelping) have some ideas about implementing a S-GW based on the current ergw code [1]. This would also involve implementing kernel side support. The current idea for extending the kernel part is to change the PDP context logic from a 'GTP-U to network device' reeation to a 'GTP-U to something' relation. The 'something' then would be the existing network device, another GTP-U socket (including TEID's), a L2TP tunnel, a IPsec session, .... an so on. My current understanding is that the LWT infrastructure should be suitable for that. This implementation would support the S1-U use case. For Iu-PS, a user space only implementation would IMHO be more suitable. Anyhow, the current kernel implementation is passing all packets that it can't handle (has no PDP context for them) onto the user space process. But all that are only halve backed ideas, nothing concrete, yet. Regards Andreas [1]: https://github.com/travelping/ergw > Anoop -------------- next part -------------- An HTML attachment was scrubbed... URL: From laforge at gnumonks.org Mon Feb 20 16:15:52 2017 From: laforge at gnumonks.org (Harald Welte) Date: Mon, 20 Feb 2017 17:15:52 +0100 Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <20170213153624.14170-2-aschultz@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-2-aschultz@tpip.net> Message-ID: <20170220161552.tnewzagg4z4azikd@nataraja> Hi Andreas, this is not really about the documentation, but it still makes sense to discuss here as the issue is best described: On Mon, Feb 13, 2017 at 04:36:17PM +0100, Andreas Schultz wrote: > +Local GTP-U entity and tunnel identification > +-------------------------------------------- > + > +GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152 for > +GTPv1-U and 3386 for GTPv0-U. > + > +There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW instance) > +per IP address. Tunnel Endpoint Identifier (TEID) are unique per GTP-U entity. > + > +A specific tunnel is only defined by the destination entity. Since the > +destination port is constant, only the destination IP and TEID define > +a tunnel. The source IP and Port have no meaning for the tunnel. Are you absolutely sure about this? > +[3GPP TS 29.281] Section 4.3.0 defines this so: > + > +> The TEID in the GTP-U header is used to de-multiplex traffic incoming from > +> remote tunnel endpoints so that it is delivered to the User plane entities > +> in a way that allows multiplexing of different users, different packet > +> protocols and different QoS levels. Therefore no two remote GTP-U endpoints > +> shall send traffic to a GTP-U protocol entity using the same TEID value except > +> for data forwarding as part of mobility procedures. > + > +The definition above only defines that two remote GTP-U endpoints *should not* > +send to the same TEID, it *does not* forbid or exclude such a scenario. In > +fact, the mentioned mobility procedures make it necessary that the GTP-U entity > +accepts traffic for TEID's from multiple or unknown peers. I so far always assumed that you use GTP-C "MODIFY PDP CONTEXT" in case of such mobility situations, i.e. the control plane explicitly notifies the GTP-U entity of a change in the SGSN/S-GW address. At least for 3G this seems to be the case, and my assumption appears to be confirmed with sources such as e.g. page 15 of http://www.nwadmin.de/presentation_mobility_manag ement_in_UMTS.pdf Also, for LTE, it seems that there's a GTPv2-C "Modify Bearer Request/Response" involved in relocation from one S-GW to another S-GW: http://www.lteandbeyond.com/2012/03/x2-based-handover-with-sgw-relocation.html > Step 3. The target Serving GW assigns addresses and TEIDs (one per > bearer) for downlink traffic from the PDN GW. The Serving GW allocates > DL TEIDs on S5/S8 even for non-accepted bearers. It sends a Modify > Bearer Request (Serving GW addresses for user plane and TEID(s)) > message per PDN connection to the PDN GW(s). The SGW also includes > User Location Information IE and/or UE Time Zone IE if it is present > in step 2. The PDN GW updates its context field and returns a Modify > Bearer Response (Charging Id, MSISDN, etc.) message to the Serving GW. > The MSISDN is included if the PDN GW has it stored in its UE context. > The PDN GW starts sending downlink packets to the target GW using the > newly received address and TEIDs. These downlink packets will use the > new downlink path via the target Serving GW to the target eNodeB. The > Serving GW shall allocate TEIDs for the failed bearers and inform to > the MME. Section 5.5.1.1.3 of TS 23.401 agrees with the GTPv2C signalling during relocation, AFAICT. > +Therefor the receiving side only identifies tunnels based on TEID's, not based > +on the source IP. I'm wondering if this really is neccesary. Do you have actual protocol traces, specs or any other literature confirming this? I find it somewhat surprising, given how much this opens the door for arbitrary spoofing from anyone (with access to the respective private network such as GRX). So in which situations specifically will thre be a S-GW side Address change without associated GTP-C signaling informing the P-GW about the new S-GW side Address + TEID? Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Mon Feb 20 16:47:38 2017 From: laforge at gnumonks.org (Harald Welte) Date: Mon, 20 Feb 2017 17:47:38 +0100 Subject: Roaming allowed for using local sims In-Reply-To: References: Message-ID: <20170220164738.bqfzyk6jpdpgqqa6@nataraja> Hi Saba, On Wed, Sep 23, 2015 at 03:16:37AM +0500, Saba Arshad wrote: > http://openbsc.osmocom.org/trac/wiki/OpenBSC_GPRS says, "it is currently > not possible". As it is long when posted on this site so I want to ask is > it possible *now* to use the local sims? The information is outdated since March 2013. Please always refer to the user manuals (linked from https://osmocom.org/projects/osmosgsn/wiki/OsmoSGSN) for more up-to-date reference information. Chapter "9.2 Authorization Policy" should answer your question. -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From aschultz at tpip.net Mon Feb 20 17:28:07 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Mon, 20 Feb 2017 18:28:07 +0100 (CET) Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <20170220161552.tnewzagg4z4azikd@nataraja> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-2-aschultz@tpip.net> <20170220161552.tnewzagg4z4azikd@nataraja> Message-ID: <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> Hi, ----- On Feb 20, 2017, at 5:15 PM, laforge laforge at gnumonks.org wrote: > Hi Andreas, > > this is not really about the documentation, but it still makes sense to > discuss here as the issue is best described: > > On Mon, Feb 13, 2017 at 04:36:17PM +0100, Andreas Schultz wrote: >> +Local GTP-U entity and tunnel identification >> +-------------------------------------------- >> + >> +GTP-U uses UDP for transporting PDU's. The receiving UDP port is 2152 for >> +GTPv1-U and 3386 for GTPv0-U. >> + >> +There is only one GTP-U entity (and therefor SGSN/GGSN/S-GW/PDN-GW instance) >> +per IP address. Tunnel Endpoint Identifier (TEID) are unique per GTP-U entity. >> + >> +A specific tunnel is only defined by the destination entity. Since the >> +destination port is constant, only the destination IP and TEID define >> +a tunnel. The source IP and Port have no meaning for the tunnel. > > Are you absolutely sure about this? Yes. It took me a while to realize this. The crux is this statement in TS 29.060, Section 7.3.1: > The SGSN shall include an SGSN Address for control plane and an SGSN address for > user traffic, which may differ from that provided by the underlying network service > (e.g. IP). The GGSN shall store these SGSN Addresses and use them when sending > control plane on this GTP tunnel or G-PDUs to the SGSN for the MS. IMHO, this implies that the source IP of GTP-U and GTP-C frames does not have to match the GSN address specified by a Create PDP Context Request. For GTPv2-C there is no such clear statement. However, GTPv2-C makes it clear that a TEID describes the local tunnel ENDPOINT, there nothing that defines the tunnel *source* (the same is true for GTPv1-C, except that is not so explicit). TS 29.274, Section 4.2.2.1, Initial Messages: > During the establishment of the GTP tunnel, the GTPv2 entity selects and > communicates to the peer GTPv2 entity the IP Destination Address at which > it expects to receive subsequent control plane Initial messages related > to that GTP tunnel via ..... TS 23.401, Annex D, Sect. D.3.3. makes is clear beyond doubt that we have to accept packet for a valid TEID from virtually any IP. If you look at figure D.3.3-1, step 16, you will see that only the new SGSN is contacting the P-GW. There is no prior advertisement of the change from the old SGSN. Step 16 would therefor not work if we limited the tunnel to a specific source IP. The same applies to figure D.3.4-1, step 18. >> +[3GPP TS 29.281] Section 4.3.0 defines this so: >> + >> +> The TEID in the GTP-U header is used to de-multiplex traffic incoming from >> +> remote tunnel endpoints so that it is delivered to the User plane entities >> +> in a way that allows multiplexing of different users, different packet >> +> protocols and different QoS levels. Therefore no two remote GTP-U endpoints >> +> shall send traffic to a GTP-U protocol entity using the same TEID value >> except >> +> for data forwarding as part of mobility procedures. >> + >> +The definition above only defines that two remote GTP-U endpoints *should not* >> +send to the same TEID, it *does not* forbid or exclude such a scenario. In >> +fact, the mentioned mobility procedures make it necessary that the GTP-U entity >> +accepts traffic for TEID's from multiple or unknown peers. > > I so far always assumed that you use GTP-C "MODIFY PDP CONTEXT" in case > of such mobility situations, i.e. the control plane explicitly notifies > the GTP-U entity of a change in the SGSN/S-GW address. Yes, it does. But that notification only applies to the tunnel endpoint at the SGSN/S-GW in the GGSN/P-GW to SGSN/S-GW direction. > At least for 3G this seems to be the case, and my assumption appears to > be confirmed with sources such as e.g. page 15 of > http://www.nwadmin.de/presentation_mobility_manag ement_in_UMTS.pdf > > Also, for LTE, it seems that there's a GTPv2-C "Modify Bearer > Request/Response" involved in relocation from one S-GW to another S-GW: > http://www.lteandbeyond.com/2012/03/x2-based-handover-with-sgw-relocation.html I think this affirms my argument, Step 3a is sending GTP packets from a source IP that is know to the P-GW before. The fact that this is only used for GTP-C does IMHO not mean that the same should not apply to GTP-U. >> Step 3. The target Serving GW assigns addresses and TEIDs (one per >> bearer) for downlink traffic from the PDN GW. The Serving GW allocates >> DL TEIDs on S5/S8 even for non-accepted bearers. It sends a Modify >> Bearer Request (Serving GW addresses for user plane and TEID(s)) >> message per PDN connection to the PDN GW(s). The SGW also includes >> User Location Information IE and/or UE Time Zone IE if it is present >> in step 2. The PDN GW updates its context field and returns a Modify >> Bearer Response (Charging Id, MSISDN, etc.) message to the Serving GW. >> The MSISDN is included if the PDN GW has it stored in its UE context. >> The PDN GW starts sending downlink packets to the target GW using the >> newly received address and TEIDs. These downlink packets will use the >> new downlink path via the target Serving GW to the target eNodeB. The >> Serving GW shall allocate TEIDs for the failed bearers and inform to >> the MME. > > Section 5.5.1.1.3 of TS 23.401 agrees with the GTPv2C signalling during > relocation, AFAICT. Again, there is nothing in there that defines a IP *source*, a F-TEID is a tunnel ENDPOINT id not a tunnel SOURCE id. >> +Therefor the receiving side only identifies tunnels based on TEID's, not based >> +on the source IP. > > I'm wondering if this really is neccesary. Do you have actual protocol > traces, specs or any other literature confirming this? So far I have only seen symmetric GTP-U tunnels. However I believe there is nothing that would stop a SGSN/S-GW from switching GPT-U tunnel source transparently across IP's (for example a system with multiple shelves might use different shelve in DL/UL direction and have therefore multiple IP's. > I find it somewhat surprising, given how much this opens the door for > arbitrary spoofing from anyone (with access to the respective private > network such as GRX). Yes it is, but it is mandated without a doubt by the specification for GTP-C. For the reasons outline before, I think that this applies to GTP-U as well. > So in which situations specifically will thre be a S-GW side Address > change without associated GTP-C signaling informing the P-GW about the > new S-GW side Address + TEID? As above, multi shelve systems with asymmetric UL/DL path. Regards, Andreas > > Regards, > Harald > -- > - Harald Welte http://laforge.gnumonks.org/ > ============================================================================ > "Privacy in residential applications is a desirable marketing option." > (ETSI EN 300 175-7 Ch. A6) From nhofmeyr at sysmocom.de Tue Feb 21 01:12:45 2017 From: nhofmeyr at sysmocom.de (Neels Hofmeyr) Date: Tue, 21 Feb 2017 02:12:45 +0100 Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-2-aschultz@tpip.net> <20170220161552.tnewzagg4z4azikd@nataraja> <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> Message-ID: <20170221011245.GB1961@my.box> On Mon, Feb 20, 2017 at 06:28:07PM +0100, Andreas Schultz wrote: > >> +A specific tunnel is only defined by the destination entity. Since the > >> +destination port is constant, only the destination IP and TEID define > >> +a tunnel. The source IP and Port have no meaning for the tunnel. > > > > Are you absolutely sure about this? > > Yes. It took me a while to realize this. The crux is this statement in TS 29.060, > Section 7.3.1: Also sounds right to me. When the tunnel is established, each side of a GTP tunnel stores the other side's IP and TEID; port number is constant (unfortunately, which is one of the ideas/reasons behind gtphub). ~N -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 819 bytes Desc: Digital signature URL: From aschultz at tpip.net Tue Feb 21 10:18:49 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:49 +0100 Subject: [PATCH net-next v4 1/7] gtp: switch from struct socket to struct sock for the GTP sockets In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-2-aschultz@tpip.net> After enabling the UDP encapsulation, only the sk member is used. Holding the socket would prevent user space from closing the socket, but holding a reference to the sk member does not have the same effect. This change will make it simpler to later detach the sockets from the netdevice. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index bda0c64..a8ce8c7 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -66,8 +66,8 @@ struct pdp_ctx { struct gtp_dev { struct list_head list; - struct socket *sock0; - struct socket *sock1u; + struct sock *sk0; + struct sock *sk1u; struct net_device *dev; @@ -261,17 +261,19 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, static void gtp_encap_disable(struct gtp_dev *gtp) { - if (gtp->sock0 && gtp->sock0->sk) { - udp_sk(gtp->sock0->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock0->sk, NULL); + if (gtp->sk0) { + udp_sk(gtp->sk0)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk0, NULL); + sock_put(gtp->sk0); } - if (gtp->sock1u && gtp->sock1u->sk) { - udp_sk(gtp->sock1u->sk)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sock1u->sk, NULL); + if (gtp->sk1u) { + udp_sk(gtp->sk1u)->encap_type = 0; + rcu_assign_sk_user_data(gtp->sk1u, NULL); + sock_put(gtp->sk1u); } - gtp->sock0 = NULL; - gtp->sock1u = NULL; + gtp->sk0 = NULL; + gtp->sk1u = NULL; } static void gtp_encap_destroy(struct sock *sk) @@ -484,14 +486,14 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, switch (pctx->gtp_version) { case GTP_V0: - if (gtp->sock0) - sk = gtp->sock0->sk; + if (gtp->sk0) + sk = gtp->sk0; else sk = NULL; break; case GTP_V1: - if (gtp->sock1u) - sk = gtp->sock1u->sk; + if (gtp->sk1u) + sk = gtp->sk1u; else sk = NULL; break; @@ -504,7 +506,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, return -ENOENT; } - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sock0->sk, + rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", @@ -839,18 +841,20 @@ static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - gtp->sock0 = sock0; - gtp->sock1u = sock1u; + sock_hold(sock0->sk); + gtp->sk0 = sock0->sk; + sock_hold(sock1u->sk); + gtp->sk1u = sock1u->sk; tuncfg.sk_user_data = gtp; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sock0->sk), gtp->sock0, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sock1u->sk), gtp->sock1u, &tuncfg); + setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); err = 0; err2: -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:48 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:48 +0100 Subject: [PATCH net-next v4 0/7] gtp: misc improvements Message-ID: <20170221101855.23940-1-aschultz@tpip.net> Hi Pablo, This is v4 of the GTP improvements. Compared to v3 it contains mostly smallish naming and spelling fixes. It also drops the documentation patch, Harald did a better job with the documentation and the some things I described do not yet match the implementation. I'll readd the relevant parts with a follow up series. This series lays the groundwork for removing the socket references from the GTP netdevice by removing duplicate code and simplifying the logic on some code paths. It slighly changes the GTP genl API by making the socket parameters optional (though one of them is still required). The removal of the socket references will break the 1:1 releation between GTP netdevice and GTP socket that prevents us to support multiple VRFs with overlaping IP addresse spaces attached to the same GTP-U entity (needed for multi APN support, comming a follow up series). Pablo found a socket hold problem in v2. In order to solve that I had to switch the socket references from the struct socket to the internal struct sock. This should have no functionl impact, but we can now hang on to the reference without blocking user space from closing the GTP socket. v3->v4: * drop the documentation patch * spelling fixes * pass nlattr instead of genl_info into gtp_find_dev, makes the code slightly more compact and readable v2->v3: * add documentation to explain the goal of all these changes * incorporate review comments * switch from struct socket to struct sock Regards Andreas -- Andreas Schultz (7): gtp: switch from struct socket to struct sock for the GTP sockets gtp: make GTP sockets in gtp_newlink optional gtp: merge gtp_get_net and gtp_genl_find_dev gtp: consolidate gtp socket rx path gtp: unify genl_find_pdp and prepare for per socket lookup gtp: consolidate pdp context destruction into helper gtp: add socket to pdp context drivers/net/gtp.c | 543 +++++++++++++++++++++++++++--------------------------- 1 file changed, 269 insertions(+), 274 deletions(-) -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:51 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:51 +0100 Subject: [PATCH net-next v4 3/7] gtp: merge gtp_get_net and gtp_genl_find_dev In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-4-aschultz@tpip.net> Both function are always used together with the final goal to get the gtp_dev. This simplifies the code by merging them together. The netdevice lookup is changed to use the regular dev_get_by_index. The gtp netdevice list is now only used to find the PDP contexts for imcomming packets. It can be completely eliminated Once the TEID hash is moved into the GTP socket. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 146 ++++++++++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 77 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 96bb89b..961fb3c 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -744,21 +744,6 @@ static struct rtnl_link_ops gtp_link_ops __read_mostly = { .fill_info = gtp_fill_info, }; -static struct net *gtp_genl_get_net(struct net *src_net, struct nlattr *tb[]) -{ - struct net *net; - - /* Examine the link attributes and figure out which network namespace - * we are talking about. - */ - if (tb[GTPA_NET_NS_FD]) - net = get_net_ns_by_fd(nla_get_u32(tb[GTPA_NET_NS_FD])); - else - net = get_net(src_net); - - return net; -} - static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize) { int i; @@ -872,16 +857,30 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) return 0; } -static struct net_device *gtp_find_dev(struct net *net, int ifindex) +static struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[]) { - struct gtp_net *gn = net_generic(net, gtp_net_id); - struct gtp_dev *gtp; - - list_for_each_entry_rcu(gtp, &gn->gtp_dev_list, list) { - if (ifindex == gtp->dev->ifindex) - return gtp->dev; - } - return NULL; + struct gtp_dev *gtp = NULL; + struct net_device *dev; + struct net *net; + + /* Examine the link attributes and figure out which network namespace + * we are talking about. + */ + if (nla[GTPA_NET_NS_FD]) + net = get_net_ns_by_fd(nla_get_u32(nla[GTPA_NET_NS_FD])); + else + net = get_net(src_net); + + if (IS_ERR(net)) + return NULL; + + /* Check if there's an existing gtpX device to configure */ + dev = dev_get_by_index_rcu(net, nla_get_u32(nla[GTPA_LINK])); + if (dev->netdev_ops == >p_netdev_ops) + gtp = netdev_priv(dev); + + put_net(net); + return gtp; } static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) @@ -911,9 +910,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) { - struct gtp_dev *gtp = netdev_priv(dev); + struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; struct pdp_ctx *pctx; bool found = false; @@ -990,8 +989,8 @@ static int ipv4_pdp_add(struct net_device *dev, struct genl_info *info) static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; - struct net *net; + struct gtp_dev *gtp; + int err; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK] || @@ -1015,77 +1014,79 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - return ipv4_pdp_add(dev, info); + err = ipv4_pdp_add(gtp, info); + +out_unlock: + rcu_read_unlock(); + return err; } static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) { - struct net_device *dev; struct pdp_ctx *pctx; struct gtp_dev *gtp; - struct net *net; + int err = 0; if (!info->attrs[GTPA_VERSION] || !info->attrs[GTPA_LINK]) return -EINVAL; - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); + rcu_read_lock(); - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto out_unlock; } - put_net(net); - - gtp = netdev_priv(dev); switch (nla_get_u32(info->attrs[GTPA_VERSION])) { case GTP_V0: - if (!info->attrs[GTPA_TID]) - return -EINVAL; + if (!info->attrs[GTPA_TID]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); break; case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) - return -EINVAL; + if (!info->attrs[GTPA_I_TEI]) { + err = -EINVAL; + goto out_unlock; + } pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); break; default: - return -EINVAL; + err = -EINVAL; + goto out_unlock; } - if (pctx == NULL) - return -ENOENT; + if (!pctx) { + err = -ENOENT; + goto out_unlock; + } if (pctx->gtp_version == GTP_V0) - netdev_dbg(dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); kfree_rcu(pctx, rcu_head); - return 0; +out_unlock: + rcu_read_unlock(); + return err; } static struct genl_family gtp_genl_family; @@ -1129,11 +1130,9 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; - struct net_device *dev; struct sk_buff *skb2; struct gtp_dev *gtp; u32 gtp_version; - struct net *net; int err; if (!info->attrs[GTPA_VERSION] || @@ -1149,21 +1148,14 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } - net = gtp_genl_get_net(sock_net(skb->sk), info->attrs); - if (IS_ERR(net)) - return PTR_ERR(net); - - /* Check if there's an existing gtpX device to configure */ - dev = gtp_find_dev(net, nla_get_u32(info->attrs[GTPA_LINK])); - if (dev == NULL) { - put_net(net); - return -ENODEV; - } - put_net(net); - - gtp = netdev_priv(dev); - rcu_read_lock(); + + gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); + if (!gtp) { + err = -ENODEV; + goto err_unlock; + } + if (gtp_version == GTP_V0 && info->attrs[GTPA_TID]) { u64 tid = nla_get_u64(info->attrs[GTPA_TID]); -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:52 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:52 +0100 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-5-aschultz@tpip.net> Add network device to gtp context in preparation for splitting the TEID from the network device. Use this to rework the socker rx path. Move the common RX part of v0 and v1 into a helper. Also move the final rx part into that helper as well. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 961fb3c..fc0fff5 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,8 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct net_device *dev; + atomic_t tx_seq; struct rcu_head rcu_head; }; @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, + bool xnet) +{ + struct pcpu_sw_netstats *stats; + + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); + return 1; + } + + /* Get rid of the GTP + UDP headers. */ + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + return -1; + + netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); + + /* Now that the UDP and the GTP header have been removed, set up the + * new network header. This is required by the upper layer to + * calculate the transport header. + */ + skb_reset_network_header(skb); + + skb->dev = pctx->dev; + + stats = this_cpu_ptr(pctx->dev->tstats); + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += skb->len; + u64_stats_update_end(&stats->syncp); + + netif_rx(skb); + return 0; +} + /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, bool xnet) @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); - return 1; - } - - /* Get rid of the GTP + UDP headers. */ - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); + return gtp_rx(pctx, skb, hdrlen, xnet); } static void gtp_encap_destroy(struct sock *sk) @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) */ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { - struct pcpu_sw_netstats *stats; struct gtp_dev *gtp; + int ret = 0; bool xnet; - int ret; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) switch (ret) { case 1: netdev_dbg(gtp->dev, "pass up to the process\n"); - return 1; + break; case 0: - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); break; case -1: netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); kfree_skb(skb); - return 0; + ret = 0; + break; } - /* Now that the UDP and the GTP header have been removed, set up the - * new network header. This is required by the upper layer to - * calculate the transport header. - */ - skb_reset_network_header(skb); - - skb->dev = gtp->dev; - - stats = this_cpu_ptr(gtp->dev->tstats); - u64_stats_update_begin(&stats->syncp); - stats->rx_packets++; - stats->rx_bytes += skb->len; - u64_stats_update_end(&stats->syncp); - - netif_rx(skb); - - return 0; + return ret; } static int gtp_dev_init(struct net_device *dev) @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:54 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:54 +0100 Subject: [PATCH net-next v4 6/7] gtp: consolidate pdp context destruction into helper In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-7-aschultz@tpip.net> Consolidate duplicate code into helper. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index f529d1e..b6aae68 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -86,6 +86,8 @@ struct gtp_net { static u32 gtp_h_initval; +static void pdp_context_delete(struct pdp_ctx *pctx); + static inline u32 gtp0_hashfn(u64 tid) { u32 *tid32 = (u32 *) &tid; @@ -780,13 +782,10 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) struct pdp_ctx *pctx; int i; - for (i = 0; i < gtp->hash_size; i++) { - hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) { - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); - } - } + for (i = 0; i < gtp->hash_size; i++) + hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) + pdp_context_delete(pctx); + synchronize_rcu(); kfree(gtp->addr_hash); kfree(gtp->tid_hash); @@ -995,6 +994,13 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_delete(struct pdp_ctx *pctx) +{ + hlist_del_rcu(&pctx->hlist_tid); + hlist_del_rcu(&pctx->hlist_addr); + kfree_rcu(pctx, rcu_head); +} + static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { struct gtp_dev *gtp; @@ -1100,9 +1106,7 @@ static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); - hlist_del_rcu(&pctx->hlist_tid); - hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + pdp_context_delete(pctx); out_unlock: rcu_read_unlock(); -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:53 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:53 +0100 Subject: [PATCH net-next v4 5/7] gtp: unify genl_find_pdp and prepare for per socket lookup In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-6-aschultz@tpip.net> This unifies duplicate code into a helper. It also prepares the groundwork to add a lookup version that uses the socket to find attached pdp contexts. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 121 ++++++++++++++++++++++-------------------------------- 1 file changed, 50 insertions(+), 71 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index fc0fff5..f529d1e 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -1037,55 +1037,67 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return err; } +static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net, + struct nlattr *nla[]) +{ + struct gtp_dev *gtp; + + gtp = gtp_find_dev(net, nla); + if (!gtp) + return ERR_PTR(-ENODEV); + + if (nla[GTPA_MS_ADDRESS]) { + __be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]); + + return ipv4_pdp_find(gtp, ip); + } else if (nla[GTPA_VERSION]) { + u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]); + + if (gtp_version == GTP_V0 && nla[GTPA_TID]) + return gtp0_pdp_find(gtp, nla_get_u64(nla[GTPA_TID])); + else if (gtp_version == GTP_V1 && nla[GTPA_I_TEI]) + return gtp1_pdp_find(gtp, nla_get_u32(nla[GTPA_I_TEI])); + } + + return ERR_PTR(-EINVAL); +} + +static struct pdp_ctx *gtp_find_pdp(struct net *net, struct nlattr *nla[]) +{ + struct pdp_ctx *pctx; + + if (nla[GTPA_LINK]) + pctx = gtp_find_pdp_by_link(net, nla); + else + pctx = ERR_PTR(-EINVAL); + + if (!pctx) + pctx = ERR_PTR(-ENOENT); + + return pctx; +} + static int gtp_genl_del_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx; - struct gtp_dev *gtp; int err = 0; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) + if (!info->attrs[GTPA_VERSION]) return -EINVAL; rcu_read_lock(); - gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto out_unlock; - } - - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { - case GTP_V0: - if (!info->attrs[GTPA_TID]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp0_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_TID])); - break; - case GTP_V1: - if (!info->attrs[GTPA_I_TEI]) { - err = -EINVAL; - goto out_unlock; - } - pctx = gtp1_pdp_find(gtp, nla_get_u64(info->attrs[GTPA_I_TEI])); - break; - - default: - err = -EINVAL; - goto out_unlock; - } - - if (!pctx) { - err = -ENOENT; + pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto out_unlock; } if (pctx->gtp_version == GTP_V0) - netdev_dbg(gtp->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv0-U: deleting tunnel id = %llx (pdp %p)\n", pctx->u.v0.tid, pctx); else if (pctx->gtp_version == GTP_V1) - netdev_dbg(gtp->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", + netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); hlist_del_rcu(&pctx->hlist_tid); @@ -1139,49 +1151,16 @@ static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) { struct pdp_ctx *pctx = NULL; struct sk_buff *skb2; - struct gtp_dev *gtp; - u32 gtp_version; int err; - if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK]) + if (!info->attrs[GTPA_VERSION]) return -EINVAL; - gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - switch (gtp_version) { - case GTP_V0: - case GTP_V1: - break; - default: - return -EINVAL; - } - rcu_read_lock(); - gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); - if (!gtp) { - err = -ENODEV; - goto err_unlock; - } - - if (gtp_version == GTP_V0 && - info->attrs[GTPA_TID]) { - u64 tid = nla_get_u64(info->attrs[GTPA_TID]); - - pctx = gtp0_pdp_find(gtp, tid); - } else if (gtp_version == GTP_V1 && - info->attrs[GTPA_I_TEI]) { - u32 tid = nla_get_u32(info->attrs[GTPA_I_TEI]); - - pctx = gtp1_pdp_find(gtp, tid); - } else if (info->attrs[GTPA_MS_ADDRESS]) { - __be32 ip = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]); - - pctx = ipv4_pdp_find(gtp, ip); - } - - if (pctx == NULL) { - err = -ENOENT; + pctx = gtp_find_pdp(sock_net(skb->sk), info->attrs); + if (IS_ERR(pctx)) { + err = PTR_ERR(pctx); goto err_unlock; } -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:55 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:55 +0100 Subject: [PATCH net-next v4 7/7] gtp: add socket to pdp context In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-8-aschultz@tpip.net> Having the socket present in context simplifies the sending logic. It also fixes the invalid assumption that we have to use the same sending socket for all client IP's on a specific gtp interface. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 94 +++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index b6aae68..ea868de 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -58,6 +58,7 @@ struct pdp_ctx { struct in_addr ms_addr_ip4; struct in_addr sgsn_addr_ip4; + struct sock *sk; struct net_device *dev; atomic_t tx_seq; @@ -179,8 +180,7 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, return false; } -static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, - bool xnet) +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen) { struct pcpu_sw_netstats *stats; @@ -190,7 +190,8 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* Get rid of the GTP + UDP headers. */ - if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, + !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) return -1; netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); @@ -214,8 +215,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen } /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ -static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp0_header); @@ -239,11 +239,10 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } -static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, - bool xnet) +static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb) { unsigned int hdrlen = sizeof(struct udphdr) + sizeof(struct gtp1_header); @@ -282,7 +281,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return 1; } - return gtp_rx(pctx, skb, hdrlen, xnet); + return gtp_rx(pctx, skb, hdrlen); } static void gtp_encap_destroy(struct sock *sk) @@ -318,7 +317,6 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct gtp_dev *gtp; int ret = 0; - bool xnet; gtp = rcu_dereference_sk_user_data(sk); if (!gtp) @@ -326,16 +324,14 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk); - xnet = !net_eq(sock_net(sk), dev_net(gtp->dev)); - switch (udp_sk(sk)->encap_type) { case UDP_ENCAP_GTP0: netdev_dbg(gtp->dev, "received GTP0 packet\n"); - ret = gtp0_udp_encap_recv(gtp, skb, xnet); + ret = gtp0_udp_encap_recv(gtp, skb); break; case UDP_ENCAP_GTP1U: netdev_dbg(gtp->dev, "received GTP1U packet\n"); - ret = gtp1u_udp_encap_recv(gtp, skb, xnet); + ret = gtp1u_udp_encap_recv(gtp, skb); break; default: ret = -1; /* Shouldn't happen. */ @@ -378,8 +374,9 @@ static void gtp_dev_uninit(struct net_device *dev) free_percpu(dev->tstats); } -static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, - const struct sock *sk, __be32 daddr) +static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr) { memset(fl4, 0, sizeof(*fl4)); fl4->flowi4_oif = sk->sk_bound_dev_if; @@ -388,7 +385,7 @@ static struct rtable *ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, fl4->flowi4_tos = RT_CONN_FLAGS(sk); fl4->flowi4_proto = sk->sk_protocol; - return ip_route_output_key(net, fl4); + return ip_route_output_key(sock_net(sk), fl4); } static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) @@ -477,7 +474,6 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, struct rtable *rt; struct flowi4 fl4; struct iphdr *iph; - struct sock *sk; __be16 df; int mtu; @@ -493,30 +489,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, } netdev_dbg(dev, "found PDP context %p\n", pctx); - switch (pctx->gtp_version) { - case GTP_V0: - if (gtp->sk0) - sk = gtp->sk0; - else - sk = NULL; - break; - case GTP_V1: - if (gtp->sk1u) - sk = gtp->sk1u; - else - sk = NULL; - break; - default: - return -ENOENT; - } - - if (!sk) { - netdev_dbg(dev, "no userspace socket is available, skip\n"); - return -ENOENT; - } - - rt = ip4_route_output_gtp(sock_net(sk), &fl4, gtp->sk0, - pctx->sgsn_addr_ip4.s_addr); + rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->sgsn_addr_ip4.s_addr); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to SSGN %pI4\n", &pctx->sgsn_addr_ip4.s_addr); @@ -561,7 +534,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev, goto err_rt; } - gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev); + gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev); gtp_push_header(skb, pktinfo); return 0; @@ -916,7 +889,8 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) } } -static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) +static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk, + struct genl_info *info) { struct net_device *dev = gtp->dev; u32 hash_ms, hash_tid = 0; @@ -957,6 +931,8 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) if (pctx == NULL) return -ENOMEM; + sock_hold(sk); + pctx->sk = sk; pctx->dev = gtp->dev; ipv4_pdp_fill(pctx, info); atomic_set(&pctx->tx_seq, 0); @@ -994,16 +970,26 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) return 0; } +static void pdp_context_free(struct rcu_head *head) +{ + struct pdp_ctx *pctx = container_of(head, struct pdp_ctx, rcu_head); + + sock_put(pctx->sk); + kfree(pctx); +} + static void pdp_context_delete(struct pdp_ctx *pctx) { hlist_del_rcu(&pctx->hlist_tid); hlist_del_rcu(&pctx->hlist_addr); - kfree_rcu(pctx, rcu_head); + call_rcu(&pctx->rcu_head, pdp_context_free); } static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) { + unsigned int version; struct gtp_dev *gtp; + struct sock *sk; int err; if (!info->attrs[GTPA_VERSION] || @@ -1012,7 +998,9 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) !info->attrs[GTPA_MS_ADDRESS]) return -EINVAL; - switch (nla_get_u32(info->attrs[GTPA_VERSION])) { + version = nla_get_u32(info->attrs[GTPA_VERSION]); + + switch (version) { case GTP_V0: if (!info->attrs[GTPA_TID] || !info->attrs[GTPA_FLOW]) @@ -1036,7 +1024,19 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } - err = ipv4_pdp_add(gtp, info); + if (version == GTP_V0) + sk = gtp->sk0; + else if (version == GTP_V1) + sk = gtp->sk1u; + else + sk = NULL; + + if (!sk) { + err = -ENODEV; + goto out_unlock; + } + + err = ipv4_pdp_add(gtp, sk, info); out_unlock: rcu_read_unlock(); -- 2.10.2 From aschultz at tpip.net Tue Feb 21 10:18:50 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 11:18:50 +0100 Subject: [PATCH net-next v4 2/7] gtp: make GTP sockets in gtp_newlink optional In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221101855.23940-3-aschultz@tpip.net> Having both GTPv0-U and GTPv1-U is not always desirable. Fallback from GTPv1-U to GTPv0-U was depreciated from 3GPP Rel-8 onwards. Post Rel-8 implementation are discuraged from listening on the v0 port (see 3GPP TS 29.281, Sect. 1). A future change will completely decouple the sockets from the network device. Till then, at least one of the sockets needs to be specified (either v0 or v1), the other is optional. Signed-off-by: Andreas Schultz --- drivers/net/gtp.c | 148 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 70 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index a8ce8c7..96bb89b 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -259,30 +259,30 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); } -static void gtp_encap_disable(struct gtp_dev *gtp) -{ - if (gtp->sk0) { - udp_sk(gtp->sk0)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk0, NULL); - sock_put(gtp->sk0); - } - if (gtp->sk1u) { - udp_sk(gtp->sk1u)->encap_type = 0; - rcu_assign_sk_user_data(gtp->sk1u, NULL); - sock_put(gtp->sk1u); - } - - gtp->sk0 = NULL; - gtp->sk1u = NULL; -} - static void gtp_encap_destroy(struct sock *sk) { struct gtp_dev *gtp; gtp = rcu_dereference_sk_user_data(sk); - if (gtp) - gtp_encap_disable(gtp); + if (gtp) { + udp_sk(sk)->encap_type = 0; + rcu_assign_sk_user_data(sk, NULL); + sock_put(sk); + } +} + +static void gtp_encap_disable_sock(struct sock *sk) +{ + if (!sk) + return; + + gtp_encap_destroy(sk); +} + +static void gtp_encap_disable(struct gtp_dev *gtp) +{ + gtp_encap_disable_sock(gtp->sk0); + gtp_encap_disable_sock(gtp->sk1u); } /* UDP encapsulation receive handler. See net/ipv4/udp.c. @@ -642,27 +642,23 @@ static void gtp_link_setup(struct net_device *dev) static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize); static void gtp_hashtable_free(struct gtp_dev *gtp); -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1); +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]); static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - int hashsize, err, fd0, fd1; struct gtp_dev *gtp; 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]) return -EINVAL; gtp = netdev_priv(dev); - fd0 = nla_get_u32(data[IFLA_GTP_FD0]); - fd1 = nla_get_u32(data[IFLA_GTP_FD1]); - - err = gtp_encap_enable(dev, gtp, fd0, fd1); + err = gtp_encap_enable(gtp, data); if (err < 0) - goto out_err; + return err; if (!data[IFLA_GTP_PDP_HASHSIZE]) hashsize = 1024; @@ -690,7 +686,6 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, gtp_hashtable_free(gtp); out_encap: gtp_encap_disable(gtp); -out_err: return err; } @@ -805,63 +800,76 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) kfree(gtp->tid_hash); } -static int gtp_encap_enable(struct net_device *dev, struct gtp_dev *gtp, - int fd_gtp0, int fd_gtp1) +static struct sock *gtp_encap_enable_socket(int fd, int type, + struct gtp_dev *gtp) { struct udp_tunnel_sock_cfg tuncfg = {NULL}; - struct socket *sock0, *sock1u; + struct socket *sock; + struct sock *sk; int err; - netdev_dbg(dev, "enable gtp on %d, %d\n", fd_gtp0, fd_gtp1); + pr_debug("enable gtp on %d, %d\n", fd, type); - sock0 = sockfd_lookup(fd_gtp0, &err); - if (sock0 == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp0)\n", fd_gtp0); - return -ENOENT; + sock = sockfd_lookup(fd, &err); + if (!sock) { + pr_debug("gtp socket fd=%d not found\n", fd); + return NULL; } - if (sock0->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp0); - err = -EINVAL; - goto err1; + if (sock->sk->sk_protocol != IPPROTO_UDP) { + pr_debug("socket fd=%d not UDP\n", fd); + sk = ERR_PTR(-EINVAL); + goto out_sock; } - sock1u = sockfd_lookup(fd_gtp1, &err); - if (sock1u == NULL) { - netdev_dbg(dev, "socket fd=%d not found (gtp1u)\n", fd_gtp1); - err = -ENOENT; - goto err1; + if (rcu_dereference_sk_user_data(sock->sk)) { + sk = ERR_PTR(-EBUSY); + goto out_sock; } - if (sock1u->sk->sk_protocol != IPPROTO_UDP) { - netdev_dbg(dev, "socket fd=%d not UDP\n", fd_gtp1); - err = -EINVAL; - goto err2; - } - - netdev_dbg(dev, "enable gtp on %p, %p\n", sock0, sock1u); - - sock_hold(sock0->sk); - gtp->sk0 = sock0->sk; - sock_hold(sock1u->sk); - gtp->sk1u = sock1u->sk; + sk = sock->sk; + sock_hold(sk); tuncfg.sk_user_data = gtp; + tuncfg.encap_type = type; tuncfg.encap_rcv = gtp_encap_recv; tuncfg.encap_destroy = gtp_encap_destroy; - tuncfg.encap_type = UDP_ENCAP_GTP0; - setup_udp_tunnel_sock(sock_net(gtp->sk0), sock0, &tuncfg); - - tuncfg.encap_type = UDP_ENCAP_GTP1U; - setup_udp_tunnel_sock(sock_net(gtp->sk1u), sock1u, &tuncfg); - - err = 0; -err2: - sockfd_put(sock1u); -err1: - sockfd_put(sock0); - return err; + setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg); + +out_sock: + sockfd_put(sock); + return sk; +} + +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) +{ + struct sock *sk1u = NULL; + struct sock *sk0 = NULL; + + if (data[IFLA_GTP_FD0]) { + u32 fd0 = nla_get_u32(data[IFLA_GTP_FD0]); + + sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp); + if (IS_ERR(sk0)) + return PTR_ERR(sk0); + } + + if (data[IFLA_GTP_FD1]) { + u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); + + sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); + if (IS_ERR(sk1u)) { + if (sk0) + gtp_encap_disable_sock(sk0); + return PTR_ERR(sk1u); + } + } + + gtp->sk0 = sk0; + gtp->sk1u = sk1u; + + return 0; } static struct net_device *gtp_find_dev(struct net *net, int ifindex) -- 2.10.2 From aschultz at tpip.net Tue Feb 21 11:13:22 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 12:13:22 +0100 (CET) Subject: [PATCH net-next v4 0/7] gtp: misc improvements In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <1340046157.178143.1487675602597.JavaMail.zimbra@tpip.net> Hi, In case anyone want's to have a look at the changes that I'm working on for follow up series can find them at: https://github.com/RoadRunnr/net-next/tree/gtp Regards Andreas ----- On Feb 21, 2017, at 11:18 AM, Andreas Schultz aschultz at tpip.net wrote: > Hi Pablo, > > This is v4 of the GTP improvements. Compared to v3 it contains mostly > smallish naming and spelling fixes. It also drops the documentation > patch, Harald did a better job with the documentation and the some things > I described do not yet match the implementation. I'll readd the relevant > parts with a follow up series. > > This series lays the groundwork for removing the socket references from > the GTP netdevice by removing duplicate code and simplifying the logic on > some code paths. > > It slighly changes the GTP genl API by making the socket parameters optional > (though one of them is still required). > > The removal of the socket references will break the 1:1 releation between > GTP netdevice and GTP socket that prevents us to support multiple VRFs with > overlaping IP addresse spaces attached to the same GTP-U entity (needed for > multi APN support, comming a follow up series). > > Pablo found a socket hold problem in v2. In order to solve that I had to > switch the socket references from the struct socket to the internal > struct sock. This should have no functionl impact, but we can now hang > on to the reference without blocking user space from closing the GTP socket. > > v3->v4: > * drop the documentation patch > * spelling fixes > * pass nlattr instead of genl_info into gtp_find_dev, > makes the code slightly more compact and readable > v2->v3: > * add documentation to explain the goal of all these changes > * incorporate review comments > * switch from struct socket to struct sock > > Regards > Andreas > > -- > Andreas Schultz (7): > gtp: switch from struct socket to struct sock for the GTP sockets > gtp: make GTP sockets in gtp_newlink optional > gtp: merge gtp_get_net and gtp_genl_find_dev > gtp: consolidate gtp socket rx path > gtp: unify genl_find_pdp and prepare for per socket lookup > gtp: consolidate pdp context destruction into helper > gtp: add socket to pdp context > > drivers/net/gtp.c | 543 +++++++++++++++++++++++++++--------------------------- > 1 file changed, 269 insertions(+), 274 deletions(-) > > -- > 2.10.2 From davem at davemloft.net Tue Feb 21 18:15:27 2017 From: davem at davemloft.net (David Miller) Date: Tue, 21 Feb 2017 13:15:27 -0500 (EST) Subject: [PATCH net-next v4 0/7] gtp: misc improvements In-Reply-To: <20170221101855.23940-1-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> Message-ID: <20170221.131527.1278036562998923405.davem@davemloft.net> The net-next tree is closed at this time, you'll need to resubmit this series when the net-next tree opens back up after the merge window. Thanks. From aschultz at tpip.net Tue Feb 21 18:27:45 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Tue, 21 Feb 2017 19:27:45 +0100 (CET) Subject: [PATCH net-next v4 0/7] gtp: misc improvements In-Reply-To: <20170221.131527.1278036562998923405.davem@davemloft.net> References: <20170221101855.23940-1-aschultz@tpip.net> <20170221.131527.1278036562998923405.davem@davemloft.net> Message-ID: <69101837.182247.1487701665343.JavaMail.zimbra@tpip.net> Hi Pablo, Can you review and queue the patches at your GTP tree? I would really like to get this moving. The queue of GTP changes it getting longer and longer.... The full queue is here: https://github.com/RoadRunnr/net-next/compare/gtp But don't pull that, it's not entirely stable, yet. Thanks Andreas ----- On Feb 21, 2017, at 7:15 PM, David S. Miller davem at davemloft.net wrote: > The net-next tree is closed at this time, you'll need to resubmit this series > when the net-next tree opens back up after the merge window. > > Thanks. From laforge at gnumonks.org Wed Feb 22 08:36:31 2017 From: laforge at gnumonks.org (Harald Welte) Date: Wed, 22 Feb 2017 09:36:31 +0100 Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-2-aschultz@tpip.net> <20170220161552.tnewzagg4z4azikd@nataraja> <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> Message-ID: <20170222083631.722ycqfcyc2ma34u@nataraja> Hi Andreas, thanks for your feedback. On Mon, Feb 20, 2017 at 06:28:07PM +0100, Andreas Schultz wrote: > > Are you absolutely sure about this? > > Yes. It took me a while to realize this. The crux is this statement in TS 29.060, > Section 7.3.1: > > > The SGSN shall include an SGSN Address for control plane and an SGSN address for > > user traffic, which may differ from that provided by the underlying network service > > (e.g. IP). The GGSN shall store these SGSN Addresses and use them when sending > > control plane on this GTP tunnel or G-PDUs to the SGSN for the MS. > > IMHO, this implies that the source IP of GTP-U and GTP-C frames does not have to > match the GSN address specified by a Create PDP Context Request. Yes, I follow that reasoning for GTP-C (i.e. the transport-layer S-GW source IP of the CREATE PDP CTX REQ may differ from the one signalled inside GTP-C. For GTP-U however, this just means that GTP-U in general can use different IP addresses from GTP-C. This is well-known and I also have no doubts about this. But what I still doubt is whether within an ongoing GTP-U tunnel, the source IP address can change at any time without any signaling announcing that change. > TS 29.274, Section 4.2.2.1, Initial Messages: > > > During the establishment of the GTP tunnel, the GTPv2 entity selects and > > communicates to the peer GTPv2 entity the IP Destination Address at which > > it expects to receive subsequent control plane Initial messages related > > to that GTP tunnel via ..... > > TS 23.401, Annex D, Sect. D.3.3. makes is clear beyond doubt that we have to > accept packet for a valid TEID from virtually any IP. If you look at figure > D.3.3-1, step 16, you will see that only the new SGSN is contacting the P-GW. > There is no prior advertisement of the change from the old SGSN. Yes, but that is again GTP-C, and not GTP-U. > Step 16 would therefor not work if we limited the tunnel to a specific source > IP. correct. But it is a GTP-C transaction. > > I so far always assumed that you use GTP-C "MODIFY PDP CONTEXT" in case > > of such mobility situations, i.e. the control plane explicitly notifies > > the GTP-U entity of a change in the SGSN/S-GW address. > > Yes, it does. But that notification only applies to the tunnel endpoint at > the SGSN/S-GW in the GGSN/P-GW to SGSN/S-GW direction. > > > At least for 3G this seems to be the case, and my assumption appears to > > be confirmed with sources such as e.g. page 15 of > > http://www.nwadmin.de/presentation_mobility_manag ement_in_UMTS.pdf > > > > Also, for LTE, it seems that there's a GTPv2-C "Modify Bearer > > Request/Response" involved in relocation from one S-GW to another S-GW: > > http://www.lteandbeyond.com/2012/03/x2-based-handover-with-sgw-relocation.html > > I think this affirms my argument, Step 3a is sending GTP packets from a > source IP that is know to the P-GW before. The fact that this is only > used for GTP-C does IMHO not mean that the same should not apply to > GTP-U. > > >> Step 3. The target Serving GW assigns addresses and TEIDs (one per > >> bearer) for downlink traffic from the PDN GW. The Serving GW allocates > >> DL TEIDs on S5/S8 even for non-accepted bearers. It sends a Modify > >> Bearer Request (Serving GW addresses for user plane and TEID(s)) > >> message per PDN connection to the PDN GW(s). The SGW also includes > >> User Location Information IE and/or UE Time Zone IE if it is present > >> in step 2. The PDN GW updates its context field and returns a Modify > >> Bearer Response (Charging Id, MSISDN, etc.) message to the Serving GW. > >> The MSISDN is included if the PDN GW has it stored in its UE context. > >> The PDN GW starts sending downlink packets to the target GW using the > >> newly received address and TEIDs. These downlink packets will use the > >> new downlink path via the target Serving GW to the target eNodeB. The > >> Serving GW shall allocate TEIDs for the failed bearers and inform to > >> the MME. > > > > Section 5.5.1.1.3 of TS 23.401 agrees with the GTPv2C signalling during > > relocation, AFAICT. > > Again, there is nothing in there that defines a IP *source*, a F-TEID is > a tunnel ENDPOINT id not a tunnel SOURCE id. > > >> +Therefor the receiving side only identifies tunnels based on TEID's, not based > >> +on the source IP. > > > > I'm wondering if this really is neccesary. Do you have actual protocol > > traces, specs or any other literature confirming this? > > So far I have only seen symmetric GTP-U tunnels. However I believe there > is nothing that would stop a SGSN/S-GW from switching GPT-U tunnel source > transparently across IP's (for example a system with multiple shelves might > use different shelve in DL/UL direction and have therefore multiple IP's. yes, it might. But then, on the other hand - at least for what I know about 2G/3G - it is typically very cumbersome to announce IP addresses to roaming partners. It involves manual updating of related GSMA [paper?] forms, exchanging them with all roaming partners, and coordinating a change with them. AFAIK, it is not possible to announce 'this is my range of S-GW IP addreses', but you have to individually manually announce each of them in the related documents. There are projects out there where people are building additional layers of address translation just to work around those long and error-prone manual procedures, making sure that the externally visible IP addresses of a given SGSN/GGSN are always the same ones. So yes, all of the above are practical/procedural concerns, but they would be explanations why this scenario - even if it might be supported by the specs - is unlikely in practise, at least within current/past procedures and practises. Finally, from the point of view of packet filtering (which operators typically implement, otherwise the manual announcement of SGSN IP addresses by the above procedures wouldn't be required), it would be impossible to do state based filtering if there were use cases where an random IP address colud send valid traffic for a given tunnel. > > I find it somewhat surprising, given how much this opens the door for > > arbitrary spoofing from anyone (with access to the respective private > > network such as GRX). > > Yes it is, but it is mandated without a doubt by the specification for > GTP-C. For the reasons outline before, I think that this applies to > GTP-U as well. For GTP-U it may be the case. I'm not entirely convinced, though. Also, even if the specifications wanted to support such scenarios, I think it is doubtful that this is actually implemented in practise. If it was my choice, and I had to support the "loose matching", I would make it a configuration option (sysctl? netlink attribute?) and default to the more strict matching, including the source address. It just seems to make much more sense and be more safe/sane. For those people who really need the loose matching (and who are willing to pay the price for it in terms of opening up to spoofing and making packet filtering harder), they can set the option. But then, I'm not the one doing that implementation, so it is up to you. My opinion is just an opinion. Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From aschultz at tpip.net Wed Feb 22 11:30:07 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Wed, 22 Feb 2017 12:30:07 +0100 (CET) Subject: [PATCH net-next v3 1/8] gtp: add documentation In-Reply-To: <20170222083631.722ycqfcyc2ma34u@nataraja> References: <20170213153624.14170-1-aschultz@tpip.net> <20170213153624.14170-2-aschultz@tpip.net> <20170220161552.tnewzagg4z4azikd@nataraja> <1839823227.173727.1487611687205.JavaMail.zimbra@tpip.net> <20170222083631.722ycqfcyc2ma34u@nataraja> Message-ID: <404391156.187750.1487763007868.JavaMail.zimbra@tpip.net> Hi Harald, ----- On Feb 22, 2017, at 9:36 AM, laforge laforge at gnumonks.org wrote: > Hi Andreas, > > thanks for your feedback. > > On Mon, Feb 20, 2017 at 06:28:07PM +0100, Andreas Schultz wrote: >> > Are you absolutely sure about this? >> >> Yes. It took me a while to realize this. The crux is this statement in TS >> 29.060, >> Section 7.3.1: >> >> > The SGSN shall include an SGSN Address for control plane and an SGSN address for >> > user traffic, which may differ from that provided by the underlying network >> > service >> > (e.g. IP). The GGSN shall store these SGSN Addresses and use them when sending >> > control plane on this GTP tunnel or G-PDUs to the SGSN for the MS. >> >> IMHO, this implies that the source IP of GTP-U and GTP-C frames does not have to >> match the GSN address specified by a Create PDP Context Request. > > Yes, I follow that reasoning for GTP-C (i.e. the transport-layer S-GW > source IP of the CREATE PDP CTX REQ may differ from the one signalled > inside GTP-C. > > For GTP-U however, this just means that GTP-U in general can use > different IP addresses from GTP-C. This is well-known and I also have > no doubts about this. > > But what I still doubt is whether within an ongoing GTP-U tunnel, the > source IP address can change at any time without any signaling > announcing that change. > >> TS 29.274, Section 4.2.2.1, Initial Messages: >> >> > During the establishment of the GTP tunnel, the GTPv2 entity selects and >> > communicates to the peer GTPv2 entity the IP Destination Address at which >> > it expects to receive subsequent control plane Initial messages related >> > to that GTP tunnel via ..... >> >> TS 23.401, Annex D, Sect. D.3.3. makes is clear beyond doubt that we have to >> accept packet for a valid TEID from virtually any IP. If you look at figure >> D.3.3-1, step 16, you will see that only the new SGSN is contacting the P-GW. >> There is no prior advertisement of the change from the old SGSN. > > Yes, but that is again GTP-C, and not GTP-U. > >> Step 16 would therefor not work if we limited the tunnel to a specific source >> IP. > > correct. But it is a GTP-C transaction. > >> > I so far always assumed that you use GTP-C "MODIFY PDP CONTEXT" in case >> > of such mobility situations, i.e. the control plane explicitly notifies >> > the GTP-U entity of a change in the SGSN/S-GW address. >> >> Yes, it does. But that notification only applies to the tunnel endpoint at >> the SGSN/S-GW in the GGSN/P-GW to SGSN/S-GW direction. >> >> > At least for 3G this seems to be the case, and my assumption appears to >> > be confirmed with sources such as e.g. page 15 of >> > http://www.nwadmin.de/presentation_mobility_manag ement_in_UMTS.pdf >> > >> > Also, for LTE, it seems that there's a GTPv2-C "Modify Bearer >> > Request/Response" involved in relocation from one S-GW to another S-GW: >> > http://www.lteandbeyond.com/2012/03/x2-based-handover-with-sgw-relocation.html >> >> I think this affirms my argument, Step 3a is sending GTP packets from a >> source IP that is know to the P-GW before. The fact that this is only >> used for GTP-C does IMHO not mean that the same should not apply to >> GTP-U. >> >> >> Step 3. The target Serving GW assigns addresses and TEIDs (one per >> >> bearer) for downlink traffic from the PDN GW. The Serving GW allocates >> >> DL TEIDs on S5/S8 even for non-accepted bearers. It sends a Modify >> >> Bearer Request (Serving GW addresses for user plane and TEID(s)) >> >> message per PDN connection to the PDN GW(s). The SGW also includes >> >> User Location Information IE and/or UE Time Zone IE if it is present >> >> in step 2. The PDN GW updates its context field and returns a Modify >> >> Bearer Response (Charging Id, MSISDN, etc.) message to the Serving GW. >> >> The MSISDN is included if the PDN GW has it stored in its UE context. >> >> The PDN GW starts sending downlink packets to the target GW using the >> >> newly received address and TEIDs. These downlink packets will use the >> >> new downlink path via the target Serving GW to the target eNodeB. The >> >> Serving GW shall allocate TEIDs for the failed bearers and inform to >> >> the MME. >> > >> > Section 5.5.1.1.3 of TS 23.401 agrees with the GTPv2C signalling during >> > relocation, AFAICT. >> >> Again, there is nothing in there that defines a IP *source*, a F-TEID is >> a tunnel ENDPOINT id not a tunnel SOURCE id. >> >> >> +Therefor the receiving side only identifies tunnels based on TEID's, not based >> >> +on the source IP. >> > >> > I'm wondering if this really is neccesary. Do you have actual protocol >> > traces, specs or any other literature confirming this? >> >> So far I have only seen symmetric GTP-U tunnels. However I believe there >> is nothing that would stop a SGSN/S-GW from switching GPT-U tunnel source >> transparently across IP's (for example a system with multiple shelves might >> use different shelve in DL/UL direction and have therefore multiple IP's. > > yes, it might. But then, on the other hand - at least for what I know > about 2G/3G - it is typically very cumbersome to announce IP addresses > to roaming partners. It involves manual updating of related GSMA > [paper?] forms, exchanging them with all roaming partners, and > coordinating a change with them. AFAIK, it is not possible to announce > 'this is my range of S-GW IP addreses', but you have to individually > manually announce each of them in the related documents. There are > projects out there where people are building additional layers of > address translation just to work around those long and error-prone > manual procedures, making sure that the externally visible IP addresses > of a given SGSN/GGSN are always the same ones. > > So yes, all of the above are practical/procedural concerns, but they > would be explanations why this scenario - even if it might be supported > by the specs - is unlikely in practise, at least within current/past > procedures and practises. > > Finally, from the point of view of packet filtering (which operators > typically implement, otherwise the manual announcement of SGSN IP > addresses by the above procedures wouldn't be required), it would be > impossible to do state based filtering if there were use cases where an > random IP address colud send valid traffic for a given tunnel. All of above applies to roaming, however provider internal traffic is usually not filtered and bound to GSMA IR.21 process. The use cases and procedures supported in roaming might therefore be different from the ones when connected to the home PLMN. If a firewall is deployed, then it's a decision made at that point to block or allow certain scenarios, but that should not have an impact on what we would like to support at the GTP-U instance. I believe we should attempt to support all defined and legal use-cases and leave the filtering to other layers (iptables, nftables or external firewalls). The GSMA has also something to say about firewall and GTP IP source addresses (although I think this mostly applies to GTP-C), GSMA IR.33 v8.01, Section A.2: > Unlike GTP version 0, in GTP version 1 the GGSN is allowed to send > GTP response messages back to an SGSN with the source IP address set > to an IP address different to that which was in the destination address > of the associated GTP request message. The change was made in 3GPP to > optimize internal processing of GGSNs. I then goes on to describe how state-aware firewalls will have problems with that. Anyhow, this clearly indicates that using different source IP's is a valid use case (whether or not that might break in roaming scenarios). >> > I find it somewhat surprising, given how much this opens the door for >> > arbitrary spoofing from anyone (with access to the respective private >> > network such as GRX). >> >> Yes it is, but it is mandated without a doubt by the specification for >> GTP-C. For the reasons outline before, I think that this applies to >> GTP-U as well. > > For GTP-U it may be the case. I'm not entirely convinced, though. > Also, even if the specifications wanted to support such scenarios, I > think it is doubtful that this is actually implemented in practise. I've done a test with a GTP implementation in a router from a big vendor (an ePDG, not a GGSN/PGW). That implementation did not like asymmetric GTP-U endpoint IP's. However, this seems to be a limitation of some kind of receive path optimization and not a deliberate filter (at least when I interpret the error messages correctly). > If it was my choice, and I had to support the "loose matching", I would > make it a configuration option (sysctl? netlink attribute?) and default > to the more strict matching, including the source address. It just > seems to make much more sense and be more safe/sane. For those people > who really need the loose matching (and who are willing to pay the price > for it in terms of opening up to spoofing and making packet filtering > harder), they can set the option. That might be an idea, not in the sense of matching but as an additional filter. After the all the TEID is supposed the unique identifier for the tunnel in the GTP-U entity. We currently do not match or filter on the peer GSN source IP. That should IMHO remain the default. Adding a per PDP context flag to filter on the peer GSN address should be relatively simple. > But then, I'm not the one doing that implementation, so it is up to you. > My opinion is just an opinion. > > Regards, > Harald Regards, Andreas > > -- > - Harald Welte http://laforge.gnumonks.org/ > ============================================================================ > "Privacy in residential applications is a desirable marketing option." > (ETSI EN 300 175-7 Ch. A6) From aschultz at tpip.net Thu Feb 23 09:19:51 2017 From: aschultz at tpip.net (Andreas Schultz) Date: Thu, 23 Feb 2017 10:19:51 +0100 (CET) Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> Message-ID: <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> Hi Tom, ----- On Feb 22, 2017, at 6:41 PM, Tom Herbert tom at herbertland.com wrote: > On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz wrote: >> Add network device to gtp context in preparation for splitting >> the TEID from the network device. >> >> Use this to rework the socker rx path. Move the common RX part >> of v0 and v1 into a helper. Also move the final rx part into >> that helper as well. >> > Andeas, > > How are these GTP kernel patches being tested? We rn each version in a test setup with a ePDG and a SGW that connects to a full GGSN/P-GW instance (based on erGW). We also run performance test (less often) with a commercial test software. > Is it possible to > create some sort of GTP network device > that separates out just the datapath for development in the > same way that VXLAN did this? We had this discussion about another patch: (http://marc.info/?l=linux-netdev&m=148611438811696&w=2) Currently the kernel module only supports the GGSN/P-GW side of the GTP tunnel. This is because we check the UE IP address in the GTP socket and use the destination IP in the network interface to find the PDP context. For a deployment in a real EPC, doing it the other way makes no sense with the current code. However for a test setup it makes perfect sense (either to use it as a driver to test other GTP nodes or to test out own implementation). So, I hope that we can integrate this soonish. libgtpnl contains two tools that be used for testing. gtp-link creates a network device and GTP sockets and keeps them alive. gtp-tunnel can then be used add PDP context to that. The only missing part for a bidirectional test setup is the above mentioned patch with the direction flag and support for that in the libgtpnl tools. Adding static tunnel support into the kernel module in any form makes IMHO no sense. GTP as defined by 3GPP always need a control instance and there are much better options for static tunnel encapsulations. Andreas > > Tom > >> Signed-off-by: Andreas Schultz >> --- >> drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- >> 1 file changed, 44 insertions(+), 36 deletions(-) >> >> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c >> index 961fb3c..fc0fff5 100644 >> --- a/drivers/net/gtp.c >> +++ b/drivers/net/gtp.c >> @@ -58,6 +58,8 @@ struct pdp_ctx { >> struct in_addr ms_addr_ip4; >> struct in_addr sgsn_addr_ip4; >> >> + struct net_device *dev; >> + >> atomic_t tx_seq; >> struct rcu_head rcu_head; >> }; >> @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct >> pdp_ctx *pctx, >> return false; >> } >> >> +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int >> hdrlen, >> + bool xnet) >> +{ >> + struct pcpu_sw_netstats *stats; >> + >> + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >> + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); >> + return 1; >> + } >> + >> + /* Get rid of the GTP + UDP headers. */ >> + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) >> + return -1; >> + >> + netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); >> + >> + /* Now that the UDP and the GTP header have been removed, set up the >> + * new network header. This is required by the upper layer to >> + * calculate the transport header. >> + */ >> + skb_reset_network_header(skb); >> + >> + skb->dev = pctx->dev; >> + >> + stats = this_cpu_ptr(pctx->dev->tstats); >> + u64_stats_update_begin(&stats->syncp); >> + stats->rx_packets++; >> + stats->rx_bytes += skb->len; >> + u64_stats_update_end(&stats->syncp); >> + >> + netif_rx(skb); >> + return 0; >> +} >> + >> /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ >> static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, >> bool xnet) >> @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct >> sk_buff *skb, >> return 1; >> } >> >> - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >> - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); >> - return 1; >> - } >> - >> - /* Get rid of the GTP + UDP headers. */ >> - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); >> + return gtp_rx(pctx, skb, hdrlen, xnet); >> } >> >> static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, >> @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct >> sk_buff *skb, >> return 1; >> } >> >> - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >> - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); >> - return 1; >> - } >> - >> - /* Get rid of the GTP + UDP headers. */ >> - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); >> + return gtp_rx(pctx, skb, hdrlen, xnet); >> } >> >> static void gtp_encap_destroy(struct sock *sk) >> @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) >> */ >> static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) >> { >> - struct pcpu_sw_netstats *stats; >> struct gtp_dev *gtp; >> + int ret = 0; >> bool xnet; >> - int ret; >> >> gtp = rcu_dereference_sk_user_data(sk); >> if (!gtp) >> @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff >> *skb) >> switch (ret) { >> case 1: >> netdev_dbg(gtp->dev, "pass up to the process\n"); >> - return 1; >> + break; >> case 0: >> - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); >> break; >> case -1: >> netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); >> kfree_skb(skb); >> - return 0; >> + ret = 0; >> + break; >> } >> >> - /* Now that the UDP and the GTP header have been removed, set up the >> - * new network header. This is required by the upper layer to >> - * calculate the transport header. >> - */ >> - skb_reset_network_header(skb); >> - >> - skb->dev = gtp->dev; >> - >> - stats = this_cpu_ptr(gtp->dev->tstats); >> - u64_stats_update_begin(&stats->syncp); >> - stats->rx_packets++; >> - stats->rx_bytes += skb->len; >> - u64_stats_update_end(&stats->syncp); >> - >> - netif_rx(skb); >> - >> - return 0; >> + return ret; >> } >> >> static int gtp_dev_init(struct net_device *dev) >> @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct >> genl_info *info) >> if (pctx == NULL) >> return -ENOMEM; >> >> + pctx->dev = gtp->dev; >> ipv4_pdp_fill(pctx, info); >> atomic_set(&pctx->tx_seq, 0); >> >> -- >> 2.10.2 From laforge at gnumonks.org Thu Feb 23 16:46:57 2017 From: laforge at gnumonks.org (Harald Welte) Date: Thu, 23 Feb 2017 17:46:57 +0100 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> Message-ID: <20170223164657.tmj7djdpgcdlm2ri@nataraja> Hi Tom, On Thu, Feb 23, 2017 at 08:28:47AM -0800, Tom Herbert wrote: > If there is a way for us to test this without setting up a full mobile > network please provide details on how to do that in the commit log. There are different ways how to do this. With the existing in-kernel code (that lacks the "SGSN role" patch which would be for testing only), you can e.g. use two relatively small C-language programs from the openggnsn package [http://git.osmocom.org/openggsn/]: * OpenGGSN with support for libgtpnl and the kernel GTP-U module * sgsnemu (included in openggsn source tree) which implements a minimal SGSN-side of the tunnel. It will * perform the GTP-C signaling required with OpenGGSN (which in turn will then instruct the kernel to open a tunnel via the netlink interface). * start a tun/tap device for the "mobile station end" of the tunnel perform en/decapsulation of data between that tun/tap device and GTP in userspace The wiki at https://osmocom.org/projects/openggsn/wiki/OpenGGSN contains step-by-step instructions how to build and configure OpenGGSN with support for kernel GTP-U. It's nothing more than autotools based compile+install of libgtpnl followed by "./configure --enable-gtp-linux" and "make" for OpenGGSN What is not described on the above page is how to use sgsnemu to simulate the SGSN side, as within Osmocom we typically run the open source OsmoSGSN (a more "real" SGSN). So using two small binaries that can be compiled without much external dependencies (and very few lines of configuration), it is possible to do some functional testing of the kernel GTP module. For performance testing this of course won't work, as sgsnemu is running entirely in userspace. For performance testing, you would need a SGSN-side implementation of GTP-U that performs equally well or better than the GGSN-side implementation in the kernel. One option is the patch that has recently been submitted to netdev and which is under discussion. However, then you simply test one implementation against itself, which provides no interoperability guarantees with other implementations, and thus also limited in different regards. > > Adding static tunnel support into the kernel module in any form > > makes IMHO no sense. GTP as defined by 3GPP always need a control > > instance and there are much better options for static tunnel > > encapsulations. > > > That misses the point. From the kernel point of view GTP is just > another encapsulation protocol and we now have a lot of experience > with those. The problem is when you post patches to improve it or fix > issues, if there is no practical way to perform independent analysis > or investigation. This makes GTP no different than a proprietary > protocol to us and that severely limits the value of trying to work > with the community. I wholeheartedly agree. That's one of the reasons why I recently posted a RFC about what to do for (regression and other) testing of the kernel GTP-U module. I'll try to cook up some instructions extending https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also sgsnemu for a basic use case of establishing one single tunnel. That's of course like a manual "HOWTO" and not yet anything that can be tested automatically. Regards, Harald -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From tom at herbertland.com Wed Feb 22 17:41:17 2017 From: tom at herbertland.com (Tom Herbert) Date: Wed, 22 Feb 2017 09:41:17 -0800 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: <20170221101855.23940-5-aschultz@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> Message-ID: On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz wrote: > Add network device to gtp context in preparation for splitting > the TEID from the network device. > > Use this to rework the socker rx path. Move the common RX part > of v0 and v1 into a helper. Also move the final rx part into > that helper as well. > Andeas, How are these GTP kernel patches being tested? Is it possible to create some sort of GTP network device that separates out just the datapath for development in the same way that VXLAN did this? Tom > Signed-off-by: Andreas Schultz > --- > drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- > 1 file changed, 44 insertions(+), 36 deletions(-) > > diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c > index 961fb3c..fc0fff5 100644 > --- a/drivers/net/gtp.c > +++ b/drivers/net/gtp.c > @@ -58,6 +58,8 @@ struct pdp_ctx { > struct in_addr ms_addr_ip4; > struct in_addr sgsn_addr_ip4; > > + struct net_device *dev; > + > atomic_t tx_seq; > struct rcu_head rcu_head; > }; > @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx, > return false; > } > > +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen, > + bool xnet) > +{ > + struct pcpu_sw_netstats *stats; > + > + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { > + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); > + return 1; > + } > + > + /* Get rid of the GTP + UDP headers. */ > + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) > + return -1; > + > + netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); > + > + /* Now that the UDP and the GTP header have been removed, set up the > + * new network header. This is required by the upper layer to > + * calculate the transport header. > + */ > + skb_reset_network_header(skb); > + > + skb->dev = pctx->dev; > + > + stats = this_cpu_ptr(pctx->dev->tstats); > + u64_stats_update_begin(&stats->syncp); > + stats->rx_packets++; > + stats->rx_bytes += skb->len; > + u64_stats_update_end(&stats->syncp); > + > + netif_rx(skb); > + return 0; > +} > + > /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ > static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, > bool xnet) > @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, > return 1; > } > > - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { > - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); > - return 1; > - } > - > - /* Get rid of the GTP + UDP headers. */ > - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); > + return gtp_rx(pctx, skb, hdrlen, xnet); > } > > static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, > @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, > return 1; > } > > - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { > - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); > - return 1; > - } > - > - /* Get rid of the GTP + UDP headers. */ > - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); > + return gtp_rx(pctx, skb, hdrlen, xnet); > } > > static void gtp_encap_destroy(struct sock *sk) > @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) > */ > static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) > { > - struct pcpu_sw_netstats *stats; > struct gtp_dev *gtp; > + int ret = 0; > bool xnet; > - int ret; > > gtp = rcu_dereference_sk_user_data(sk); > if (!gtp) > @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) > switch (ret) { > case 1: > netdev_dbg(gtp->dev, "pass up to the process\n"); > - return 1; > + break; > case 0: > - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); > break; > case -1: > netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); > kfree_skb(skb); > - return 0; > + ret = 0; > + break; > } > > - /* Now that the UDP and the GTP header have been removed, set up the > - * new network header. This is required by the upper layer to > - * calculate the transport header. > - */ > - skb_reset_network_header(skb); > - > - skb->dev = gtp->dev; > - > - stats = this_cpu_ptr(gtp->dev->tstats); > - u64_stats_update_begin(&stats->syncp); > - stats->rx_packets++; > - stats->rx_bytes += skb->len; > - u64_stats_update_end(&stats->syncp); > - > - netif_rx(skb); > - > - return 0; > + return ret; > } > > static int gtp_dev_init(struct net_device *dev) > @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info) > if (pctx == NULL) > return -ENOMEM; > > + pctx->dev = gtp->dev; > ipv4_pdp_fill(pctx, info); > atomic_set(&pctx->tx_seq, 0); > > -- > 2.10.2 > From tom at herbertland.com Thu Feb 23 16:28:47 2017 From: tom at herbertland.com (Tom Herbert) Date: Thu, 23 Feb 2017 08:28:47 -0800 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> Message-ID: On Thu, Feb 23, 2017 at 1:19 AM, Andreas Schultz wrote: > Hi Tom, > > ----- On Feb 22, 2017, at 6:41 PM, Tom Herbert tom at herbertland.com wrote: > >> On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz wrote: >>> Add network device to gtp context in preparation for splitting >>> the TEID from the network device. >>> >>> Use this to rework the socker rx path. Move the common RX part >>> of v0 and v1 into a helper. Also move the final rx part into >>> that helper as well. >>> >> Andeas, >> >> How are these GTP kernel patches being tested? > > We rn each version in a test setup with a ePDG and a SGW that > connects to a full GGSN/P-GW instance (based on erGW). > We also run performance test (less often) with a commercial > test software. > >> Is it possible to > create some sort of GTP network device >> that separates out just the datapath for development in the >> same way that VXLAN did this? > > We had this discussion about another patch: > (http://marc.info/?l=linux-netdev&m=148611438811696&w=2) > > Currently the kernel module only supports the GGSN/P-GW side > of the GTP tunnel. This is because we check the UE IP address > in the GTP socket and use the destination IP in the network > interface to find the PDP context. > > For a deployment in a real EPC, doing it the other way makes no > sense with the current code. However for a test setup it makes > perfect sense (either to use it as a driver to test other GTP > nodes or to test out own implementation). > > So, I hope that we can integrate this soonish. > > libgtpnl contains two tools that be used for testing. gtp-link > creates a network device and GTP sockets and keeps them alive. > gtp-tunnel can then be used add PDP context to that. The only > missing part for a bidirectional test setup is the above > mentioned patch with the direction flag and support for that > in the libgtpnl tools. > If there is a way for us to test this without setting up a full mobile network please provide details on how to do that in the commit log. > Adding static tunnel support into the kernel module in any form > makes IMHO no sense. GTP as defined by 3GPP always need a control > instance and there are much better options for static tunnel > encapsulations. > That misses the point. From the kernel point of view GTP is just another encapsulation protocol and we now have a lot of experience with those. The problem is when you post patches to improve it or fix issues, if there is no practical way to perform independent analysis or investigation. This makes GTP no different than a proprietary protocol to us and that severely limits the value of trying to work with the community. Tom > Andreas > >> >> Tom >> >>> Signed-off-by: Andreas Schultz >>> --- >>> drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++------------------------- >>> 1 file changed, 44 insertions(+), 36 deletions(-) >>> >>> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c >>> index 961fb3c..fc0fff5 100644 >>> --- a/drivers/net/gtp.c >>> +++ b/drivers/net/gtp.c >>> @@ -58,6 +58,8 @@ struct pdp_ctx { >>> struct in_addr ms_addr_ip4; >>> struct in_addr sgsn_addr_ip4; >>> >>> + struct net_device *dev; >>> + >>> atomic_t tx_seq; >>> struct rcu_head rcu_head; >>> }; >>> @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct >>> pdp_ctx *pctx, >>> return false; >>> } >>> >>> +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int >>> hdrlen, >>> + bool xnet) >>> +{ >>> + struct pcpu_sw_netstats *stats; >>> + >>> + if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >>> + netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); >>> + return 1; >>> + } >>> + >>> + /* Get rid of the GTP + UDP headers. */ >>> + if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet)) >>> + return -1; >>> + >>> + netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n"); >>> + >>> + /* Now that the UDP and the GTP header have been removed, set up the >>> + * new network header. This is required by the upper layer to >>> + * calculate the transport header. >>> + */ >>> + skb_reset_network_header(skb); >>> + >>> + skb->dev = pctx->dev; >>> + >>> + stats = this_cpu_ptr(pctx->dev->tstats); >>> + u64_stats_update_begin(&stats->syncp); >>> + stats->rx_packets++; >>> + stats->rx_bytes += skb->len; >>> + u64_stats_update_end(&stats->syncp); >>> + >>> + netif_rx(skb); >>> + return 0; >>> +} >>> + >>> /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */ >>> static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, >>> bool xnet) >>> @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct >>> sk_buff *skb, >>> return 1; >>> } >>> >>> - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >>> - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); >>> - return 1; >>> - } >>> - >>> - /* Get rid of the GTP + UDP headers. */ >>> - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); >>> + return gtp_rx(pctx, skb, hdrlen, xnet); >>> } >>> >>> static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb, >>> @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct >>> sk_buff *skb, >>> return 1; >>> } >>> >>> - if (!gtp_check_src_ms(skb, pctx, hdrlen)) { >>> - netdev_dbg(gtp->dev, "No PDP ctx for this MS\n"); >>> - return 1; >>> - } >>> - >>> - /* Get rid of the GTP + UDP headers. */ >>> - return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet); >>> + return gtp_rx(pctx, skb, hdrlen, xnet); >>> } >>> >>> static void gtp_encap_destroy(struct sock *sk) >>> @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp) >>> */ >>> static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb) >>> { >>> - struct pcpu_sw_netstats *stats; >>> struct gtp_dev *gtp; >>> + int ret = 0; >>> bool xnet; >>> - int ret; >>> >>> gtp = rcu_dereference_sk_user_data(sk); >>> if (!gtp) >>> @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff >>> *skb) >>> switch (ret) { >>> case 1: >>> netdev_dbg(gtp->dev, "pass up to the process\n"); >>> - return 1; >>> + break; >>> case 0: >>> - netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n"); >>> break; >>> case -1: >>> netdev_dbg(gtp->dev, "GTP packet has been dropped\n"); >>> kfree_skb(skb); >>> - return 0; >>> + ret = 0; >>> + break; >>> } >>> >>> - /* Now that the UDP and the GTP header have been removed, set up the >>> - * new network header. This is required by the upper layer to >>> - * calculate the transport header. >>> - */ >>> - skb_reset_network_header(skb); >>> - >>> - skb->dev = gtp->dev; >>> - >>> - stats = this_cpu_ptr(gtp->dev->tstats); >>> - u64_stats_update_begin(&stats->syncp); >>> - stats->rx_packets++; >>> - stats->rx_bytes += skb->len; >>> - u64_stats_update_end(&stats->syncp); >>> - >>> - netif_rx(skb); >>> - >>> - return 0; >>> + return ret; >>> } >>> >>> static int gtp_dev_init(struct net_device *dev) >>> @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct >>> genl_info *info) >>> if (pctx == NULL) >>> return -ENOMEM; >>> >>> + pctx->dev = gtp->dev; >>> ipv4_pdp_fill(pctx, info); >>> atomic_set(&pctx->tx_seq, 0); >>> >>> -- >>> 2.10.2 From tom at herbertland.com Thu Feb 23 18:07:03 2017 From: tom at herbertland.com (Tom Herbert) Date: Thu, 23 Feb 2017 10:07:03 -0800 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: <20170223164657.tmj7djdpgcdlm2ri@nataraja> References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> <20170223164657.tmj7djdpgcdlm2ri@nataraja> Message-ID: On Thu, Feb 23, 2017 at 8:46 AM, Harald Welte wrote: > Hi Tom, > > On Thu, Feb 23, 2017 at 08:28:47AM -0800, Tom Herbert wrote: > >> If there is a way for us to test this without setting up a full mobile >> network please provide details on how to do that in the commit log. > > There are different ways how to do this. With the existing in-kernel > code (that lacks the "SGSN role" patch which would be for testing only), > you can e.g. use two relatively small C-language programs from the > openggnsn package [http://git.osmocom.org/openggsn/]: > > * OpenGGSN with support for libgtpnl and the kernel GTP-U module > * sgsnemu (included in openggsn source tree) which implements a minimal > SGSN-side of the tunnel. It will > * perform the GTP-C signaling required with OpenGGSN (which in turn > will then instruct the kernel to open a tunnel via the netlink > interface). > * start a tun/tap device for the "mobile station end" of the tunnel > perform en/decapsulation of data between that tun/tap device and GTP > in userspace > > The wiki at https://osmocom.org/projects/openggsn/wiki/OpenGGSN contains > step-by-step instructions how to build and configure OpenGGSN with > support for kernel GTP-U. It's nothing more than autotools based > compile+install of libgtpnl followed by "./configure --enable-gtp-linux" > and "make" for OpenGGSN > > What is not described on the above page is how to use sgsnemu to > simulate the SGSN side, as within Osmocom we typically run the open > source OsmoSGSN (a more "real" SGSN). > > So using two small binaries that can be compiled without much external > dependencies (and very few lines of configuration), it is possible to do > some functional testing of the kernel GTP module. For performance > testing this of course won't work, as sgsnemu is running entirely in > userspace. > I'm looking at the GTP encapsulation, it's not particularly complex. Is there any real reason why we can't just implement a netdev interface with static tunnels and configuration to do performance testing and development? For instance, if we want to add GSO/GRO support to GTP that's all we really need, the control plane should be inconsequential in that case. > For performance testing, you would need a SGSN-side implementation of > GTP-U that performs equally well or better than the GGSN-side > implementation in the kernel. One option is the patch that has recently > been submitted to netdev and which is under discussion. However, then > you simply test one implementation against itself, which provides no > interoperability guarantees with other implementations, and thus also > limited in different regards. > That's always true of any protocol we implement-- you can only show interoperability with what you test against. The best thing we can do to help GTP is not treat it as being something special! Treat it like any another encapsulation protocol to support in the kernel that needs to be tested, maintained, have good performance, be interoperable, etc. >> > Adding static tunnel support into the kernel module in any form >> > makes IMHO no sense. GTP as defined by 3GPP always need a control >> > instance and there are much better options for static tunnel >> > encapsulations. >> > >> That misses the point. From the kernel point of view GTP is just >> another encapsulation protocol and we now have a lot of experience >> with those. The problem is when you post patches to improve it or fix >> issues, if there is no practical way to perform independent analysis >> or investigation. This makes GTP no different than a proprietary >> protocol to us and that severely limits the value of trying to work >> with the community. > > I wholeheartedly agree. That's one of the reasons why I recently posted > a RFC about what to do for (regression and other) testing of the kernel > GTP-U module. > > I'll try to cook up some instructions extending > https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also > sgsnemu for a basic use case of establishing one single tunnel. That's > of course like a manual "HOWTO" and not yet anything that can be tested > automatically. > That would be good. Thanks! > Regards, > Harald > > -- > - Harald Welte http://laforge.gnumonks.org/ > ============================================================================ > "Privacy in residential applications is a desirable marketing option." > (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Thu Feb 23 21:17:35 2017 From: laforge at gnumonks.org (Harald Welte) Date: Thu, 23 Feb 2017 22:17:35 +0100 Subject: [PATCH net-next v4 4/7] gtp: consolidate gtp socket rx path In-Reply-To: References: <20170221101855.23940-1-aschultz@tpip.net> <20170221101855.23940-5-aschultz@tpip.net> <754038178.195270.1487841591136.JavaMail.zimbra@tpip.net> <20170223164657.tmj7djdpgcdlm2ri@nataraja> Message-ID: <20170223211735.c47ms7yobt2omhgr@nataraja> Hi Tom, On Thu, Feb 23, 2017 at 10:07:03AM -0800, Tom Herbert wrote: > I'm looking at the GTP encapsulation, it's not particularly complex. > Is there any real reason why we can't just implement a netdev > interface with static tunnels and configuration to do performance > testing and development? For instance, if we want to add GSO/GRO > support to GTP that's all we really need, the control plane should be > inconsequential in that case. As outlined several times in this thread, GTP tunneling is not symmetric. The current mainline code can only act as one of the two roles (GGSN/G-GW), not as the other one. The rationale is that in 3GPP networks, that is the only point in the network that takes native IP data (e.g.from the internet) and puts it into GTP. At all other places in the network, you don't have this combination. By the time the inner IP data arrives at the mobile phone, it is no longer encapsulated in GTP, as the lower layers have been adapted one or multiple times to other protocols by other network elements. There's a patch that has recently been submitted which adds the capability for the SGSN/S-GW side of a GTP-U tunnel, but that patch has so far not been merged (due to concersn about its netlink interface), and the patch *only* exists for testing, it has no real-world application in 3GPP networks. So as of now, it is not possible to run both endpoints of a tunnel in Linux. > > For performance testing, you would need a SGSN-side implementation of > > I'll try to cook up some instructions extending > > https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also > > sgsnemu for a basic use case of establishing one single tunnel. That's > > of course like a manual "HOWTO" and not yet anything that can be tested > > automatically. > > > That would be good. Thanks! I've spent some hours earlier today on this, I expect the document to be ready at some point over the weekend. -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6) From laforge at gnumonks.org Fri Feb 24 16:50:59 2017 From: laforge at gnumonks.org (Harald Welte) Date: Fri, 24 Feb 2017 17:50:59 +0100 Subject: Basic test setup for testing of Kernel GTP-U Message-ID: <20170224165059.i47wdj6yg3pv6z3p@nataraja> Hi all, [intentionally breaking the thread here] On Thu, Feb 23, 2017 at 05:46:57PM +0100, Harald Welte wrote: > I'll try to cook up some instructions extending > https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also > sgsnemu for a basic use case of establishing one single tunnel. That's > of course like a manual "HOWTO" and not yet anything that can be tested > automatically. I've documented the instructions at https://osmocom.org/projects/linux-kernel-gtp-u/wiki/Basic_Testing Please let me know any updates/corrections/questions if you try to reproduce this. The above instructions were working for me yesterday. Please find an ASCII export of this below (much less readable than the wiki). Regards, Harald h1. Basic Testing This page documents some basic testing setup for the Kenrel GTP-U code. It follows the below rationale: * focus on testing the kernel GTP-U module without too much external dependencies * test GTP-U interoperability of the kernel with at least one other implementation, not just kernel-to-kernel (which currently is not supported in the kernel, as it only implements the GGSN/P-GW role) * limit testing to SGSN/S-GW and GGSN/P-GW, without a real cellular network (which is possible e.g. using [[OsmoSGSN:]] and [[OsmoPCU:]]) h2. Building / Installing dependencies In order to follow below test instructions, you will need * A Linux kernel including the GTP-U driver (@drivers/net/gtp.c@) either compiled-in or as kernel module * [[libgtpnl]] - the userspace library providing an API around the kernel GTP-U netlink interface * [[OpenGGSN:]] - a minimal C-language implementation of a 3GPP GGSN, also contains a SGSN-side emulator called [[OpenGGSN:sgsnemu]] ** make sure you use Version 0.93 or later ** make sure you compile it with @--enable-gtp-linux@ enable during the @./configure@ step You can find some instructions on how to build [[OpenGGSN:]] with support for [[libgtpnl]] and kernel GTP-U at this wiki page: [[OpenGGSN:Kernel_GTP]] h2. Test setup description We will run the GGSN natively on the host, and put the emulated SGSN inside a separate network namespace. The two namespaces are interconnected by a virtual ethernet device using the transfer network 172.31.1.0/24 The GGSN is configured to provide a pool of IP addresses from the 192.168.71.0/24 range. Each PDP context will be allocated one dynamic address from that pool h2. Test instructions h3. create the network namespace for the SGSN ip netns add sgsn h3. add veth to be used between SGSN and GGSN ip link add veth0 type veth peer name veth1 h3. remote (SGSN) side of veth device
ip link set veth1 netns sgsn
ip netns exec sgsn ip addr add 172.31.1.2/24 dev veth1
ip netns exec sgsn ip link set veth1 up
h3. local (GGSN) side of veth device
ip addr add  172.31.1.1/24 dev veth0
ip link set veth0 up
h3. execute the GGSN on the host ggsn -g -c ./ggsn.conf.test (use the file attached to this wiki page) The @-g@ option is responsible for activating kernel-GTP support. If you cannot get the example described in this document to work, try the pure GTP-U userspace implementation by removing @-g@ from the above command line. If it works then, there's something related to Kernel GTP-U that breaks the setup. h3. execute the emulated SGSN inside the sgsn namespace ip netns exec sgsn sgsnemu -d -r 172.31.1.1 -l 172.31.1.2 --defaultroute --createif h3. verify the existnace of the GTP tunnel
ggsn:~# gtp-tunnel list
version 1 tei 1/1 ms_addr 192.168.71.2 sgsn_addr 172.31.1.2
h3. further testing in the @sgsn@ namespace, there's now a default-route that points into the GTP tunnel. You can use this to ping any network address that's reachable to the GGSN host. If that host is connected to the internet, you can e.g. run a ping command from within the namespace using ip netns exec sgsn ping -c 10 8.8.8.8 which will send some IP packets to 8.8.8.8 via the tun0 device (created by [[OpenGGSN:sgsnemu]]). It will be encapsulated by the userspace GTP-U implementation of sgsnemu, sent via the veth device to the host, where it ends up inthe GTP-U kernel module, decapsulating the package and passing in on to the gtp0 device there. Anything beyond that point depends on your local routing configuration. == Building OpenGGSN == h1. OpenGGSN and Linux Kernel accelerated GTP-U OpenGGSN has support to use the Linux kernel GTP-U code to accelerate the data/user plane while still implementing the control plane (GTP-C) in userspace in OpenGGSN. For more information about the Linux kernel GTP-U code, please see [[linux-kernel-gtp-u:]] h2. Building OpenGGSN with kernel which has GTP-U support At the time of writing (2017-01-09) of this wiki, below listed distributions have support of GTP kernels : * Debian 4.9.1-1~exp1 - Debian's latest experimental build * Ubuntu 16.10 kernel 4.8 * OpenSUSE Tumbleweed (stable) 4.8 (.12) * Fedora 25 kernel 4.8 Following two Debian kernels do not activate GTP kernel module during build: 4.8.0 and 4.9.0. *It is expected that complete openbsc project and related dependencies are pre-installed.* Check if package @libc-ares-dev@ is installed and if not please add it. Ubuntu 16.10, kernel 4.8.0-30-generic is used. * Installing dependencies and build library @libgtpnl@ You can install those packages with:
sudo apt install libtalloc-dev libpcsclite libmnl-dev
Please follow instructions provided at [[cellular-infrastructure:Build from source]] in order to install following library and projects : Information about dependencies between Osmocom projects is given at the above link: * libgtpnl
  sudo make install
  sudo ldconfig
* libosmocore * openggsn
./configure --enable-gtp-linux
make 
sudo make install
sudo ldconfig
Following message is shown at end of the command: @ ./configure --enable-gtp-linux@
openggsn Configuration:
  GTP Linux kernel support:			yes
This means that appropriate header files are available. -- - Harald Welte http://laforge.gnumonks.org/ ============================================================================ "Privacy in residential applications is a desirable marketing option." (ETSI EN 300 175-7 Ch. A6)