From patchwork Mon Nov 16 15:06:51 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Schultz X-Patchwork-Id: 545061 X-Patchwork-Delegate: pablo@netfilter.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (unknown [IPv6:2a01:4f8:191:444b::2:7]) by ozlabs.org (Postfix) with ESMTP id 97C74141452 for ; Tue, 17 Nov 2015 02:07:22 +1100 (AEDT) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id 5D5C19C94; Mon, 16 Nov 2015 15:07:21 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail.tpip.net (mail.tpip.net [92.43.49.48]) by lists.osmocom.org (Postfix) with ESMTP id 493D99C5F for ; Mon, 16 Nov 2015 15:07:19 +0000 (UTC) Received: from office.tpip.net (office.tpip.net [92.43.51.2]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.tpip.net (Postfix) with ESMTPS id C14394F4DA; Mon, 16 Nov 2015 15:07:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id 07848A2CAB; Mon, 16 Nov 2015 16:07:09 +0100 (CET) Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id 62tSiu19Q-Kg; Mon, 16 Nov 2015 16:07:08 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id 42AC0A2CAA; Mon, 16 Nov 2015 16:07:08 +0100 (CET) X-Virus-Scanned: amavisd-new at tpip.net Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id kOVNzW2AfWy4; Mon, 16 Nov 2015 16:07:08 +0100 (CET) Received: from alice.tpip.org (unknown [192.168.13.53]) by office.tpip.net (Postfix) with ESMTPSA id E8CE5A2CA9; Mon, 16 Nov 2015 16:07:07 +0100 (CET) From: Andreas Schultz To: openbsc@lists.osmocom.org Subject: [PATCH 10/16] gtp: switch to iptunnel framework and no longer depend on real_dev Date: Mon, 16 Nov 2015 16:06:51 +0100 Message-Id: <1447686417-3979-11-git-send-email-aschultz@tpip.net> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1447686417-3979-1-git-send-email-aschultz@tpip.net> References: <1447686417-3979-1-git-send-email-aschultz@tpip.net> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Pablo Neira Ayuso Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" Send tunnel data on GTP-U socket insead of raw interface and use existing iptunnel helpers for that. Signed-off-by: Andreas Schultz --- gtp.c | 223 ++++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 116 insertions(+), 107 deletions(-) diff --git a/gtp.c b/gtp.c index 794b459..2ac6431 100644 --- a/gtp.c +++ b/gtp.c @@ -68,7 +68,6 @@ struct gtp_instance { struct socket *sock1u; struct net_device *dev; - struct net_device *real_dev; unsigned int hash_size; struct hlist_head *tid_hash; @@ -404,7 +403,6 @@ static int gtp_dev_init(struct net_device *dev) { struct gtp_instance *gti = netdev_priv(dev); - dev->flags = IFF_NOARP; gti->dev = dev; dev->tstats = alloc_percpu(struct pcpu_sw_netstats); @@ -424,26 +422,34 @@ static void gtp_dev_uninit(struct net_device *dev) #define IP_UDP_LEN (sizeof(struct iphdr) + sizeof(struct udphdr)) -static struct rtable * -ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, - __be32 daddr, __be32 saddr, __u8 tos, int oif) +static inline void init_gtp_flow(struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr) { memset(fl4, 0, sizeof(*fl4)); - fl4->flowi4_oif = oif; + fl4->flowi4_oif = sk->sk_bound_dev_if; fl4->daddr = daddr; - fl4->saddr = saddr; - fl4->flowi4_tos = tos; - fl4->flowi4_proto = IPPROTO_UDP; + fl4->saddr = inet_sk(sk)->inet_saddr; + fl4->flowi4_tos = RT_CONN_FLAGS(sk); + fl4->flowi4_proto = sk->sk_protocol; +} + +static struct rtable * +ip4_route_output_gtp(struct net *net, struct flowi4 *fl4, + const struct sock *sk, + __be32 daddr) +{ + init_gtp_flow(fl4, sk, daddr); return ip_route_output_key(net, fl4); } static inline void -gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx, int payload_len) +gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) { struct gtp0_header *gtp0; + int payload_len = skb->len; /* ensure there is sufficient headroom */ - skb_cow(skb, sizeof(*gtp0) + IP_UDP_LEN); gtp0 = (struct gtp0_header *) skb_push(skb, sizeof(*gtp0)); gtp0->flags = 0x1e; /* V0, GTP-non-prime */ @@ -457,12 +463,12 @@ gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx, int payload_len) } static inline void -gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx, int payload_len) +gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx) { struct gtp1_header *gtp1; + int payload_len = skb->len; /* ensure there is sufficient headroom */ - skb_cow(skb, sizeof(*gtp1) + IP_UDP_LEN); gtp1 = (struct gtp1_header *) skb_push(skb, sizeof(*gtp1)); /* Bits 8 7 6 5 4 3 2 1 @@ -502,6 +508,7 @@ gtp_iptunnel_xmit_stats(int err, struct net_device_stats *err_stats, } struct gtp_pktinfo { + struct sock *sk; union { struct iphdr *iph; struct ipv6hdr *ip6h; @@ -515,10 +522,12 @@ struct gtp_pktinfo { }; static inline void -gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo, struct iphdr *iph, +gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo, struct sock *sk, + struct iphdr *iph, struct pdp_ctx *pctx, struct rtable *rt, struct flowi4 *fl4, struct net_device *dev) { + pktinfo->sk = sk; pktinfo->iph = iph; pktinfo->pctx = pctx; pktinfo->rt = rt; @@ -530,7 +539,7 @@ static int gtp_ip4_prepare_xmit(struct sk_buff *skb, struct net_device *dev, struct gtp_pktinfo *pktinfo) { struct gtp_instance *gti = netdev_priv(dev); - struct inet_sock *inet = inet_sk(gti->sock0->sk); + struct sock *sk; struct iphdr *iph; struct pdp_ctx *pctx; struct rtable *rt; @@ -549,14 +558,23 @@ static int gtp_ip4_prepare_xmit(struct sk_buff *skb, struct net_device *dev, netdev_dbg(dev, "found PDP context %p\n", pctx); /* Obtain route for the new encapsulated GTP packet */ - rt = ip4_route_output_gtp(dev_net(dev), &fl4, - pctx->sgsn_addr.ip4.s_addr, - inet->inet_saddr, 0, - gti->real_dev->ifindex); + switch (pctx->gtp_version) { + case GTP_V0: + sk = gti->sock0->sk; + break; + case GTP_V1: + sk = gti->sock1u->sk; + break; + default: + return -ENOENT; + } + + rt = ip4_route_output_gtp(sock_net(sk), &fl4, + gti->sock0->sk, + pctx->sgsn_addr.ip4.s_addr); if (IS_ERR(rt)) { - netdev_dbg(dev, "no route to SSGN %pI4 from ifidx=%d\n", - &pctx->sgsn_addr.ip4.s_addr, - gti->real_dev->ifindex); + netdev_dbg(dev, "no route to SSGN %pI4\n", + &pctx->sgsn_addr.ip4.s_addr); dev->stats.tx_carrier_errors++; goto err; } @@ -570,12 +588,11 @@ static int gtp_ip4_prepare_xmit(struct sk_buff *skb, struct net_device *dev, } skb_dst_drop(skb); - skb_dst_set(skb, &rt->dst); /* This is similar to tnl_update_pmtu() */ df = iph->frag_off; if (df) { - mtu = dst_mtu(&rt->dst) - gti->real_dev->hard_header_len - + mtu = dst_mtu(&rt->dst) - dev->hard_header_len - sizeof(struct iphdr) - sizeof(struct udphdr); switch (pctx->gtp_version) { case GTP_V0: @@ -586,10 +603,9 @@ static int gtp_ip4_prepare_xmit(struct sk_buff *skb, struct net_device *dev, break; } } else - mtu = skb_dst(skb) ? dst_mtu(skb_dst(skb)) : dev->mtu; + mtu = dst_mtu(&rt->dst); - if (skb_dst(skb)) - skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu); + rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu); if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) && mtu < ntohs(iph->tot_len)) { @@ -600,7 +616,7 @@ static int gtp_ip4_prepare_xmit(struct sk_buff *skb, struct net_device *dev, goto err_rt; } - gtp_set_pktinfo_ipv4(pktinfo, iph, pctx, rt, &fl4, dev); + gtp_set_pktinfo_ipv4(pktinfo, sk, iph, pctx, rt, &fl4, dev); return 0; err_rt: @@ -616,34 +632,23 @@ static int gtp_ip6_prepare_xmit(struct sk_buff *skb, struct net_device *dev, return 0; } -static inline void -gtp_push_ip4hdr(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) +static inline int +gtp_udp_tunnel_xmit(struct sk_buff *skb, __be16 port, + struct gtp_pktinfo *pktinfo) { - struct iphdr *iph; - - /* Push down and install the IP header. Similar to iptunnel_xmit() */ - skb_push(skb, sizeof(struct iphdr)); - skb_reset_network_header(skb); - - iph = ip_hdr(skb); - - iph->version = 4; - iph->ihl = sizeof(struct iphdr) >> 2; - iph->frag_off = htons(IP_DF); - iph->protocol = IPPROTO_UDP; - iph->tos = pktinfo->iph->tos; - iph->daddr = pktinfo->fl4.daddr; - iph->saddr = pktinfo->fl4.saddr; - iph->ttl = ip4_dst_hoplimit(&pktinfo->rt->dst); - __ip_select_ident(dev_net(pktinfo->rt->dst.dev), iph, - (skb_shinfo(skb)->gso_segs ?: 1) - 1); - netdev_dbg(pktinfo->dev, "gtp -> IP src: %pI4 dst: %pI4\n", - &iph->saddr, &iph->daddr); + &pktinfo->iph->saddr, &pktinfo->iph->daddr); + + return udp_tunnel_xmit_skb(pktinfo->rt, pktinfo->sk, skb, + pktinfo->fl4.saddr, + pktinfo->fl4.daddr, + pktinfo->iph->tos, + ip4_dst_hoplimit(&pktinfo->rt->dst), + htons(IP_DF), port, port, true, false); } -static inline void -gtp_push_ip6hdr(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) +static inline int +gtp_ip6tunnel_xmit(struct sk_buff *skb, struct gtp_pktinfo *pktinfo) { /* TODO IPV6 support */ } @@ -654,9 +659,17 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int payload_len; struct gtp_pktinfo pktinfo; unsigned int proto = ntohs(skb->protocol); - int gtph_len, err; + int gtph_len, err = -EINVAL; + __be16 gtph_port; rcu_read_lock(); + + /* ensure there is sufficient headroom */ + if (skb_cow_head(skb, dev->needed_headroom)) + goto tx_error; + + skb_reset_inner_headers(skb); + switch (proto) { case ETH_P_IP: err = gtp_ip4_prepare_xmit(skb, dev, &pktinfo); @@ -669,56 +682,55 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev) if (err < 0) goto tx_error; - /* Annotate length of the encapsulated packet */ - payload_len = skb->len; - /* Push down GTP header */ switch (pktinfo.pctx->gtp_version) { case GTP_V0: - gtp0_push_header(skb, pktinfo.pctx, payload_len); - break; - case GTP_V1: - gtp1_push_header(skb, pktinfo.pctx, payload_len); - break; - } - - /* Push down and install the UDP header. */ - skb_push(skb, sizeof(struct udphdr)); - skb_reset_transport_header(skb); - - uh = udp_hdr(skb); - switch (pktinfo.pctx->gtp_version) { - case GTP_V0: - uh->source = uh->dest = htons(GTP0_PORT); + gtph_port = htons(GTP0_PORT); gtph_len = sizeof(struct gtp0_header); + + gtp0_push_header(skb, pktinfo.pctx); break; case GTP_V1: - uh->source = uh->dest = htons(GTP1U_PORT); + gtph_port = htons(GTP1U_PORT); gtph_len = sizeof(struct gtp1_header); + + gtp1_push_header(skb, pktinfo.pctx); break; + default: + goto tx_error; } - uh->len = htons(sizeof(struct udphdr) + payload_len + gtph_len); - uh->check = 0; - - netdev_dbg(dev, "gtp -> UDP src: %u dst: %u (len %u)\n", - ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len)); - switch (proto) { case ETH_P_IP: - gtp_push_ip4hdr(skb, &pktinfo); + err = gtp_udp_tunnel_xmit(skb, gtph_port, &pktinfo); break; case ETH_P_IPV6: - gtp_push_ip6hdr(skb, &pktinfo); + /* Annotate length of the encapsulated packet */ + payload_len = skb->len; + + /* Push down and install the UDP header. */ + skb_push(skb, sizeof(struct udphdr)); + skb_reset_transport_header(skb); + + uh = udp_hdr(skb); + + uh->source = uh->dest = gtph_port; + uh->len = htons(sizeof(struct udphdr) + payload_len + gtph_len); + uh->check = 0; + + netdev_dbg(dev, "gtp -> UDP src: %u dst: %u (len %u)\n", + ntohs(uh->source), ntohs(uh->dest), ntohs(uh->len)); + + nf_reset(skb); + + netdev_dbg(dev, "Good, now packet leaving from GGSN to SGSN\n"); + + err = gtp_ip6tunnel_xmit(skb, &pktinfo); break; } - rcu_read_unlock(); - nf_reset(skb); - - netdev_dbg(dev, "Good, now packet leaving from GGSN to SGSN\n"); + rcu_read_unlock(); - err = ip_local_out(skb); gtp_iptunnel_xmit_stats(err, &dev->stats, dev->tstats); return NETDEV_TX_OK; @@ -737,10 +749,26 @@ static const struct net_device_ops gtp_netdev_ops = { static void gtp_link_setup(struct net_device *dev) { - dev->priv_flags |= IFF_NO_QUEUE; - dev->netdev_ops = >p_netdev_ops; dev->destructor = free_netdev; + + dev->hard_header_len = 0; + dev->addr_len = 0; + + /* Zero header length */ + dev->type = ARPHRD_NONE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + dev->tx_queue_len = 1000; + + dev->priv_flags |= IFF_NO_QUEUE; + dev->features |= NETIF_F_LLTX; + netif_keep_dst(dev); + + dev->needed_headroom = LL_MAX_HEADER + + sizeof(struct iphdr) + + sizeof(struct udphdr) + + sizeof(struct gtp0_header); + } static int gtp_hashtable_new(struct gtp_instance *gti, int hsize); @@ -752,30 +780,13 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct gtp_net *gn; - struct net_device *real_dev; struct gtp_instance *gti; int hashsize, err, fd0, fd1; - if (!tb[IFLA_LINK]) - return -EINVAL; - - real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); - if (!real_dev) - return -ENODEV; - - dev_hold(real_dev); - if (!tb[IFLA_MTU]) - dev->mtu = real_dev->mtu; - else if (dev->mtu > real_dev->mtu) { - netdev_dbg(dev, "GTP mtu greater that transport MTU (%d > %d)\n", - dev->mtu, real_dev->mtu); - err = -EINVAL; - goto out_err; - } + dev->mtu = 1500; gti = netdev_priv(dev); - gti->real_dev = real_dev; fd0 = nla_get_u32(data[IFLA_GTP_FD0]); fd1 = nla_get_u32(data[IFLA_GTP_FD1]); @@ -813,7 +824,6 @@ out_encap: gtp_encap_disable(gti); out_err: - dev_put(real_dev); return err; } @@ -823,7 +833,6 @@ static void gtp_dellink(struct net_device *dev, struct list_head *head) gtp_encap_disable(gti); gtp_hashtable_free(gti); - dev_put(gti->real_dev); list_del_rcu(>i->list); unregister_netdevice_queue(dev, head); }