{"id":818041,"url":"http://patchwork.ozlabs.org/api/patches/818041/?format=json","web_url":"http://patchwork.ozlabs.org/project/netdev/patch/20170925032941.14586-11-tom@quantonium.net/","project":{"id":7,"url":"http://patchwork.ozlabs.org/api/projects/7/?format=json","name":"Linux network development","link_name":"netdev","list_id":"netdev.vger.kernel.org","list_email":"netdev@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170925032941.14586-11-tom@quantonium.net>","list_archive_url":null,"date":"2017-09-25T03:29:39","name":"[v3,net-next,10/12] gtp: Experimental encapsulation of IPv6 packets","commit_ref":null,"pull_url":null,"state":"changes-requested","archived":true,"hash":"e323cd91482cf4cbc2dc32a81259a1d8018a54dd","submitter":{"id":72064,"url":"http://patchwork.ozlabs.org/api/people/72064/?format=json","name":"Tom Herbert","email":"tom@quantonium.net"},"delegate":{"id":34,"url":"http://patchwork.ozlabs.org/api/users/34/?format=json","username":"davem","first_name":"David","last_name":"Miller","email":"davem@davemloft.net"},"mbox":"http://patchwork.ozlabs.org/project/netdev/patch/20170925032941.14586-11-tom@quantonium.net/mbox/","series":[{"id":4865,"url":"http://patchwork.ozlabs.org/api/series/4865/?format=json","web_url":"http://patchwork.ozlabs.org/project/netdev/list/?series=4865","date":"2017-09-25T03:29:29","name":"gtp: Additional feature support - Part I","version":3,"mbox":"http://patchwork.ozlabs.org/series/4865/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/818041/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/818041/checks/","tags":{},"related":[],"headers":{"Return-Path":"<netdev-owner@vger.kernel.org>","X-Original-To":"patchwork-incoming@ozlabs.org","Delivered-To":"patchwork-incoming@ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=quantonium-net.20150623.gappssmtp.com\n\theader.i=@quantonium-net.20150623.gappssmtp.com\n\theader.b=\"fuTVGWlo\"; dkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3y0qNY218xz9t4Z\n\tfor <patchwork-incoming@ozlabs.org>;\n\tMon, 25 Sep 2017 13:30:37 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S933128AbdIYDag (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tSun, 24 Sep 2017 23:30:36 -0400","from mail-pf0-f180.google.com ([209.85.192.180]:47507 \"EHLO\n\tmail-pf0-f180.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S933022AbdIYDaW (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Sun, 24 Sep 2017 23:30:22 -0400","by mail-pf0-f180.google.com with SMTP id u12so3144432pfl.4\n\tfor <netdev@vger.kernel.org>; Sun, 24 Sep 2017 20:30:22 -0700 (PDT)","from localhost.localdomain (c-73-162-13-107.hsd1.ca.comcast.net.\n\t[73.162.13.107]) by smtp.gmail.com with ESMTPSA id\n\tj2sm9112907pgn.26.2017.09.24.20.30.20\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tSun, 24 Sep 2017 20:30:21 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=quantonium-net.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=6pKmF1ebjceotI+8CDC/igN0VTICOLDDuxXuQvdd7Xc=;\n\tb=fuTVGWlo7nGkxWX7ix/eU76RVxXoMg4sngOYIzdVhd9cp5tukDWAh3MKskwDW93aH4\n\t28PBbct8GAzyoowqCBT/3TG/9noIqNVxYIfeGetJ1MExNwb4w68VLHaybD/yV3THbT54\n\tuiH3O1prNtwVGg1xIbuBiXYJLO5XeL4dahlG7iU8J/o4JXEMTO7rjfLtqHpFAXc8UMqC\n\tnE5ymhyX+5d0SKVSHCbfvXWeUrjf2ure7T17Ktjz9f6Rg56qdFPgUGZ0oiSt5BPsBBLG\n\t+AAT1hrgjFC7LWtMmIoQw3T4Cy8cFyfpyLLx9CYCNuWaEP7XTuAElffnrOsfzfYIs8C1\n\treFA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=6pKmF1ebjceotI+8CDC/igN0VTICOLDDuxXuQvdd7Xc=;\n\tb=bzbsyqN6ip7OIUoHZk4AZPQx/5Z86tQG/zSgVduGrTP0w8JB3DLdI9DnBCAfXOgiCQ\n\t8FMqfBjAZjyg/hkjFNshIGX672iK76TW4DpVaggp5FON/f+yu0INO5UrnseKRaMYvwHo\n\tJNW3Uymkkcvr05rVZqhaAGLsEqQtK66vginc5777ZUEsdyKD48tS0uZ7TB25LJOMnhNB\n\tbEVYE9usB56uKf0Sr+IyiRSr3ButAcDk+XGvHs7p3euIsIgtXQoT8ia+8d3iKHvyul1Z\n\t+MYt54agMs30cJbwRlIGs3h+/9fRo/x+FO/Si0DFA/SJTwL2dDqU1MXW0USCtjIbAGJk\n\tHQcw==","X-Gm-Message-State":"AHPjjUj/46DwSKG5jr4IOxL550qX6WFxQi6QCdp8ZDRdBMPeSYN3hQFB\n\tCmPPhF91CL5K/2BPsBbRF+je6lbj","X-Google-Smtp-Source":"AOwi7QBIF3znow7xM3VlWHZ6b1LBcCKKk3yZhaPqS0XNWvDemh8+LOhtFt19vUkgp3YBLxgKxe41/w==","X-Received":"by 10.84.165.171 with SMTP id y40mr6307060pla.373.1506310222220; \n\tSun, 24 Sep 2017 20:30:22 -0700 (PDT)","From":"Tom Herbert <tom@quantonium.net>","To":"davem@davemloft.net","Cc":"pablo@netfilter.org, laforge@gnumonks.org, aschultz@tpip.net,\n\tnetdev@vger.kernel.org, rohit@quantonium.net,\n\tTom Herbert <tom@quantonium.net>","Subject":"[PATCH v3 net-next 10/12] gtp: Experimental encapsulation of IPv6\n\tpackets","Date":"Sun, 24 Sep 2017 20:29:39 -0700","Message-Id":"<20170925032941.14586-11-tom@quantonium.net>","X-Mailer":"git-send-email 2.11.0","In-Reply-To":"<20170925032941.14586-1-tom@quantonium.net>","References":"<20170925032941.14586-1-tom@quantonium.net>","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"},"content":"Allow IPv6 mobile subscriber packets. This entails adding an IPv6 mobile\nsubscriber address to pdp context and IPv6 specific variants to find pdp\ncontexts by address.\n\nNote that this is experimental support of IPv6, more work is\nnecessary to make this compliant with 3GPP standard.\n\nSigned-off-by: Tom Herbert <tom@quantonium.net>\n---\n drivers/net/Kconfig      |  12 +-\n drivers/net/gtp.c        | 324 +++++++++++++++++++++++++++++++++++++++--------\n include/uapi/linux/gtp.h |   1 +\n 3 files changed, 280 insertions(+), 57 deletions(-)","diff":"diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig\nindex aba0d652095b..8e55367ab6d4 100644\n--- a/drivers/net/Kconfig\n+++ b/drivers/net/Kconfig\n@@ -225,7 +225,17 @@ config GTP\n \t  3GPP TS 29.060 standards.\n \n \t  To compile this drivers as a module, choose M here: the module\n-\t  wil be called gtp.\n+\t  will be called gtp.\n+\n+config GTP_IPV6_EXPERIMENTAL\n+\tbool \"GTP IPv6 datapath (EXPERIMENTAL)\"\n+\tdefault n\n+\tdepends on GTP\n+\t---help---\n+\t  This is an experimental implementation that allows encapsulating\n+\t  IPv6 over GTP and using GTP over IPv6 for testing and development\n+\t  purpose. This is not a standards conformant implementation for\n+\t  IPv6 and GTP. More work is needed reach that level.\n \n config MACSEC\n \ttristate \"IEEE 802.1AE MAC-level encryption (MACsec)\"\ndiff --git a/drivers/net/gtp.c b/drivers/net/gtp.c\nindex 44844eba8df2..919ec6e14973 100644\n--- a/drivers/net/gtp.c\n+++ b/drivers/net/gtp.c\n@@ -36,6 +36,8 @@\n #include <net/netns/generic.h>\n #include <net/gtp.h>\n \n+#define GTP_IPV6 IS_ENABLED(CONFIG_GTP_IPV6_EXPERIMENTAL)\n+\n /* An active session for the subscriber. */\n struct pdp_ctx {\n \tstruct hlist_node\thlist_tid;\n@@ -55,9 +57,17 @@ struct pdp_ctx {\n \tu8\t\t\tgtp_version;\n \tu8\t\t\thlen;\n \t__be16\t\t\tgtp_port;\n-\tu16\t\t\taf;\n \n-\tstruct in_addr\t\tms_addr_ip4;\n+\tu16\t\t\tms_af;\n+#if GTP_IPV6\n+\tunion {\n+\t\tstruct in_addr\tms_addr_ip4;\n+\t\tstruct in6_addr\tms_addr_ip6;\n+\t};\n+#else\n+\tstruct in_addr\tms_addr_ip4;\n+#endif\n+\n \tstruct in_addr\t\tpeer_addr_ip4;\n \n \tstruct sock\t\t*sk;\n@@ -81,7 +91,11 @@ struct gtp_dev {\n \tunsigned int\t\trole;\n \tunsigned int\t\thash_size;\n \tstruct hlist_head\t*tid_hash;\n-\tstruct hlist_head\t*addr_hash;\n+\n+\tstruct hlist_head\t*addr4_hash;\n+#if GTP_IPV6\n+\tstruct hlist_head\t*addr6_hash;\n+#endif\n \n \tstruct gro_cells\tgro_cells;\n };\n@@ -99,6 +113,7 @@ static void pdp_context_delete(struct pdp_ctx *pctx);\n static inline u32 gtp0_hashfn(u64 tid)\n {\n \tu32 *tid32 = (u32 *) &tid;\n+\n \treturn jhash_2words(tid32[0], tid32[1], gtp_h_initval);\n }\n \n@@ -107,11 +122,6 @@ static inline u32 gtp1u_hashfn(u32 tid)\n \treturn jhash_1word(tid, gtp_h_initval);\n }\n \n-static inline u32 ipv4_hashfn(__be32 ip)\n-{\n-\treturn jhash_1word((__force u32)ip, gtp_h_initval);\n-}\n-\n /* Resolve a PDP context structure based on the 64bit TID. */\n static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)\n {\n@@ -144,16 +154,21 @@ static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid)\n \treturn NULL;\n }\n \n+static inline u32 gtp_ipv4_hashfn(__be32 ip)\n+{\n+\treturn jhash_1word((__force u32)ip, gtp_h_initval);\n+}\n+\n /* Resolve a PDP context based on IPv4 address of MS. */\n static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)\n {\n \tstruct hlist_head *head;\n \tstruct pdp_ctx *pdp;\n \n-\thead = &gtp->addr_hash[ipv4_hashfn(ms_addr) % gtp->hash_size];\n+\thead = &gtp->addr4_hash[gtp_ipv4_hashfn(ms_addr) % gtp->hash_size];\n \n \thlist_for_each_entry_rcu(pdp, head, hlist_addr) {\n-\t\tif (pdp->af == AF_INET &&\n+\t\tif (pdp->ms_af == AF_INET &&\n \t\t    pdp->ms_addr_ip4.s_addr == ms_addr)\n \t\t\treturn pdp;\n \t}\n@@ -177,33 +192,109 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,\n \t\treturn iph->saddr == pctx->ms_addr_ip4.s_addr;\n }\n \n+#if GTP_IPV6\n+\n+static inline u32 gtp_ipv6_hashfn(const struct in6_addr *a)\n+{\n+\treturn __ipv6_addr_jhash(a, gtp_h_initval);\n+}\n+\n+/* Resolve a PDP context based on IPv6 address of MS. */\n+static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,\n+\t\t\t\t     const struct in6_addr *ms_addr)\n+{\n+\tstruct hlist_head *head;\n+\tstruct pdp_ctx *pdp;\n+\n+\thead = &gtp->addr6_hash[gtp_ipv6_hashfn(ms_addr) % gtp->hash_size];\n+\n+\thlist_for_each_entry_rcu(pdp, head, hlist_addr) {\n+\t\tif (pdp->ms_af == AF_INET6 &&\n+\t\t    ipv6_addr_equal(&pdp->ms_addr_ip6, ms_addr))\n+\t\t\treturn pdp;\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,\n+\t\t\t      unsigned int hdrlen, unsigned int role)\n+{\n+\tstruct ipv6hdr *ipv6h;\n+\n+\tif (!pskb_may_pull(skb, hdrlen + sizeof(struct ipv6hdr)))\n+\t\treturn false;\n+\n+\tipv6h = (struct ipv6hdr *)(skb->data + hdrlen);\n+\n+\tif (role == GTP_ROLE_SGSN)\n+\t\treturn ipv6_addr_equal(&ipv6h->daddr, &pctx->ms_addr_ip6);\n+\telse\n+\t\treturn ipv6_addr_equal(&ipv6h->saddr, &pctx->ms_addr_ip6);\n+}\n+\n+#endif\n+\n /* Check if the inner IP address in this packet is assigned to any\n  * existing mobile subscriber.\n  */\n static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,\n \t\t\t     unsigned int hdrlen, unsigned int role)\n {\n-\tswitch (ntohs(skb->protocol)) {\n-\tcase ETH_P_IP:\n+\tstruct iphdr *iph;\n+\n+\t/* Minimally there needs to be an IPv4 header */\n+\tif (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr)))\n+\t\treturn false;\n+\n+\tiph = (struct iphdr *)(skb->data + hdrlen);\n+\n+\tswitch (iph->version) {\n+\tcase 4:\n \t\treturn gtp_check_ms_ipv4(skb, pctx, hdrlen, role);\n+#if GTP_IPV6\n+\tcase 6:\n+\t\treturn gtp_check_ms_ipv6(skb, pctx, hdrlen, role);\n+#endif\n \t}\n+\n \treturn false;\n }\n \n+static u16 ipver_to_eth(struct iphdr *iph)\n+{\n+\tswitch (iph->version) {\n+\tcase 4:\n+\t\treturn htons(ETH_P_IP);\n+#if GTP_IPV6\n+\tcase 6:\n+\t\treturn htons(ETH_P_IPV6);\n+#endif\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,\n-\t\t\tunsigned int hdrlen, unsigned int role)\n+\t\t  unsigned int hdrlen, unsigned int role)\n {\n \tstruct gtp_dev *gtp = netdev_priv(pctx->dev);\n \tstruct pcpu_sw_netstats *stats;\n+\tu16 inner_protocol;\n \n \tif (!gtp_check_ms(skb, pctx, hdrlen, role)) {\n \t\tnetdev_dbg(pctx->dev, \"No PDP ctx for this MS\\n\");\n \t\treturn 1;\n \t}\n \n+\tinner_protocol = ipver_to_eth((struct iphdr *)(skb->data + hdrlen));\n+\tif (!inner_protocol)\n+\t\treturn -1;\n+\n \t/* Get rid of the GTP + UDP headers. */\n-\tif (iptunnel_pull_header(skb, hdrlen, skb->protocol,\n-\t\t\t\t !net_eq(sock_net(pctx->sk), dev_net(pctx->dev))))\n+\tif (iptunnel_pull_header(skb, hdrlen, inner_protocol,\n+\t\t\t\t !net_eq(sock_net(pctx->sk),\n+\t\t\t\t\t dev_net(pctx->dev))))\n \t\treturn -1;\n \n \tnetdev_dbg(pctx->dev, \"forwarding packet from GGSN to uplink\\n\");\n@@ -241,7 +332,8 @@ static int gtp0_udp_encap_recv(struct sock *sk, struct sk_buff *skb)\n \tif (!gtp)\n \t\tgoto pass;\n \n-\tif (!pskb_may_pull(skb, hdrlen))\n+\t/* Pull through IP header since gtp_rx looks at IP version */\n+\tif (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr)))\n \t\tgoto drop;\n \n \tgtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));\n@@ -287,7 +379,8 @@ static int gtp1u_udp_encap_recv(struct sock *sk, struct sk_buff *skb)\n \tif (!gtp)\n \t\tgoto pass;\n \n-\tif (!pskb_may_pull(skb, hdrlen))\n+\t/* Pull through IP header since gtp_rx looks at IP version */\n+\tif (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr)))\n \t\tgoto drop;\n \n \tgtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));\n@@ -309,8 +402,10 @@ static int gtp1u_udp_encap_recv(struct sock *sk, struct sk_buff *skb)\n \tif (gtp1->flags & GTP1_F_MASK)\n \t\thdrlen += 4;\n \n-\t/* Make sure the header is larger enough, including extensions. */\n-\tif (!pskb_may_pull(skb, hdrlen))\n+\t/* Make sure the header is larger enough, including extensions and\n+\t * also an IP header since gtp_rx looks at IP version\n+\t */\n+\tif (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr)))\n \t\tgoto drop;\n \n \tgtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));\n@@ -391,7 +486,8 @@ static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)\n \tgtp0->flags\t= 0x1e; /* v0, GTP-non-prime. */\n \tgtp0->type\t= GTP_TPDU;\n \tgtp0->length\t= htons(payload_len);\n-\tgtp0->seq\t= htons((atomic_inc_return(&pctx->tx_seq) - 1) % 0xffff);\n+\tgtp0->seq\t= htons((atomic_inc_return(&pctx->tx_seq) - 1) %\n+\t\t\t\t0xffff);\n \tgtp0->flow\t= htons(pctx->u.v0.flow);\n \tgtp0->number\t= 0xff;\n \tgtp0->spare[0]\t= gtp0->spare[1] = gtp0->spare[2] = 0xff;\n@@ -523,6 +619,25 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n \n \t\tbreak;\n \t}\n+#if GTP_IPV6\n+\tcase ETH_P_IPV6: {\n+\t\tstruct ipv6hdr *ipv6h = ipv6_hdr(skb);\n+\n+\t\tif (gtp->role == GTP_ROLE_SGSN)\n+\t\t\tpctx = ipv6_pdp_find(gtp, &ipv6h->saddr);\n+\t\telse\n+\t\t\tpctx = ipv6_pdp_find(gtp, &ipv6h->daddr);\n+\n+\t\tif (!pctx) {\n+\t\t\tnetdev_dbg(dev, \"no PDP ctx found for %pI6, skip\\n\",\n+\t\t\t\t   &ipv6h->daddr);\n+\t\t\terr = -ENOENT;\n+\t\t\tgoto tx_err;\n+\t\t}\n+\n+\t\tbreak;\n+\t}\n+#endif\n \tdefault:\n \t\terr = -EOPNOTSUPP;\n \t\tgoto tx_err;\n@@ -692,23 +807,38 @@ static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize)\n {\n \tint i;\n \n-\tgtp->addr_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL);\n-\tif (gtp->addr_hash == NULL)\n-\t\treturn -ENOMEM;\n+\tgtp->addr4_hash = kmalloc_array(hsize, sizeof(*gtp->addr4_hash),\n+\t\t\t\t\tGFP_KERNEL);\n+\tif (!gtp->addr4_hash)\n+\t\tgoto err;\n+\n+#if GTP_IPV6\n+\tgtp->addr6_hash = kmalloc_array(hsize, sizeof(*gtp->addr6_hash),\n+\t\t\t\t\tGFP_KERNEL);\n+\tif (!gtp->addr6_hash)\n+\t\tgoto err;\n+#endif\n \n-\tgtp->tid_hash = kmalloc(sizeof(struct hlist_head) * hsize, GFP_KERNEL);\n-\tif (gtp->tid_hash == NULL)\n-\t\tgoto err1;\n+\tgtp->tid_hash = kmalloc_array(hsize, sizeof(struct hlist_head),\n+\t\t\t\t      GFP_KERNEL);\n+\tif (!gtp->tid_hash)\n+\t\tgoto err;\n \n \tgtp->hash_size = hsize;\n \n \tfor (i = 0; i < hsize; i++) {\n-\t\tINIT_HLIST_HEAD(&gtp->addr_hash[i]);\n+\t\tINIT_HLIST_HEAD(&gtp->addr4_hash[i]);\n+#if GTP_IPV6\n+\t\tINIT_HLIST_HEAD(&gtp->addr6_hash[i]);\n+#endif\n \t\tINIT_HLIST_HEAD(&gtp->tid_hash[i]);\n \t}\n \treturn 0;\n-err1:\n-\tkfree(gtp->addr_hash);\n+err:\n+\tkfree(gtp->addr4_hash);\n+#if GTP_IPV6\n+\tkfree(gtp->addr6_hash);\n+#endif\n \treturn -ENOMEM;\n }\n \n@@ -722,7 +852,10 @@ static void gtp_hashtable_free(struct gtp_dev *gtp)\n \t\t\tpdp_context_delete(pctx);\n \n \tsynchronize_rcu();\n-\tkfree(gtp->addr_hash);\n+\tkfree(gtp->addr4_hash);\n+#if GTP_IPV6\n+\tkfree(gtp->addr6_hash);\n+#endif\n \tkfree(gtp->tid_hash);\n }\n \n@@ -844,16 +977,13 @@ static struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[])\n \treturn gtp;\n }\n \n-static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)\n+static void pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)\n {\n \t__be16 default_port = 0;\n \n \tpctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);\n-\tpctx->af = AF_INET;\n \tpctx->peer_addr_ip4.s_addr =\n \t\tnla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);\n-\tpctx->ms_addr_ip4.s_addr =\n-\t\tnla_get_be32(info->attrs[GTPA_MS_ADDRESS]);\n \n \tswitch (pctx->gtp_version) {\n \tcase GTP_V0:\n@@ -882,33 +1012,59 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)\n \t\tpctx->gtp_port = default_port;\n }\n \n-static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,\n-\t\t\tstruct genl_info *info)\n+static int gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,\n+\t\t       struct genl_info *info)\n {\n \tstruct net_device *dev = gtp->dev;\n+\tstruct hlist_head *addr_list;\n+\tstruct pdp_ctx *pctx = NULL;\n \tu32 hash_ms, hash_tid = 0;\n-\tstruct pdp_ctx *pctx;\n-\tbool found = false;\n-\t__be32 ms_addr;\n+#if GTP_IPV6\n+\tstruct in6_addr ms6_addr;\n+#endif\n+\t__be32 ms_addr = 0;\n+\tint ms_af;\n \tint err;\n \n-\tms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);\n-\thash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;\n+#if GTP_IPV6\n+\t/* Caller ensures we have either v4 or v6 mobile subscriber address */\n+\tif (info->attrs[GTPA_MS_ADDRESS]) {\n+\t\t/* IPv4 mobile subscriber */\n \n-\thlist_for_each_entry_rcu(pctx, &gtp->addr_hash[hash_ms], hlist_addr) {\n-\t\tif (pctx->ms_addr_ip4.s_addr == ms_addr) {\n-\t\t\tfound = true;\n-\t\t\tbreak;\n-\t\t}\n+\t\tms_addr = nla_get_in_addr(info->attrs[GTPA_MS_ADDRESS]);\n+\t\thash_ms = gtp_ipv4_hashfn(ms_addr) % gtp->hash_size;\n+\t\taddr_list = &gtp->addr4_hash[hash_ms];\n+\t\tms_af = AF_INET;\n+\n+\t\tpctx = ipv4_pdp_find(gtp, ms_addr);\n+\t} else {\n+\t\t/* IPv6 mobile subscriber */\n+\n+\t\tms6_addr = nla_get_in6_addr(info->attrs[GTPA_MS6_ADDRESS]);\n+\t\thash_ms = gtp_ipv6_hashfn(&ms6_addr) % gtp->hash_size;\n+\t\taddr_list = &gtp->addr6_hash[hash_ms];\n+\t\tms_af = AF_INET6;\n+\n+\t\tpctx = ipv6_pdp_find(gtp, &ms6_addr);\n \t}\n+#else\n+\t/* IPv4 mobile subscriber */\n \n-\tif (found) {\n+\tms_addr = nla_get_in_addr(info->attrs[GTPA_MS_ADDRESS]);\n+\thash_ms = gtp_ipv4_hashfn(ms_addr) % gtp->hash_size;\n+\taddr_list = &gtp->addr4_hash[hash_ms];\n+\tms_af = AF_INET;\n+\n+\tpctx = ipv4_pdp_find(gtp, ms_addr);\n+#endif\n+\n+\tif (pctx) {\n \t\tif (info->nlhdr->nlmsg_flags & NLM_F_EXCL)\n \t\t\treturn -EEXIST;\n \t\tif (info->nlhdr->nlmsg_flags & NLM_F_REPLACE)\n \t\t\treturn -EOPNOTSUPP;\n \n-\t\tipv4_pdp_fill(pctx, info);\n+\t\tpdp_fill(pctx, info);\n \n \t\tif (pctx->gtp_version == GTP_V0)\n \t\t\tnetdev_dbg(dev, \"GTPv0-U: update tunnel id = %llx (pdp %p)\\n\",\n@@ -934,7 +1090,20 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,\n \tsock_hold(sk);\n \tpctx->sk = sk;\n \tpctx->dev = gtp->dev;\n-\tipv4_pdp_fill(pctx, info);\n+\tpctx->ms_af = ms_af;\n+\n+\tswitch (ms_af) {\n+\tcase AF_INET:\n+\t\tpctx->ms_addr_ip4.s_addr = ms_addr;\n+\t\tbreak;\n+#if GTP_IPV6\n+\tcase AF_INET6:\n+\t\tpctx->ms_addr_ip6 = ms6_addr;\n+\t\tbreak;\n+#endif\n+\t}\n+\n+\tpdp_fill(pctx, info);\n \tatomic_set(&pctx->tx_seq, 0);\n \n \tswitch (pctx->gtp_version) {\n@@ -951,7 +1120,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct sock *sk,\n \t\tbreak;\n \t}\n \n-\thlist_add_head_rcu(&pctx->hlist_addr, &gtp->addr_hash[hash_ms]);\n+\thlist_add_head_rcu(&pctx->hlist_addr, addr_list);\n \thlist_add_head_rcu(&pctx->hlist_tid, &gtp->tid_hash[hash_tid]);\n \n \tswitch (pctx->gtp_version) {\n@@ -993,11 +1162,25 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)\n \tint err;\n \n \tif (!info->attrs[GTPA_VERSION] ||\n-\t    !info->attrs[GTPA_LINK] ||\n-\t    !info->attrs[GTPA_PEER_ADDRESS] ||\n-\t    !info->attrs[GTPA_MS_ADDRESS])\n+\t   !info->attrs[GTPA_LINK] ||\n+\t   !info->attrs[GTPA_PEER_ADDRESS])\n \t\treturn -EINVAL;\n \n+#if GTP_IPV6\n+\tif (!(!!info->attrs[GTPA_MS_ADDRESS] ^\n+\t      !!info->attrs[GTPA_MS6_ADDRESS])) {\n+\t\t/* Either v4 or v6 mobile subscriber address must be set */\n+\n+\t\treturn -EINVAL;\n+\t}\n+#else\n+\tif (!info->attrs[GTPA_MS_ADDRESS]) {\n+\t\t/* v4 mobile subscriber address must be set */\n+\n+\t\treturn -EINVAL;\n+\t}\n+#endif\n+\n \tversion = nla_get_u32(info->attrs[GTPA_VERSION]);\n \n \tswitch (version) {\n@@ -1036,7 +1219,7 @@ static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info)\n \t\tgoto out_unlock;\n \t}\n \n-\terr = ipv4_pdp_add(gtp, sk, info);\n+\terr = gtp_pdp_add(gtp, sk, info);\n \n out_unlock:\n \trcu_read_unlock();\n@@ -1056,6 +1239,13 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,\n \t\t__be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]);\n \n \t\treturn ipv4_pdp_find(gtp, ip);\n+#if GTP_IPV6\n+\t} else if (nla[GTPA_MS6_ADDRESS]) {\n+\t\tstruct in6_addr ip6 =\n+\t\t    nla_get_in6_addr(nla[GTPA_MS6_ADDRESS]);\n+\n+\t\treturn ipv6_pdp_find(gtp, &ip6);\n+#endif\n \t} else if (nla[GTPA_VERSION]) {\n \t\tu32 gtp_version = nla_get_u32(nla[GTPA_VERSION]);\n \n@@ -1126,9 +1316,27 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,\n \t\tgoto nlmsg_failure;\n \n \tif (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) ||\n-\t    nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr) ||\n-\t    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr))\n+\t    nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr))\n+\t\tgoto nla_put_failure;\n+\n+\tswitch (pctx->ms_af) {\n+\tcase AF_INET:\n+\t\tif (nla_put_be32(skb, GTPA_MS_ADDRESS,\n+\t\t\t\t pctx->ms_addr_ip4.s_addr))\n+\t\t\tgoto nla_put_failure;\n+\n+\t\tbreak;\n+#if GTP_IPV6\n+\tcase AF_INET6:\n+\t\tif (nla_put_in6_addr(skb, GTPA_MS6_ADDRESS,\n+\t\t\t\t     &pctx->ms_addr_ip6))\n+\t\t\tgoto nla_put_failure;\n+\n+\t\tbreak;\n+#endif\n+\tdefault:\n \t\tgoto nla_put_failure;\n+\t}\n \n \tswitch (pctx->gtp_version) {\n \tcase GTP_V0:\n@@ -1239,6 +1447,10 @@ static struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {\n \t[GTPA_TID]\t\t= { .type = NLA_U64, },\n \t[GTPA_PEER_ADDRESS]\t= { .type = NLA_U32, },\n \t[GTPA_MS_ADDRESS]\t= { .type = NLA_U32, },\n+#if GTP_IPV6\n+\t[GTPA_MS6_ADDRESS]\t= { .len = FIELD_SIZEOF(struct ipv6hdr,\n+\t\t\t\t\t\t\tdaddr) },\n+#endif\n \t[GTPA_FLOW]\t\t= { .type = NLA_U16, },\n \t[GTPA_NET_NS_FD]\t= { .type = NLA_U32, },\n \t[GTPA_I_TEI]\t\t= { .type = NLA_U32, },\ndiff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h\nindex b2283a5c6d7f..ae4e632c0360 100644\n--- a/include/uapi/linux/gtp.h\n+++ b/include/uapi/linux/gtp.h\n@@ -28,6 +28,7 @@ enum gtp_attrs {\n \tGTPA_O_TEI,\t/* for GTPv1 only */\n \tGTPA_PAD,\n \tGTPA_PORT,\n+\tGTPA_MS6_ADDRESS,\n \t__GTPA_MAX,\n };\n #define GTPA_MAX (__GTPA_MAX + 1)\n","prefixes":["v3","net-next","10/12"]}