[PATCH iproute2-next 3/6] seg6: add support for the End.M.GTP6.E behavior

Yuya Kusakabe
2026-05-03T15:30:46Z

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).