Netdev List
 help / color / mirror / Atom feed
From: Ido Schimmel <idosch@nvidia.com>
To: Zhiling Zou <zhilinz@nebusec.ai>
Cc: netdev@vger.kernel.org, dsahern@kernel.org, davem@davemloft.net,
	edumazet@google.com, kuba@kernel.org, pabeni@redhat.com,
	horms@kernel.org, atenart@kernel.org, yuehaibing@huawei.com,
	kuniyu@google.com, kees@kernel.org, kylebot@openai.com,
	thorsten.blum@linux.dev, maoyixie.tju@gmail.com, vega@nebusec.ai
Subject: Re: [PATCH net v4 1/2] ip6_gre: fix hardware header length for NBMA tunnels
Date: Mon, 17 Aug 2026 09:42:50 +0300	[thread overview]
Message-ID: <20260817064250.GA196908@shredder> (raw)
In-Reply-To: <64b46542bbe1701f07702aaa50273e2a87903db5.1786542637.git.zhilinz@nebusec.ai>

On Thu, Aug 13, 2026 at 12:22:34AM +0800, Zhiling Zou wrote:
> ip6gre_tnl_link_config_route() accumulates the lower device's hardware
> header length into dev->hard_header_len whenever header_ops is set. This
> is incorrect for both users of header_ops.
> 
> ip6gretap and ip6erspan have a fixed Ethernet hardware header length.
> For an NBMA ip6gre tunnel, ip6gre_header() creates only the GRE header,
> the optional FOU or GUE header, and the outer IPv6 header. The lower
> device header is headroom needed later, not part of the tunnel device's
> hardware header.
> 
> Keep the lower device header in needed_headroom. Set hard_header_len to
> the tunnel header length only for ARPHRD_IP6GRE devices with header_ops,
> and leave the fixed Ethernet header length unchanged for tap and erspan
> devices.
> 
> Fixes: 832ba596494b ("net: ip6_gre: set dev->hard_header_len when using header_ops")
> Cc: stable@vger.kernel.org
> Reported-by: Vega <vega@nebusec.ai>

It wasn't reported by Vega.

> Suggested-by: Ido Schimmel <idosch@nvidia.com>
> Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>

Reviewed-by: Ido Schimmel <idosch@nvidia.com>

You are expected to reply to AI feedback.

"
Patch authors are expected to proactively look into the AI-generated
reviews and handle such feedback as any other kind of review: either
debate it or address it. In both cases a reply on the mailing list is
expected.
"

https://docs.kernel.org/next/process/maintainer-netdev.html#review-timelines

> ---
>  net/ipv6/ip6_gre.c | 13 ++++---------
>  1 file changed, 4 insertions(+), 9 deletions(-)
> 
> diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
> index b843116e9b703..70c1710910203 100644
> --- a/net/ipv6/ip6_gre.c
> +++ b/net/ipv6/ip6_gre.c
> @@ -1137,13 +1137,8 @@ static void ip6gre_tnl_link_config_route(struct ip6_tnl *t, int set_mtu,
>  			return;
>  
>  		if (rt->dst.dev) {
> -			unsigned short dst_len = rt->dst.dev->hard_header_len +
> -						 t_hlen;
> -
> -			if (t->dev->header_ops)
> -				dev->hard_header_len = dst_len;
> -			else
> -				dev->needed_headroom = dst_len;
> +			dev->needed_headroom = rt->dst.dev->hard_header_len +
> +					       t_hlen;

"
Since dev->hard_header_len was already set to t_hlen in ip6gre_calc_hlen()
for NBMA tunnels, doesn't adding t_hlen here double-count the tunnel header
length?
"

No. ip6gre_tnl_link_config_route() is a NOP for NBMA tunnels since they
don't have IP6_TNL_F_CAP_XMIT set.

>  
>  			if (set_mtu) {
>  				int mtu = rt->dst.dev->mtu - t_hlen;
> @@ -1171,8 +1166,8 @@ static int ip6gre_calc_hlen(struct ip6_tnl *tunnel)
>  
>  	t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
>  
> -	if (tunnel->dev->header_ops)
> -		tunnel->dev->hard_header_len = LL_MAX_HEADER + t_hlen;
> +	if (tunnel->dev->header_ops && tunnel->dev->type == ARPHRD_IP6GRE)
> +		tunnel->dev->hard_header_len = t_hlen;
>  	else
>  		tunnel->dev->needed_headroom = LL_MAX_HEADER + t_hlen;

"
By removing the LL_MAX_HEADER fallback for NBMA ip6gre tunnels, what happens
if rt6_lookup() fails in ip6gre_tnl_link_config_route() (which is expected
for NBMA tunnels with an ANY remote address)?
"

ip6gre_tnl_link_config_route() is not executed for NBMA tunnels.

"
Without the fallback, dev->needed_headroom would remain 0, and the total
allocated headroom would just be t_hlen. When transmitted, the packet will
require t_hlen + physical_dev->hard_header_len bytes, which would miss the
physical device's headroom and force a reallocation and copy via
pskb_expand_head() on every transmitted packet.
"

dev->needed_headroom was 0 even before the patch, but it's irrelevant
since NBMA tunnels seem to be completely broken. See below.

"
This isn't a bug introduced by this patch, but I noticed NBMA IPv6 GRE 
tunnels might be double-encapsulating packets and corrupting headers.

In net/ipv6/ip6_gre.c:__gre6_xmit(), the code relies on the dummy header
pushed by ip6gre_header() to read the destination address:

    if (dev->header_ops && dev->type == ARPHRD_IP6GRE)
        fl6->daddr = ((struct ipv6hdr *)skb->data)->daddr;

However, the dummy header is not pulled before pushing the real outer GRE
and IPv6 headers. Doesn't this leave the dummy headers in the payload,
causing double-encapsulation ([Real IPv6] [Real GRE] [Dummy IPv6]
[Dummy GRE] [Payload])?
"

This does look buggy, but NBMA tunnels are completely broken.
ip6gre_tunnel_xmit() is passing the wrong addresses to
ip6_tnl_xmit_ctl(), so it is always returning 0 and ip6gre_tunnel_xmit()
is dropping the packets. ip6gre_tunnel_xmit() is deriving the addresses
from the tunnel configuration (ANY) instead of from the packet, as
should be done with an NBMA tunnel.

And:

"
This is also a pre-existing issue, but looking at 
net/ipv6/ip6_gre.c:ip6gre_header():

    ipv6h = skb_push(skb, needed);
    ...
    p = (__be16 *)(ipv6h + 1);
    p[0] = ip_tunnel_flags_to_be16(t->parms.o_flags);
    p[1] = htons(type);

This function allocates the full header length via skb_push() and 
initializes the IPv6 header and the first 4 bytes of the GRE header.

Since the remaining bytes of t->hlen (up to 24 bytes for GRE keys, 
checksums, and sequence) are not initialized, and __gre6_xmit() fails 
to pull this dummy header, does this leak uninitialized kernel heap memory 
to the network in every packet?
"

Again, these packets don't even reach __gre6_xmit(), so they don't end
up on the wire.

"
This isn't a bug introduced by this patch, but now that hard_header_len is
set to exactly t_hlen, does it make the disagreement with ip6gre_header()
explicit?
"

Yes, it's returning the wrong value (needs to return 'needed'), but it's
pre-existing and NBMA tunnels wouldn't work even if this is fixed.

  reply	other threads:[~2026-08-17  6:43 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12 16:22 [PATCH net v4 0/2] net: fix IP6GRE header length before capping tunnel headroom Zhiling Zou
2026-08-12 16:22 ` [PATCH net v4 1/2] ip6_gre: fix hardware header length for NBMA tunnels Zhiling Zou
2026-08-17  6:42   ` Ido Schimmel [this message]
2026-08-12 16:22 ` [PATCH net v4 2/2] net: cap advertised IP tunnel headroom Zhiling Zou
2026-08-17  6:43   ` Ido Schimmel

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=20260817064250.GA196908@shredder \
    --to=idosch@nvidia.com \
    --cc=atenart@kernel.org \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kees@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=kylebot@openai.com \
    --cc=maoyixie.tju@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=thorsten.blum@linux.dev \
    --cc=vega@nebusec.ai \
    --cc=yuehaibing@huawei.com \
    --cc=zhilinz@nebusec.ai \
    /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