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 --- include/uapi/linux/seg6_local.h | 3 +++ ip/iproute.c | 6 ++++-- ip/iproute_lwtunnel.c | 45 ++++++++++++++++++++++++++++++++++++++--- man/man8/ip-route.8.in | 29 ++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 5 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 e009e7480e76..6cd19c2c2b00 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 38e806b053c5..a63dfe379a89 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,6 +622,22 @@ 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 print_encap_mpls(FILE *fp, struct rtattr *encap) { struct rtattr *tb[MPLS_IPTUNNEL_MAX+1]; @@ -1489,7 +1511,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; @@ -1497,7 +1519,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) { @@ -1621,6 +1643,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++) @@ -1680,7 +1719,7 @@ static int parse_encap_seg6local(struct rtattr *rta, size_t len, int *argcp, 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