public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433)
@ 2026-05-03 15:30 Yuya Kusakabe
  2026-05-03 15:30 ` [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior Yuya Kusakabe
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

This series adds the iproute2 frontend for the SRv6 Mobile User Plane
(MUP) endpoint behaviors of RFC 9433.  It is sent in parallel with the
matching kernel net-next series; each commit here is self-contained
and brings in the seg6local UAPI bits it needs from the in-progress
kernel UAPI header (include/uapi/linux/seg6_local.h):

  Section 6.2  End.MAP
  Section 6.3  End.M.GTP6.D
  Section 6.4  End.M.GTP6.D.Di
  Section 6.5  End.M.GTP6.E
  Section 6.6  End.M.GTP4.E
  Section 6.7  H.M.GTP4.D

The series adds these seg6local CLI keywords:

  src                  IPv6 source-address template
  v4_mask_len          length of the IPv4 DA portion of the SID, in
                       bits (1..32)
  sr_prefix_len        locator length of the egress End.M.GTP*.E SID,
                       in bits (1..88, leaving 40 bits for the
                       Args.Mob.Session field)
  v6_src_prefix_len    Source UPF Prefix length P in the IPv6 SA
                       template (1..127, defaults to 64); requires
                       P + v4_mask_len <= 128
  pdu_type             GTP-U PDU Session Container PDU Type (3GPP
                       TS 38.415 Section 5.5.2): downlink|dl|uplink|ul
                       or 0..15.  When omitted, the egress emits a
                       short GTPv1-U header (no PDU Session Container)
                       regardless of the QFI in the SID; 5G N3
                       deployments must set pdu_type explicitly.

A small per-action attribute validator (introduced in patch 1 and
extended by each subsequent behavior) rejects obvious typos in the
seg6local block at the userspace layer instead of leaving the
operator with an opaque kernel EINVAL.

Link: https://datatracker.ietf.org/doc/html/rfc9433

Yuya Kusakabe (6):
  seg6: add support for the End.MAP behavior
  seg6: add support for the End.M.GTP4.E behavior
  seg6: add support for the End.M.GTP6.E behavior
  seg6: add support for the End.M.GTP6.D behavior
  seg6: add support for the End.M.GTP6.D.Di behavior
  seg6: add support for the H.M.GTP4.D behavior

 include/uapi/linux/seg6_local.h |  17 +++
 ip/ip_common.h                  |   2 +-
 ip/ipnexthop.c                  |   2 +-
 ip/iproute.c                    |  14 +-
 ip/iproute_lwtunnel.c           | 263 +++++++++++++++++++++++++++++++-
 man/man8/ip-route.8.in          | 154 +++++++++++++++++++
 6 files changed, 442 insertions(+), 10 deletions(-)


base-commit: 4f5de57e2ff11a5925dacdf3deeeabee7ba9502a
-- 
2.50.1


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

* [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior
  2026-05-03 15:30 [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
@ 2026-05-03 15:30 ` Yuya Kusakabe
  2026-05-03 15:30   ` [PATCH iproute2-next 2/6] seg6: add support for the End.M.GTP4.E behavior Yuya Kusakabe
  2026-05-03 16:45 ` [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
  2026-05-03 21:05 ` Stephen Hemminger
  2 siblings, 1 reply; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the End.MAP behavior, which swaps the IPv6 destination
address with the next SID without consuming the SRH.  The new SID is
specified using the existing nh6 attribute.

A small per-action attribute validator is also introduced so that
invalid keyword combinations fail at the userspace layer instead of
returning an opaque kernel EINVAL.  Subsequent SRv6 Mobile behaviors
extend the validator with their own keyword bookkeeping.

Example:
ip -6 r a 2001:db8:f::/64 encap seg6local action End.MAP \
    nh6 2001:db8:1::beef dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |  2 ++
 ip/iproute.c                    |  3 ++-
 ip/iproute_lwtunnel.c           | 13 +++++++++++++
 man/man8/ip-route.8.in          |  9 +++++++++
 4 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index 6e71d97f6f44..1678db71e8e7 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -67,6 +67,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_BPF	= 15,
 	/* decap and lookup of DA in v4 or v6 table */
 	SEG6_LOCAL_ACTION_END_DT46	= 16,
+	/* swap DA with new SID, leave SRH untouched (RFC 9433 Section 6.2) */
+	SEG6_LOCAL_ACTION_END_MAP	= 17,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/iproute.c b/ip/iproute.c
index 5b9e7ac1134a..61394847018f 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -105,7 +105,8 @@ static void usage(void)
 		"SEG6LOCAL := action ACTION [ OPTIONS ] [ count ]\n"
 		"ACTION := { End | End.X | End.T | End.DX2 | End.DX6 | End.DX4 |\n"
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
-		"            End.BM | End.S | End.AS | End.AM | End.BPF }\n"
+		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
+		"            End.MAP }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME }\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 00b4f7565be6..15c0077a4566 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -405,6 +405,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_AM]		= "End.AM",
 	[SEG6_LOCAL_ACTION_END_BPF]		= "End.BPF",
 	[SEG6_LOCAL_ACTION_END_DT46]		= "End.DT46",
+	[SEG6_LOCAL_ACTION_END_MAP]		= "End.MAP",
 };
 
 static const char *format_action_type(int action)
@@ -578,6 +579,16 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
 		print_seg6_local_flavors(fp, tb[SEG6_LOCAL_FLAVORS]);
 }
 
+static void seg6local_action_check_attrs(int action, int nh6_ok)
+{
+	switch (action) {
+	case SEG6_LOCAL_ACTION_END_MAP:
+		if (!nh6_ok)
+			invarg("End.MAP requires \"nh6\"\n", "");
+		break;
+	}
+}
+
 static void print_encap_mpls(FILE *fp, struct rtattr *encap)
 {
 	struct rtattr *tb[MPLS_IPTUNNEL_MAX+1];
@@ -1571,6 +1582,8 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 		exit(-1);
 	}
 
+	seg6local_action_check_attrs(action, nh6_ok);
+
 	if (srh_ok) {
 		int srhlen;
 
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index 9f29fd436f59..c0b1e87ad022 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1024,6 +1024,15 @@ followed by the specified SRH. The destination address of the outer IPv6
 header is set to the first segment of the new SRH. The source
 address is set as described in \fBip-sr\fR(8).
 
+.B End.MAP nh6
+.IR ADDRESS
+- SRv6 Mobile User Plane End.MAP behavior (RFC 9433 Section 6.2).
+Decrement the IPv6 Hop Limit, replace the IPv6 destination address
+with the configured next SID
+.RI ( nh6 ),
+and forward via the IPv6 FIB. The Segment Routing Header is left
+untouched.
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* [PATCH iproute2-next 2/6] seg6: add support for the End.M.GTP4.E behavior
  2026-05-03 15:30 ` [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior Yuya Kusakabe
@ 2026-05-03 15:30   ` Yuya Kusakabe
  2026-05-03 15:30     ` [PATCH iproute2-next 3/6] seg6: add support for the End.M.GTP6.E behavior Yuya Kusakabe
  0 siblings, 1 reply; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the End.M.GTP4.E behavior, which translates SRv6
traffic into IPv4/GTP-U.  Four new keywords are introduced:

  src                  IPv6 source-address template
  v4_mask_len          IPv4 DA portion of the SID, in bits (1..32)
  v6_src_prefix_len    Source UPF Prefix length P in the IPv6 SA
                       template (1..127, defaults to 64); requires
                       P + v4_mask_len <= 128
  pdu_session_type     GTP-U PDU Session Type (downlink|dl|uplink|ul
                       or 0..15)

The route prefix length is propagated through lwt_parse_encap() so
that route_prefix_len + v4_mask_len + 40 <= 128 can be enforced at
parse time.

Example:
ip -6 r a 2001:db8:1::/56 encap seg6local action End.M.GTP4.E \
    src 2001:db8::1 v4_mask_len 32 v6_src_prefix_len 64 \
    pdu_session_type ul dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |   6 ++
 ip/ip_common.h                  |   2 +-
 ip/ipnexthop.c                  |   2 +-
 ip/iproute.c                    |  11 ++-
 ip/iproute_lwtunnel.c           | 159 +++++++++++++++++++++++++++++++-
 man/man8/ip-route.8.in          |  34 +++++++
 6 files changed, 203 insertions(+), 11 deletions(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index 1678db71e8e7..8bb3cdc3a649 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -29,6 +29,10 @@ enum {
 	SEG6_LOCAL_VRFTABLE,
 	SEG6_LOCAL_COUNTERS,
 	SEG6_LOCAL_FLAVORS,
+	SEG6_LOCAL_MOBILE_SRC_ADDR,
+	SEG6_LOCAL_MOBILE_V4_MASK_LEN,
+	SEG6_LOCAL_MOBILE_PDU_TYPE,
+	SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN,
 	__SEG6_LOCAL_MAX,
 };
 #define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
@@ -69,6 +73,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_DT46	= 16,
 	/* swap DA with new SID, leave SRH untouched (RFC 9433 Section 6.2) */
 	SEG6_LOCAL_ACTION_END_MAP	= 17,
+	/* SRv6 to IPv4/GTP-U encap (RFC 9433 Section 6.6) */
+	SEG6_LOCAL_ACTION_END_M_GTP4_E	= 18,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/ip_common.h b/ip/ip_common.h
index 3f55ea336b37..f9a8fd6e576c 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -153,7 +153,7 @@ extern const struct ipstats_stat_desc ipstats_stat_desc_xstats_slave_bond_group;
 
 /* iproute_lwtunnel.c */
 int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
-		    int encap_attr, int encap_type_attr);
+		    int encap_attr, int encap_type_attr, __u8 dst_len);
 void lwt_print_encap(FILE *fp, struct rtattr *encap_type, struct rtattr *encap);
 
 /* iplink_xdp.c */
diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c
index 14b525aa0d67..aaa75680b585 100644
--- a/ip/ipnexthop.c
+++ b/ip/ipnexthop.c
@@ -1100,7 +1100,7 @@ static int ipnh_modify(int cmd, unsigned int flags, int argc, char **argv)
 			rta->rta_len = RTA_LENGTH(0);
 
 			lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
-					NHA_ENCAP, NHA_ENCAP_TYPE);
+					NHA_ENCAP, NHA_ENCAP_TYPE, 0);
 
 			if (rta->rta_len > RTA_LENGTH(0)) {
 				addraw_l(&req.n, 1024, RTA_DATA(rta),
diff --git a/ip/iproute.c b/ip/iproute.c
index 61394847018f..628bccc28074 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -106,10 +106,12 @@ static void usage(void)
 		"ACTION := { End | End.X | End.T | End.DX2 | End.DX6 | End.DX4 |\n"
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
 		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
-		"            End.MAP }\n"
+		"            End.MAP | End.M.GTP4.E }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
-		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME }\n"
+		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME | MOBILE_OPTION }\n"
+		"MOBILE_OPTION := { src ADDR | v4_mask_len BITS | v6_src_prefix_len BITS |\n"
+		"                   pdu_type { downlink | dl | uplink | ul | 0..15 } }\n"
 		"FLAVORS := { FLAVOR[,FLAVOR] }\n"
 		"FLAVOR := { psp | usp | usd | next-csid }\n"
 		"IOAM6HDR := trace prealloc type IOAM6_TRACE_TYPE ns IOAM6_NAMESPACE size IOAM6_TRACE_SIZE\n"
@@ -1076,7 +1078,7 @@ static int parse_one_nh(struct nlmsghdr *n, struct rtmsg *r,
 			int old_len = rta->rta_len;
 
 			if (lwt_parse_encap(rta, len, &argc, &argv,
-					    RTA_ENCAP, RTA_ENCAP_TYPE))
+					    RTA_ENCAP, RTA_ENCAP_TYPE, 0))
 				return -1;
 			rtnh->rtnh_len += rta->rta_len - old_len;
 		} else if (strcmp(*argv, "as") == 0) {
@@ -1523,7 +1525,8 @@ static int iproute_modify(int cmd, unsigned int flags, int argc, char **argv)
 			rta->rta_len = RTA_LENGTH(0);
 
 			lwt_parse_encap(rta, sizeof(buf), &argc, &argv,
-					RTA_ENCAP, RTA_ENCAP_TYPE);
+					RTA_ENCAP, RTA_ENCAP_TYPE,
+					req.r.rtm_dst_len);
 
 			if (rta->rta_len > RTA_LENGTH(0))
 				addraw_l(&req.n, 1024
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 15c0077a4566..1a92b7d94e88 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -406,6 +406,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_BPF]		= "End.BPF",
 	[SEG6_LOCAL_ACTION_END_DT46]		= "End.DT46",
 	[SEG6_LOCAL_ACTION_END_MAP]		= "End.MAP",
+	[SEG6_LOCAL_ACTION_END_M_GTP4_E]	= "End.M.GTP4.E",
 };
 
 static const char *format_action_type(int action)
@@ -577,15 +578,92 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
 
 	if (tb[SEG6_LOCAL_FLAVORS])
 		print_seg6_local_flavors(fp, tb[SEG6_LOCAL_FLAVORS]);
+
+	if (tb[SEG6_LOCAL_MOBILE_SRC_ADDR])
+		print_string(PRINT_ANY, "src", "src %s ",
+			     rt_addr_n2a_rta(AF_INET6,
+					     tb[SEG6_LOCAL_MOBILE_SRC_ADDR]));
+
+	if (tb[SEG6_LOCAL_MOBILE_V4_MASK_LEN])
+		print_uint(PRINT_ANY, "v4_mask_len", "v4_mask_len %u ",
+			   rta_getattr_u8(tb[SEG6_LOCAL_MOBILE_V4_MASK_LEN]));
+
+	if (tb[SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN])
+		print_uint(PRINT_ANY, "v6_src_prefix_len",
+			   "v6_src_prefix_len %u ",
+			   rta_getattr_u8(tb[SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN]));
+
+	if (tb[SEG6_LOCAL_MOBILE_PDU_TYPE]) {
+		__u8 t = rta_getattr_u8(tb[SEG6_LOCAL_MOBILE_PDU_TYPE]);
+		const char *name = NULL;
+
+		switch (t) {
+		case 0:
+			name = "downlink";
+			break;
+		case 1:
+			name = "uplink";
+			break;
+		}
+
+		if (name)
+			print_string(PRINT_ANY, "pdu_type",
+				     "pdu_type %s ", name);
+		else
+			print_uint(PRINT_ANY, "pdu_type",
+				   "pdu_type %u ", t);
+	}
 }
 
-static void seg6local_action_check_attrs(int action, int nh6_ok)
+static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
+					 int mobile_src_ok,
+					 int mobile_v4mask_ok,
+					 int mobile_v6src_plen_ok,
+					 __u8 v4_mask_len,
+					 __u8 v6_src_prefix_len,
+					 __u8 dst_len)
 {
 	switch (action) {
 	case SEG6_LOCAL_ACTION_END_MAP:
 		if (!nh6_ok)
 			invarg("End.MAP requires \"nh6\"\n", "");
 		break;
+	case SEG6_LOCAL_ACTION_END_M_GTP4_E:
+		if (!mobile_src_ok || !mobile_v4mask_ok)
+			invarg("End.M.GTP4.E requires \"src\" and \"v4_mask_len\"\n",
+			       "");
+		if (srh_ok)
+			invarg("End.M.GTP4.E does not accept \"srh\"\n", "");
+		/*
+		 * The IPv6 source-address layout per RFC 9433 Section 6.6
+		 * Figure 10 packs the Source UPF Prefix (P bits) followed
+		 * by the IPv4 SA portion (v4_mask_len bits) and padding
+		 * inside the 128-bit IPv6 SA, so P + v4_mask_len <= 128.
+		 */
+		if (mobile_v6src_plen_ok &&
+		    (unsigned int)v6_src_prefix_len +
+		    (unsigned int)v4_mask_len > 128)
+			invarg("End.M.GTP4.E requires \"v6_src_prefix_len\" +"
+			       " \"v4_mask_len\" <= 128\n", "");
+		/*
+		 * The egress SID layout (Locator | IPv4 DA | Args.Mob.Session)
+		 * must fit in the 128-bit IPv6 destination address.  The
+		 * locator length here is the route prefix length the SID is
+		 * installed under.  dst_len == 0 means the caller did not
+		 * propagate the route prefix (e.g. nexthop encap), in which
+		 * case we leave the check to the kernel at install time.
+		 */
+		if (dst_len &&
+		    (unsigned int)dst_len + (unsigned int)v4_mask_len + 40 > 128)
+			invarg("End.M.GTP4.E requires route_prefix_len +"
+			       " \"v4_mask_len\" + 40 <= 128\n", "");
+		break;
+	default:
+		if (mobile_src_ok || mobile_v4mask_ok || mobile_v6src_plen_ok)
+			invarg("\"src\", \"v4_mask_len\", and"
+			       " \"v6_src_prefix_len\" are only valid for"
+			       " SRv6 Mobile User Plane actions\n", "");
+		break;
 	}
 }
 
@@ -1456,11 +1534,13 @@ static int seg6local_parse_flavors(struct rtattr *rta, size_t len,
 }
 
 static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
-				 char ***argvp)
+				 char ***argvp, __u8 dst_len)
 {
 	int nh4_ok = 0, nh6_ok = 0, iif_ok = 0, oif_ok = 0, flavors_ok = 0;
 	int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
 	int action_ok = 0, srh_ok = 0, bpf_ok = 0, counters_ok = 0;
+	int mobile_src_ok = 0, mobile_v4mask_ok = 0, mobile_pdusess_ok = 0;
+	int mobile_v6src_plen_ok = 0;
 	__u32 action = 0, table, vrftable, iif, oif;
 	struct ipv6_sr_hdr *srh;
 	char **argv = *argvp;
@@ -1468,6 +1548,7 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	char segbuf[1024];
 	inet_prefix addr;
 	__u32 hmac = 0;
+	__u8 v4_mask_len = 0, v6_src_prefix_len = 0;
 	int ret = 0;
 
 	while (argc > 0) {
@@ -1569,6 +1650,72 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 			if (lwt_parse_bpf(rta, len, &argc, &argv, SEG6_LOCAL_BPF,
 			    BPF_PROG_TYPE_LWT_SEG6LOCAL) < 0)
 				exit(-1);
+		} else if (strcmp(*argv, "src") == 0) {
+			/*
+			 * Mobile User Plane "src" template; scoped to the
+			 * seg6local block and unrelated to the top-level
+			 * "src" prefsrc keyword.
+			 */
+			NEXT_ARG();
+			if (mobile_src_ok++)
+				duparg2("src", *argv);
+			get_addr(&addr, *argv, AF_INET6);
+			ret = rta_addattr_l(rta, len, SEG6_LOCAL_MOBILE_SRC_ADDR,
+					    &addr.data, addr.bytelen);
+		} else if (strcmp(*argv, "v4_mask_len") == 0) {
+			NEXT_ARG();
+			if (mobile_v4mask_ok++)
+				duparg2("v4_mask_len", *argv);
+			if (get_u8(&v4_mask_len, *argv, 0) ||
+			    v4_mask_len == 0 || v4_mask_len > 32)
+				invarg("\"v4_mask_len\" must be in the range 1..32\n",
+				       *argv);
+			ret = rta_addattr8(rta, len, SEG6_LOCAL_MOBILE_V4_MASK_LEN,
+					   v4_mask_len);
+		} else if (strcmp(*argv, "v6_src_prefix_len") == 0) {
+			NEXT_ARG();
+			if (mobile_v6src_plen_ok++)
+				duparg2("v6_src_prefix_len", *argv);
+			/*
+			 * Per RFC 9433 Section 6.6 Figure 10, the IPv6 SA is
+			 * "Source UPF Prefix (P bits) | IPv4 SA (b bits) |
+			 * padding (128 - P - b)".  P must be a non-zero
+			 * multiple of 8 up to 128; the combined check
+			 * (P + v4_mask_len <= 128) is enforced per-action.
+			 */
+			if (get_u8(&v6_src_prefix_len, *argv, 0) ||
+			    v6_src_prefix_len == 0 ||
+			    v6_src_prefix_len > 127)
+				invarg("\"v6_src_prefix_len\" must be in the range 1..127\n",
+				       *argv);
+			ret = rta_addattr8(rta, len,
+					   SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN,
+					   v6_src_prefix_len);
+		} else if (strcmp(*argv, "pdu_type") == 0) {
+			__u8 psc_type;
+
+			NEXT_ARG();
+			if (mobile_pdusess_ok++)
+				duparg2("pdu_type", *argv);
+			/*
+			 * 3GPP TS 38.415 PDU Session Type is a 4-bit field; the
+			 * kernel mirrors that range (0..15).  0 = DL, 1 = UL.
+			 */
+			if (strcmp(*argv, "downlink") == 0 ||
+			    strcmp(*argv, "dl") == 0) {
+				psc_type = 0;
+			} else if (strcmp(*argv, "uplink") == 0 ||
+				   strcmp(*argv, "ul") == 0) {
+				psc_type = 1;
+			} else if (get_u8(&psc_type, *argv, 0) ||
+				   psc_type > 15) {
+				invarg("invalid \"pdu_type\" value"
+				       " (must be downlink|dl|uplink|ul"
+				       " or 0..15)\n", *argv);
+			}
+			ret = rta_addattr8(rta, len,
+					   SEG6_LOCAL_MOBILE_PDU_TYPE,
+					   psc_type);
 		} else {
 			break;
 		}
@@ -1582,7 +1729,9 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 		exit(-1);
 	}
 
-	seg6local_action_check_attrs(action, nh6_ok);
+	seg6local_action_check_attrs(action, srh_ok, nh6_ok, mobile_src_ok,
+				     mobile_v4mask_ok, mobile_v6src_plen_ok,
+				     v4_mask_len, v6_src_prefix_len, dst_len);
 
 	if (srh_ok) {
 		int srhlen;
@@ -2289,7 +2438,7 @@ static int parse_encap_xfrm(struct rtattr *rta, size_t len,
 }
 
 int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
-		    int encap_attr, int encap_type_attr)
+		    int encap_attr, int encap_type_attr, __u8 dst_len)
 {
 	struct rtattr *nest;
 	int argc = *argcp;
@@ -2328,7 +2477,7 @@ int lwt_parse_encap(struct rtattr *rta, size_t len, int *argcp, char ***argvp,
 		ret = parse_encap_seg6(rta, len, &argc, &argv);
 		break;
 	case LWTUNNEL_ENCAP_SEG6_LOCAL:
-		ret = parse_encap_seg6local(rta, len, &argc, &argv);
+		ret = parse_encap_seg6local(rta, len, &argc, &argv, dst_len);
 		break;
 	case LWTUNNEL_ENCAP_RPL:
 		ret = parse_encap_rpl(rta, len, &argc, &argv);
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index c0b1e87ad022..a878d4375f03 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1033,6 +1033,40 @@ with the configured next SID
 and forward via the IPv6 FIB. The Segment Routing Header is left
 untouched.
 
+.B End.M.GTP4.E src
+.IR ADDRESS
+.B v4_mask_len
+.IR BITS
+.RB [ "v6_src_prefix_len"
+.IR BITS ]
+.RB [ "pdu_type"
+.IR DIR ]
+- SRv6 Mobile User Plane End.M.GTP4.E behavior (RFC 9433 Section 6.6).
+At the SR egress gateway, the matching SRv6 packet is converted into
+an IPv4/UDP/GTP-U packet for delivery to a legacy IPv4-attached gNB or
+eNB.  The IPv6 destination address of the matching SID encodes
+.IR Locator " | " "IPv4 DA" " (\fBv4_mask_len\fR bits) | "
+.IR "Args.Mob.Session" " (40 bits, RFC 9433 Section 6.1)" ,
+and the IPv6 source address is built from
+.B src
+as
+.IR "Source UPF Prefix" " (\fBv6_src_prefix_len\fR bits) | "
+.IR "IPv4 SA" " (\fBv4_mask_len\fR bits) | padding" .
+.B v4_mask_len
+must be in 1..32 and
+.B v6_src_prefix_len
+in 1..127 (default 64); the route prefix length plus
+.BR v4_mask_len " + 40"
+and
+.BR v6_src_prefix_len " + " v4_mask_len
+must each fit in 128 bits.
+.B pdu_type
+.RB ( downlink | dl | uplink | ul " or " 0..15 )
+forces a GTP-U PDU Session Container (3GPP TS 38.415) with the given
+PDU Type; when omitted no Container is inserted, so 5G N3 deployments
+must set it explicitly.
+The action only accepts packets with Segments Left = 0 or no SRH.
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* [PATCH iproute2-next 3/6] seg6: add support for the End.M.GTP6.E behavior
  2026-05-03 15:30   ` [PATCH iproute2-next 2/6] seg6: add support for the End.M.GTP4.E behavior Yuya Kusakabe
@ 2026-05-03 15:30     ` Yuya Kusakabe
  2026-05-03 15:30       ` [PATCH iproute2-next 4/6] seg6: add support for the End.M.GTP6.D behavior Yuya Kusakabe
  0 siblings, 1 reply; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the End.M.GTP6.E behavior, which translates SRv6
traffic into IPv6/GTP-U.  The behavior reuses the src and
pdu_session_type keywords introduced for End.M.GTP4.E; v4_mask_len is
not meaningful for an IPv6/GTP-U tunnel and is rejected.

Example:
ip -6 r a 2001:db8:1::/64 encap seg6local action End.M.GTP6.E \
    src 2001:db8::1 pdu_session_type ul dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |  2 ++
 ip/iproute.c                    |  2 +-
 ip/iproute_lwtunnel.c           |  7 +++++++
 man/man8/ip-route.8.in          | 21 +++++++++++++++++++++
 4 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index 8bb3cdc3a649..6af145259ffb 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -75,6 +75,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_MAP	= 17,
 	/* SRv6 to IPv4/GTP-U encap (RFC 9433 Section 6.6) */
 	SEG6_LOCAL_ACTION_END_M_GTP4_E	= 18,
+	/* SRv6 to IPv6/GTP-U encap (RFC 9433 Section 6.5) */
+	SEG6_LOCAL_ACTION_END_M_GTP6_E	= 19,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/iproute.c b/ip/iproute.c
index 628bccc28074..a77df0da0efe 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -106,7 +106,7 @@ static void usage(void)
 		"ACTION := { End | End.X | End.T | End.DX2 | End.DX6 | End.DX4 |\n"
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
 		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
-		"            End.MAP | End.M.GTP4.E }\n"
+		"            End.MAP | End.M.GTP4.E | End.M.GTP6.E }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME | MOBILE_OPTION }\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 1a92b7d94e88..7a4acc33602b 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -407,6 +407,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_DT46]		= "End.DT46",
 	[SEG6_LOCAL_ACTION_END_MAP]		= "End.MAP",
 	[SEG6_LOCAL_ACTION_END_M_GTP4_E]	= "End.M.GTP4.E",
+	[SEG6_LOCAL_ACTION_END_M_GTP6_E]	= "End.M.GTP6.E",
 };
 
 static const char *format_action_type(int action)
@@ -628,6 +629,12 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 		if (!nh6_ok)
 			invarg("End.MAP requires \"nh6\"\n", "");
 		break;
+	case SEG6_LOCAL_ACTION_END_M_GTP6_E:
+		if (!mobile_src_ok)
+			invarg("End.M.GTP6.E requires \"src\"\n", "");
+		if (srh_ok)
+			invarg("End.M.GTP6.E does not accept \"srh\"\n", "");
+		break;
 	case SEG6_LOCAL_ACTION_END_M_GTP4_E:
 		if (!mobile_src_ok || !mobile_v4mask_ok)
 			invarg("End.M.GTP4.E requires \"src\" and \"v4_mask_len\"\n",
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index a878d4375f03..7cf97924d699 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1067,6 +1067,27 @@ PDU Type; when omitted no Container is inserted, so 5G N3 deployments
 must set it explicitly.
 The action only accepts packets with Segments Left = 0 or no SRH.
 
+.B End.M.GTP6.E src
+.IR ADDRESS
+.RB [ "pdu_type"
+.IR DIR ]
+- SRv6 Mobile User Plane End.M.GTP6.E behavior (RFC 9433 Section 6.5).
+At the SR egress gateway, an SRv6 packet whose current SID is an
+End.M.GTP6.E SID is converted into an IPv6/UDP/GTP-U packet directed at
+the next segment held in
+.IR SRH[0] .
+The 40-bit Args.Mob.Session field defined in RFC 9433 Section 6.1 is
+read from the right-aligned tail of the matching SID, and its 32-bit
+PDU Session ID portion is used as the GTP-U TEID.  The IPv6 source
+address of the new tunnel is set to the user-provided template
+.BR src .
+The action requires Segments Left to equal 1; other matching packets are
+dropped.  The optional
+.B pdu_type
+takes the same syntax and semantics as in
+.B End.M.GTP4.E
+above (no PDU Session Container is inserted unless explicitly set).
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* [PATCH iproute2-next 4/6] seg6: add support for the End.M.GTP6.D behavior
  2026-05-03 15:30     ` [PATCH iproute2-next 3/6] seg6: add support for the End.M.GTP6.E behavior Yuya Kusakabe
@ 2026-05-03 15:30       ` Yuya Kusakabe
  2026-05-03 15:30         ` [PATCH iproute2-next 5/6] seg6: add support for the End.M.GTP6.D.Di behavior Yuya Kusakabe
  0 siblings, 1 reply; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the End.M.GTP6.D behavior, which translates IPv6/GTP-U
traffic into an SRv6 SR Policy.  The SR Policy is supplied through the
existing srh segs syntax, and a new sr_prefix_len keyword specifies
the locator length of the egress End.M.GTP6.E SID (1..88, leaving
40 bits for the Args.Mob.Session field).

Example:
ip -6 r a 2001:db8:f::/64 encap seg6local action End.M.GTP6.D \
    srh segs 2001:db8:2::1,2001:db8:3::e \
    src 2001:db8::1 sr_prefix_len 88 dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |  3 ++
 ip/iproute.c                    |  6 ++--
 ip/iproute_lwtunnel.c           | 61 +++++++++++++++++++++++++++++----
 man/man8/ip-route.8.in          | 29 ++++++++++++++++
 4 files changed, 90 insertions(+), 9 deletions(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index 6af145259ffb..ed44fb858600 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -33,6 +33,7 @@ enum {
 	SEG6_LOCAL_MOBILE_V4_MASK_LEN,
 	SEG6_LOCAL_MOBILE_PDU_TYPE,
 	SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN,
+	SEG6_LOCAL_MOBILE_SR_PREFIX_LEN,
 	__SEG6_LOCAL_MAX,
 };
 #define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1)
@@ -77,6 +78,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_M_GTP4_E	= 18,
 	/* SRv6 to IPv6/GTP-U encap (RFC 9433 Section 6.5) */
 	SEG6_LOCAL_ACTION_END_M_GTP6_E	= 19,
+	/* IPv6/GTP-U decap into SRv6 (RFC 9433 Section 6.3) */
+	SEG6_LOCAL_ACTION_END_M_GTP6_D	= 20,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/iproute.c b/ip/iproute.c
index a77df0da0efe..8ced32a4f84e 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -106,11 +106,13 @@ static void usage(void)
 		"ACTION := { End | End.X | End.T | End.DX2 | End.DX6 | End.DX4 |\n"
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
 		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
-		"            End.MAP | End.M.GTP4.E | End.M.GTP6.E }\n"
+		"            End.MAP | End.M.GTP4.E | End.M.GTP6.E |\n"
+		"            End.M.GTP6.D }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME | MOBILE_OPTION }\n"
-		"MOBILE_OPTION := { src ADDR | v4_mask_len BITS | v6_src_prefix_len BITS |\n"
+		"MOBILE_OPTION := { src ADDR | v4_mask_len BITS | sr_prefix_len BITS |\n"
+		"                   v6_src_prefix_len BITS |\n"
 		"                   pdu_type { downlink | dl | uplink | ul | 0..15 } }\n"
 		"FLAVORS := { FLAVOR[,FLAVOR] }\n"
 		"FLAVOR := { psp | usp | usd | next-csid }\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 7a4acc33602b..d54bf4628f6f 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -408,6 +408,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_MAP]		= "End.MAP",
 	[SEG6_LOCAL_ACTION_END_M_GTP4_E]	= "End.M.GTP4.E",
 	[SEG6_LOCAL_ACTION_END_M_GTP6_E]	= "End.M.GTP6.E",
+	[SEG6_LOCAL_ACTION_END_M_GTP6_D]	= "End.M.GTP6.D",
 };
 
 static const char *format_action_type(int action)
@@ -589,6 +590,11 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
 		print_uint(PRINT_ANY, "v4_mask_len", "v4_mask_len %u ",
 			   rta_getattr_u8(tb[SEG6_LOCAL_MOBILE_V4_MASK_LEN]));
 
+	if (tb[SEG6_LOCAL_MOBILE_SR_PREFIX_LEN])
+		print_uint(PRINT_ANY, "sr_prefix_len",
+			   "sr_prefix_len %u ",
+			   rta_getattr_u8(tb[SEG6_LOCAL_MOBILE_SR_PREFIX_LEN]));
+
 	if (tb[SEG6_LOCAL_MOBILE_V6_SRC_PREFIX_LEN])
 		print_uint(PRINT_ANY, "v6_src_prefix_len",
 			   "v6_src_prefix_len %u ",
@@ -616,9 +622,26 @@ static void print_encap_seg6local(FILE *fp, struct rtattr *encap)
 	}
 }
 
+/*
+ * SRH-supplying actions (the seg6local equivalents of seg6 inline mode)
+ * pass the entire segment list explicitly; parse_srh() must not append the
+ * implicit terminating SID it adds for inline-style callers.
+ */
+static bool seg6local_action_excludes_final_seg(int action)
+{
+	switch (action) {
+	case SEG6_LOCAL_ACTION_END_B6_ENCAP:
+	case SEG6_LOCAL_ACTION_END_M_GTP6_D:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 					 int mobile_src_ok,
 					 int mobile_v4mask_ok,
+					 int mobile_sr_plen_ok,
 					 int mobile_v6src_plen_ok,
 					 __u8 v4_mask_len,
 					 __u8 v6_src_prefix_len,
@@ -635,6 +658,11 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 		if (srh_ok)
 			invarg("End.M.GTP6.E does not accept \"srh\"\n", "");
 		break;
+	case SEG6_LOCAL_ACTION_END_M_GTP6_D:
+		if (!srh_ok || !mobile_src_ok || !mobile_sr_plen_ok)
+			invarg("End.M.GTP6.D requires \"srh segs\", \"src\","
+			       " and \"sr_prefix_len\"\n", "");
+		break;
 	case SEG6_LOCAL_ACTION_END_M_GTP4_E:
 		if (!mobile_src_ok || !mobile_v4mask_ok)
 			invarg("End.M.GTP4.E requires \"src\" and \"v4_mask_len\"\n",
@@ -666,9 +694,10 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 			       " \"v4_mask_len\" + 40 <= 128\n", "");
 		break;
 	default:
-		if (mobile_src_ok || mobile_v4mask_ok || mobile_v6src_plen_ok)
-			invarg("\"src\", \"v4_mask_len\", and"
-			       " \"v6_src_prefix_len\" are only valid for"
+		if (mobile_src_ok || mobile_v4mask_ok || mobile_sr_plen_ok ||
+		    mobile_v6src_plen_ok)
+			invarg("\"src\", \"v4_mask_len\", \"sr_prefix_len\","
+			       " and \"v6_src_prefix_len\" are only valid for"
 			       " SRv6 Mobile User Plane actions\n", "");
 		break;
 	}
@@ -1547,7 +1576,7 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	int segs_ok = 0, hmac_ok = 0, table_ok = 0, vrftable_ok = 0;
 	int action_ok = 0, srh_ok = 0, bpf_ok = 0, counters_ok = 0;
 	int mobile_src_ok = 0, mobile_v4mask_ok = 0, mobile_pdusess_ok = 0;
-	int mobile_v6src_plen_ok = 0;
+	int mobile_sr_plen_ok = 0, mobile_v6src_plen_ok = 0;
 	__u32 action = 0, table, vrftable, iif, oif;
 	struct ipv6_sr_hdr *srh;
 	char **argv = *argvp;
@@ -1555,7 +1584,7 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	char segbuf[1024];
 	inet_prefix addr;
 	__u32 hmac = 0;
-	__u8 v4_mask_len = 0, v6_src_prefix_len = 0;
+	__u8 v4_mask_len = 0, sr_prefix_len = 0, v6_src_prefix_len = 0;
 	int ret = 0;
 
 	while (argc > 0) {
@@ -1679,6 +1708,23 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 				       *argv);
 			ret = rta_addattr8(rta, len, SEG6_LOCAL_MOBILE_V4_MASK_LEN,
 					   v4_mask_len);
+		} else if (strcmp(*argv, "sr_prefix_len") == 0) {
+			NEXT_ARG();
+			if (mobile_sr_plen_ok++)
+				duparg2("sr_prefix_len", *argv);
+			/*
+			 * The egress SID must leave room for the 40-bit
+			 * Args.Mob.Session field, so the locator can be at
+			 * most (128 - 40) = 88 bits.
+			 */
+			if (get_u8(&sr_prefix_len, *argv, 0) ||
+			    sr_prefix_len == 0 ||
+			    sr_prefix_len > 88)
+				invarg("\"sr_prefix_len\" must be in the range 1..88\n",
+				       *argv);
+			ret = rta_addattr8(rta, len,
+					   SEG6_LOCAL_MOBILE_SR_PREFIX_LEN,
+					   sr_prefix_len);
 		} else if (strcmp(*argv, "v6_src_prefix_len") == 0) {
 			NEXT_ARG();
 			if (mobile_v6src_plen_ok++)
@@ -1737,14 +1783,15 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	}
 
 	seg6local_action_check_attrs(action, srh_ok, nh6_ok, mobile_src_ok,
-				     mobile_v4mask_ok, mobile_v6src_plen_ok,
+				     mobile_v4mask_ok, mobile_sr_plen_ok,
+				     mobile_v6src_plen_ok,
 				     v4_mask_len, v6_src_prefix_len, dst_len);
 
 	if (srh_ok) {
 		int srhlen;
 
 		srh = parse_srh(segbuf, hmac,
-				action == SEG6_LOCAL_ACTION_END_B6_ENCAP);
+				seg6local_action_excludes_final_seg(action));
 		srhlen = (srh->hdrlen + 1) << 3;
 		ret = rta_addattr_l(rta, len, SEG6_LOCAL_SRH, srh, srhlen);
 		free(srh);
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index 7cf97924d699..35e6e2080a1f 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1088,6 +1088,35 @@ takes the same syntax and semantics as in
 .B End.M.GTP4.E
 above (no PDU Session Container is inserted unless explicitly set).
 
+.B End.M.GTP6.D srh segs
+.IR SEGMENTS
+.B src
+.IR ADDRESS
+.B sr_prefix_len
+.IR BITS
+- SRv6 Mobile User Plane End.M.GTP6.D behavior (RFC 9433 Section 6.3).
+At the SR ingress gateway, the matching IPv6/UDP/GTP-U packet has its
+GTP-U envelope removed and the inner T-PDU is re-encapsulated in SRv6
+using the supplied SR Policy
+.RI ( srh
+\fBsegs\fR\~\fISEGMENTS\fR).  The TEID is folded into the 40-bit
+Args.Mob.Session field placed immediately after the egress
+End.M.GTP6.E SID's locator (RFC 9433 Section 6.5),
+.B sr_prefix_len
+specifying the locator length in bits (1..88 -- the upper bound
+leaves room for the 40-bit Args.Mob.Session field within the
+128-bit egress SID).  The egress SID's locator
+length cannot be inferred from local state at the SR Gateway.  The
+egress
+.B End.M.GTP6.E
+SID can then recover the per-session identifier from the same offset.
+The new outer IPv6 source address is taken from
+.BR src .
+The action requires either no SRH or an SRH with
+.B Segments Left
+equal to zero on the inbound packet; other matching packets are
+dropped.
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* [PATCH iproute2-next 5/6] seg6: add support for the End.M.GTP6.D.Di behavior
  2026-05-03 15:30       ` [PATCH iproute2-next 4/6] seg6: add support for the End.M.GTP6.D behavior Yuya Kusakabe
@ 2026-05-03 15:30         ` Yuya Kusakabe
  2026-05-03 15:30           ` [PATCH iproute2-next 6/6] seg6: add support for the H.M.GTP4.D behavior Yuya Kusakabe
  0 siblings, 1 reply; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the End.M.GTP6.D.Di drop-in interconnect behavior,
which translates IPv6/GTP-U traffic into an SRv6 SR Policy while
preserving the original outer IPv6 destination as the final SRH
segment.  Unlike End.M.GTP6.D, this behavior does not take
sr_prefix_len.

Example:
ip -6 r a 2001:db8:f::/64 encap seg6local action End.M.GTP6.D.Di \
    srh segs 2001:db8:2::1,2001:db8:3::e \
    src 2001:db8::1 dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |  2 ++
 ip/iproute.c                    |  2 +-
 ip/iproute_lwtunnel.c           | 10 ++++++++++
 man/man8/ip-route.8.in          | 16 ++++++++++++++++
 4 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index ed44fb858600..0ca8405df2f2 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -80,6 +80,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_M_GTP6_E	= 19,
 	/* IPv6/GTP-U decap into SRv6 (RFC 9433 Section 6.3) */
 	SEG6_LOCAL_ACTION_END_M_GTP6_D	= 20,
+	/* IPv6/GTP-U decap into SRv6, drop-in mode (RFC 9433 Section 6.4) */
+	SEG6_LOCAL_ACTION_END_M_GTP6_D_DI = 21,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/iproute.c b/ip/iproute.c
index 8ced32a4f84e..1604545febc8 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -107,7 +107,7 @@ static void usage(void)
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
 		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
 		"            End.MAP | End.M.GTP4.E | End.M.GTP6.E |\n"
-		"            End.M.GTP6.D }\n"
+		"            End.M.GTP6.D | End.M.GTP6.D.Di }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME | MOBILE_OPTION }\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index d54bf4628f6f..1234c9f146bf 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -409,6 +409,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_M_GTP4_E]	= "End.M.GTP4.E",
 	[SEG6_LOCAL_ACTION_END_M_GTP6_E]	= "End.M.GTP6.E",
 	[SEG6_LOCAL_ACTION_END_M_GTP6_D]	= "End.M.GTP6.D",
+	[SEG6_LOCAL_ACTION_END_M_GTP6_D_DI]	= "End.M.GTP6.D.Di",
 };
 
 static const char *format_action_type(int action)
@@ -632,6 +633,7 @@ static bool seg6local_action_excludes_final_seg(int action)
 	switch (action) {
 	case SEG6_LOCAL_ACTION_END_B6_ENCAP:
 	case SEG6_LOCAL_ACTION_END_M_GTP6_D:
+	case SEG6_LOCAL_ACTION_END_M_GTP6_D_DI:
 		return true;
 	default:
 		return false;
@@ -663,6 +665,14 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 			invarg("End.M.GTP6.D requires \"srh segs\", \"src\","
 			       " and \"sr_prefix_len\"\n", "");
 		break;
+	case SEG6_LOCAL_ACTION_END_M_GTP6_D_DI:
+		if (!srh_ok || !mobile_src_ok)
+			invarg("End.M.GTP6.D.Di requires \"srh segs\" and \"src\"\n",
+			       "");
+		if (mobile_sr_plen_ok)
+			invarg("End.M.GTP6.D.Di does not accept \"sr_prefix_len\"\n",
+			       "");
+		break;
 	case SEG6_LOCAL_ACTION_END_M_GTP4_E:
 		if (!mobile_src_ok || !mobile_v4mask_ok)
 			invarg("End.M.GTP4.E requires \"src\" and \"v4_mask_len\"\n",
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index 35e6e2080a1f..0487338707c6 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1117,6 +1117,22 @@ The action requires either no SRH or an SRH with
 equal to zero on the inbound packet; other matching packets are
 dropped.
 
+.B End.M.GTP6.D.Di srh segs
+.IR SEGMENTS
+.B src
+.IR ADDRESS
+- SRv6 Mobile User Plane End.M.GTP6.D.Di drop-in interconnect behavior
+(RFC 9433 Section 6.4).  Identical to
+.B End.M.GTP6.D
+except that the original outer IPv6 destination address of the
+incoming GTP-U packet is preserved as the final segment of the new
+SRH, allowing existing SRv6 networks to be inserted into a legacy
+mobile path without changing the destination semantics.
+.B sr_prefix_len
+is rejected for this action: the original outer destination is
+preserved verbatim instead of being repacked with an Args.Mob.Session
+field, so no locator length needs to be carried.
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* [PATCH iproute2-next 6/6] seg6: add support for the H.M.GTP4.D behavior
  2026-05-03 15:30         ` [PATCH iproute2-next 5/6] seg6: add support for the End.M.GTP6.D.Di behavior Yuya Kusakabe
@ 2026-05-03 15:30           ` Yuya Kusakabe
  0 siblings, 0 replies; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 15:30 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev, Yuya Kusakabe

From: Yuya Kusakabe <yuya.kusakabe@gmail.com>

Add support for the H.M.GTP4.D headend behavior, which translates
IPv4/GTP-U traffic into an SRv6 SR Policy.  H.M.GTP4.D is installed
on an IPv4 route and reuses the existing src, v4_mask_len,
sr_prefix_len, and v6_src_prefix_len keywords.

The userspace parser fails fast when sr_prefix_len + v4_mask_len
exceeds 88 bits, since the locator, the embedded IPv4 destination,
and the 40-bit Args.Mob.Session field together must fit inside the
128-bit egress SID.

Example:
ip -4 r a 10.0.0.0/24 encap seg6local action H.M.GTP4.D \
    nh6 2001:db8:f:: src 2001:db8::1 \
    v4_mask_len 32 sr_prefix_len 56 v6_src_prefix_len 64 dev sr0

Link: https://datatracker.ietf.org/doc/html/rfc9433

Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
---
 include/uapi/linux/seg6_local.h |  2 ++
 ip/iproute.c                    |  2 +-
 ip/iproute_lwtunnel.c           | 31 ++++++++++++++++++++++-
 man/man8/ip-route.8.in          | 45 +++++++++++++++++++++++++++++++++
 4 files changed, 78 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
index 0ca8405df2f2..69a875fcad73 100644
--- a/include/uapi/linux/seg6_local.h
+++ b/include/uapi/linux/seg6_local.h
@@ -82,6 +82,8 @@ enum {
 	SEG6_LOCAL_ACTION_END_M_GTP6_D	= 20,
 	/* IPv6/GTP-U decap into SRv6, drop-in mode (RFC 9433 Section 6.4) */
 	SEG6_LOCAL_ACTION_END_M_GTP6_D_DI = 21,
+	/* SR headend: IPv4/GTP-U decap, encap in SRv6 (RFC 9433 Section 6.7) */
+	SEG6_LOCAL_ACTION_H_M_GTP4_D	= 22,
 
 	__SEG6_LOCAL_ACTION_MAX,
 };
diff --git a/ip/iproute.c b/ip/iproute.c
index 1604545febc8..c3025f426f2e 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -107,7 +107,7 @@ static void usage(void)
 		"            End.DT6 | End.DT4 | End.DT46 | End.B6 | End.B6.Encaps |\n"
 		"            End.BM | End.S | End.AS | End.AM | End.BPF |\n"
 		"            End.MAP | End.M.GTP4.E | End.M.GTP6.E |\n"
-		"            End.M.GTP6.D | End.M.GTP6.D.Di }\n"
+		"            End.M.GTP6.D | End.M.GTP6.D.Di | H.M.GTP4.D }\n"
 		"OPTIONS := OPTION [ OPTIONS ]\n"
 		"OPTION := { flavors FLAVORS | srh SEG6HDR | nh4 ADDR | nh6 ADDR | iif DEV | oif DEV |\n"
 		"            table TABLEID | vrftable TABLEID | endpoint PROGNAME | MOBILE_OPTION }\n"
diff --git a/ip/iproute_lwtunnel.c b/ip/iproute_lwtunnel.c
index 1234c9f146bf..dfbe8899bca0 100644
--- a/ip/iproute_lwtunnel.c
+++ b/ip/iproute_lwtunnel.c
@@ -410,6 +410,7 @@ static const char *seg6_action_names[SEG6_LOCAL_ACTION_MAX + 1] = {
 	[SEG6_LOCAL_ACTION_END_M_GTP6_E]	= "End.M.GTP6.E",
 	[SEG6_LOCAL_ACTION_END_M_GTP6_D]	= "End.M.GTP6.D",
 	[SEG6_LOCAL_ACTION_END_M_GTP6_D_DI]	= "End.M.GTP6.D.Di",
+	[SEG6_LOCAL_ACTION_H_M_GTP4_D]		= "H.M.GTP4.D",
 };
 
 static const char *format_action_type(int action)
@@ -646,6 +647,7 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 					 int mobile_sr_plen_ok,
 					 int mobile_v6src_plen_ok,
 					 __u8 v4_mask_len,
+					 __u8 sr_prefix_len,
 					 __u8 v6_src_prefix_len,
 					 __u8 dst_len)
 {
@@ -673,6 +675,32 @@ static void seg6local_action_check_attrs(int action, int srh_ok, int nh6_ok,
 			invarg("End.M.GTP6.D.Di does not accept \"sr_prefix_len\"\n",
 			       "");
 		break;
+	case SEG6_LOCAL_ACTION_H_M_GTP4_D:
+		if (!nh6_ok || !mobile_src_ok || !mobile_v4mask_ok ||
+		    !mobile_sr_plen_ok)
+			invarg("H.M.GTP4.D requires \"nh6\", \"src\","
+			       " \"v4_mask_len\", and \"sr_prefix_len\"\n", "");
+		/*
+		 * The 128-bit egress SID built by H.M.GTP4.D packs the SR
+		 * locator, the embedded IPv4 destination, and the 40-bit
+		 * Args.Mob.Session field, so sr_prefix_len + v4_mask_len
+		 * must leave room for it.
+		 */
+		if ((unsigned int)sr_prefix_len +
+		    (unsigned int)v4_mask_len > 88)
+			invarg("H.M.GTP4.D requires \"sr_prefix_len\" +"
+			       " \"v4_mask_len\" <= 88"
+			       " (40 bits reserved for Args.Mob.Session)\n", "");
+		/*
+		 * IPv6 SA layout per RFC 9433 Section 6.6 Figure 10 reused
+		 * by H.M.GTP4.D headend; same combined bound as End.M.GTP4.E.
+		 */
+		if (mobile_v6src_plen_ok &&
+		    (unsigned int)v6_src_prefix_len +
+		    (unsigned int)v4_mask_len > 128)
+			invarg("H.M.GTP4.D requires \"v6_src_prefix_len\" +"
+			       " \"v4_mask_len\" <= 128\n", "");
+		break;
 	case SEG6_LOCAL_ACTION_END_M_GTP4_E:
 		if (!mobile_src_ok || !mobile_v4mask_ok)
 			invarg("End.M.GTP4.E requires \"src\" and \"v4_mask_len\"\n",
@@ -1795,7 +1823,8 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp,
 	seg6local_action_check_attrs(action, srh_ok, nh6_ok, mobile_src_ok,
 				     mobile_v4mask_ok, mobile_sr_plen_ok,
 				     mobile_v6src_plen_ok,
-				     v4_mask_len, v6_src_prefix_len, dst_len);
+				     v4_mask_len, sr_prefix_len,
+				     v6_src_prefix_len, dst_len);
 
 	if (srh_ok) {
 		int srhlen;
diff --git a/man/man8/ip-route.8.in b/man/man8/ip-route.8.in
index 0487338707c6..7badfcc1e8c3 100644
--- a/man/man8/ip-route.8.in
+++ b/man/man8/ip-route.8.in
@@ -1133,6 +1133,51 @@ is rejected for this action: the original outer destination is
 preserved verbatim instead of being repacked with an Args.Mob.Session
 field, so no locator length needs to be carried.
 
+.B H.M.GTP4.D nh6
+.IR ADDRESS
+.B src
+.IR ADDRESS
+.B v4_mask_len
+.IR BITS
+.B sr_prefix_len
+.IR BITS
+.RB [ "v6_src_prefix_len"
+.IR BITS ]
+- SRv6 Mobile User Plane H.M.GTP4.D headend behavior (RFC 9433 Section
+6.7).  Match an IPv4/UDP/GTP-U packet, strip the GTP-U envelope, and
+re-encapsulate the inner T-PDU in a new IPv6 header whose addresses
+encode the per-session identifiers expected by an
+.B End.M.GTP4.E
+SID at the egress SR gateway.  The destination UPF prefix template is
+specified by
+.BR nh6 ,
+the source UPF prefix template by
+.BR src ;
+.B v4_mask_len
+is the bit length reserved for the original IPv4 destination/source
+address, and
+.B sr_prefix_len
+is the locator length of the egress End.M.GTP4.E SID (1..88).
+The 40-bit Args.Mob.Session field defined
+by RFC 9433 Section 6.1 follows the embedded IPv4 destination at the
+offset implied by
+.BR sr_prefix_len " + " v4_mask_len ;
+its width is fixed by the RFC and is not exposed as a knob.
+.BR sr_prefix_len " + " v4_mask_len
+must therefore be at most 88 bits so the resulting 128-bit SID can
+hold all three fields.
+.B v6_src_prefix_len
+controls the IPv6 SA layout per RFC 9433 Section 6.6 Figure 10
+(\fIP\fR | IPv4 SA | padding) in the same way as for End.M.GTP4.E:
+1..127, and
+.BR v6_src_prefix_len " + " v4_mask_len " <= 128" ;
+defaults to 64 when omitted.
+.PP
+.B Note:
+because H.M.GTP4.D matches IPv4/GTP-U packets, the route must be
+installed on the IPv4 FIB (\fBip -4 route add ...\fR); installing it
+under \fBip -6 route\fR is rejected by the kernel.
+
 .B Flavors parameters
 
 The flavors represent additional operations that can modify or extend a
-- 
2.50.1


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

* Re: [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433)
  2026-05-03 15:30 [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
  2026-05-03 15:30 ` [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior Yuya Kusakabe
@ 2026-05-03 16:45 ` Yuya Kusakabe
  2026-05-03 21:05 ` Stephen Hemminger
  2 siblings, 0 replies; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-03 16:45 UTC (permalink / raw)
  To: stephen, dsahern; +Cc: netdev

Please ignore this thread; resent due to a local git config issue
that put the Message-IDs under the wrong domain.

Correct thread:
  https://lore.kernel.org/netdev/20260503154510.912576-1-yuya.kusakabe@gmail.com/

On Mon, May 4, 2026 at 12:30 AM Yuya Kusakabe <yuya.kusakabe@gmail.com> wrote:
>
> From: Yuya Kusakabe <yuya.kusakabe@gmail.com>
>
> This series adds the iproute2 frontend for the SRv6 Mobile User Plane
> (MUP) endpoint behaviors of RFC 9433.  It is sent in parallel with the
> matching kernel net-next series; each commit here is self-contained
> and brings in the seg6local UAPI bits it needs from the in-progress
> kernel UAPI header (include/uapi/linux/seg6_local.h):
>
>   Section 6.2  End.MAP
>   Section 6.3  End.M.GTP6.D
>   Section 6.4  End.M.GTP6.D.Di
>   Section 6.5  End.M.GTP6.E
>   Section 6.6  End.M.GTP4.E
>   Section 6.7  H.M.GTP4.D
>
> The series adds these seg6local CLI keywords:
>
>   src                  IPv6 source-address template
>   v4_mask_len          length of the IPv4 DA portion of the SID, in
>                        bits (1..32)
>   sr_prefix_len        locator length of the egress End.M.GTP*.E SID,
>                        in bits (1..88, leaving 40 bits for the
>                        Args.Mob.Session field)
>   v6_src_prefix_len    Source UPF Prefix length P in the IPv6 SA
>                        template (1..127, defaults to 64); requires
>                        P + v4_mask_len <= 128
>   pdu_type             GTP-U PDU Session Container PDU Type (3GPP
>                        TS 38.415 Section 5.5.2): downlink|dl|uplink|ul
>                        or 0..15.  When omitted, the egress emits a
>                        short GTPv1-U header (no PDU Session Container)
>                        regardless of the QFI in the SID; 5G N3
>                        deployments must set pdu_type explicitly.
>
> A small per-action attribute validator (introduced in patch 1 and
> extended by each subsequent behavior) rejects obvious typos in the
> seg6local block at the userspace layer instead of leaving the
> operator with an opaque kernel EINVAL.
>
> Link: https://datatracker.ietf.org/doc/html/rfc9433
>
> Yuya Kusakabe (6):
>   seg6: add support for the End.MAP behavior
>   seg6: add support for the End.M.GTP4.E behavior
>   seg6: add support for the End.M.GTP6.E behavior
>   seg6: add support for the End.M.GTP6.D behavior
>   seg6: add support for the End.M.GTP6.D.Di behavior
>   seg6: add support for the H.M.GTP4.D behavior
>
>  include/uapi/linux/seg6_local.h |  17 +++
>  ip/ip_common.h                  |   2 +-
>  ip/ipnexthop.c                  |   2 +-
>  ip/iproute.c                    |  14 +-
>  ip/iproute_lwtunnel.c           | 263 +++++++++++++++++++++++++++++++-
>  man/man8/ip-route.8.in          | 154 +++++++++++++++++++
>  6 files changed, 442 insertions(+), 10 deletions(-)
>
>
> base-commit: 4f5de57e2ff11a5925dacdf3deeeabee7ba9502a
> --
> 2.50.1
>


-- 
Yuya Kusakabe

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

* Re: [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433)
  2026-05-03 15:30 [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
  2026-05-03 15:30 ` [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior Yuya Kusakabe
  2026-05-03 16:45 ` [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
@ 2026-05-03 21:05 ` Stephen Hemminger
  2026-05-04  1:19   ` Yuya Kusakabe
  2 siblings, 1 reply; 10+ messages in thread
From: Stephen Hemminger @ 2026-05-03 21:05 UTC (permalink / raw)
  To: Yuya Kusakabe; +Cc: dsahern, netdev

On Mon,  4 May 2026 00:30:00 +0900
Yuya Kusakabe <yuya.kusakabe@gmail.com> wrote:

> From: Yuya Kusakabe <yuya.kusakabe@gmail.com>
> 
> This series adds the iproute2 frontend for the SRv6 Mobile User Plane
> (MUP) endpoint behaviors of RFC 9433.  It is sent in parallel with the
> matching kernel net-next series; each commit here is self-contained
> and brings in the seg6local UAPI bits it needs from the in-progress
> kernel UAPI header (include/uapi/linux/seg6_local.h):
> 
>   Section 6.2  End.MAP
>   Section 6.3  End.M.GTP6.D
>   Section 6.4  End.M.GTP6.D.Di
>   Section 6.5  End.M.GTP6.E
>   Section 6.6  End.M.GTP4.E
>   Section 6.7  H.M.GTP4.D
> 
> The series adds these seg6local CLI keywords:
> 
>   src                  IPv6 source-address template
>   v4_mask_len          length of the IPv4 DA portion of the SID, in
>                        bits (1..32)
>   sr_prefix_len        locator length of the egress End.M.GTP*.E SID,
>                        in bits (1..88, leaving 40 bits for the
>                        Args.Mob.Session field)
>   v6_src_prefix_len    Source UPF Prefix length P in the IPv6 SA
>                        template (1..127, defaults to 64); requires
>                        P + v4_mask_len <= 128
>   pdu_type             GTP-U PDU Session Container PDU Type (3GPP
>                        TS 38.415 Section 5.5.2): downlink|dl|uplink|ul
>                        or 0..15.  When omitted, the egress emits a
>                        short GTPv1-U header (no PDU Session Container)
>                        regardless of the QFI in the SID; 5G N3
>                        deployments must set pdu_type explicitly.
> 
> A small per-action attribute validator (introduced in patch 1 and
> extended by each subsequent behavior) rejects obvious typos in the
> seg6local block at the userspace layer instead of leaving the
> operator with an opaque kernel EINVAL.
> 
> Link: https://datatracker.ietf.org/doc/html/rfc9433
> 
> Yuya Kusakabe (6):
>   seg6: add support for the End.MAP behavior
>   seg6: add support for the End.M.GTP4.E behavior
>   seg6: add support for the End.M.GTP6.E behavior
>   seg6: add support for the End.M.GTP6.D behavior
>   seg6: add support for the End.M.GTP6.D.Di behavior
>   seg6: add support for the H.M.GTP4.D behavior
> 
>  include/uapi/linux/seg6_local.h |  17 +++
>  ip/ip_common.h                  |   2 +-
>  ip/ipnexthop.c                  |   2 +-
>  ip/iproute.c                    |  14 +-
>  ip/iproute_lwtunnel.c           | 263 +++++++++++++++++++++++++++++++-
>  man/man8/ip-route.8.in          | 154 +++++++++++++++++++
>  6 files changed, 442 insertions(+), 10 deletions(-)
> 
> 
> base-commit: 4f5de57e2ff11a5925dacdf3deeeabee7ba9502a

Automated AI review of iproute2 is not setup yet, but manual run showed:

Subject: Re: [PATCH iproute2-next v1 RESEND 1/6] seg6: add support for the End.MAP behavior

On Mon, 4 May 2026, Yuya Kusakabe wrote:

> +static void seg6local_action_check_attrs(int action, int nh6_ok)
> +{
> +	switch (action) {
> +	case SEG6_LOCAL_ACTION_END_MAP:
> +		if (!nh6_ok)
> +			invarg("End.MAP requires \"nh6\"\n", "");
> +		break;
> +	}
> +}

The invarg() helper expects the invalid argument string as its second parameter,
not an empty string. This should be:

			invarg("End.MAP requires \"nh6\"", argv_value);

However, since this is a validation function called after parsing and doesn't have
access to argv, a different error reporting approach is needed. Consider using
fprintf(stderr, ...) directly or restructuring to keep the argv value available.

This same issue appears in all patches in this series whenever invarg() is called
from seg6local_action_check_attrs():
- Patch 1: End.MAP validation
- Patch 2: End.M.GTP4.E validation (multiple instances)
- Patch 3: End.M.GTP6.E validation
- Patch 4: End.M.GTP6.D validation
- Patch 5: End.M.GTP6.D.Di validation
- Patch 6: H.M.GTP4.D validation

The rest of the implementation looks good:
- Correctly uses strcmp() instead of matches() for new argument parsing
- Properly uses print_XXX() helpers with PRINT_ANY for JSON output
- Includes proper documentation updates

Please fix the invarg() usage throughout the series.

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

* Re: [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433)
  2026-05-03 21:05 ` Stephen Hemminger
@ 2026-05-04  1:19   ` Yuya Kusakabe
  0 siblings, 0 replies; 10+ messages in thread
From: Yuya Kusakabe @ 2026-05-04  1:19 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Yuya Kusakabe, David Ahern, netdev

On Sun, 03 May 2026 14:05:47 -0700, Stephen Hemminger wrote:
> Automated AI review of iproute2 is not setup yet, but manual run showed:
> ...
> Please fix the invarg() usage throughout the series.

Thanks for running the AI review against this series.

v2 will drop the seg6local_action_check_attrs() helper entirely
instead of fixing the invarg() call sites, matching how every other
seg6local action lets the kernel return EINVAL via netlink extack.
v2 will be posted once the 24-hour repost window has elapsed.

Thanks,
Yuya

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

end of thread, other threads:[~2026-05-04  1:19 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-03 15:30 [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
2026-05-03 15:30 ` [PATCH iproute2-next 1/6] seg6: add support for the End.MAP behavior Yuya Kusakabe
2026-05-03 15:30   ` [PATCH iproute2-next 2/6] seg6: add support for the End.M.GTP4.E behavior Yuya Kusakabe
2026-05-03 15:30     ` [PATCH iproute2-next 3/6] seg6: add support for the End.M.GTP6.E behavior Yuya Kusakabe
2026-05-03 15:30       ` [PATCH iproute2-next 4/6] seg6: add support for the End.M.GTP6.D behavior Yuya Kusakabe
2026-05-03 15:30         ` [PATCH iproute2-next 5/6] seg6: add support for the End.M.GTP6.D.Di behavior Yuya Kusakabe
2026-05-03 15:30           ` [PATCH iproute2-next 6/6] seg6: add support for the H.M.GTP4.D behavior Yuya Kusakabe
2026-05-03 16:45 ` [PATCH iproute2-next 0/6] seg6: SRv6 Mobile User Plane (RFC 9433) Yuya Kusakabe
2026-05-03 21:05 ` Stephen Hemminger
2026-05-04  1:19   ` Yuya Kusakabe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox