Linux Documentation
 help / color / mirror / Atom feed
From: Andrea Mayer <andrea@common-net.org>
To: Yuya Kusakabe <yuya.kusakabe@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	David Ahern <dsahern@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	Justin Iurman <justin.iurman@gmail.com>,
	Shuah Khan <shuah@kernel.org>, Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
	linux-kselftest@vger.kernel.org, linux-doc@vger.kernel.org,
	stefano.salsano@uniroma2.it, ahabdels@cisco.com,
	Andrea Mayer <andrea.mayer@uniroma2.it>,
	andrea@common-net.org
Subject: Re: [PATCH v2 3/7] seg6: add End.M.GTP6.E behavior
Date: Fri, 5 Jun 2026 03:20:01 +0200	[thread overview]
Message-ID: <20260605032001.2f46e6a55f69896d29da69df@common-net.org> (raw)
In-Reply-To: <20260505-seg6-mobile-v2-3-9e8022bdfdb6@gmail.com>

On Tue, 05 May 2026 01:30:13 +0900
Yuya Kusakabe <yuya.kusakabe@gmail.com> wrote:

Hi Yuya,

I do not repeat below the points from my cover letter and patch 1-2 replies
(drop reasons, OIF/VRF removal, C helper, coding style, etc.).

> Add the End.M.GTP6.E behavior (RFC 9433 Section 6.5), the IPv6 dual
> of End.M.GTP4.E.  An End.M.GTP6.E SID always sits in the penultimate
> position of an SR Policy (RFC 9433 Section 6.5 Notes); when it
> becomes the active SID (segments_left == 1) the kernel pops the
> IPv6/SRH outer, recovers TEID and QFI from the 40-bit
> Args.Mob.Session field encoded in the locator-relative slice of the
> SID, and re-encapsulates the inner T-PDU in IPv6/UDP/GTP-U toward
> the next segment held in SRH[0].
> 
> The flow info, traffic class and hop limit are propagated from the
> inbound IPv6 outer to the new outer (RFC 6040).
> 
> When net.netfilter.nf_hooks_lwtunnel=1, the inner T-PDU traverses
> NF_INET_PRE_ROUTING between the SRv6 strip and the GTP-U push,
> mirroring End.DX4 / End.DX6.
> 
> Configuration:
> 
>   ip -6 route add 2001:db8:e::/64 \
>       encap seg6local action End.M.GTP6.E src 2001:db8:2::1 \
>       dev <dev>

SEG6_LOCAL_MOBILE_SRC_ADDR (the "src" attribute) is copied verbatim into
the outer IPv6 source address. In patch 2 (End.M.GTP4.E) the same
attribute is used as a template from which bits are extracted to form
the IPv4 source address, and may be entirely unused depending on
v4_mask_len.
This UAPI overload needs revision.

>
> Link: https://www.rfc-editor.org/rfc/rfc9433.html#section-6.5
> Link: https://www.rfc-editor.org/rfc/rfc6040
> Signed-off-by: Yuya Kusakabe <yuya.kusakabe@gmail.com>
> ---
>  include/uapi/linux/seg6_local.h                    |   2 +
>  net/ipv6/seg6_local.c                              | 312 ++++++++++++++++
>  tools/testing/selftests/net/Makefile               |   1 +
>  .../selftests/net/srv6_end_m_gtp6_e_test.sh        | 402 +++++++++++++++++++++
>  4 files changed, 717 insertions(+)
> 
> diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h
> index b42cb526bb81..8e46ede2980d 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/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
> index 4051fe89e6d1..4e5d138c3657 100644
> --- a/net/ipv6/seg6_local.c
> +++ b/net/ipv6/seg6_local.c

> + [snip]

> +static int input_action_end_m_gtp6_e_finish(struct net *net,
> +					    struct sock *sk,
> +					    struct sk_buff *skb)
> +{
> +	enum skb_drop_reason reason = SKB_DROP_REASON_SEG6_MOBILE_NOMEM;
> +	struct seg6_mobile_gtp6_e_cb cb = *SEG6_MOBILE_GTP6_E_CB(skb);
> +	struct dst_entry *orig_dst = skb_dst(skb);
> +	const struct seg6_mobile_info *minfo;
> +	struct seg6_local_lwt *slwt;
> +	struct ipv6hdr *new_ip6h;
> +	struct udphdr *uh;
> +
> +	slwt = seg6_local_lwtunnel(orig_dst->lwtstate);
> +	minfo = &slwt->mobile_info;
> +

Same dst/lwtstate issue as patch 2.

> +	/* Reject GSO packets that would not fit the egress IPv6/UDP/GTP-U
> +	 * path after our outer headers are added; the GSO segmenter cannot
> +	 * adjust mss across SRv6 -> GTP-U conversion.  Skip the check
> +	 * entirely when no MTU is known on the current dst.
> +	 */
> +	if (skb_is_gso(skb)) {
> +		unsigned int ovhd = sizeof(*new_ip6h) + sizeof(*uh) +
> +				    sizeof(struct gtp1_header_long) +
> +				    sizeof(struct seg6_mobile_pdu_session_ext);
> +		unsigned int mtu = dst_mtu(skb_dst(skb));
> +
> +		if (mtu && (mtu <= ovhd ||
> +			    !skb_gso_validate_network_len(skb, mtu - ovhd))) {
> +			reason = SKB_DROP_REASON_SEG6_MOBILE_MTU_EXCEEDED;
> +			goto drop;
> +		}
> +	}
> +
> +	/* Reserve worst-case headroom for the entire outer chain we are about
> +	 * to push: IPv6 + UDP + GTP-U long header + PDU Session extension.
> +	 * Subsequent skb_cow_head() calls inside seg6_mobile_push_gtpu() then
> +	 * become no-ops.
> +	 */
> +	if (skb_cow_head(skb,
> +			 sizeof(*new_ip6h) + sizeof(*uh) +
> +			 sizeof(struct gtp1_header_long) +
> +			 sizeof(struct seg6_mobile_pdu_session_ext)))

Same ovhd scoping point as patch 2.

> +		goto drop;
> +

Same missing iptunnel_handle_offloads() as patch 2.

> +	if (seg6_mobile_push_gtpu(skb, cb.teid, cb.qfi, cb.pdu_type,
> +				  cb.pdu_type_set))
> +		goto drop;
> +
> +	uh = skb_push(skb, sizeof(*uh));
> +	skb_reset_transport_header(skb);
> +	uh->source = htons(GTP1U_PORT);
> +	uh->dest = htons(GTP1U_PORT);
> +	uh->len = htons(skb->len);
> +

Same fixed source port question as patch 2.

> +	new_ip6h = skb_push(skb, sizeof(*new_ip6h));
> +	skb_reset_network_header(skb);
> +	memset(new_ip6h, 0, sizeof(*new_ip6h));
> +	ip6_flow_hdr(new_ip6h, cb.tclass, cb.flowlabel);
> +	new_ip6h->payload_len = htons(skb->len - sizeof(*new_ip6h));
> +	new_ip6h->nexthdr = IPPROTO_UDP;
> +	new_ip6h->hop_limit = cb.hop_limit;
> +	new_ip6h->saddr = minfo->src_addr;
> +	new_ip6h->daddr = cb.next_sid;
> +
> +	/* RFC 8200 requires UDP/IPv6 checksums.  Initialise the
> +	 * pseudo-header sum and let the stack/NIC complete it via
> +	 * CHECKSUM_PARTIAL so we do not pay a per-packet linear sum and
> +	 * we cooperate with offload.
> +	 */
> +	skb->ip_summed = CHECKSUM_PARTIAL;
> +	skb->csum_start = (unsigned char *)uh - skb->head;
> +	skb->csum_offset = offsetof(struct udphdr, check);
> +	uh->check = ~csum_ipv6_magic(&new_ip6h->saddr, &new_ip6h->daddr,
> +				     skb->len - sizeof(*new_ip6h),
> +				     IPPROTO_UDP, 0);
> +

udp6_set_csum() already handles the CHECKSUM_PARTIAL + pseudo-header seed
setup and also covers the GSO case. Using it would avoid open-coding this
sequence.

> +	skb->protocol = htons(ETH_P_IPV6);
> +	nf_reset_ct(skb);
> +	skb_dst_drop(skb);
> +
> +	seg6_lookup_any_nexthop(skb, &cb.next_sid, 0, false, slwt->oif);
> +	return dst_input(skb);
> +
> +drop:
> +	kfree_skb_reason(skb, reason);
> +	return -EINVAL;
> +}

seg6_lookup_any_nexthop() already calls skb_dst_drop() internally. The
explicit call above is redundant.

> + [snip]

> +static int input_action_end_m_gtp6_e(struct sk_buff *skb,
> +				     struct seg6_local_lwt *slwt)
> +{
> +	enum skb_drop_reason reason = SKB_DROP_REASON_SEG6_MOBILE_BAD_SID;
> +	const struct seg6_mobile_info *minfo = &slwt->mobile_info;
> +	struct seg6_mobile_gtp6_e_cb *cb;
> +	struct in6_addr next_sid;
> +	struct ipv6_sr_hdr *srh;
> +	u8 hop_limit, tclass, qfi;
> +	unsigned int outer_len;
> +	struct ipv6hdr *ip6h;
> +	int inner_nfproto;
> +	__be32 flowlabel;
> +	__be16 frag_off;
> +	u64 args_mob;
> +	u32 teid;
> +	int off;
> +	u8 nh;
> +

Same reverse Christmas tree issue as patch 2.

> + [snip]

> +	/* RFC 6040 outer-to-outer propagation: copy DSCP+ECN (tclass) and
> +	 * the flow label from the SRv6 outer to the new IPv6 outer.  Use
> +	 * ip6_flowlabel() (not ip6_flowinfo()) so the tclass byte is
> +	 * supplied exactly once via the @tclass argument of ip6_flow_hdr().
> +	 */
> +	flowlabel = ip6_flowlabel(ip6h);
> +	tclass = ipv6_get_dsfield(ip6h);
> +	hop_limit = ip6h->hop_limit;
> +

Same RFC 6040 question as patch 2 (here also flow label).

> +	/* RFC 9433 Section 6.5 upper-layer S02 mandates "Pop the IPv6
> +	 * header and all its extension headers".  ipv6_skip_exthdr()
> +	 * walks every extension header (HBH/Routing/Dest-Opts/Fragment)
> +	 * so HBH-before-SRH and DOpts-after-SRH are handled too.  The
> +	 * terminal next-header value also selects NFPROTO_IPV4 /
> +	 * NFPROTO_IPV6 for the NF_INET_PRE_ROUTING hook below.
> +	 */
> +	nh = ip6h->nexthdr;
> +	off = ipv6_skip_exthdr(skb, sizeof(*ip6h), &nh, &frag_off);
> +	if (off < 0) {
> +		reason = SKB_DROP_REASON_SEG6_MOBILE_BAD_INNER;
> +		goto drop;
> +	}
> +	outer_len = off;
> +

Same BAD_INNER misuse as patch 2.

Same frag_off check missing after ipv6_skip_exthdr() as patch 2.

> + [snip]

> +	/* For inner IP traffic that may traverse NF_INET_PRE_ROUTING below,
> +	 * pull the full inner IP header into the linear area so a netfilter
> +	 * hook reading skb_transport_header() does not access stale data.
> +	 * Non-IP inner is forwarded as-is via the GTP-U T-PDU payload.
> +	 */
> +	if (!pskb_may_pull(skb, outer_len + ((inner_nfproto == NFPROTO_IPV4) ?
> +					     sizeof(struct iphdr) :
> +					     (inner_nfproto == NFPROTO_IPV6) ?
> +					     sizeof(struct ipv6hdr) : 0))) {
> +		reason = SKB_DROP_REASON_SEG6_MOBILE_BAD_INNER;
> +		goto drop;
> +	}
> +

Same repeated ternary as patch 2.

> + [snip]

>  static struct seg6_action_desc seg6_action_table[] = {
>  	{
> @@ -2153,6 +2431,17 @@ static struct seg6_action_desc seg6_action_table[] = {
>  			.build_state = seg6_mobile_v4_validate,
>  		},
>  	},
> +	{
> +		.action		= SEG6_LOCAL_ACTION_END_M_GTP6_E,
> +		.attrs		= SEG6_F_ATTR(SEG6_LOCAL_MOBILE_SRC_ADDR),
> +		.optattrs	= SEG6_F_LOCAL_COUNTERS |
> +				  SEG6_F_ATTR(SEG6_LOCAL_MOBILE_PDU_TYPE) |
> +				  SEG6_F_ATTR(SEG6_LOCAL_OIF),
> +		.input		= input_action_end_m_gtp6_e,
> +		.slwt_ops	= {
> +			.build_state = seg6_mobile_gtp6_e_validate,
> +		},
> +	},

> + [snip]

> +/* End.M.GTP6.E SID layout (RFC 9433 Section 6.5):
> + *
> + *   | locator (route prefix)  | Args.Mob.Session (40) | pad |
> + *
> + * The locator length is the route's IPv6 destination prefix length.
> + * Reject route additions whose prefix leaves no room for the 40-bit
> + * Args.Mob.Session field at setup time so the operator gets a clear
> + * error from `ip route add` instead of silent per-packet drops.
> + */
> +static int seg6_mobile_gtp6_e_validate(struct seg6_local_lwt *slwt,
> +				       const void *cfg,
> +				       struct netlink_ext_ack *extack)
> +{
> +	const struct fib6_config *fib6_cfg = cfg;
> +
> +	if ((unsigned int)fib6_cfg->fc_dst_len + SEG6_MOBILE_ARGS_MOB_LEN > 128) {

Nit: fc_dst_len is int in struct fib6_config (IPv6 prefix length, range
0..128); the (unsigned int) cast is not needed.

> + [snip]

Thanks,

Ciao,
Andrea

P.S. I am temporarily writing from another address due to a mail
delivery issue at my @uniroma2.it address. Please always Cc my default
andrea.mayer@uniroma2.it address on replies.

  reply	other threads:[~2026-06-05  1:27 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-04 16:30 [PATCH v2 0/7] seg6: add SRv6 Mobile User Plane (RFC 9433) behaviors Yuya Kusakabe
2026-05-04 16:30 ` [PATCH v2 1/7] seg6: add End.MAP behavior Yuya Kusakabe
2026-05-19  1:31   ` Andrea Mayer
2026-05-25  1:44     ` Yuya Kusakabe
2026-05-04 16:30 ` [PATCH v2 2/7] seg6: add End.M.GTP4.E behavior Yuya Kusakabe
2026-05-27  1:09   ` Andrea Mayer
2026-06-11  2:59     ` Yuya Kusakabe
2026-05-04 16:30 ` [PATCH v2 3/7] seg6: add End.M.GTP6.E behavior Yuya Kusakabe
2026-06-05  1:20   ` Andrea Mayer [this message]
2026-06-12  3:14     ` Yuya Kusakabe
2026-05-04 16:30 ` [PATCH v2 4/7] seg6: add End.M.GTP6.D behavior Yuya Kusakabe
2026-06-07  0:05   ` Andrea Mayer
2026-05-04 16:30 ` [PATCH v2 5/7] seg6: add End.M.GTP6.D.Di behavior Yuya Kusakabe
2026-06-07 14:01   ` Andrea Mayer
2026-05-04 16:30 ` [PATCH v2 6/7] seg6: add H.M.GTP4.D behavior Yuya Kusakabe
2026-05-04 16:30 ` [PATCH v2 7/7] Documentation: networking: add seg6_mobile guide Yuya Kusakabe
2026-05-04 23:39 ` [PATCH v2 0/7] seg6: add SRv6 Mobile User Plane (RFC 9433) behaviors Jakub Kicinski
2026-05-05  1:22   ` Yuya Kusakabe
2026-05-05  1:28     ` Jakub Kicinski
2026-05-08  1:32       ` Andrea Mayer
2026-05-09  1:53         ` Yuya Kusakabe
2026-05-10 12:02           ` Yuya Kusakabe
2026-05-16 16:25 ` Andrea Mayer
2026-05-20  3:12   ` Yuya Kusakabe
2026-06-08  0:39     ` Andrea Mayer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260605032001.2f46e6a55f69896d29da69df@common-net.org \
    --to=andrea@common-net.org \
    --cc=ahabdels@cisco.com \
    --cc=andrea.mayer@uniroma2.it \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=justin.iurman@gmail.com \
    --cc=kuba@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=shuah@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=stefano.salsano@uniroma2.it \
    --cc=yuya.kusakabe@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox