All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next 00/12] gtp updates for net-next (v2)
@ 2024-04-25 10:51 Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 01/12] gtp: remove useless initialization Pablo Neira Ayuso
                   ` (12 more replies)
  0 siblings, 13 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Hi,

-o-
This v2 includes a sparse fix for patch #5 reported by Jakub.
-o-

The following patchset contains IPv6 GTP driver support for net-next,
this also includes IPv6 over IPv4 and vice-versa:

Patch #1 removes a unnecessary stack variable initialization in the
         socket routine.

Patch #2 deals with GTP extension headers. This variable length extension
         header to decapsulate packets accordingly. Otherwise, packets are
         dropped when these extension headers are present which breaks
         interoperation with other non-Linux based GTP implementations.

Patch #3 prepares for IPv6 support by moving IPv4 specific fields in PDP
         context objects to a union.

Patch #4 adds IPv6 support while retaining backward compatibility.
         Three new attributes allows to declare an IPv6 GTP tunnel
         GTPA_FAMILY, GTPA_PEER_ADDR6 and GTPA_MS_ADDR6 as well as
         IFLA_GTP_LOCAL6 to declare the IPv6 GTP UDP socket. Up to this
         patch, only IPv6 outer in IPv6 inner is supported.

Patch #5 uses IPv6 address /64 prefix for UE/MS in the inner headers.
         Unlike IPv4, which provides a 1:1 mapping between UE/MS,
         IPv6 tunnel encapsulates traffic for /64 address as specified
         by 3GPP TS. Patch has been split from Patch #4 to highlight
         this behaviour.

Patch #6 passes up IPv6 link-local traffic, such as IPv6 SLAAC, for
         handling to userspace so they are handled as control packets.

Patch #7 prepares to allow for GTP IPv4 over IPv6 and vice-versa by
         moving IP specific debugging out of the function to build
         IPv4 and IPv6 GTP packets.

Patch #8 generalizes TOS/DSCP handling following similar approach as
         in the existing iptunnel infrastructure.

Patch #9 adds a helper function to build an IPv4 GTP packet in the outer
         header.

Patch #10 adds a helper function to build an IPv6 GTP packet in the outer
          header.

Patch #11 adds support for GTP IPv4-over-IPv6 and vice-versa.

Patch #12 allows to use the same TID/TEID (tunnel identifier) for inner
          IPv4 and IPv6 packets for better UE/MS dual stack integration.

This series integrates with the osmocom.org project CI and TTCN-3 test
infrastructure (Oliver Smith) as well as the userspace libgtpnl library.

Thanks to Harald Welte, Oliver Smith and Pau Espin for reviewing and
providing feedback through the osmocom.org redmine platform to make this
happen.

Please, pull these changes from:

  git://git.kernel.org/pub/scm/linux/kernel/git/pablo/gtp.git tags/gtp-24-04-25

Thanks.

----------------------------------------------------------------

The following changes since commit f42800e9b8bad1f9d1a88c61c10ea060c557fc20:

  gtp: remove useless initialization (2024-04-23 23:44:24 +0200)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/pablo/gtp.git tags/gtp-24-04-25

for you to fetch changes up to 1dd87a4c0c5c43fa41312345874e3e5b27746103:

  gtp: identify tunnel via GTP device + GTP version + TEID + family (2024-04-24 17:58:49 +0200)

----------------------------------------------------------------
gtp pull request 24-04-25

----------------------------------------------------------------
Pablo Neira Ayuso (11):
      gtp: properly parse extension headers
      gtp: prepare for IPv6 support
      gtp: add IPv6 support
      gtp: use IPv6 address /64 prefix for UE/MS
      gtp: pass up link local traffic to userspace socket
      gtp: move debugging to skbuff build helper function
      gtp: remove IPv4 and IPv6 header from context object
      gtp: add helper function to build GTP packets from an IPv4 packet
      gtp: add helper function to build GTP packets from an IPv6 packet
      gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP
      gtp: identify tunnel via GTP device + GTP version + TEID + family

 drivers/net/gtp.c            | 852 ++++++++++++++++++++++++++++++++++++-------
 include/net/gtp.h            |   5 +
 include/uapi/linux/gtp.h     |   3 +
 include/uapi/linux/if_link.h |   2 +
 4 files changed, 734 insertions(+), 128 deletions(-)

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH net-next 01/12] gtp: remove useless initialization
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 02/12] gtp: properly parse extension headers Pablo Neira Ayuso
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Update b20dc3c68458 ("gtp: Allow to create GTP device without FDs") to
remove useless initialization to NULL, sockets are initialized to
non-NULL just a few lines of code after this.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index ba4704c2c640..4680cdf4fa70 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1009,8 +1009,8 @@ static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp)
 
 static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[])
 {
-	struct sock *sk1u = NULL;
-	struct sock *sk0 = NULL;
+	struct sock *sk1u;
+	struct sock *sk0;
 
 	sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp);
 	if (IS_ERR(sk0))
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 02/12] gtp: properly parse extension headers
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 01/12] gtp: remove useless initialization Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-26 20:28   ` Simon Horman
  2024-04-25 10:51 ` [PATCH net-next 03/12] gtp: prepare for IPv6 support Pablo Neira Ayuso
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Currently GTP packets are dropped if the next extension field is set to
non-zero value, but this are valid GTP packets.

TS 29.281 provides a longer header format, which is defined as struct
gtp1_header_long. Such long header format is used if any of the S, PN, E
flags is set.

This long header is 4 bytes longer than struct gtp1_header, plus
variable length (optional) extension headers. The next extension header
field is zero is no extension header is provided.

The extension header is composed of a length field which includes total
number of 4 byte words including the extension header itself (1 byte),
payload (variable length) and next type (1 byte). The extension header
size and its payload is aligned to 4 bytes.

A GTP packet might come with a chain extensions headers, which makes it
slightly cumbersome to parse because the extension next header field
comes at the end of the extension header, and there is a need to check
if this field becomes zero to stop the extension header parser.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 41 +++++++++++++++++++++++++++++++++++++++++
 include/net/gtp.h |  5 +++++
 2 files changed, 46 insertions(+)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 4680cdf4fa70..9451c74c1a7d 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -567,6 +567,43 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 				       msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC);
 }
 
+static int gtp_parse_exthdrs(struct sk_buff *skb, unsigned int *hdrlen)
+{
+	struct gtp_ext_hdr *gtp_exthdr, _gtp_exthdr;
+	unsigned int offset = *hdrlen;
+	__u8 *next_type, _next_type;
+
+	/* From 29.060: "The Extension Header Length field specifies the length
+	 * of the particular Extension header in 4 octets units."
+	 *
+	 * This length field includes length field size itself (1 byte),
+	 * payload (variable length) and next type (1 byte). The extension
+	 * header is aligned to to 4 bytes.
+	 */
+
+	do {
+		gtp_exthdr = skb_header_pointer(skb, offset, sizeof(gtp_exthdr),
+						&_gtp_exthdr);
+		if (!gtp_exthdr || !gtp_exthdr->len)
+			return -1;
+
+		offset += gtp_exthdr->len * 4;
+
+		/* From 29.060: "If no such Header follows, then the value of
+		 * the Next Extension Header Type shall be 0."
+		 */
+		next_type = skb_header_pointer(skb, offset - 1,
+					       sizeof(_next_type), &_next_type);
+		if (!next_type)
+			return -1;
+
+	} while (*next_type != 0);
+
+	*hdrlen = offset;
+
+	return 0;
+}
+
 static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 {
 	unsigned int hdrlen = sizeof(struct udphdr) +
@@ -616,6 +653,10 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 		return 1;
 	}
 
+	if (gtp1->flags & GTP1_F_EXTHDR &&
+	    gtp_parse_exthdrs(skb, &hdrlen) < 0)
+		return -1;
+
 	return gtp_rx(pctx, skb, hdrlen, gtp->role);
 }
 
diff --git a/include/net/gtp.h b/include/net/gtp.h
index 2a503f035d18..c0253c8702d0 100644
--- a/include/net/gtp.h
+++ b/include/net/gtp.h
@@ -78,4 +78,9 @@ static inline bool netif_is_gtp(const struct net_device *dev)
 #define GTP1_F_EXTHDR	0x04
 #define GTP1_F_MASK	0x07
 
+struct gtp_ext_hdr {
+	__u8	len;
+	__u8	data[];
+};
+
 #endif
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 03/12] gtp: prepare for IPv6 support
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 01/12] gtp: remove useless initialization Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 02/12] gtp: properly parse extension headers Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 04/12] gtp: add " Pablo Neira Ayuso
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Use union artifact to prepare for IPv6 support.
Add and use GTP_{IPV4,TH}_MAXLEN.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 151 +++++++++++++++++++++++++++++-----------------
 1 file changed, 96 insertions(+), 55 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 9451c74c1a7d..1c4429d24cfc 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -50,8 +50,12 @@ struct pdp_ctx {
 	u8			gtp_version;
 	u16			af;
 
-	struct in_addr		ms_addr_ip4;
-	struct in_addr		peer_addr_ip4;
+	union {
+		struct in_addr	addr;
+	} ms;
+	union {
+		struct in_addr	addr;
+	} peer;
 
 	struct sock		*sk;
 	struct net_device       *dev;
@@ -80,9 +84,15 @@ struct gtp_dev {
 };
 
 struct echo_info {
-	struct in_addr		ms_addr_ip4;
-	struct in_addr		peer_addr_ip4;
+	u16			af;
 	u8			gtp_version;
+
+	union {
+		struct in_addr	addr;
+	} ms;
+	union {
+		struct in_addr	addr;
+	} peer;
 };
 
 static unsigned int gtp_net_id __read_mostly;
@@ -163,7 +173,7 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
 		if (pdp->af == AF_INET &&
-		    pdp->ms_addr_ip4.s_addr == ms_addr)
+		    pdp->ms.addr.s_addr == ms_addr)
 			return pdp;
 	}
 
@@ -181,9 +191,9 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
 	iph = (struct iphdr *)(skb->data + hdrlen);
 
 	if (role == GTP_ROLE_SGSN)
-		return iph->daddr == pctx->ms_addr_ip4.s_addr;
+		return iph->daddr == pctx->ms.addr.s_addr;
 	else
-		return iph->saddr == pctx->ms_addr_ip4.s_addr;
+		return iph->saddr == pctx->ms.addr.s_addr;
 }
 
 /* Check if the inner IP address in this packet is assigned to any
@@ -292,13 +302,39 @@ static void gtp0_build_echo_msg(struct gtp0_header *hdr, __u8 msg_type)
 		hdr->length = 0;
 }
 
+static int gtp0_send_echo_resp_ip(struct gtp_dev *gtp, struct sk_buff *skb)
+{
+	struct iphdr *iph = ip_hdr(skb);
+	struct flowi4 fl4;
+	struct rtable *rt;
+
+	/* find route to the sender,
+	 * src address becomes dst address and vice versa.
+	 */
+	rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr);
+	if (IS_ERR(rt)) {
+		netdev_dbg(gtp->dev, "no route for echo response from %pI4\n",
+			   &iph->saddr);
+		return -1;
+	}
+
+	udp_tunnel_xmit_skb(rt, gtp->sk0, skb,
+			    fl4.saddr, fl4.daddr,
+			    iph->tos,
+			    ip4_dst_hoplimit(&rt->dst),
+			    0,
+			    htons(GTP0_PORT), htons(GTP0_PORT),
+			    !net_eq(sock_net(gtp->sk1u),
+				    dev_net(gtp->dev)),
+			    false);
+
+	return 0;
+}
+
 static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 {
 	struct gtp0_packet *gtp_pkt;
 	struct gtp0_header *gtp0;
-	struct rtable *rt;
-	struct flowi4 fl4;
-	struct iphdr *iph;
 	__be16 seq;
 
 	gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));
@@ -325,27 +361,15 @@ static int gtp0_send_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 	gtp_pkt->ie.tag = GTPIE_RECOVERY;
 	gtp_pkt->ie.val = gtp->restart_count;
 
-	iph = ip_hdr(skb);
-
-	/* find route to the sender,
-	 * src address becomes dst address and vice versa.
-	 */
-	rt = ip4_route_output_gtp(&fl4, gtp->sk0, iph->saddr, iph->daddr);
-	if (IS_ERR(rt)) {
-		netdev_dbg(gtp->dev, "no route for echo response from %pI4\n",
-			   &iph->saddr);
+	switch (gtp->sk0->sk_family) {
+	case AF_INET:
+		if (gtp0_send_echo_resp_ip(gtp, skb) < 0)
+			return -1;
+		break;
+	case AF_INET6:
 		return -1;
 	}
 
-	udp_tunnel_xmit_skb(rt, gtp->sk0, skb,
-			    fl4.saddr, fl4.daddr,
-			    iph->tos,
-			    ip4_dst_hoplimit(&rt->dst),
-			    0,
-			    htons(GTP0_PORT), htons(GTP0_PORT),
-			    !net_eq(sock_net(gtp->sk1u),
-				    dev_net(gtp->dev)),
-			    false);
 	return 0;
 }
 
@@ -360,8 +384,8 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 		goto failure;
 
 	if (nla_put_u32(skb, GTPA_VERSION, echo.gtp_version) ||
-	    nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer_addr_ip4.s_addr) ||
-	    nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms_addr_ip4.s_addr))
+	    nla_put_be32(skb, GTPA_PEER_ADDRESS, echo.peer.addr.s_addr) ||
+	    nla_put_be32(skb, GTPA_MS_ADDRESS, echo.ms.addr.s_addr))
 		goto failure;
 
 	genlmsg_end(skb, genlh);
@@ -372,12 +396,20 @@ static int gtp_genl_fill_echo(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 	return -EMSGSIZE;
 }
 
+static void gtp0_handle_echo_resp_ip(struct sk_buff *skb, struct echo_info *echo)
+{
+	struct iphdr *iph = ip_hdr(skb);
+
+	echo->ms.addr.s_addr = iph->daddr;
+	echo->peer.addr.s_addr = iph->saddr;
+	echo->gtp_version = GTP_V0;
+}
+
 static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 {
 	struct gtp0_header *gtp0;
 	struct echo_info echo;
 	struct sk_buff *msg;
-	struct iphdr *iph;
 	int ret;
 
 	gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));
@@ -385,10 +417,13 @@ static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 	if (!gtp0_validate_echo_hdr(gtp0))
 		return -1;
 
-	iph = ip_hdr(skb);
-	echo.ms_addr_ip4.s_addr = iph->daddr;
-	echo.peer_addr_ip4.s_addr = iph->saddr;
-	echo.gtp_version = GTP_V0;
+	switch (gtp->sk0->sk_family) {
+	case AF_INET:
+		gtp0_handle_echo_resp_ip(skb, &echo);
+		break;
+	case AF_INET6:
+		return -1;
+	}
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
 	if (!msg)
@@ -549,8 +584,8 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 		return -1;
 
 	iph = ip_hdr(skb);
-	echo.ms_addr_ip4.s_addr = iph->daddr;
-	echo.peer_addr_ip4.s_addr = iph->saddr;
+	echo.ms.addr.s_addr = iph->daddr;
+	echo.peer.addr.s_addr = iph->saddr;
 	echo.gtp_version = GTP_V1;
 
 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -801,9 +836,15 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
 
 struct gtp_pktinfo {
 	struct sock		*sk;
-	struct iphdr		*iph;
-	struct flowi4		fl4;
-	struct rtable		*rt;
+	union {
+		struct iphdr	*iph;
+	};
+	union {
+		struct flowi4	fl4;
+	};
+	union {
+		struct rtable	*rt;
+	};
 	struct pdp_ctx		*pctx;
 	struct net_device	*dev;
 	__be16			gtph_port;
@@ -864,18 +905,18 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	}
 	netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-	rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr,
+	rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer.addr.s_addr,
 				  inet_sk(pctx->sk)->inet_saddr);
 	if (IS_ERR(rt)) {
 		netdev_dbg(dev, "no route to SSGN %pI4\n",
-			   &pctx->peer_addr_ip4.s_addr);
+			   &pctx->peer.addr.s_addr);
 		dev->stats.tx_carrier_errors++;
 		goto err;
 	}
 
 	if (rt->dst.dev == dev) {
 		netdev_dbg(dev, "circular route to SSGN %pI4\n",
-			   &pctx->peer_addr_ip4.s_addr);
+			   &pctx->peer.addr.s_addr);
 		dev->stats.collisions++;
 		goto err_rt;
 	}
@@ -977,11 +1018,11 @@ static const struct device_type gtp_type = {
 	.name = "gtp",
 };
 
+#define GTP_TH_MAXLEN	(sizeof(struct udphdr) + sizeof(struct gtp0_header))
+#define GTP_IPV4_MAXLEN	(sizeof(struct iphdr) + GTP_TH_MAXLEN)
+
 static void gtp_link_setup(struct net_device *dev)
 {
-	unsigned int max_gtp_header_len = sizeof(struct iphdr) +
-					  sizeof(struct udphdr) +
-					  sizeof(struct gtp0_header);
 	struct gtp_dev *gtp = netdev_priv(dev);
 
 	dev->netdev_ops		= &gtp_netdev_ops;
@@ -990,7 +1031,7 @@ static void gtp_link_setup(struct net_device *dev)
 
 	dev->hard_header_len = 0;
 	dev->addr_len = 0;
-	dev->mtu = ETH_DATA_LEN - max_gtp_header_len;
+	dev->mtu = ETH_DATA_LEN - GTP_IPV4_MAXLEN;
 
 	/* Zero header length. */
 	dev->type = ARPHRD_NONE;
@@ -1001,7 +1042,7 @@ static void gtp_link_setup(struct net_device *dev)
 	dev->features	|= NETIF_F_LLTX;
 	netif_keep_dst(dev);
 
-	dev->needed_headroom	= LL_MAX_HEADER + max_gtp_header_len;
+	dev->needed_headroom	= LL_MAX_HEADER + GTP_IPV4_MAXLEN;
 	gtp->dev = dev;
 }
 
@@ -1340,9 +1381,9 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
 	pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);
 	pctx->af = AF_INET;
-	pctx->peer_addr_ip4.s_addr =
+	pctx->peer.addr.s_addr =
 		nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
-	pctx->ms_addr_ip4.s_addr =
+	pctx->ms.addr.s_addr =
 		nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 
 	switch (pctx->gtp_version) {
@@ -1443,13 +1484,13 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 	switch (pctx->gtp_version) {
 	case GTP_V0:
 		netdev_dbg(dev, "GTPv0-U: new PDP ctx id=%llx ssgn=%pI4 ms=%pI4 (pdp=%p)\n",
-			   pctx->u.v0.tid, &pctx->peer_addr_ip4,
-			   &pctx->ms_addr_ip4, pctx);
+			   pctx->u.v0.tid, &pctx->peer.addr,
+			   &pctx->ms.addr, pctx);
 		break;
 	case GTP_V1:
 		netdev_dbg(dev, "GTPv1-U: new PDP ctx id=%x/%x ssgn=%pI4 ms=%pI4 (pdp=%p)\n",
 			   pctx->u.v1.i_tei, pctx->u.v1.o_tei,
-			   &pctx->peer_addr_ip4, &pctx->ms_addr_ip4, pctx);
+			   &pctx->peer.addr, &pctx->ms.addr, pctx);
 		break;
 	}
 
@@ -1621,8 +1662,8 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 
 	if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) ||
 	    nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex) ||
-	    nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer_addr_ip4.s_addr) ||
-	    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms_addr_ip4.s_addr))
+	    nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr) ||
+	    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
 		goto nla_put_failure;
 
 	switch (pctx->gtp_version) {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 04/12] gtp: add IPv6 support
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 03/12] gtp: prepare for IPv6 support Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-26 20:41   ` Simon Horman
  2024-04-25 10:51 ` [PATCH net-next 05/12] gtp: use IPv6 address /64 prefix for UE/MS Pablo Neira Ayuso
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Add new iflink attributes to configure in-kernel UDP listener socket
address: IFLA_GTP_LOCAL and IFLA_GTP_LOCAL6. If none of these attributes
are specified, default is still to IPv4 INADDR_ANY for backward
compatibility.

Add new attributes to set up family and IPv6 address of GTP tunnels:
GTPA_FAMILY, GTPA_PEER_ADDR6 and GTPA_MS_ADDR6. If no GTPA_FAMILY is
specified, AF_INET is assumed for backward compatibility.

setsockopt IPV6_ADDRFORM allows to downgrade socket from IPv6 to IPv4
after socket is bound. Assumption is that socket listener that is
attached to the gtp device needs to be either IPv4 or IPv6. Therefore,
GTP socket listener does not allow for IPv4-mapped-IPv6 listener.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c            | 390 ++++++++++++++++++++++++++++++++---
 include/uapi/linux/gtp.h     |   3 +
 include/uapi/linux/if_link.h |   2 +
 3 files changed, 368 insertions(+), 27 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 1c4429d24cfc..69d865e592df 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -24,6 +24,7 @@
 #include <net/net_namespace.h>
 #include <net/protocol.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/udp.h>
 #include <net/udp_tunnel.h>
 #include <net/icmp.h>
@@ -52,9 +53,11 @@ struct pdp_ctx {
 
 	union {
 		struct in_addr	addr;
+		struct in6_addr	addr6;
 	} ms;
 	union {
 		struct in_addr	addr;
+		struct in6_addr	addr6;
 	} peer;
 
 	struct sock		*sk;
@@ -131,6 +134,11 @@ static inline u32 ipv4_hashfn(__be32 ip)
 	return jhash_1word((__force u32)ip, gtp_h_initval);
 }
 
+static inline u32 ipv6_hashfn(const struct in6_addr *ip6)
+{
+	return jhash(ip6, sizeof(*ip6), gtp_h_initval);
+}
+
 /* Resolve a PDP context structure based on the 64bit TID. */
 static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
 {
@@ -180,6 +188,23 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
 	return NULL;
 }
 
+static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,
+				     const struct in6_addr *ms_addr)
+{
+	struct hlist_head *head;
+	struct pdp_ctx *pdp;
+
+	head = &gtp->addr_hash[ipv6_hashfn(ms_addr) % gtp->hash_size];
+
+	hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
+		if (pdp->af == AF_INET6 &&
+		    memcmp(&pdp->ms.addr6, ms_addr, sizeof(struct in6_addr)) == 0)
+			return pdp;
+	}
+
+	return NULL;
+}
+
 static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
 				  unsigned int hdrlen, unsigned int role)
 {
@@ -196,6 +221,28 @@ static bool gtp_check_ms_ipv4(struct sk_buff *skb, struct pdp_ctx *pctx,
 		return iph->saddr == pctx->ms.addr.s_addr;
 }
 
+static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,
+			      unsigned int hdrlen, unsigned int role)
+{
+	struct ipv6hdr *ip6h;
+	int ret;
+
+	if (!pskb_may_pull(skb, hdrlen + sizeof(struct ipv6hdr)))
+		return false;
+
+	ip6h = (struct ipv6hdr *)(skb->data + hdrlen);
+
+	if (role == GTP_ROLE_SGSN) {
+		ret = memcmp(&ip6h->daddr, &pctx->ms.addr6,
+			     sizeof(struct in6_addr));
+	} else {
+		ret = memcmp(&ip6h->saddr, &pctx->ms.addr6,
+			     sizeof(struct in6_addr));
+	}
+
+	return ret == 0;
+}
+
 /* Check if the inner IP address in this packet is assigned to any
  * existing mobile subscriber.
  */
@@ -205,6 +252,8 @@ static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
 	switch (ntohs(skb->protocol)) {
 	case ETH_P_IP:
 		return gtp_check_ms_ipv4(skb, pctx, hdrlen, role);
+	case ETH_P_IPV6:
+		return gtp_check_ms_ipv6(skb, pctx, hdrlen, role);
 	}
 	return false;
 }
@@ -260,6 +309,27 @@ static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
 	return ip_route_output_key(sock_net(sk), fl4);
 }
 
+static struct rt6_info *ip6_route_output_gtp(struct net *net,
+					     struct flowi6 *fl6,
+					     const struct sock *sk,
+					     const struct in6_addr *daddr,
+					     struct in6_addr *saddr)
+{
+	struct dst_entry *dst;
+
+	memset(fl6, 0, sizeof(*fl6));
+	fl6->flowi6_oif		= sk->sk_bound_dev_if;
+	fl6->daddr		= *daddr;
+	fl6->saddr		= *saddr;
+	fl6->flowi6_proto	= sk->sk_protocol;
+
+	dst = ipv6_stub->ipv6_dst_lookup_flow(net, sk, fl6, NULL);
+	if (IS_ERR(dst))
+		return ERR_PTR(-ENETUNREACH);
+
+	return (struct rt6_info *)dst;
+}
+
 /* GSM TS 09.60. 7.3
  * In all Path Management messages:
  * - TID: is not used and shall be set to 0.
@@ -838,12 +908,15 @@ struct gtp_pktinfo {
 	struct sock		*sk;
 	union {
 		struct iphdr	*iph;
+		struct ipv6hdr	*ip6h;
 	};
 	union {
 		struct flowi4	fl4;
+		struct flowi6	fl6;
 	};
 	union {
 		struct rtable	*rt;
+		struct rt6_info	*rt6;
 	};
 	struct pdp_ctx		*pctx;
 	struct net_device	*dev;
@@ -878,6 +951,20 @@ static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
 	pktinfo->dev	= dev;
 }
 
+static inline void gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo,
+					struct sock *sk, struct ipv6hdr *ip6h,
+					struct pdp_ctx *pctx, struct rt6_info *rt6,
+					struct flowi6 *fl6,
+					struct net_device *dev)
+{
+	pktinfo->sk	= sk;
+	pktinfo->ip6h	= ip6h;
+	pktinfo->pctx	= pctx;
+	pktinfo->rt6	= rt6;
+	pktinfo->fl6	= *fl6;
+	pktinfo->dev	= dev;
+}
+
 static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 			     struct gtp_pktinfo *pktinfo)
 {
@@ -959,6 +1046,81 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	return -EBADMSG;
 }
 
+static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
+			     struct gtp_pktinfo *pktinfo)
+{
+	struct gtp_dev *gtp = netdev_priv(dev);
+	struct net *net = gtp->net;
+	struct dst_entry *dst;
+	struct pdp_ctx *pctx;
+	struct ipv6hdr *ip6h;
+	struct rt6_info *rt;
+	struct flowi6 fl6;
+	int mtu;
+
+	/* Read the IP destination address and resolve the PDP context.
+	 * Prepend PDP header with TEI/TID from PDP ctx.
+	 */
+	ip6h = ipv6_hdr(skb);
+	if (gtp->role == GTP_ROLE_SGSN)
+		pctx = ipv6_pdp_find(gtp, &ip6h->saddr);
+	else
+		pctx = ipv6_pdp_find(gtp, &ip6h->daddr);
+
+	if (!pctx) {
+		netdev_dbg(dev, "no PDP ctx found for %pI6, skip\n",
+			   &ip6h->daddr);
+		return -ENOENT;
+	}
+	netdev_dbg(dev, "found PDP context %p\n", pctx);
+
+	rt = ip6_route_output_gtp(net, &fl6, pctx->sk, &pctx->peer.addr6,
+				  &inet6_sk(pctx->sk)->saddr);
+	if (IS_ERR(rt)) {
+		netdev_dbg(dev, "no route to SSGN %pI6\n",
+			   &pctx->peer.addr6);
+		dev->stats.tx_carrier_errors++;
+		goto err;
+	}
+	dst = &rt->dst;
+
+	if (rt->dst.dev == dev) {
+		netdev_dbg(dev, "circular route to SSGN %pI6\n",
+			   &pctx->peer.addr6);
+		dev->stats.collisions++;
+		goto err_rt;
+	}
+
+	mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
+		sizeof(struct ipv6hdr) - sizeof(struct udphdr);
+	switch (pctx->gtp_version) {
+	case GTP_V0:
+		mtu -= sizeof(struct gtp0_header);
+		break;
+	case GTP_V1:
+		mtu -= sizeof(struct gtp1_header);
+		break;
+	}
+
+	skb_dst_update_pmtu_no_confirm(skb, mtu);
+
+	if ((!skb_is_gso(skb) && skb->len > mtu) ||
+	    (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) {
+		netdev_dbg(dev, "packet too big, fragmentation needed\n");
+		icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+		goto err_rt;
+	}
+
+	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, ip6h, pctx, rt, &fl6, dev);
+	gtp_push_header(skb, pktinfo);
+
+	return 0;
+err_rt:
+	dst_release(dst);
+err:
+	return -EBADMSG;
+}
+
 static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	unsigned int proto = ntohs(skb->protocol);
@@ -977,6 +1139,9 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	case ETH_P_IP:
 		err = gtp_build_skb_ip4(skb, dev, &pktinfo);
 		break;
+	case ETH_P_IPV6:
+		err = gtp_build_skb_ip6(skb, dev, &pktinfo);
+		break;
 	default:
 		err = -EOPNOTSUPP;
 		break;
@@ -1000,6 +1165,21 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 					    dev_net(dev)),
 				    false);
 		break;
+	case ETH_P_IPV6:
+#if IS_ENABLED(CONFIG_IPV6)
+		netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI6 dst: %pI6\n",
+			   &pktinfo.ip6h->saddr, &pktinfo.ip6h->daddr);
+		udp_tunnel6_xmit_skb(&pktinfo.rt6->dst, pktinfo.sk, skb, dev,
+				     &pktinfo.fl6.saddr, &pktinfo.fl6.daddr,
+				     ipv6_get_dsfield(pktinfo.ip6h),
+				     ip6_dst_hoplimit(&pktinfo.rt->dst),
+				     0,
+				     pktinfo.gtph_port, pktinfo.gtph_port,
+				     false);
+#else
+		goto tx_err;
+#endif
+		break;
 	}
 
 	return NETDEV_TX_OK;
@@ -1057,17 +1237,45 @@ static void gtp_destructor(struct net_device *dev)
 	kfree(gtp->tid_hash);
 }
 
-static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp)
+static int gtp_sock_udp_config(struct udp_port_cfg *udp_conf,
+			       const struct nlattr *nla, int family)
+{
+	udp_conf->family = family;
+
+	switch (udp_conf->family) {
+	case AF_INET:
+		udp_conf->local_ip.s_addr = nla_get_be32(nla);
+		break;
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+		udp_conf->local_ip6 = nla_get_in6_addr(nla);
+		break;
+#endif
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp,
+				    const struct nlattr *nla, int family)
 {
 	struct udp_tunnel_sock_cfg tuncfg = {};
-	struct udp_port_cfg udp_conf = {
-		.local_ip.s_addr	= htonl(INADDR_ANY),
-		.family			= AF_INET,
-	};
+	struct udp_port_cfg udp_conf = {};
 	struct net *net = gtp->net;
 	struct socket *sock;
 	int err;
 
+	if (nla) {
+		err = gtp_sock_udp_config(&udp_conf, nla, family);
+		if (err < 0)
+			return ERR_PTR(err);
+	} else {
+		udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
+		udp_conf.family = AF_INET;
+	}
+
 	if (type == UDP_ENCAP_GTP0)
 		udp_conf.local_udp_port = htons(GTP0_PORT);
 	else if (type == UDP_ENCAP_GTP1U)
@@ -1089,16 +1297,17 @@ static struct sock *gtp_create_sock(int type, struct gtp_dev *gtp)
 	return sock->sk;
 }
 
-static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[])
+static int gtp_create_sockets(struct gtp_dev *gtp, const struct nlattr *nla,
+			      int family)
 {
 	struct sock *sk1u;
 	struct sock *sk0;
 
-	sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp);
+	sk0 = gtp_create_sock(UDP_ENCAP_GTP0, gtp, nla, family);
 	if (IS_ERR(sk0))
 		return PTR_ERR(sk0);
 
-	sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp);
+	sk1u = gtp_create_sock(UDP_ENCAP_GTP1U, gtp, nla, family);
 	if (IS_ERR(sk1u)) {
 		udp_tunnel_sock_release(sk0->sk_socket);
 		return PTR_ERR(sk1u);
@@ -1111,6 +1320,9 @@ static int gtp_create_sockets(struct gtp_dev *gtp, struct nlattr *data[])
 	return 0;
 }
 
+#define GTP_TH_MAXLEN	(sizeof(struct udphdr) + sizeof(struct gtp0_header))
+#define GTP_IPV6_MAXLEN	(sizeof(struct ipv6hdr) + GTP_TH_MAXLEN)
+
 static int gtp_newlink(struct net *src_net, struct net_device *dev,
 		       struct nlattr *tb[], struct nlattr *data[],
 		       struct netlink_ext_ack *extack)
@@ -1120,6 +1332,11 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 	struct gtp_net *gn;
 	int hashsize, err;
 
+#if !IS_ENABLED(CONFIG_IPV6)
+	if (data[IFLA_GTP_LOCAL6])
+		return -EAFNOSUPPORT;
+#endif
+
 	gtp = netdev_priv(dev);
 
 	if (!data[IFLA_GTP_PDP_HASHSIZE]) {
@@ -1148,13 +1365,24 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
 	if (err < 0)
 		return err;
 
-	if (data[IFLA_GTP_CREATE_SOCKETS])
-		err = gtp_create_sockets(gtp, data);
-	else
+	if (data[IFLA_GTP_CREATE_SOCKETS]) {
+		if (data[IFLA_GTP_LOCAL6])
+			err = gtp_create_sockets(gtp, data[IFLA_GTP_LOCAL6], AF_INET6);
+		else
+			err = gtp_create_sockets(gtp, data[IFLA_GTP_LOCAL], AF_INET);
+	} else {
 		err = gtp_encap_enable(gtp, data);
+	}
+
 	if (err < 0)
 		goto out_hashtable;
 
+	if ((gtp->sk0 && gtp->sk0->sk_family == AF_INET6) ||
+	    (gtp->sk1u && gtp->sk1u->sk_family == AF_INET6)) {
+		dev->mtu = ETH_DATA_LEN - GTP_IPV6_MAXLEN;
+		dev->needed_headroom = LL_MAX_HEADER + GTP_IPV6_MAXLEN;
+	}
+
 	err = register_netdevice(dev);
 	if (err < 0) {
 		netdev_dbg(dev, "failed to register new netdev %d\n", err);
@@ -1198,6 +1426,8 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
 	[IFLA_GTP_ROLE]			= { .type = NLA_U32 },
 	[IFLA_GTP_CREATE_SOCKETS]	= { .type = NLA_U8 },
 	[IFLA_GTP_RESTART_COUNT]	= { .type = NLA_U8 },
+	[IFLA_GTP_LOCAL]		= { .type = NLA_U32 },
+	[IFLA_GTP_LOCAL6]		= { .len = sizeof(struct in6_addr) },
 };
 
 static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1297,6 +1527,12 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
 		goto out_sock;
 	}
 
+	if (sk->sk_family == AF_INET6 &&
+	    !sk->sk_ipv6only) {
+		sk = ERR_PTR(-EADDRNOTAVAIL);
+		goto out_sock;
+	}
+
 	lock_sock(sk);
 	if (sk->sk_user_data) {
 		sk = ERR_PTR(-EBUSY);
@@ -1348,6 +1584,13 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
 	gtp->sk0 = sk0;
 	gtp->sk1u = sk1u;
 
+	if (sk0 && sk1u &&
+	    sk0->sk_family != sk1u->sk_family) {
+		gtp_encap_disable_sock(sk0);
+		gtp_encap_disable_sock(sk1u);
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -1377,14 +1620,9 @@ static struct gtp_dev *gtp_find_dev(struct net *src_net, struct nlattr *nla[])
 	return gtp;
 }
 
-static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
+static void gtp_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
 	pctx->gtp_version = nla_get_u32(info->attrs[GTPA_VERSION]);
-	pctx->af = AF_INET;
-	pctx->peer.addr.s_addr =
-		nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
-	pctx->ms.addr.s_addr =
-		nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 
 	switch (pctx->gtp_version) {
 	case GTP_V0:
@@ -1404,21 +1642,78 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 	}
 }
 
+static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
+{
+	pctx->peer.addr.s_addr =
+		nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
+	pctx->ms.addr.s_addr =
+		nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
+	gtp_pdp_fill(pctx, info);
+}
+
+static void ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
+{
+	pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]);
+	pctx->ms.addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
+	gtp_pdp_fill(pctx, info);
+}
+
 static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 				   struct genl_info *info)
 {
 	struct pdp_ctx *pctx, *pctx_tid = NULL;
 	struct net_device *dev = gtp->dev;
 	u32 hash_ms, hash_tid = 0;
+	struct in6_addr ms_addr6;
 	unsigned int version;
 	bool found = false;
 	__be32 ms_addr;
+	int family;
 
-	ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
-	hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;
 	version = nla_get_u32(info->attrs[GTPA_VERSION]);
 
-	pctx = ipv4_pdp_find(gtp, ms_addr);
+	if (info->attrs[GTPA_FAMILY])
+		family = nla_get_u8(info->attrs[GTPA_FAMILY]);
+	else
+		family = AF_INET;
+
+#if !IS_ENABLED(CONFIG_IPV6)
+	if (family == AF_INET6)
+		return ERR_PTR(-EAFNOSUPPORT);
+#endif
+
+	if ((info->attrs[GTPA_PEER_ADDRESS] &&
+	     sk->sk_family == AF_INET6) ||
+	    (info->attrs[GTPA_PEER_ADDR6] &&
+	     sk->sk_family == AF_INET))
+		return ERR_PTR(-EAFNOSUPPORT);
+
+	switch (family) {
+	case AF_INET:
+		if (!info->attrs[GTPA_MS_ADDRESS] ||
+		    !info->attrs[GTPA_PEER_ADDRESS] ||
+		    info->attrs[GTPA_MS_ADDR6] ||
+		    info->attrs[GTPA_PEER_ADDR6])
+			return ERR_PTR(-EINVAL);
+
+		ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
+		hash_ms = ipv4_hashfn(ms_addr) % gtp->hash_size;
+		pctx = ipv4_pdp_find(gtp, ms_addr);
+		break;
+	case AF_INET6:
+		if (!info->attrs[GTPA_MS_ADDR6] ||
+		    !info->attrs[GTPA_PEER_ADDR6] ||
+		    info->attrs[GTPA_MS_ADDRESS] ||
+		    info->attrs[GTPA_PEER_ADDRESS])
+			return ERR_PTR(-EINVAL);
+
+		ms_addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
+		hash_ms = ipv6_hashfn(&ms_addr6) % gtp->hash_size;
+		pctx = ipv6_pdp_find(gtp, &ms_addr6);
+		break;
+	default:
+		return ERR_PTR(-EAFNOSUPPORT);
+	}
 	if (pctx)
 		found = true;
 	if (version == GTP_V0)
@@ -1441,7 +1736,14 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 		if (!pctx)
 			pctx = pctx_tid;
 
-		ipv4_pdp_fill(pctx, info);
+		switch (pctx->af) {
+		case AF_INET:
+			ipv4_pdp_fill(pctx, info);
+			break;
+		case AF_INET6:
+			ipv6_pdp_fill(pctx, info);
+			break;
+		}
 
 		if (pctx->gtp_version == GTP_V0)
 			netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n",
@@ -1461,7 +1763,24 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 	sock_hold(sk);
 	pctx->sk = sk;
 	pctx->dev = gtp->dev;
-	ipv4_pdp_fill(pctx, info);
+	pctx->af = family;
+
+	switch (pctx->af) {
+	case AF_INET:
+		if (!info->attrs[GTPA_MS_ADDRESS] ||
+		    !info->attrs[GTPA_PEER_ADDRESS])
+			return ERR_PTR(-EINVAL);
+
+		ipv4_pdp_fill(pctx, info);
+		break;
+	case AF_INET6:
+		if (!info->attrs[GTPA_MS_ADDR6] ||
+		    !info->attrs[GTPA_PEER_ADDR6])
+			return ERR_PTR(-EINVAL);
+
+		ipv6_pdp_fill(pctx, info);
+		break;
+	}
 	atomic_set(&pctx->tx_seq, 0);
 
 	switch (pctx->gtp_version) {
@@ -1523,9 +1842,7 @@ 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_MS_ADDRESS])
+	    !info->attrs[GTPA_LINK])
 		return -EINVAL;
 
 	version = nla_get_u32(info->attrs[GTPA_VERSION]);
@@ -1592,6 +1909,10 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
 		__be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]);
 
 		return ipv4_pdp_find(gtp, ip);
+	} else if (nla[GTPA_MS_ADDR6]) {
+		struct in6_addr addr = nla_get_in6_addr(nla[GTPA_MS_ADDR6]);
+
+		return ipv6_pdp_find(gtp, &addr);
 	} else if (nla[GTPA_VERSION]) {
 		u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]);
 
@@ -1662,10 +1983,22 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 
 	if (nla_put_u32(skb, GTPA_VERSION, pctx->gtp_version) ||
 	    nla_put_u32(skb, GTPA_LINK, pctx->dev->ifindex) ||
-	    nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr) ||
-	    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
+	    nla_put_u8(skb, GTPA_FAMILY, pctx->af))
 		goto nla_put_failure;
 
+	switch (pctx->af) {
+	case AF_INET:
+		if (nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr) ||
+		    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
+			goto nla_put_failure;
+		break;
+	case AF_INET6:
+		if (nla_put_in6_addr(skb, GTPA_PEER_ADDR6, &pctx->peer.addr6) ||
+		    nla_put_in6_addr(skb, GTPA_MS_ADDR6, &pctx->ms.addr6))
+			goto nla_put_failure;
+		break;
+	}
+
 	switch (pctx->gtp_version) {
 	case GTP_V0:
 		if (nla_put_u64_64bit(skb, GTPA_TID, pctx->u.v0.tid, GTPA_PAD) ||
@@ -1892,6 +2225,9 @@ static const struct nla_policy gtp_genl_policy[GTPA_MAX + 1] = {
 	[GTPA_NET_NS_FD]	= { .type = NLA_U32, },
 	[GTPA_I_TEI]		= { .type = NLA_U32, },
 	[GTPA_O_TEI]		= { .type = NLA_U32, },
+	[GTPA_PEER_ADDR6]	= { .len = sizeof(struct in6_addr), },
+	[GTPA_MS_ADDR6]		= { .len = sizeof(struct in6_addr), },
+	[GTPA_FAMILY]		= { .type = NLA_U8, },
 };
 
 static const struct genl_small_ops gtp_genl_ops[] = {
diff --git a/include/uapi/linux/gtp.h b/include/uapi/linux/gtp.h
index 3dcdb9e33cba..40f5388d6de0 100644
--- a/include/uapi/linux/gtp.h
+++ b/include/uapi/linux/gtp.h
@@ -31,6 +31,9 @@ enum gtp_attrs {
 	GTPA_I_TEI,	/* for GTPv1 only */
 	GTPA_O_TEI,	/* for GTPv1 only */
 	GTPA_PAD,
+	GTPA_PEER_ADDR6,
+	GTPA_MS_ADDR6,
+	GTPA_FAMILY,
 	__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 ffa637b38c93..c37cf994f4de 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1466,6 +1466,8 @@ enum {
 	IFLA_GTP_ROLE,
 	IFLA_GTP_CREATE_SOCKETS,
 	IFLA_GTP_RESTART_COUNT,
+	IFLA_GTP_LOCAL,
+	IFLA_GTP_LOCAL6,
 	__IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 05/12] gtp: use IPv6 address /64 prefix for UE/MS
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (3 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 04/12] gtp: add " Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 06/12] gtp: pass up link local traffic to userspace socket Pablo Neira Ayuso
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Harald Welte reports that according to 3GPP TS 29.060:

    PDN Connection: the association between a MS represented by one IPv4
address and/or one IPv6 prefix and a PDN represented by an APN.

this clearly states that IPv4 is a single address while IPv6 is a single prefix.

Then, 3GPP TS 29.061, Section 11.2.1.3:

    For APNs that are configured for IPv6 address allocation, the GGSN/P-GW
shall only use the Prefix part of the IPv6 address for forwarding of mobile
terminated IP packets. The size of the prefix shall be according to the maximum
prefix length for a global IPv6 address as specified in the IPv6 Addressing
Architecture, see RFC 4291 [82].

RFC 4291 section 2.5.4 states

    All Global Unicast addresses other than those that start with binary 000
have a 64-bit interface ID field (i.e., n + m = 64) ...

3GPP TS 29.61 Section 11.2.1.3.2a:

    In the procedure in the cases of using GTP-based S5/S8, P-GW acts as an
access router, and allocates to a UE a globally unique /64 IPv6 prefix if the
PLMN allocates the prefix.

Therefore, compare IPv6 address /64 prefix only since MS/UE is not a single
address like in the IPv4 case.

Reject IPv6 address with EADDRNOTAVAIL if it lower 64 bits of the IPv6 address
from the control plane are set.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 49 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 39 insertions(+), 10 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 69d865e592df..ca3b1df19e6d 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -136,7 +136,8 @@ static inline u32 ipv4_hashfn(__be32 ip)
 
 static inline u32 ipv6_hashfn(const struct in6_addr *ip6)
 {
-	return jhash(ip6, sizeof(*ip6), gtp_h_initval);
+	return jhash_2words((__force u32)ip6->s6_addr32[0],
+			    (__force u32)ip6->s6_addr32[1], gtp_h_initval);
 }
 
 /* Resolve a PDP context structure based on the 64bit TID. */
@@ -188,6 +189,24 @@ static struct pdp_ctx *ipv4_pdp_find(struct gtp_dev *gtp, __be32 ms_addr)
 	return NULL;
 }
 
+/* 3GPP TS 29.060: PDN Connection: the association between a MS represented by
+ * [...] one IPv6 *prefix* and a PDN represented by an APN.
+ *
+ * Then, 3GPP TS 29.061, Section 11.2.1.3 says: The size of the prefix shall be
+ * according to the maximum prefix length for a global IPv6 address as
+ * specified in the IPv6 Addressing Architecture, see RFC 4291.
+ *
+ * Finally, RFC 4291 section 2.5.4 states: All Global Unicast addresses other
+ * than those that start with binary 000 have a 64-bit interface ID field
+ * (i.e., n + m = 64).
+ */
+static bool ipv6_pdp_addr_equal(const struct in6_addr *a,
+				const struct in6_addr *b)
+{
+	return a->s6_addr32[0] == b->s6_addr32[0] &&
+	       a->s6_addr32[1] == b->s6_addr32[1];
+}
+
 static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,
 				     const struct in6_addr *ms_addr)
 {
@@ -198,7 +217,7 @@ static struct pdp_ctx *ipv6_pdp_find(struct gtp_dev *gtp,
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_addr) {
 		if (pdp->af == AF_INET6 &&
-		    memcmp(&pdp->ms.addr6, ms_addr, sizeof(struct in6_addr)) == 0)
+		    ipv6_pdp_addr_equal(&pdp->ms.addr6, ms_addr))
 			return pdp;
 	}
 
@@ -233,14 +252,12 @@ static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,
 	ip6h = (struct ipv6hdr *)(skb->data + hdrlen);
 
 	if (role == GTP_ROLE_SGSN) {
-		ret = memcmp(&ip6h->daddr, &pctx->ms.addr6,
-			     sizeof(struct in6_addr));
+		ret = ipv6_pdp_addr_equal(&ip6h->daddr, &pctx->ms.addr6);
 	} else {
-		ret = memcmp(&ip6h->saddr, &pctx->ms.addr6,
-			     sizeof(struct in6_addr));
+		ret = ipv6_pdp_addr_equal(&ip6h->saddr, &pctx->ms.addr6);
 	}
 
-	return ret == 0;
+	return ret;
 }
 
 /* Check if the inner IP address in this packet is assigned to any
@@ -1651,11 +1668,17 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 	gtp_pdp_fill(pctx, info);
 }
 
-static void ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
+static bool ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
 	pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]);
 	pctx->ms.addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
+	if (pctx->ms.addr6.s6_addr32[2] ||
+	    pctx->ms.addr6.s6_addr32[3])
+		return false;
+
 	gtp_pdp_fill(pctx, info);
+
+	return true;
 }
 
 static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
@@ -1741,7 +1764,8 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 			ipv4_pdp_fill(pctx, info);
 			break;
 		case AF_INET6:
-			ipv6_pdp_fill(pctx, info);
+			if (!ipv6_pdp_fill(pctx, info))
+				return ERR_PTR(-EADDRNOTAVAIL);
 			break;
 		}
 
@@ -1778,7 +1802,8 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 		    !info->attrs[GTPA_PEER_ADDR6])
 			return ERR_PTR(-EINVAL);
 
-		ipv6_pdp_fill(pctx, info);
+		if (!ipv6_pdp_fill(pctx, info))
+			return ERR_PTR(-EADDRNOTAVAIL);
 		break;
 	}
 	atomic_set(&pctx->tx_seq, 0);
@@ -1912,6 +1937,10 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
 	} else if (nla[GTPA_MS_ADDR6]) {
 		struct in6_addr addr = nla_get_in6_addr(nla[GTPA_MS_ADDR6]);
 
+		if (addr.s6_addr32[2] ||
+		    addr.s6_addr32[3])
+			return ERR_PTR(-EADDRNOTAVAIL);
+
 		return ipv6_pdp_find(gtp, &addr);
 	} else if (nla[GTPA_VERSION]) {
 		u32 gtp_version = nla_get_u32(nla[GTPA_VERSION]);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 06/12] gtp: pass up link local traffic to userspace socket
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (4 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 05/12] gtp: use IPv6 address /64 prefix for UE/MS Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 07/12] gtp: move debugging to skbuff build helper function Pablo Neira Ayuso
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

According to TS 29.061, it is possible to see IPv6 link-local traffic in
the GTP tunnel, see 11.2.1.3.2 IPv6 Stateless Address Autoconfiguration
(IPv6 SLAAC).

Pass up these packets to the userspace daemon to handle them as control
GTP traffic.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index ca3b1df19e6d..52f4aeecb8f8 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -251,6 +251,10 @@ static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,
 
 	ip6h = (struct ipv6hdr *)(skb->data + hdrlen);
 
+	if ((ipv6_addr_type(&ip6h->saddr) & IPV6_ADDR_LINKLOCAL) ||
+	    (ipv6_addr_type(&ip6h->daddr) & IPV6_ADDR_LINKLOCAL))
+		return false;
+
 	if (role == GTP_ROLE_SGSN) {
 		ret = ipv6_pdp_addr_equal(&ip6h->daddr, &pctx->ms.addr6);
 	} else {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 07/12] gtp: move debugging to skbuff build helper function
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (5 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 06/12] gtp: pass up link local traffic to userspace socket Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 08/12] gtp: remove IPv4 and IPv6 header from context object Pablo Neira Ayuso
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Move debugging to the routine to build GTP packets in preparation
for supporting IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 52f4aeecb8f8..3d3818646387 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1060,6 +1060,9 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
 	gtp_push_header(skb, pktinfo);
 
+	netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
+		   &iph->saddr, &iph->daddr);
+
 	return 0;
 err_rt:
 	ip_rt_put(rt);
@@ -1135,6 +1138,9 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, ip6h, pctx, rt, &fl6, dev);
 	gtp_push_header(skb, pktinfo);
 
+	netdev_dbg(dev, "gtp -> IP src: %pI6 dst: %pI6\n",
+		   &ip6h->saddr, &ip6h->daddr);
+
 	return 0;
 err_rt:
 	dst_release(dst);
@@ -1174,8 +1180,6 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 
 	switch (proto) {
 	case ETH_P_IP:
-		netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n",
-			   &pktinfo.iph->saddr, &pktinfo.iph->daddr);
 		udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
 				    pktinfo.fl4.saddr, pktinfo.fl4.daddr,
 				    pktinfo.iph->tos,
@@ -1188,8 +1192,6 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 		break;
 	case ETH_P_IPV6:
 #if IS_ENABLED(CONFIG_IPV6)
-		netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI6 dst: %pI6\n",
-			   &pktinfo.ip6h->saddr, &pktinfo.ip6h->daddr);
 		udp_tunnel6_xmit_skb(&pktinfo.rt6->dst, pktinfo.sk, skb, dev,
 				     &pktinfo.fl6.saddr, &pktinfo.fl6.daddr,
 				     ipv6_get_dsfield(pktinfo.ip6h),
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 08/12] gtp: remove IPv4 and IPv6 header from context object
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (6 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 07/12] gtp: move debugging to skbuff build helper function Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 09/12] gtp: add helper function to build GTP packets from an IPv4 packet Pablo Neira Ayuso
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Based on the idea that ip_tunnel_get_dsfield() provides the tos field
regardless the IP version, use either iph->tos or ipv6_get_dsfield().

This comes in preparation to support for IPv4-in-IPv6-GTP and
IPv6-in-IPv4-GTP.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 3d3818646387..5e4f3102fffe 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -927,10 +927,6 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
 
 struct gtp_pktinfo {
 	struct sock		*sk;
-	union {
-		struct iphdr	*iph;
-		struct ipv6hdr	*ip6h;
-	};
 	union {
 		struct flowi4	fl4;
 		struct flowi6	fl6;
@@ -941,6 +937,7 @@ struct gtp_pktinfo {
 	};
 	struct pdp_ctx		*pctx;
 	struct net_device	*dev;
+	__u8			tos;
 	__be16			gtph_port;
 };
 
@@ -959,13 +956,13 @@ static void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo)
 }
 
 static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
-					struct sock *sk, struct iphdr *iph,
+					struct sock *sk, __u8 tos,
 					struct pdp_ctx *pctx, struct rtable *rt,
 					struct flowi4 *fl4,
 					struct net_device *dev)
 {
 	pktinfo->sk	= sk;
-	pktinfo->iph	= iph;
+	pktinfo->tos	= tos;
 	pktinfo->pctx	= pctx;
 	pktinfo->rt	= rt;
 	pktinfo->fl4	= *fl4;
@@ -973,13 +970,13 @@ static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
 }
 
 static inline void gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo,
-					struct sock *sk, struct ipv6hdr *ip6h,
+					struct sock *sk, __u8 tos,
 					struct pdp_ctx *pctx, struct rt6_info *rt6,
 					struct flowi6 *fl6,
 					struct net_device *dev)
 {
 	pktinfo->sk	= sk;
-	pktinfo->ip6h	= ip6h;
+	pktinfo->tos	= tos;
 	pktinfo->pctx	= pctx;
 	pktinfo->rt6	= rt6;
 	pktinfo->fl6	= *fl6;
@@ -1057,7 +1054,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 		goto err_rt;
 	}
 
-	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
+	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph->tos, pctx, rt, &fl4, dev);
 	gtp_push_header(skb, pktinfo);
 
 	netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
@@ -1080,6 +1077,7 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 	struct ipv6hdr *ip6h;
 	struct rt6_info *rt;
 	struct flowi6 fl6;
+	__u8 tos;
 	int mtu;
 
 	/* Read the IP destination address and resolve the PDP context.
@@ -1135,7 +1133,8 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 		goto err_rt;
 	}
 
-	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, ip6h, pctx, rt, &fl6, dev);
+	tos = ipv6_get_dsfield(ip6h);
+	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, tos, pctx, rt, &fl6, dev);
 	gtp_push_header(skb, pktinfo);
 
 	netdev_dbg(dev, "gtp -> IP src: %pI6 dst: %pI6\n",
@@ -1182,7 +1181,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	case ETH_P_IP:
 		udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
 				    pktinfo.fl4.saddr, pktinfo.fl4.daddr,
-				    pktinfo.iph->tos,
+				    pktinfo.tos,
 				    ip4_dst_hoplimit(&pktinfo.rt->dst),
 				    0,
 				    pktinfo.gtph_port, pktinfo.gtph_port,
@@ -1194,7 +1193,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 #if IS_ENABLED(CONFIG_IPV6)
 		udp_tunnel6_xmit_skb(&pktinfo.rt6->dst, pktinfo.sk, skb, dev,
 				     &pktinfo.fl6.saddr, &pktinfo.fl6.daddr,
-				     ipv6_get_dsfield(pktinfo.ip6h),
+				     pktinfo.tos,
 				     ip6_dst_hoplimit(&pktinfo.rt->dst),
 				     0,
 				     pktinfo.gtph_port, pktinfo.gtph_port,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 09/12] gtp: add helper function to build GTP packets from an IPv4 packet
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (7 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 08/12] gtp: remove IPv4 and IPv6 header from context object Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 10/12] gtp: add helper function to build GTP packets from an IPv6 packet Pablo Neira Ayuso
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Add routine to attach an IPv4 route for the encapsulated packet, deal
with Path MTU and push GTP header.

This helper function will be used to deal with IPv6-in-IPv4-GTP.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 69 ++++++++++++++++++++++++++++-------------------
 1 file changed, 42 insertions(+), 27 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 5e4f3102fffe..3293528bd365 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -983,33 +983,16 @@ static inline void gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo,
 	pktinfo->dev	= dev;
 }
 
-static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
-			     struct gtp_pktinfo *pktinfo)
+static int gtp_build_skb_outer_ip4(struct sk_buff *skb, struct net_device *dev,
+				   struct gtp_pktinfo *pktinfo,
+				   struct pdp_ctx *pctx, __u8 tos,
+				   __be16 frag_off)
 {
-	struct gtp_dev *gtp = netdev_priv(dev);
-	struct pdp_ctx *pctx;
 	struct rtable *rt;
 	struct flowi4 fl4;
-	struct iphdr *iph;
 	__be16 df;
 	int mtu;
 
-	/* Read the IP destination address and resolve the PDP context.
-	 * Prepend PDP header with TEI/TID from PDP ctx.
-	 */
-	iph = ip_hdr(skb);
-	if (gtp->role == GTP_ROLE_SGSN)
-		pctx = ipv4_pdp_find(gtp, iph->saddr);
-	else
-		pctx = ipv4_pdp_find(gtp, iph->daddr);
-
-	if (!pctx) {
-		netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
-			   &iph->daddr);
-		return -ENOENT;
-	}
-	netdev_dbg(dev, "found PDP context %p\n", pctx);
-
 	rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer.addr.s_addr,
 				  inet_sk(pctx->sk)->inet_saddr);
 	if (IS_ERR(rt)) {
@@ -1027,7 +1010,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	}
 
 	/* This is similar to tnl_update_pmtu(). */
-	df = iph->frag_off;
+	df = frag_off;
 	if (df) {
 		mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
 			sizeof(struct iphdr) - sizeof(struct udphdr);
@@ -1045,7 +1028,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 
 	skb_dst_update_pmtu_no_confirm(skb, mtu);
 
-	if (iph->frag_off & htons(IP_DF) &&
+	if (frag_off & htons(IP_DF) &&
 	    ((!skb_is_gso(skb) && skb->len > mtu) ||
 	     (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu)))) {
 		netdev_dbg(dev, "packet too big, fragmentation needed\n");
@@ -1054,12 +1037,9 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 		goto err_rt;
 	}
 
-	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph->tos, pctx, rt, &fl4, dev);
+	gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, tos, pctx, rt, &fl4, dev);
 	gtp_push_header(skb, pktinfo);
 
-	netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
-		   &iph->saddr, &iph->daddr);
-
 	return 0;
 err_rt:
 	ip_rt_put(rt);
@@ -1067,6 +1047,41 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	return -EBADMSG;
 }
 
+static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
+			     struct gtp_pktinfo *pktinfo)
+{
+	struct gtp_dev *gtp = netdev_priv(dev);
+	struct pdp_ctx *pctx;
+	struct iphdr *iph;
+	int ret;
+
+	/* Read the IP destination address and resolve the PDP context.
+	 * Prepend PDP header with TEI/TID from PDP ctx.
+	 */
+	iph = ip_hdr(skb);
+	if (gtp->role == GTP_ROLE_SGSN)
+		pctx = ipv4_pdp_find(gtp, iph->saddr);
+	else
+		pctx = ipv4_pdp_find(gtp, iph->daddr);
+
+	if (!pctx) {
+		netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
+			   &iph->daddr);
+		return -ENOENT;
+	}
+	netdev_dbg(dev, "found PDP context %p\n", pctx);
+
+	ret = gtp_build_skb_outer_ip4(skb, dev, pktinfo, pctx,
+				      iph->tos, iph->frag_off);
+	if (ret < 0)
+		return ret;
+
+	netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
+		   &iph->saddr, &iph->daddr);
+
+	return 0;
+}
+
 static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 			     struct gtp_pktinfo *pktinfo)
 {
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 10/12] gtp: add helper function to build GTP packets from an IPv6 packet
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (8 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 09/12] gtp: add helper function to build GTP packets from an IPv4 packet Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Add routine to attach an IPv6 route for the encapsulated packet, deal
with Path MTU and push GTP header.

This helper function will be used to deal with IPv4-in-IPv6-GTP.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 109 ++++++++++++++++++++++++++--------------------
 1 file changed, 62 insertions(+), 47 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 3293528bd365..d7ee3633f98c 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -1047,6 +1047,63 @@ static int gtp_build_skb_outer_ip4(struct sk_buff *skb, struct net_device *dev,
 	return -EBADMSG;
 }
 
+static int gtp_build_skb_outer_ip6(struct net *net, struct sk_buff *skb,
+				   struct net_device *dev,
+				   struct gtp_pktinfo *pktinfo,
+				   struct pdp_ctx *pctx, __u8 tos)
+{
+	struct dst_entry *dst;
+	struct rt6_info *rt;
+	struct flowi6 fl6;
+	int mtu;
+
+	rt = ip6_route_output_gtp(net, &fl6, pctx->sk, &pctx->peer.addr6,
+				  &inet6_sk(pctx->sk)->saddr);
+	if (IS_ERR(rt)) {
+		netdev_dbg(dev, "no route to SSGN %pI6\n",
+			   &pctx->peer.addr6);
+		dev->stats.tx_carrier_errors++;
+		goto err;
+	}
+	dst = &rt->dst;
+
+	if (rt->dst.dev == dev) {
+		netdev_dbg(dev, "circular route to SSGN %pI6\n",
+			   &pctx->peer.addr6);
+		dev->stats.collisions++;
+		goto err_rt;
+	}
+
+	mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
+		sizeof(struct ipv6hdr) - sizeof(struct udphdr);
+	switch (pctx->gtp_version) {
+	case GTP_V0:
+		mtu -= sizeof(struct gtp0_header);
+		break;
+	case GTP_V1:
+		mtu -= sizeof(struct gtp1_header);
+		break;
+	}
+
+	skb_dst_update_pmtu_no_confirm(skb, mtu);
+
+	if ((!skb_is_gso(skb) && skb->len > mtu) ||
+	    (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) {
+		netdev_dbg(dev, "packet too big, fragmentation needed\n");
+		icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+		goto err_rt;
+	}
+
+	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, tos, pctx, rt, &fl6, dev);
+	gtp_push_header(skb, pktinfo);
+
+	return 0;
+err_rt:
+	dst_release(dst);
+err:
+	return -EBADMSG;
+}
+
 static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 			     struct gtp_pktinfo *pktinfo)
 {
@@ -1087,13 +1144,10 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 {
 	struct gtp_dev *gtp = netdev_priv(dev);
 	struct net *net = gtp->net;
-	struct dst_entry *dst;
 	struct pdp_ctx *pctx;
 	struct ipv6hdr *ip6h;
-	struct rt6_info *rt;
-	struct flowi6 fl6;
 	__u8 tos;
-	int mtu;
+	int ret;
 
 	/* Read the IP destination address and resolve the PDP context.
 	 * Prepend PDP header with TEI/TID from PDP ctx.
@@ -1111,55 +1165,16 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 	}
 	netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-	rt = ip6_route_output_gtp(net, &fl6, pctx->sk, &pctx->peer.addr6,
-				  &inet6_sk(pctx->sk)->saddr);
-	if (IS_ERR(rt)) {
-		netdev_dbg(dev, "no route to SSGN %pI6\n",
-			   &pctx->peer.addr6);
-		dev->stats.tx_carrier_errors++;
-		goto err;
-	}
-	dst = &rt->dst;
-
-	if (rt->dst.dev == dev) {
-		netdev_dbg(dev, "circular route to SSGN %pI6\n",
-			   &pctx->peer.addr6);
-		dev->stats.collisions++;
-		goto err_rt;
-	}
-
-	mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
-		sizeof(struct ipv6hdr) - sizeof(struct udphdr);
-	switch (pctx->gtp_version) {
-	case GTP_V0:
-		mtu -= sizeof(struct gtp0_header);
-		break;
-	case GTP_V1:
-		mtu -= sizeof(struct gtp1_header);
-		break;
-	}
-
-	skb_dst_update_pmtu_no_confirm(skb, mtu);
-
-	if ((!skb_is_gso(skb) && skb->len > mtu) ||
-	    (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))) {
-		netdev_dbg(dev, "packet too big, fragmentation needed\n");
-		icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
-		goto err_rt;
-	}
-
 	tos = ipv6_get_dsfield(ip6h);
-	gtp_set_pktinfo_ipv6(pktinfo, pctx->sk, tos, pctx, rt, &fl6, dev);
-	gtp_push_header(skb, pktinfo);
+
+	ret = gtp_build_skb_outer_ip6(net, skb, dev, pktinfo, pctx, tos);
+	if (ret < 0)
+		return ret;
 
 	netdev_dbg(dev, "gtp -> IP src: %pI6 dst: %pI6\n",
 		   &ip6h->saddr, &ip6h->daddr);
 
 	return 0;
-err_rt:
-	dst_release(dst);
-err:
-	return -EBADMSG;
 }
 
 static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (9 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 10/12] gtp: add helper function to build GTP packets from an IPv6 packet Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-25 10:51 ` [PATCH net-next 12/12] gtp: identify tunnel via GTP device + GTP version + TEID + family Pablo Neira Ayuso
  2024-04-26  2:29 ` [PATCH net-next 00/12] gtp updates for net-next (v2) Jakub Kicinski
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

Add new protocol field to PDP context that determines the transmit path
IP protocol to encapsulate the original packets, either IPv4 or IPv6.

Relax existing netlink attribute checks to allow to specify different
family in MS and peer attributes from the control plane.

Use build helpers to tx path to encapsulate IPv4-in-IPv6-GTP and
IPv6-in-IPv4-GTP according to the user-specified configuration.

From rx path, snoop for the inner protocol header since outer
skb->protocol might differ and use this to validate for valid PDP
context and to restore skb->protocol after decapsulation.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 129 ++++++++++++++++++++++++++++++++++++----------
 1 file changed, 101 insertions(+), 28 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index d7ee3633f98c..586de5f013b5 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -268,9 +268,10 @@ static bool gtp_check_ms_ipv6(struct sk_buff *skb, struct pdp_ctx *pctx,
  * existing mobile subscriber.
  */
 static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
-			     unsigned int hdrlen, unsigned int role)
+			 unsigned int hdrlen, unsigned int role,
+			 __u16 inner_proto)
 {
-	switch (ntohs(skb->protocol)) {
+	switch (inner_proto) {
 	case ETH_P_IP:
 		return gtp_check_ms_ipv4(skb, pctx, hdrlen, role);
 	case ETH_P_IPV6:
@@ -279,16 +280,47 @@ static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
 	return false;
 }
 
+static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,
+			   __u16 *inner_proto)
+{
+	__u8 *ip_version, _ip_version;
+
+	ip_version = skb_header_pointer(skb, hdrlen, sizeof(ip_version),
+					&_ip_version);
+	if (!ip_version)
+		return -1;
+
+	switch (*ip_version & 0xf0) {
+	case 0x40:
+		*inner_proto = ETH_P_IP;
+		break;
+	case 0x60:
+		*inner_proto = ETH_P_IPV6;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
 static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
-			unsigned int hdrlen, unsigned int role)
+		  unsigned int hdrlen, unsigned int role)
 {
-	if (!gtp_check_ms(skb, pctx, hdrlen, role)) {
+	__u16 inner_proto;
+
+	if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
+		netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
+		return -1;
+	}
+
+	if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) {
 		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,
+	if (iptunnel_pull_header(skb, hdrlen, htons(inner_proto),
 			 !net_eq(sock_net(pctx->sk), dev_net(pctx->dev)))) {
 		pctx->dev->stats.rx_length_errors++;
 		goto err;
@@ -1108,6 +1140,7 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 			     struct gtp_pktinfo *pktinfo)
 {
 	struct gtp_dev *gtp = netdev_priv(dev);
+	struct net *net = gtp->net;
 	struct pdp_ctx *pctx;
 	struct iphdr *iph;
 	int ret;
@@ -1128,8 +1161,21 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 	}
 	netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-	ret = gtp_build_skb_outer_ip4(skb, dev, pktinfo, pctx,
-				      iph->tos, iph->frag_off);
+	switch (pctx->sk->sk_family) {
+	case AF_INET:
+		ret = gtp_build_skb_outer_ip4(skb, dev, pktinfo, pctx,
+					      iph->tos, iph->frag_off);
+		break;
+	case AF_INET6:
+		ret = gtp_build_skb_outer_ip6(net, skb, dev, pktinfo, pctx,
+					      iph->tos);
+		break;
+	default:
+		ret = -1;
+		WARN_ON_ONCE(1);
+		break;
+	}
+
 	if (ret < 0)
 		return ret;
 
@@ -1167,7 +1213,19 @@ static int gtp_build_skb_ip6(struct sk_buff *skb, struct net_device *dev,
 
 	tos = ipv6_get_dsfield(ip6h);
 
-	ret = gtp_build_skb_outer_ip6(net, skb, dev, pktinfo, pctx, tos);
+	switch (pctx->sk->sk_family) {
+	case AF_INET:
+		ret = gtp_build_skb_outer_ip4(skb, dev, pktinfo, pctx, tos, 0);
+		break;
+	case AF_INET6:
+		ret = gtp_build_skb_outer_ip6(net, skb, dev, pktinfo, pctx, tos);
+		break;
+	default:
+		ret = -1;
+		WARN_ON_ONCE(1);
+		break;
+	}
+
 	if (ret < 0)
 		return ret;
 
@@ -1207,8 +1265,8 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (err < 0)
 		goto tx_err;
 
-	switch (proto) {
-	case ETH_P_IP:
+	switch (pktinfo.pctx->sk->sk_family) {
+	case AF_INET:
 		udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
 				    pktinfo.fl4.saddr, pktinfo.fl4.daddr,
 				    pktinfo.tos,
@@ -1219,7 +1277,7 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 					    dev_net(dev)),
 				    false);
 		break;
-	case ETH_P_IPV6:
+	case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
 		udp_tunnel6_xmit_skb(&pktinfo.rt6->dst, pktinfo.sk, skb, dev,
 				     &pktinfo.fl6.saddr, &pktinfo.fl6.daddr,
@@ -1694,10 +1752,19 @@ static void gtp_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 	}
 }
 
+static void ip_pdp_peer_fill(struct pdp_ctx *pctx, struct genl_info *info)
+{
+	if (info->attrs[GTPA_PEER_ADDRESS]) {
+		pctx->peer.addr.s_addr =
+			nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
+	} else if (info->attrs[GTPA_PEER_ADDR6]) {
+		pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]);
+	}
+}
+
 static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
-	pctx->peer.addr.s_addr =
-		nla_get_be32(info->attrs[GTPA_PEER_ADDRESS]);
+	ip_pdp_peer_fill(pctx, info);
 	pctx->ms.addr.s_addr =
 		nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
 	gtp_pdp_fill(pctx, info);
@@ -1705,7 +1772,7 @@ static void ipv4_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 
 static bool ipv6_pdp_fill(struct pdp_ctx *pctx, struct genl_info *info)
 {
-	pctx->peer.addr6 = nla_get_in6_addr(info->attrs[GTPA_PEER_ADDR6]);
+	ip_pdp_peer_fill(pctx, info);
 	pctx->ms.addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
 	if (pctx->ms.addr6.s6_addr32[2] ||
 	    pctx->ms.addr6.s6_addr32[3])
@@ -1739,6 +1806,9 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 	if (family == AF_INET6)
 		return ERR_PTR(-EAFNOSUPPORT);
 #endif
+	if (!info->attrs[GTPA_PEER_ADDRESS] &&
+	    !info->attrs[GTPA_PEER_ADDR6])
+		return ERR_PTR(-EINVAL);
 
 	if ((info->attrs[GTPA_PEER_ADDRESS] &&
 	     sk->sk_family == AF_INET6) ||
@@ -1749,9 +1819,7 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 	switch (family) {
 	case AF_INET:
 		if (!info->attrs[GTPA_MS_ADDRESS] ||
-		    !info->attrs[GTPA_PEER_ADDRESS] ||
-		    info->attrs[GTPA_MS_ADDR6] ||
-		    info->attrs[GTPA_PEER_ADDR6])
+		    info->attrs[GTPA_MS_ADDR6])
 			return ERR_PTR(-EINVAL);
 
 		ms_addr = nla_get_be32(info->attrs[GTPA_MS_ADDRESS]);
@@ -1760,9 +1828,7 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 		break;
 	case AF_INET6:
 		if (!info->attrs[GTPA_MS_ADDR6] ||
-		    !info->attrs[GTPA_PEER_ADDR6] ||
-		    info->attrs[GTPA_MS_ADDRESS] ||
-		    info->attrs[GTPA_PEER_ADDRESS])
+		    info->attrs[GTPA_MS_ADDRESS])
 			return ERR_PTR(-EINVAL);
 
 		ms_addr6 = nla_get_in6_addr(info->attrs[GTPA_MS_ADDR6]);
@@ -1826,15 +1892,13 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 
 	switch (pctx->af) {
 	case AF_INET:
-		if (!info->attrs[GTPA_MS_ADDRESS] ||
-		    !info->attrs[GTPA_PEER_ADDRESS])
+		if (!info->attrs[GTPA_MS_ADDRESS])
 			return ERR_PTR(-EINVAL);
 
 		ipv4_pdp_fill(pctx, info);
 		break;
 	case AF_INET6:
-		if (!info->attrs[GTPA_MS_ADDR6] ||
-		    !info->attrs[GTPA_PEER_ADDR6])
+		if (!info->attrs[GTPA_MS_ADDR6])
 			return ERR_PTR(-EINVAL);
 
 		if (!ipv6_pdp_fill(pctx, info))
@@ -2052,13 +2116,22 @@ static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq,
 
 	switch (pctx->af) {
 	case AF_INET:
-		if (nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr) ||
-		    nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
+		if (nla_put_be32(skb, GTPA_MS_ADDRESS, pctx->ms.addr.s_addr))
+			goto nla_put_failure;
+		break;
+	case AF_INET6:
+		if (nla_put_in6_addr(skb, GTPA_MS_ADDR6, &pctx->ms.addr6))
+			goto nla_put_failure;
+		break;
+	}
+
+	switch (pctx->sk->sk_family) {
+	case AF_INET:
+		if (nla_put_be32(skb, GTPA_PEER_ADDRESS, pctx->peer.addr.s_addr))
 			goto nla_put_failure;
 		break;
 	case AF_INET6:
-		if (nla_put_in6_addr(skb, GTPA_PEER_ADDR6, &pctx->peer.addr6) ||
-		    nla_put_in6_addr(skb, GTPA_MS_ADDR6, &pctx->ms.addr6))
+		if (nla_put_in6_addr(skb, GTPA_PEER_ADDR6, &pctx->peer.addr6))
 			goto nla_put_failure;
 		break;
 	}
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH net-next 12/12] gtp: identify tunnel via GTP device + GTP version + TEID + family
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (10 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP Pablo Neira Ayuso
@ 2024-04-25 10:51 ` Pablo Neira Ayuso
  2024-04-26  2:29 ` [PATCH net-next 00/12] gtp updates for net-next (v2) Jakub Kicinski
  12 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-04-25 10:51 UTC (permalink / raw)
  To: netdev; +Cc: davem, laforge, pespin, osmith, kuba, pabeni, edumazet, fw

This allows to define a GTP tunnel for dual stack MS/UE with both IPv4
and IPv6 addresses while using the same TEID via two PDP context
objects.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 drivers/net/gtp.c | 85 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 63 insertions(+), 22 deletions(-)

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 586de5f013b5..9880f7d5c56c 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -141,7 +141,7 @@ static inline u32 ipv6_hashfn(const struct in6_addr *ip6)
 }
 
 /* Resolve a PDP context structure based on the 64bit TID. */
-static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
+static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid, u16 family)
 {
 	struct hlist_head *head;
 	struct pdp_ctx *pdp;
@@ -149,7 +149,8 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
 	head = &gtp->tid_hash[gtp0_hashfn(tid) % gtp->hash_size];
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
-		if (pdp->gtp_version == GTP_V0 &&
+		if (pdp->af == family &&
+		    pdp->gtp_version == GTP_V0 &&
 		    pdp->u.v0.tid == tid)
 			return pdp;
 	}
@@ -157,7 +158,7 @@ static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
 }
 
 /* Resolve a PDP context structure based on the 32bit TEI. */
-static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid)
+static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid, u16 family)
 {
 	struct hlist_head *head;
 	struct pdp_ctx *pdp;
@@ -165,7 +166,8 @@ static struct pdp_ctx *gtp1_pdp_find(struct gtp_dev *gtp, u32 tid)
 	head = &gtp->tid_hash[gtp1u_hashfn(tid) % gtp->hash_size];
 
 	hlist_for_each_entry_rcu(pdp, head, hlist_tid) {
-		if (pdp->gtp_version == GTP_V1 &&
+		if (pdp->af == family &&
+		    pdp->gtp_version == GTP_V1 &&
 		    pdp->u.v1.i_tei == tid)
 			return pdp;
 	}
@@ -305,15 +307,8 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,
 }
 
 static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
-		  unsigned int hdrlen, unsigned int role)
+		  unsigned int hdrlen, unsigned int role, __u16 inner_proto)
 {
-	__u16 inner_proto;
-
-	if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
-		netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
-		return -1;
-	}
-
 	if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) {
 		netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
 		return 1;
@@ -562,6 +557,21 @@ static int gtp0_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
 				       msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC);
 }
 
+static int gtp_proto_to_family(__u16 proto)
+{
+	switch (proto) {
+	case ETH_P_IP:
+		return AF_INET;
+	case ETH_P_IPV6:
+		return AF_INET6;
+	default:
+		WARN_ON_ONCE(1);
+		break;
+	}
+
+	return AF_UNSPEC;
+}
+
 /* 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)
 {
@@ -569,6 +579,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 			      sizeof(struct gtp0_header);
 	struct gtp0_header *gtp0;
 	struct pdp_ctx *pctx;
+	__u16 inner_proto;
 
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
@@ -591,13 +602,19 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	if (gtp0->type != GTP_TPDU)
 		return 1;
 
-	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
+	if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
+		netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
+		return -1;
+	}
+
+	pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid),
+			     gtp_proto_to_family(inner_proto));
 	if (!pctx) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
 		return 1;
 	}
 
-	return gtp_rx(pctx, skb, hdrlen, gtp->role);
+	return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
 }
 
 /* msg_type has to be GTP_ECHO_REQ or GTP_ECHO_RSP */
@@ -768,6 +785,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 			      sizeof(struct gtp1_header);
 	struct gtp1_header *gtp1;
 	struct pdp_ctx *pctx;
+	__u16 inner_proto;
 
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
@@ -803,9 +821,15 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	if (!pskb_may_pull(skb, hdrlen))
 		return -1;
 
+	if (gtp_inner_proto(skb, hdrlen, &inner_proto) < 0) {
+		netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
+		return -1;
+	}
+
 	gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
 
-	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
+	pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid),
+			     gtp_proto_to_family(inner_proto));
 	if (!pctx) {
 		netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
 		return 1;
@@ -815,7 +839,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
 	    gtp_parse_exthdrs(skb, &hdrlen) < 0)
 		return -1;
 
-	return gtp_rx(pctx, skb, hdrlen, gtp->role);
+	return gtp_rx(pctx, skb, hdrlen, gtp->role, inner_proto);
 }
 
 static void __gtp_encap_destroy(struct sock *sk)
@@ -1842,10 +1866,12 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
 		found = true;
 	if (version == GTP_V0)
 		pctx_tid = gtp0_pdp_find(gtp,
-					 nla_get_u64(info->attrs[GTPA_TID]));
+					 nla_get_u64(info->attrs[GTPA_TID]),
+					 family);
 	else if (version == GTP_V1)
 		pctx_tid = gtp1_pdp_find(gtp,
-					 nla_get_u32(info->attrs[GTPA_I_TEI]));
+					 nla_get_u32(info->attrs[GTPA_I_TEI]),
+					 family);
 	if (pctx_tid)
 		found = true;
 
@@ -2024,6 +2050,12 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
 					    struct nlattr *nla[])
 {
 	struct gtp_dev *gtp;
+	int family;
+
+	if (nla[GTPA_FAMILY])
+		family = nla_get_u8(nla[GTPA_FAMILY]);
+	else
+		family = AF_INET;
 
 	gtp = gtp_find_dev(net, nla);
 	if (!gtp)
@@ -2032,10 +2064,16 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
 	if (nla[GTPA_MS_ADDRESS]) {
 		__be32 ip = nla_get_be32(nla[GTPA_MS_ADDRESS]);
 
+		if (family != AF_INET)
+			return ERR_PTR(-EINVAL);
+
 		return ipv4_pdp_find(gtp, ip);
 	} else if (nla[GTPA_MS_ADDR6]) {
 		struct in6_addr addr = nla_get_in6_addr(nla[GTPA_MS_ADDR6]);
 
+		if (family != AF_INET6)
+			return ERR_PTR(-EINVAL);
+
 		if (addr.s6_addr32[2] ||
 		    addr.s6_addr32[3])
 			return ERR_PTR(-EADDRNOTAVAIL);
@@ -2044,10 +2082,13 @@ static struct pdp_ctx *gtp_find_pdp_by_link(struct net *net,
 	} 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]));
+		if (gtp_version == GTP_V0 && nla[GTPA_TID]) {
+			return gtp0_pdp_find(gtp, nla_get_u64(nla[GTPA_TID]),
+					     family);
+		} else if (gtp_version == GTP_V1 && nla[GTPA_I_TEI]) {
+			return gtp1_pdp_find(gtp, nla_get_u32(nla[GTPA_I_TEI]),
+					     family);
+		}
 	}
 
 	return ERR_PTR(-EINVAL);
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH net-next 00/12] gtp updates for net-next (v2)
  2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
                   ` (11 preceding siblings ...)
  2024-04-25 10:51 ` [PATCH net-next 12/12] gtp: identify tunnel via GTP device + GTP version + TEID + family Pablo Neira Ayuso
@ 2024-04-26  2:29 ` Jakub Kicinski
  12 siblings, 0 replies; 18+ messages in thread
From: Jakub Kicinski @ 2024-04-26  2:29 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: netdev, davem, laforge, pespin, osmith, pabeni, edumazet, fw

On Thu, 25 Apr 2024 12:51:26 +0200 Pablo Neira Ayuso wrote:
> This v2 includes a sparse fix for patch #5 reported by Jakub.

Sorry one more semi-automated compiler warning, clang has this to
say about patch 12:

../drivers/net/gtp.c:606:14: warning: variable 'pctx' is uninitialized when used here [-Wuninitialized]
  606 |                 netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
      |                            ^~~~
../include/net/net_debug.h:57:21: note: expanded from macro 'netdev_dbg'
   57 |         dynamic_netdev_dbg(__dev, format, ##args);              \
      |                            ^~~~~
../include/linux/dynamic_debug.h:278:7: note: expanded from macro 'dynamic_netdev_dbg'
  278 |                            dev, fmt, ##__VA_ARGS__)
      |                            ^~~
../include/linux/dynamic_debug.h:250:59: note: expanded from macro '_dynamic_func_call'
  250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
      |                                                                  ^~~~~~~~~~~
../include/linux/dynamic_debug.h:248:65: note: expanded from macro '_dynamic_func_call_cls'
  248 |         __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
      |                                                                        ^~~~~~~~~~~
../include/linux/dynamic_debug.h:224:15: note: expanded from macro '__dynamic_func_call_cls'
  224 |                 func(&id, ##__VA_ARGS__);                       \
      |                             ^~~~~~~~~~~
../drivers/net/gtp.c:581:22: note: initialize the variable 'pctx' to silence this warning
  581 |         struct pdp_ctx *pctx;
      |                             ^
      |                              = NULL
../drivers/net/gtp.c:825:14: warning: variable 'pctx' is uninitialized when used here [-Wuninitialized]
  825 |                 netdev_dbg(pctx->dev, "GTP packet does not encapsulate an IP packet\n");
      |                            ^~~~
../include/net/net_debug.h:57:21: note: expanded from macro 'netdev_dbg'
   57 |         dynamic_netdev_dbg(__dev, format, ##args);              \
      |                            ^~~~~
../include/linux/dynamic_debug.h:278:7: note: expanded from macro 'dynamic_netdev_dbg'
  278 |                            dev, fmt, ##__VA_ARGS__)
      |                            ^~~
../include/linux/dynamic_debug.h:250:59: note: expanded from macro '_dynamic_func_call'
  250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
      |                                                                  ^~~~~~~~~~~
../include/linux/dynamic_debug.h:248:65: note: expanded from macro '_dynamic_func_call_cls'
  248 |         __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
      |                                                                        ^~~~~~~~~~~
../include/linux/dynamic_debug.h:224:15: note: expanded from macro '__dynamic_func_call_cls'
  224 |                 func(&id, ##__VA_ARGS__);                       \
      |                             ^~~~~~~~~~~
../drivers/net/gtp.c:787:22: note: initialize the variable 'pctx' to silence this warning
  787 |         struct pdp_ctx *pctx;
      |                             ^
      |                              = NULL

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH net-next 02/12] gtp: properly parse extension headers
  2024-04-25 10:51 ` [PATCH net-next 02/12] gtp: properly parse extension headers Pablo Neira Ayuso
@ 2024-04-26 20:28   ` Simon Horman
  2024-05-02 10:36     ` Pablo Neira Ayuso
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Horman @ 2024-04-26 20:28 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: netdev, davem, laforge, pespin, osmith, kuba, pabeni, edumazet,
	fw

On Thu, Apr 25, 2024 at 12:51:28PM +0200, Pablo Neira Ayuso wrote:
> Currently GTP packets are dropped if the next extension field is set to
> non-zero value, but this are valid GTP packets.
> 
> TS 29.281 provides a longer header format, which is defined as struct
> gtp1_header_long. Such long header format is used if any of the S, PN, E
> flags is set.
> 
> This long header is 4 bytes longer than struct gtp1_header, plus
> variable length (optional) extension headers. The next extension header
> field is zero is no extension header is provided.
> 
> The extension header is composed of a length field which includes total
> number of 4 byte words including the extension header itself (1 byte),
> payload (variable length) and next type (1 byte). The extension header
> size and its payload is aligned to 4 bytes.
> 
> A GTP packet might come with a chain extensions headers, which makes it
> slightly cumbersome to parse because the extension next header field
> comes at the end of the extension header, and there is a need to check
> if this field becomes zero to stop the extension header parser.
> 
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
>  drivers/net/gtp.c | 41 +++++++++++++++++++++++++++++++++++++++++
>  include/net/gtp.h |  5 +++++
>  2 files changed, 46 insertions(+)
> 
> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
> index 4680cdf4fa70..9451c74c1a7d 100644
> --- a/drivers/net/gtp.c
> +++ b/drivers/net/gtp.c
> @@ -567,6 +567,43 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
>  				       msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC);
>  }
>  
> +static int gtp_parse_exthdrs(struct sk_buff *skb, unsigned int *hdrlen)
> +{
> +	struct gtp_ext_hdr *gtp_exthdr, _gtp_exthdr;
> +	unsigned int offset = *hdrlen;
> +	__u8 *next_type, _next_type;
> +
> +	/* From 29.060: "The Extension Header Length field specifies the length
> +	 * of the particular Extension header in 4 octets units."
> +	 *
> +	 * This length field includes length field size itself (1 byte),
> +	 * payload (variable length) and next type (1 byte). The extension
> +	 * header is aligned to to 4 bytes.
> +	 */
> +
> +	do {
> +		gtp_exthdr = skb_header_pointer(skb, offset, sizeof(gtp_exthdr),

Hi Pablo,

Should this be sizeof(*gtp_exthdr)?

And likewise, in the ip_version calculation in gtp_inner_proto()
in [PATCH 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP 

Flagged by Coccinelle.

> +						&_gtp_exthdr);
> +		if (!gtp_exthdr || !gtp_exthdr->len)
> +			return -1;
> +
> +		offset += gtp_exthdr->len * 4;
> +
> +		/* From 29.060: "If no such Header follows, then the value of
> +		 * the Next Extension Header Type shall be 0."
> +		 */
> +		next_type = skb_header_pointer(skb, offset - 1,
> +					       sizeof(_next_type), &_next_type);
> +		if (!next_type)
> +			return -1;
> +
> +	} while (*next_type != 0);
> +
> +	*hdrlen = offset;
> +
> +	return 0;
> +}

...

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH net-next 04/12] gtp: add IPv6 support
  2024-04-25 10:51 ` [PATCH net-next 04/12] gtp: add " Pablo Neira Ayuso
@ 2024-04-26 20:41   ` Simon Horman
  2024-05-02 10:38     ` Pablo Neira Ayuso
  0 siblings, 1 reply; 18+ messages in thread
From: Simon Horman @ 2024-04-26 20:41 UTC (permalink / raw)
  To: Pablo Neira Ayuso
  Cc: netdev, davem, laforge, pespin, osmith, kuba, pabeni, edumazet,
	fw

On Thu, Apr 25, 2024 at 12:51:30PM +0200, Pablo Neira Ayuso wrote:
> Add new iflink attributes to configure in-kernel UDP listener socket
> address: IFLA_GTP_LOCAL and IFLA_GTP_LOCAL6. If none of these attributes
> are specified, default is still to IPv4 INADDR_ANY for backward
> compatibility.
> 
> Add new attributes to set up family and IPv6 address of GTP tunnels:
> GTPA_FAMILY, GTPA_PEER_ADDR6 and GTPA_MS_ADDR6. If no GTPA_FAMILY is
> specified, AF_INET is assumed for backward compatibility.
> 
> setsockopt IPV6_ADDRFORM allows to downgrade socket from IPv6 to IPv4
> after socket is bound. Assumption is that socket listener that is
> attached to the gtp device needs to be either IPv4 or IPv6. Therefore,
> GTP socket listener does not allow for IPv4-mapped-IPv6 listener.
> 
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
>  drivers/net/gtp.c            | 390 ++++++++++++++++++++++++++++++++---
>  include/uapi/linux/gtp.h     |   3 +
>  include/uapi/linux/if_link.h |   2 +
>  3 files changed, 368 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
> index 1c4429d24cfc..69d865e592df 100644
> --- a/drivers/net/gtp.c
> +++ b/drivers/net/gtp.c
> @@ -24,6 +24,7 @@
>  #include <net/net_namespace.h>
>  #include <net/protocol.h>
>  #include <net/ip.h>
> +#include <net/ipv6.h>
>  #include <net/udp.h>
>  #include <net/udp_tunnel.h>
>  #include <net/icmp.h>
> @@ -52,9 +53,11 @@ struct pdp_ctx {
>  
>  	union {
>  		struct in_addr	addr;
> +		struct in6_addr	addr6;
>  	} ms;
>  	union {
>  		struct in_addr	addr;
> +		struct in6_addr	addr6;
>  	} peer;
>  
>  	struct sock		*sk;
> @@ -131,6 +134,11 @@ static inline u32 ipv4_hashfn(__be32 ip)
>  	return jhash_1word((__force u32)ip, gtp_h_initval);
>  }
>  
> +static inline u32 ipv6_hashfn(const struct in6_addr *ip6)
> +{
> +	return jhash(ip6, sizeof(*ip6), gtp_h_initval);
> +}
> +

Hi Pablo,

I'm would naively expect that the compiler can work out if this needs to
be inline.

>  /* Resolve a PDP context structure based on the 64bit TID. */
>  static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
>  {

...

> @@ -878,6 +951,20 @@ static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
>  	pktinfo->dev	= dev;
>  }
>  
> +static inline void gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo,
> +					struct sock *sk, struct ipv6hdr *ip6h,
> +					struct pdp_ctx *pctx, struct rt6_info *rt6,
> +					struct flowi6 *fl6,
> +					struct net_device *dev)
> +{
> +	pktinfo->sk	= sk;
> +	pktinfo->ip6h	= ip6h;
> +	pktinfo->pctx	= pctx;
> +	pktinfo->rt6	= rt6;
> +	pktinfo->fl6	= *fl6;
> +	pktinfo->dev	= dev;
> +}

Here too.

...

> @@ -1441,7 +1736,14 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
>  		if (!pctx)
>  			pctx = pctx_tid;
>  
> -		ipv4_pdp_fill(pctx, info);
> +		switch (pctx->af) {
> +		case AF_INET:
> +			ipv4_pdp_fill(pctx, info);
> +			break;
> +		case AF_INET6:
> +			ipv6_pdp_fill(pctx, info);
> +			break;
> +		}
>  
>  		if (pctx->gtp_version == GTP_V0)
>  			netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n",

The code just before the following hunk is:

	pctx = kmalloc(sizeof(*pctx), GFP_ATOMIC);
	if (pctx == NULL)
		return ERR_PTR(-ENOMEM);


> @@ -1461,7 +1763,24 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
>  	sock_hold(sk);
>  	pctx->sk = sk;
>  	pctx->dev = gtp->dev;
> -	ipv4_pdp_fill(pctx, info);
> +	pctx->af = family;
> +
> +	switch (pctx->af) {
> +	case AF_INET:
> +		if (!info->attrs[GTPA_MS_ADDRESS] ||
> +		    !info->attrs[GTPA_PEER_ADDRESS])
> +			return ERR_PTR(-EINVAL);

So this appears to leak pctx.

> +
> +		ipv4_pdp_fill(pctx, info);
> +		break;
> +	case AF_INET6:
> +		if (!info->attrs[GTPA_MS_ADDR6] ||
> +		    !info->attrs[GTPA_PEER_ADDR6])
> +			return ERR_PTR(-EINVAL);

Likewise here.

Flagged by Smatch.

> +
> +		ipv6_pdp_fill(pctx, info);
> +		break;
> +	}
>  	atomic_set(&pctx->tx_seq, 0);
>  
>  	switch (pctx->gtp_version) {

...

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH net-next 02/12] gtp: properly parse extension headers
  2024-04-26 20:28   ` Simon Horman
@ 2024-05-02 10:36     ` Pablo Neira Ayuso
  0 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-05-02 10:36 UTC (permalink / raw)
  To: Simon Horman
  Cc: netdev, davem, laforge, pespin, osmith, kuba, pabeni, edumazet,
	fw

On Fri, Apr 26, 2024 at 09:28:52PM +0100, Simon Horman wrote:
> On Thu, Apr 25, 2024 at 12:51:28PM +0200, Pablo Neira Ayuso wrote:
> > Currently GTP packets are dropped if the next extension field is set to
> > non-zero value, but this are valid GTP packets.
> > 
> > TS 29.281 provides a longer header format, which is defined as struct
> > gtp1_header_long. Such long header format is used if any of the S, PN, E
> > flags is set.
> > 
> > This long header is 4 bytes longer than struct gtp1_header, plus
> > variable length (optional) extension headers. The next extension header
> > field is zero is no extension header is provided.
> > 
> > The extension header is composed of a length field which includes total
> > number of 4 byte words including the extension header itself (1 byte),
> > payload (variable length) and next type (1 byte). The extension header
> > size and its payload is aligned to 4 bytes.
> > 
> > A GTP packet might come with a chain extensions headers, which makes it
> > slightly cumbersome to parse because the extension next header field
> > comes at the end of the extension header, and there is a need to check
> > if this field becomes zero to stop the extension header parser.
> > 
> > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > ---
> >  drivers/net/gtp.c | 41 +++++++++++++++++++++++++++++++++++++++++
> >  include/net/gtp.h |  5 +++++
> >  2 files changed, 46 insertions(+)
> > 
> > diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
> > index 4680cdf4fa70..9451c74c1a7d 100644
> > --- a/drivers/net/gtp.c
> > +++ b/drivers/net/gtp.c
> > @@ -567,6 +567,43 @@ static int gtp1u_handle_echo_resp(struct gtp_dev *gtp, struct sk_buff *skb)
> >  				       msg, 0, GTP_GENL_MCGRP, GFP_ATOMIC);
> >  }
> >  
> > +static int gtp_parse_exthdrs(struct sk_buff *skb, unsigned int *hdrlen)
> > +{
> > +	struct gtp_ext_hdr *gtp_exthdr, _gtp_exthdr;
> > +	unsigned int offset = *hdrlen;
> > +	__u8 *next_type, _next_type;
> > +
> > +	/* From 29.060: "The Extension Header Length field specifies the length
> > +	 * of the particular Extension header in 4 octets units."
> > +	 *
> > +	 * This length field includes length field size itself (1 byte),
> > +	 * payload (variable length) and next type (1 byte). The extension
> > +	 * header is aligned to to 4 bytes.
> > +	 */
> > +
> > +	do {
> > +		gtp_exthdr = skb_header_pointer(skb, offset, sizeof(gtp_exthdr),
> 
> Hi Pablo,
> 
> Should this be sizeof(*gtp_exthdr)?

Indeed, coincidentally, extension header size if 4 bytes, then this is
checking 8 bytes on x86_64 and 4 bytes in x86.

> And likewise, in the ip_version calculation in gtp_inner_proto()
> in [PATCH 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP 
> 
> Flagged by Coccinelle.

Thanks; I will fix and revamp.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH net-next 04/12] gtp: add IPv6 support
  2024-04-26 20:41   ` Simon Horman
@ 2024-05-02 10:38     ` Pablo Neira Ayuso
  0 siblings, 0 replies; 18+ messages in thread
From: Pablo Neira Ayuso @ 2024-05-02 10:38 UTC (permalink / raw)
  To: Simon Horman
  Cc: netdev, davem, laforge, pespin, osmith, kuba, pabeni, edumazet,
	fw

Hi Simon,

On Fri, Apr 26, 2024 at 09:41:01PM +0100, Simon Horman wrote:
> On Thu, Apr 25, 2024 at 12:51:30PM +0200, Pablo Neira Ayuso wrote:
[...]
> > @@ -131,6 +134,11 @@ static inline u32 ipv4_hashfn(__be32 ip)
> >  	return jhash_1word((__force u32)ip, gtp_h_initval);
> >  }
> >  
> > +static inline u32 ipv6_hashfn(const struct in6_addr *ip6)
> > +{
> > +	return jhash(ip6, sizeof(*ip6), gtp_h_initval);
> > +}
> > +
> 
> Hi Pablo,
> 
> I'm would naively expect that the compiler can work out if this needs to
> be inline.

I will remove inline, I saw the warnings from patchwork on this too
after my v2.

> >  /* Resolve a PDP context structure based on the 64bit TID. */
> >  static struct pdp_ctx *gtp0_pdp_find(struct gtp_dev *gtp, u64 tid)
> >  {
> 
> ...
> 
> > @@ -878,6 +951,20 @@ static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
> >  	pktinfo->dev	= dev;
> >  }
> >  
> > +static inline void gtp_set_pktinfo_ipv6(struct gtp_pktinfo *pktinfo,
> > +					struct sock *sk, struct ipv6hdr *ip6h,
> > +					struct pdp_ctx *pctx, struct rt6_info *rt6,
> > +					struct flowi6 *fl6,
> > +					struct net_device *dev)
> > +{
> > +	pktinfo->sk	= sk;
> > +	pktinfo->ip6h	= ip6h;
> > +	pktinfo->pctx	= pctx;
> > +	pktinfo->rt6	= rt6;
> > +	pktinfo->fl6	= *fl6;
> > +	pktinfo->dev	= dev;
> > +}
> 
> Here too.

OK.

> > @@ -1441,7 +1736,14 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
> >  		if (!pctx)
> >  			pctx = pctx_tid;
> >  
> > -		ipv4_pdp_fill(pctx, info);
> > +		switch (pctx->af) {
> > +		case AF_INET:
> > +			ipv4_pdp_fill(pctx, info);
> > +			break;
> > +		case AF_INET6:
> > +			ipv6_pdp_fill(pctx, info);
> > +			break;
> > +		}
> >  
> >  		if (pctx->gtp_version == GTP_V0)
> >  			netdev_dbg(dev, "GTPv0-U: update tunnel id = %llx (pdp %p)\n",
> 
> The code just before the following hunk is:
> 
> 	pctx = kmalloc(sizeof(*pctx), GFP_ATOMIC);
> 	if (pctx == NULL)
> 		return ERR_PTR(-ENOMEM);
> 
> 
> > @@ -1461,7 +1763,24 @@ static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk,
> >  	sock_hold(sk);
> >  	pctx->sk = sk;
> >  	pctx->dev = gtp->dev;
> > -	ipv4_pdp_fill(pctx, info);
> > +	pctx->af = family;
> > +
> > +	switch (pctx->af) {
> > +	case AF_INET:
> > +		if (!info->attrs[GTPA_MS_ADDRESS] ||
> > +		    !info->attrs[GTPA_PEER_ADDRESS])
> > +			return ERR_PTR(-EINVAL);
> 
> So this appears to leak pctx.

Good catch.

> > +
> > +		ipv4_pdp_fill(pctx, info);
> > +		break;
> > +	case AF_INET6:
> > +		if (!info->attrs[GTPA_MS_ADDR6] ||
> > +		    !info->attrs[GTPA_PEER_ADDR6])
> > +			return ERR_PTR(-EINVAL);
> 
> Likewise here.
> 
> Flagged by Smatch.

Thanks Simon.

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2024-05-02 10:38 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-25 10:51 [PATCH net-next 00/12] gtp updates for net-next (v2) Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 01/12] gtp: remove useless initialization Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 02/12] gtp: properly parse extension headers Pablo Neira Ayuso
2024-04-26 20:28   ` Simon Horman
2024-05-02 10:36     ` Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 03/12] gtp: prepare for IPv6 support Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 04/12] gtp: add " Pablo Neira Ayuso
2024-04-26 20:41   ` Simon Horman
2024-05-02 10:38     ` Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 05/12] gtp: use IPv6 address /64 prefix for UE/MS Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 06/12] gtp: pass up link local traffic to userspace socket Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 07/12] gtp: move debugging to skbuff build helper function Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 08/12] gtp: remove IPv4 and IPv6 header from context object Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 09/12] gtp: add helper function to build GTP packets from an IPv4 packet Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 10/12] gtp: add helper function to build GTP packets from an IPv6 packet Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 11/12] gtp: support for IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP Pablo Neira Ayuso
2024-04-25 10:51 ` [PATCH net-next 12/12] gtp: identify tunnel via GTP device + GTP version + TEID + family Pablo Neira Ayuso
2024-04-26  2:29 ` [PATCH net-next 00/12] gtp updates for net-next (v2) Jakub Kicinski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.