From patchwork Sat Oct 28 00:09:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Herbert X-Patchwork-Id: 831558 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=quantonium-net.20150623.gappssmtp.com header.i=@quantonium-net.20150623.gappssmtp.com header.b="oz5/e2DH"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yP1NX268Yz9t4X for ; Sat, 28 Oct 2017 11:10:36 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751854AbdJ1AKe (ORCPT ); Fri, 27 Oct 2017 20:10:34 -0400 Received: from mail-pg0-f65.google.com ([74.125.83.65]:46437 "EHLO mail-pg0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751686AbdJ1AK3 (ORCPT ); Fri, 27 Oct 2017 20:10:29 -0400 Received: by mail-pg0-f65.google.com with SMTP id k7so6416983pga.3 for ; Fri, 27 Oct 2017 17:10:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quantonium-net.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=aQt4CjPVoeTVU8UiK2CbV9vkeGMOIcjM40JwLNpg7Ew=; b=oz5/e2DHvRTHOXPIuCx19ch7Qcy45vJVPRDpkSmcC/I6ojPHmVXs1s20eZd6rVid0z cn0xrKR5YoOiesBeMhaFTfOgv1RzBPoG9mTAbP2Hbt2QcX7zgl3865/eIZprJLht9mB/ Ex5Lii0H9t+CIevw4RJlDqlSO7CSswKHsTrT2IZy640mDbN9DXs0XotltRd6xulatVmx 0fB2ZpEBCue2WWDPlzbHwBwxNtz/di98XRsJO+H5+DTgxlgIKtqPb6nB76C3QpX2Gtuq IgoGU+ULlNSG0U5Vq9/tBZ+yJbIoPSR7wdvmIXSFoPNP5FypnwPsvGaX2soFyqNB68gG VsGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=aQt4CjPVoeTVU8UiK2CbV9vkeGMOIcjM40JwLNpg7Ew=; b=YXYB2OA9gbkMP6BO+lurQLIVNZ9x5bg1pmEFRTrK6pppXFIJN0OfHXyZzlnvXU+Y4T UGUbAM1oFev3t9nYnbbLfZUFyO5Q6y6eTSyfnBnwylW6psKu5sSFdR5/rqchOQFuRTKe 0bUPP7ydwSUwR2QKe3Y3uAUuyhfU8sVu67oYTOrwvPRNx3jpSm627mQ2LDE1/BZhR1yb lw+fakVzjPB41PxXIq5oKRIojy8Gq15TG74ED8n/ackcWoeBFxQc6oQphzC3IMh90/Um nNaUM6/MCFHyg/3RzSwf6OGBANhQrQFJvbNuFMiKosdBu/OLX5248gOUSrPkmdOMgKV7 5ADQ== X-Gm-Message-State: AMCzsaUJ6JqKEyJc/YqJXbsQsZxF0+RKaT9TzdBAjSk5lv9Nta7ID/C+ B7dsCRVrSyLakPDQjQYROX2HWQ== X-Google-Smtp-Source: ABhQp+S0KX699/6EvYTymwf55NuqKKx2u6yJiwHRRnKT6zK3ge2R2X21FtaM/zwMYiz+1PD+4UenHA== X-Received: by 10.99.152.17 with SMTP id q17mr1587096pgd.287.1509149429032; Fri, 27 Oct 2017 17:10:29 -0700 (PDT) Received: from localhost.localdomain (c-73-162-13-107.hsd1.ca.comcast.net. [73.162.13.107]) by smtp.gmail.com with ESMTPSA id 2sm9068398pgb.30.2017.10.27.17.10.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 27 Oct 2017 17:10:28 -0700 (PDT) From: Tom Herbert To: davem@davemloft.net Cc: pablo@netfilter.org, laforge@gnumonks.org, aschultz@tpip.net, netdev@vger.kernel.org, rohit@quantonium.net, Tom Herbert Subject: [PATCH v7 net-next 12/13] gtp: Experimental support encpasulating over IPv6 Date: Fri, 27 Oct 2017 17:09:36 -0700 Message-Id: <20171028000937.2631-13-tom@quantonium.net> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171028000937.2631-1-tom@quantonium.net> References: <20171028000937.2631-1-tom@quantonium.net> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Allows using GTP datapath over IPv6. Remote peers are indicated by IPv6. Note this is experimental, more work is needed to make this compliant with 3GPP standard. Signed-off-by: Tom Herbert --- drivers/net/gtp.c | 248 ++++++++++++++++++++++++++++++++++--------- include/uapi/linux/gtp.h | 1 + include/uapi/linux/if_link.h | 3 + 3 files changed, 200 insertions(+), 52 deletions(-) diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 6190631574dc..1f9c0fc4833a 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -59,16 +60,22 @@ struct pdp_ctx { __be16 gtp_port; u16 ms_af; + u16 peer_af; #if GTP_IPV6 union { struct in_addr ms_addr_ip4; struct in6_addr ms_addr_ip6; }; + + union { + struct in_addr peer_addr_ip4; + struct in6_addr peer_addr_ip6; + }; #else struct in_addr ms_addr_ip4; + struct in_addr peer_addr_ip4; #endif - struct in_addr peer_addr_ip4; struct sock *sk; struct net_device *dev; @@ -93,8 +100,11 @@ struct gtp_dev { struct hlist_head *tid_hash; struct hlist_head *addr4_hash; + #if GTP_IPV6 struct hlist_head *addr6_hash; + + unsigned int is_ipv6:1; #endif struct gro_cells gro_cells; @@ -541,8 +551,6 @@ static int gtp_xmit(struct sk_buff *skb, struct net_device *dev, { struct iphdr *inner_iph = NULL; struct sock *sk = pctx->sk; - __be32 saddr = inet_sk(sk)->inet_saddr; - struct rtable *rt; int err = 0; if (skb->protocol == ETH_P_IP) @@ -555,38 +563,84 @@ static int gtp_xmit(struct sk_buff *skb, struct net_device *dev, skb_reset_inner_headers(skb); - /* Source address returned by route lookup is ignored since - * we get the address from a socket. - */ - rt = ip_tunnel_get_route(dev, skb, sk->sk_protocol, - sk->sk_bound_dev_if, RT_CONN_FLAGS(sk), - pctx->peer_addr_ip4.s_addr, &saddr, - pctx->gtp_port, pctx->gtp_port, - &pctx->dst_cache, NULL); - - if (IS_ERR(rt)) { - err = PTR_ERR(rt); - goto out_err; - } + if (pctx->peer_af == AF_INET) { + __be32 saddr = inet_sk(sk)->inet_saddr; + struct rtable *rt; + + /* Source address returned by route lookup is ignored since + * we get the address from a socket. + */ + rt = ip_tunnel_get_route(dev, skb, sk->sk_protocol, + sk->sk_bound_dev_if, RT_CONN_FLAGS(sk), + pctx->peer_addr_ip4.s_addr, &saddr, + pctx->gtp_port, pctx->gtp_port, + &pctx->dst_cache, NULL); + + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto out_err; + } + + skb_dst_drop(skb); - skb_dst_drop(skb); + gtp_push_header(skb, pctx); - gtp_push_header(skb, pctx); + if (inner_iph) + __iptunnel_update_pmtu(dev, skb, &rt->dst, + !!inner_iph->frag_off, + inner_iph, pctx->hlen, + pctx->peer_addr_ip4.s_addr); - if (inner_iph) - __iptunnel_update_pmtu(dev, skb, &rt->dst, - !!inner_iph->frag_off, - inner_iph, pctx->hlen, - pctx->peer_addr_ip4.s_addr); + udp_tunnel_xmit_skb(rt, sk, skb, saddr, + pctx->peer_addr_ip4.s_addr, + 0, ip4_dst_hoplimit(&rt->dst), 0, + pctx->gtp_port, pctx->gtp_port, + false, false); - udp_tunnel_xmit_skb(rt, sk, skb, saddr, - pctx->peer_addr_ip4.s_addr, - 0, ip4_dst_hoplimit(&rt->dst), 0, - pctx->gtp_port, pctx->gtp_port, - false, false); + netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n", + &saddr, &pctx->peer_addr_ip4.s_addr); - netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n", - &saddr, &pctx->peer_addr_ip4.s_addr); +#if GTP_IPV6 +#if IS_ENABLED(CONFIG_IPV6) + } else if (pctx->peer_af == AF_INET6) { + struct in6_addr saddr = inet6_sk(sk)->saddr; + struct dst_entry *dst; + + /* Source address returned by route lookup is ignored since + * we get the address from a socket. + */ + dst = ip6_tnl_get_route(dev, skb, sk, sk->sk_protocol, + sk->sk_bound_dev_if, 0, + 0, &pctx->peer_addr_ip6, &saddr, + pctx->gtp_port, pctx->gtp_port, + &pctx->dst_cache, NULL); + + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + goto out_err; + } + + skb_dst_drop(skb); + + gtp_push_header(skb, pctx); + + if (inner_iph) + __iptunnel_update_pmtu(dev, skb, dst, + !!inner_iph->frag_off, + inner_iph, pctx->hlen, 0); + + udp_tunnel6_xmit_skb(dst, sk, skb, dev, + &saddr, &pctx->peer_addr_ip6, + 0, ip6_dst_hoplimit(dst), 0, + pctx->gtp_port, pctx->gtp_port, + false); + + netdev_dbg(dev, "gtp -> IP src: %pI6 dst: %pI6\n", + &saddr, &pctx->peer_addr_ip6); + +#endif +#endif + } return 0; @@ -695,7 +749,12 @@ static void gtp_link_setup(struct net_device *dev) /* Assume largest header, ie. GTPv0. */ dev->needed_headroom = LL_MAX_HEADER + +#if GTP_IPV6 + max_t(int, sizeof(struct iphdr), + sizeof(struct ipv6hdr)) + +#else sizeof(struct iphdr) + +#endif sizeof(struct udphdr) + sizeof(struct gtp0_header); @@ -704,12 +763,15 @@ 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 gtp_dev *gtp, struct nlattr *data[]); +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[], + bool is_ipv6); static int gtp_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { + unsigned int role = GTP_ROLE_GGSN; + bool is_ipv6 = false; struct gtp_dev *gtp; struct gtp_net *gn; int hashsize, err; @@ -717,9 +779,32 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1]) return -EINVAL; + if (data[IFLA_GTP_ROLE]) { + role = nla_get_u32(data[IFLA_GTP_ROLE]); + if (role > GTP_ROLE_SGSN) + return -EINVAL; + } + + if (data[IFLA_GTP_AF]) { + u16 af = nla_get_u16(data[IFLA_GTP_AF]); + + switch (af) { + case AF_INET: + is_ipv6 = false; + break; +#if GTP_IPV6 + case AF_INET6: + is_ipv6 = true; + break; +#endif + default: + return -EINVAL; + } + } + gtp = netdev_priv(dev); - err = gtp_encap_enable(gtp, data); + err = gtp_encap_enable(gtp, data, is_ipv6); if (err < 0) return err; @@ -738,6 +823,11 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev, goto out_hashtable; } + gtp->role = role; +#if GTP_IPV6 + gtp->is_ipv6 = is_ipv6; +#endif + gn = net_generic(dev_net(dev), gtp_net_id); list_add_rcu(>p->list, &gn->gtp_dev_list); @@ -867,7 +957,8 @@ static void gtp_hashtable_free(struct gtp_dev *gtp) } static struct sock *gtp_encap_enable_socket(int fd, int type, - struct gtp_dev *gtp) + struct gtp_dev *gtp, + bool is_ipv6) { struct udp_tunnel_sock_cfg tuncfg = {NULL}; struct socket *sock; @@ -888,6 +979,12 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, goto out_sock; } + if (sock->sk->sk_family != (is_ipv6 ? AF_INET6 : AF_INET)) { + pr_debug("socket fd=%d not right family\n", fd); + sk = ERR_PTR(-EINVAL); + goto out_sock; + } + if (rcu_dereference_sk_user_data(sock->sk)) { sk = ERR_PTR(-EBUSY); goto out_sock; @@ -920,16 +1017,16 @@ static struct sock *gtp_encap_enable_socket(int fd, int type, return sk; } -static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) +static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[], + bool is_ipv6) { - struct sock *sk1u = NULL; - struct sock *sk0 = NULL; - unsigned int role = GTP_ROLE_GGSN; + struct sock *sk0 = NULL, *sk1u = 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); + sk0 = gtp_encap_enable_socket(fd0, UDP_ENCAP_GTP0, gtp, + is_ipv6); if (IS_ERR(sk0)) return PTR_ERR(sk0); } @@ -937,7 +1034,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) if (data[IFLA_GTP_FD1]) { u32 fd1 = nla_get_u32(data[IFLA_GTP_FD1]); - sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); + sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp, + is_ipv6); if (IS_ERR(sk1u)) { if (sk0) gtp_encap_disable_sock(sk0); @@ -945,15 +1043,8 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]) } } - if (data[IFLA_GTP_ROLE]) { - role = nla_get_u32(data[IFLA_GTP_ROLE]); - if (role > GTP_ROLE_SGSN) - return -EINVAL; - } - gtp->sk0 = sk0; gtp->sk1u = sk1u; - gtp->role = role; return 0; } @@ -989,8 +1080,18 @@ static void pdp_fill(struct pdp_ctx *pctx, struct genl_info *info) __be16 default_port = 0; pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]); - pctx->peer_addr_ip4.s_addr = - nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]); + + if (info->attrs[GTPA_PEER_ADDRESS]) { + pctx->peer_af = AF_INET; + pctx->peer_addr_ip4.s_addr = + nla_get_in_addr(info->attrs[GTPA_PEER_ADDRESS]); +#if GTP_IPV6 + } else if (info->attrs[GTPA_PEER6_ADDRESS]) { + pctx->peer_af = AF_INET6; + pctx->peer_addr_ip6 = nla_get_in6_addr( + info->attrs[GTPA_PEER6_ADDRESS]); +#endif + } switch (pctx->gtp_version) { case GTP_V0: @@ -1169,11 +1270,17 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) int err; if (!info->attrs[GTPA_VERSION] || - !info->attrs[GTPA_LINK] || - !info->attrs[GTPA_PEER_ADDRESS]) + !info->attrs[GTPA_LINK]) return -EINVAL; #if GTP_IPV6 + if (!(!!info->attrs[GTPA_PEER_ADDRESS] ^ + !!info->attrs[GTPA_PEER6_ADDRESS])) { + /* Either v4 or v6 peer address must be set */ + + return -EINVAL; + } + if (!(!!info->attrs[GTPA_MS_ADDRESS] ^ !!info->attrs[GTPA_MS6_ADDRESS])) { /* Either v4 or v6 mobile subscriber address must be set */ @@ -1181,6 +1288,12 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } #else + if (!info->attrs[GTPA_PEER_ADDRESS]) { + /* v4 peer address must be set */ + + return -EINVAL; + } + if (!info->attrs[GTPA_MS_ADDRESS]) { /* v4 mobile subscriber address must be set */ @@ -1214,6 +1327,14 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) goto out_unlock; } +#if GTP_IPV6 + if ((info->attrs[GTPA_PEER_ADDRESS] && gtp->is_ipv6) || + (info->attrs[GTPA_PEER6_ADDRESS] && !gtp->is_ipv6)) { + err = -EINVAL; + goto out_unlock; + } +#endif + if (version == GTP_V0) sk = gtp->sk0; else if (version == GTP_V1) @@ -1322,10 +1443,31 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, if (genlh == NULL) goto nlmsg_failure; - if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) || - nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr)) + if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version)) goto nla_put_failure; + if (nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex)) + goto nla_put_failure; + + switch (pctx->peer_af) { + case AF_INET: + if (nla_put_be32(skb, GTPA_PEER_ADDRESS, + pctx->peer_addr_ip4.s_addr)) + goto nla_put_failure; + + break; +#if GTP_IPV6 + case AF_INET6: + if (nla_put_in6_addr(skb, GTPA_PEER6_ADDRESS, + &pctx->peer_addr_ip6)) + goto nla_put_failure; + + break; +#endif + default: + goto nla_put_failure; + } + switch (pctx->ms_af) { case AF_INET: if (nla_put_be32(skb, GTPA_MS_ADDRESS, @@ -1455,6 +1597,8 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = { [GTPA_PEER_ADDRESS] = { .type = NLA_U32, }, [GTPA_MS_ADDRESS] = { .type = NLA_U32, }, #if GTP_IPV6 + [GTPA_PEER6_ADDRESS] = { .len = FIELD_SIZEOF(struct ipv6hdr, + daddr) }, [GTPA_MS6_ADDRESS] = { .len = FIELD_SIZEOF(struct ipv6hdr, daddr) }, #endif diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h index ae4e632c0360..8eec519fa754 100644 --- a/include/uapi/linux/gtp.h +++ b/include/uapi/linux/gtp.h @@ -29,6 +29,7 @@ enum gtp_attrs { GTPA_PAD, GTPA_PORT, GTPA_MS6_ADDRESS, + GTPA_PEER6_ADDRESS, __GTPA_MAX, }; #define GTPA_MAX (__GTPA_MAX + 1) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b037e0ab1975..1abf0f5c01fc 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -555,6 +555,9 @@ enum { IFLA_GTP_FD1, IFLA_GTP_PDP_HASHSIZE, IFLA_GTP_ROLE, + IFLA_GTP_AF, + IFLA_GTP_PORT0, + IFLA_GTP_PORT1, __IFLA_GTP_MAX, }; #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)