diff mbox series

[net-next] net: skip offload for NETIF_F_IPV6_CSUM if ipv6 header contains extension

Message ID 0dc0c2af98e96b1df20bd36aeaed4eb4e27d507e.1728056028.git.benoit.monin@gmx.fr (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series [net-next] net: skip offload for NETIF_F_IPV6_CSUM if ipv6 header contains extension | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 6 this patch: 6
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers success CCed 4 of 4 maintainers
netdev/build_clang success Errors and warnings before: 6 this patch: 6
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 10 this patch: 10
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 16 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 79 this patch: 79
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-10-06--15-00 (tests: 775)

Commit Message

Benoît Monin Oct. 4, 2024, 3:45 p.m. UTC
Devices with NETIF_F_IP_CSUM capability can checksum TCP and UDP over
IPv4 with an IP header that may contains options; whereas devices with
NETIF_F_IPV6_CSUM capability can only checksum TCP and UDP over IPv6 if
the IP header does not contains extension.

Enforce that in skb_csum_hwoffload_help by checking the network header
length in the case where the IP header version is 6. We cannot simply
rely on the network header length since the IPv4 header can from 20 to
60 bytes whereas the IPv6 header must be 40 bytes. So we check the
version field which is common to IPv4 and IPv6 headers.

This fixes checksumming errors seen with ip6_tunnel and fou6
encapsulation, for example with GRE-in-UDP over IPv6:
* fou6 adds a UDP header with a partial checksum if the inner packet
does not contains a valid checksum.
* ip6_tunnel adds an IPv6 header with a destination option extension
header if encap_limit is non-zero (the default value is 4).

Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
---
 net/core/dev.c | 4 ++++
 1 file changed, 4 insertions(+)

Comments

Willem de Bruijn Oct. 7, 2024, 12:10 a.m. UTC | #1
Benoît Monin wrote:
> Devices with NETIF_F_IP_CSUM capability can checksum TCP and UDP over
> IPv4 with an IP header that may contains options; whereas devices with
> NETIF_F_IPV6_CSUM capability can only checksum TCP and UDP over IPv6 if
> the IP header does not contains extension.

Are both these statements universally true across devices?

I can believe for NETIF_F_IP_CSUM that this is the definition, and
that devices that cannot handle options must fix it up indivually in
ndo_features_check.

And same for NETIF_F_IPV6_CSUM with extension headers.

But it would be good to see where this is asserted in the code, or
examples of drivers that have to perform such actions.

> Enforce that in skb_csum_hwoffload_help by checking the network header
> length in the case where the IP header version is 6. We cannot simply
> rely on the network header length since the IPv4 header can from 20 to
> 60 bytes whereas the IPv6 header must be 40 bytes. So we check the
> version field which is common to IPv4 and IPv6 headers.
> 
> This fixes checksumming errors seen with ip6_tunnel and fou6
> encapsulation, for example with GRE-in-UDP over IPv6:
> * fou6 adds a UDP header with a partial checksum if the inner packet
> does not contains a valid checksum.

Where in the code is this conditional on the inner packet csum?

> * ip6_tunnel adds an IPv6 header with a destination option extension
> header if encap_limit is non-zero (the default value is 4).


If this is a fix, we'll need to target net and best effort find a
suitable fixes tag.
 
> Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> ---
>  net/core/dev.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/net/core/dev.c b/net/core/dev.c
> index ea5fbcd133ae..199831d86ec1 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
>  		return 0;
> 
>  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> +		if (ip_hdr(skb)->version == 6 &&
> +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> +			goto sw_checksum;
>  		switch (skb->csum_offset) {
>  		case offsetof(struct tcphdr, check):
>  		case offsetof(struct udphdr, check):
> @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
>  		}
>  	}
> 
> +sw_checksum:
>  	return skb_checksum_help(skb);
>  }
>  EXPORT_SYMBOL(skb_csum_hwoffload_help);
Benoît Monin Oct. 7, 2024, 2:54 p.m. UTC | #2
07/10/2024 Willem de Bruijn wrote :
> Benoît Monin wrote:
> > Devices with NETIF_F_IP_CSUM capability can checksum TCP and UDP over
> > IPv4 with an IP header that may contains options; whereas devices with
> > NETIF_F_IPV6_CSUM capability can only checksum TCP and UDP over IPv6 if
> > the IP header does not contains extension.
> 
> Are both these statements universally true across devices?
> 
> I can believe for NETIF_F_IP_CSUM that this is the definition, and
> that devices that cannot handle options must fix it up indivually in
> ndo_features_check.
> 
> And same for NETIF_F_IPV6_CSUM with extension headers.
> 
> But it would be good to see where this is asserted in the code, or
> examples of drivers that have to perform such actions.
> 
I was referring to the documentation in skbuff.h that describes 
NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM.

For NETIF_F_IPV6_CSUM, at least fsl_dpa and r8169 expect 
ipv6_hdr(skb)->nexthdr to be IPPROTO_{TCP,UDP} to compute the correct 
checksum for IPv6.

I posted more details about the problem I am trying to fix with this 
patch in the following thread: 
https://lore.kernel.org/netdev/26548921.1r3eYUQgxm@benoit.monin/T/#u

> > Enforce that in skb_csum_hwoffload_help by checking the network header
> > length in the case where the IP header version is 6. We cannot simply
> > rely on the network header length since the IPv4 header can from 20 to
> > 60 bytes whereas the IPv6 header must be 40 bytes. So we check the
> > version field which is common to IPv4 and IPv6 headers.
> > 
> > This fixes checksumming errors seen with ip6_tunnel and fou6
> > encapsulation, for example with GRE-in-UDP over IPv6:
> > * fou6 adds a UDP header with a partial checksum if the inner packet
> > does not contains a valid checksum.
> 
> Where in the code is this conditional on the inner packet csum?
> 
This is done by udp6_set_csum, which called by fou6_build_udp.

> > * ip6_tunnel adds an IPv6 header with a destination option extension
> > header if encap_limit is non-zero (the default value is 4).
> 
> 
> If this is a fix, we'll need to target net and best effort find a
> suitable fixes tag.
>  
I guess the particular problem I have found is present since the merge 
of fou6 in 4.7, but it might not be the only code path to create an 
IPv6 packet with an extension header and a partial checksum.

> > Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> > ---
> >  net/core/dev.c | 4 ++++
> >  1 file changed, 4 insertions(+)
> > 
> > diff --git a/net/core/dev.c b/net/core/dev.c
> > index ea5fbcd133ae..199831d86ec1 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> >  		return 0;
> > 
> >  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> > +		if (ip_hdr(skb)->version == 6 &&
> > +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> > +			goto sw_checksum;
> >  		switch (skb->csum_offset) {
> >  		case offsetof(struct tcphdr, check):
> >  		case offsetof(struct udphdr, check):
> > @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> >  		}
> >  	}
> > 
> > +sw_checksum:
> >  	return skb_checksum_help(skb);
> >  }
> >  EXPORT_SYMBOL(skb_csum_hwoffload_help);
>
Willem de Bruijn Oct. 7, 2024, 8:44 p.m. UTC | #3
Benoît Monin wrote:
> 07/10/2024 Willem de Bruijn wrote :
> > Benoît Monin wrote:
> > > Devices with NETIF_F_IP_CSUM capability can checksum TCP and UDP over
> > > IPv4 with an IP header that may contains options; whereas devices with
> > > NETIF_F_IPV6_CSUM capability can only checksum TCP and UDP over IPv6 if
> > > the IP header does not contains extension.
> > 
> > Are both these statements universally true across devices?
> > 
> > I can believe for NETIF_F_IP_CSUM that this is the definition, and
> > that devices that cannot handle options must fix it up indivually in
> > ndo_features_check.
> > 
> > And same for NETIF_F_IPV6_CSUM with extension headers.
> > 
> > But it would be good to see where this is asserted in the code, or
> > examples of drivers that have to perform such actions.
> > 
> I was referring to the documentation in skbuff.h that describes 
> NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM.

Excellent. I had missed that when trying to find the source.
Can you add that pointer to the commit message?
 
> For NETIF_F_IPV6_CSUM, at least fsl_dpa and r8169 expect 
> ipv6_hdr(skb)->nexthdr to be IPPROTO_{TCP,UDP} to compute the correct 
> checksum for IPv6.
> 
> I posted more details about the problem I am trying to fix with this 
> patch in the following thread: 
> https://lore.kernel.org/netdev/26548921.1r3eYUQgxm@benoit.monin/T/#u

And I had missed this thread entirely too. It's very helpful.

Please add a Link: tag that refers to it.
 
> > > Enforce that in skb_csum_hwoffload_help by checking the network header
> > > length in the case where the IP header version is 6. We cannot simply
> > > rely on the network header length since the IPv4 header can from 20 to
> > > 60 bytes whereas the IPv6 header must be 40 bytes. So we check the
> > > version field which is common to IPv4 and IPv6 headers.
> > > 
> > > This fixes checksumming errors seen with ip6_tunnel and fou6
> > > encapsulation, for example with GRE-in-UDP over IPv6:
> > > * fou6 adds a UDP header with a partial checksum if the inner packet
> > > does not contains a valid checksum.
> > 
> > Where in the code is this conditional on the inner packet csum?
> > 
> This is done by udp6_set_csum, which called by fou6_build_udp.

        else if (skb->ip_summed == CHECKSUM_PARTIAL) {
                uh->check = 0;
                uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb));
                if (uh->check == 0)
                        uh->check = CSUM_MANGLED_0;
        } else {
                skb->ip_summed = CHECKSUM_PARTIAL;
                skb->csum_start = skb_transport_header(skb) - skb->head;
                skb->csum_offset = offsetof(struct udphdr, check);
                uh->check = ~udp_v6_check(len, saddr, daddr, 0);
        }

It either leaves the inner header as CHECKSUM_PARTIAL, and fills in
the outer header entirely, based on knowledge that the inner header
will add up to zero (local checksum offload).

Or it assumes that the inner header is already filled in (whether
computed or CSUM_MANGLED_0) and then uses CHECKSUM_PARTIAL offloading
for the outer packet.

The issue you are seeing is because CHECKSUM_PARTIAL with
NETIF_F_IPV6_CSUM does not work if extension headers are present. If
so, it should even affect non-tunneled packets. I think this reference
to a dependency on the state of an inner checksum adds confusion only.
Unless I'm missing something.

> > > * ip6_tunnel adds an IPv6 header with a destination option extension
> > > header if encap_limit is non-zero (the default value is 4).
> > 
> > 
> > If this is a fix, we'll need to target net and best effort find a
> > suitable fixes tag.
> >  
> I guess the particular problem I have found is present since the merge 
> of fou6 in 4.7, but it might not be the only code path to create an 
> IPv6 packet with an extension header and a partial checksum.

True. The fix as is won't be backportable before commit 62fafcd63139
("net: support ip generic csum processing in skb_csum_hwoffload_help")
in v5.12 anyway.

Maybe use that as the Fixes tag, but add a comment in the tag that it
did not introduce the bug, but the fix depends on that logic.

> > > Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> > > ---
> > >  net/core/dev.c | 4 ++++
> > >  1 file changed, 4 insertions(+)
> > > 
> > > diff --git a/net/core/dev.c b/net/core/dev.c
> > > index ea5fbcd133ae..199831d86ec1 100644
> > > --- a/net/core/dev.c
> > > +++ b/net/core/dev.c
> > > @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > >  		return 0;
> > > 
> > >  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> > > +		if (ip_hdr(skb)->version == 6 &&
> > > +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> > > +			goto sw_checksum;

This check depends on skb->transport_header and skb->network_header
being set. This is likely true for all CHECKSUM_PARTIAL packets that
originate in the local stack. As well as for the injected packets and
forwarded packets, as far as I see, so Ack.

Access to the network header at this point likely requires
skb_header_pointer, however. As also used in qdisc_pkt_len_init called
from the same __dev_queue_xmit_nit.

Perhaps this test should be in can_checksum_protocol, which already
checks that the packet is IPv6 when testing NETIF_F_IPV6_CSUM.

> > >  		switch (skb->csum_offset) {
> > >  		case offsetof(struct tcphdr, check):
> > >  		case offsetof(struct udphdr, check):
> > > @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > >  		}
> > >  	}
> > > 
> > > +sw_checksum:
> > >  	return skb_checksum_help(skb);
> > >  }
> > >  EXPORT_SYMBOL(skb_csum_hwoffload_help);
> > 
> 
> -- 
> Benoît
> 
>
Benoît Monin Oct. 10, 2024, 4:02 p.m. UTC | #4
07/10/2024 Willem de Bruijn wrote:
> Benoît Monin wrote:
> > 07/10/2024 Willem de Bruijn wrote :
> > > Benoît Monin wrote:
> > > > Devices with NETIF_F_IP_CSUM capability can checksum TCP and UDP over
> > > > IPv4 with an IP header that may contains options; whereas devices with
> > > > NETIF_F_IPV6_CSUM capability can only checksum TCP and UDP over IPv6 if
> > > > the IP header does not contains extension.
> > > 
> > > Are both these statements universally true across devices?
> > > 
> > > I can believe for NETIF_F_IP_CSUM that this is the definition, and
> > > that devices that cannot handle options must fix it up indivually in
> > > ndo_features_check.
> > > 
> > > And same for NETIF_F_IPV6_CSUM with extension headers.
> > > 
> > > But it would be good to see where this is asserted in the code, or
> > > examples of drivers that have to perform such actions.
> > > 
> > I was referring to the documentation in skbuff.h that describes 
> > NETIF_F_IP_CSUM and NETIF_F_IPV6_CSUM.
> 
> Excellent. I had missed that when trying to find the source.
> Can you add that pointer to the commit message?
>  
Agreed, I will add that to the commit message.

> > For NETIF_F_IPV6_CSUM, at least fsl_dpa and r8169 expect 
> > ipv6_hdr(skb)->nexthdr to be IPPROTO_{TCP,UDP} to compute the correct 
> > checksum for IPv6.
> > 
> > I posted more details about the problem I am trying to fix with this 
> > patch in the following thread: 
> > https://lore.kernel.org/netdev/26548921.1r3eYUQgxm@benoit.monin/T/#u
> 
> And I had missed this thread entirely too. It's very helpful.
> 
> Please add a Link: tag that refers to it.
>  
Ok.

> > > > Enforce that in skb_csum_hwoffload_help by checking the network header
> > > > length in the case where the IP header version is 6. We cannot simply
> > > > rely on the network header length since the IPv4 header can from 20 to
> > > > 60 bytes whereas the IPv6 header must be 40 bytes. So we check the
> > > > version field which is common to IPv4 and IPv6 headers.
> > > > 
> > > > This fixes checksumming errors seen with ip6_tunnel and fou6
> > > > encapsulation, for example with GRE-in-UDP over IPv6:
> > > > * fou6 adds a UDP header with a partial checksum if the inner packet
> > > > does not contains a valid checksum.
> > > 
> > > Where in the code is this conditional on the inner packet csum?
> > > 
> > This is done by udp6_set_csum, which called by fou6_build_udp.
> 
>         else if (skb->ip_summed == CHECKSUM_PARTIAL) {
>                 uh->check = 0;
>                 uh->check = udp_v6_check(len, saddr, daddr, lco_csum(skb));
>                 if (uh->check == 0)
>                         uh->check = CSUM_MANGLED_0;
>         } else {
>                 skb->ip_summed = CHECKSUM_PARTIAL;
>                 skb->csum_start = skb_transport_header(skb) - skb->head;
>                 skb->csum_offset = offsetof(struct udphdr, check);
>                 uh->check = ~udp_v6_check(len, saddr, daddr, 0);
>         }
> 
> It either leaves the inner header as CHECKSUM_PARTIAL, and fills in
> the outer header entirely, based on knowledge that the inner header
> will add up to zero (local checksum offload).
> 
> Or it assumes that the inner header is already filled in (whether
> computed or CSUM_MANGLED_0) and then uses CHECKSUM_PARTIAL offloading
> for the outer packet.
> 
> The issue you are seeing is because CHECKSUM_PARTIAL with
> NETIF_F_IPV6_CSUM does not work if extension headers are present. If
> so, it should even affect non-tunneled packets. I think this reference
> to a dependency on the state of an inner checksum adds confusion only.
> Unless I'm missing something.
> 
I also forgot to mention that the problem only affects encapsulated packets. 
UDP packets emitted from user-space to an IPv6 address go through 
ip6_make_skb(), which calls __ip6_append_data() where a check is done on the 
header size before setting CHECKSUM_PARTIAL:

	/* CHECKSUM_PARTIAL only with no extension headers and when
	 * we are not going to fragment
	 */
	if (transhdrlen && sk->sk_protocol == IPPROTO_UDP &&
	    headersize == sizeof(struct ipv6hdr) &&
	    length <= mtu - headersize &&
	    (!(flags & MSG_MORE) || cork->gso_size) &&
	    rt->dst.dev->features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
		csummode = CHECKSUM_PARTIAL;

This is done before calling ip6_local_out(). No such check is done by 
ip6_tnl_xmit() before it calls ip6_local_out(), because there is no reason to 
check for device offload feature at this point. It is assumed that it will be 
verified by validate_xmit_skb().

I will add this explanation to the commit message.

> > > > * ip6_tunnel adds an IPv6 header with a destination option extension
> > > > header if encap_limit is non-zero (the default value is 4).
> > > 
> > > 
> > > If this is a fix, we'll need to target net and best effort find a
> > > suitable fixes tag.
> > >  
> > I guess the particular problem I have found is present since the merge 
> > of fou6 in 4.7, but it might not be the only code path to create an 
> > IPv6 packet with an extension header and a partial checksum.
> 
> True. The fix as is won't be backportable before commit 62fafcd63139
> ("net: support ip generic csum processing in skb_csum_hwoffload_help")
> in v5.12 anyway.
> 
> Maybe use that as the Fixes tag, but add a comment in the tag that it
> did not introduce the bug, but the fix depends on that logic.
> 
Ok, I will add a Fixes tag.

> > > > Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> > > > ---
> > > >  net/core/dev.c | 4 ++++
> > > >  1 file changed, 4 insertions(+)
> > > > 
> > > > diff --git a/net/core/dev.c b/net/core/dev.c
> > > > index ea5fbcd133ae..199831d86ec1 100644
> > > > --- a/net/core/dev.c
> > > > +++ b/net/core/dev.c
> > > > @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > > >  		return 0;
> > > > 
> > > >  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> > > > +		if (ip_hdr(skb)->version == 6 &&
> > > > +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> > > > +			goto sw_checksum;
> 
> This check depends on skb->transport_header and skb->network_header
> being set. This is likely true for all CHECKSUM_PARTIAL packets that
> originate in the local stack. As well as for the injected packets and
> forwarded packets, as far as I see, so Ack.
> 
> Access to the network header at this point likely requires
> skb_header_pointer, however. As also used in qdisc_pkt_len_init called
> from the same __dev_queue_xmit_nit.
> 
> Perhaps this test should be in can_checksum_protocol, which already
> checks that the packet is IPv6 when testing NETIF_F_IPV6_CSUM.
> 
You're right, moving this to can_checksum_protocol() makes more sense. I will 
do that, retest and post a new version of the patch.

> > > >  		switch (skb->csum_offset) {
> > > >  		case offsetof(struct tcphdr, check):
> > > >  		case offsetof(struct udphdr, check):
> > > > @@ -3646,6 +3649,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > > >  		}
> > > >  	}
> > > > 
> > > > +sw_checksum:
> > > >  	return skb_checksum_help(skb);
> > > >  }
> > > >  EXPORT_SYMBOL(skb_csum_hwoffload_help);
> > > 
> > 
> 
Thanks for your thorough review!
Benoît Monin Oct. 21, 2024, 3:25 p.m. UTC | #5
10/10/2024 Benoît Monin wrote:
> 07/10/2024 Willem de Bruijn wrote:
> > Benoît Monin wrote:
> > > 07/10/2024 Willem de Bruijn wrote :
> > > > Benoît Monin wrote:
[...]
> > > > > Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> > > > > ---
> > > > >  net/core/dev.c | 4 ++++
> > > > >  1 file changed, 4 insertions(+)
> > > > > 
> > > > > diff --git a/net/core/dev.c b/net/core/dev.c
> > > > > index ea5fbcd133ae..199831d86ec1 100644
> > > > > --- a/net/core/dev.c
> > > > > +++ b/net/core/dev.c
> > > > > @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > > > >  		return 0;
> > > > > 
> > > > >  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> > > > > +		if (ip_hdr(skb)->version == 6 &&
> > > > > +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> > > > > +			goto sw_checksum;
> > 
> > This check depends on skb->transport_header and skb->network_header
> > being set. This is likely true for all CHECKSUM_PARTIAL packets that
> > originate in the local stack. As well as for the injected packets and
> > forwarded packets, as far as I see, so Ack.
> > 
> > Access to the network header at this point likely requires
> > skb_header_pointer, however. As also used in qdisc_pkt_len_init called
> > from the same __dev_queue_xmit_nit.
> > 
> > Perhaps this test should be in can_checksum_protocol, which already
> > checks that the packet is IPv6 when testing NETIF_F_IPV6_CSUM.
> > 
> You're right, moving this to can_checksum_protocol() makes more sense. I will 
> do that, retest and post a new version of the patch.
> 
Looking more into it, can_checksum_protocol() is called from multiple places 
where network header length cannot easily extracted, in particular from 
vxlan_features_check().

How about keeping the length check in skb_csum_hwoffload_help() but using 
vlan_get_protocol() to check for IPv6 instead of ip_hdr(skb)->version?
Willem de Bruijn Oct. 21, 2024, 10:04 p.m. UTC | #6
Benoît Monin wrote:
> 10/10/2024 Benoît Monin wrote:
> > 07/10/2024 Willem de Bruijn wrote:
> > > Benoît Monin wrote:
> > > > 07/10/2024 Willem de Bruijn wrote :
> > > > > Benoît Monin wrote:
> [...]
> > > > > > Signed-off-by: Benoît Monin <benoit.monin@gmx.fr>
> > > > > > ---
> > > > > >  net/core/dev.c | 4 ++++
> > > > > >  1 file changed, 4 insertions(+)
> > > > > > 
> > > > > > diff --git a/net/core/dev.c b/net/core/dev.c
> > > > > > index ea5fbcd133ae..199831d86ec1 100644
> > > > > > --- a/net/core/dev.c
> > > > > > +++ b/net/core/dev.c
> > > > > > @@ -3639,6 +3639,9 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
> > > > > >  		return 0;
> > > > > > 
> > > > > >  	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
> > > > > > +		if (ip_hdr(skb)->version == 6 &&
> > > > > > +		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
> > > > > > +			goto sw_checksum;
> > > 
> > > This check depends on skb->transport_header and skb->network_header
> > > being set. This is likely true for all CHECKSUM_PARTIAL packets that
> > > originate in the local stack. As well as for the injected packets and
> > > forwarded packets, as far as I see, so Ack.
> > > 
> > > Access to the network header at this point likely requires
> > > skb_header_pointer, however. As also used in qdisc_pkt_len_init called
> > > from the same __dev_queue_xmit_nit.
> > > 
> > > Perhaps this test should be in can_checksum_protocol, which already
> > > checks that the packet is IPv6 when testing NETIF_F_IPV6_CSUM.
> > > 
> > You're right, moving this to can_checksum_protocol() makes more sense. I will 
> > do that, retest and post a new version of the patch.
> > 
> Looking more into it, can_checksum_protocol() is called from multiple places 
> where network header length cannot easily extracted, in particular from 
> vxlan_features_check().
> 
> How about keeping the length check in skb_csum_hwoffload_help() but using 
> vlan_get_protocol() to check for IPv6 instead of ip_hdr(skb)->version?

Yes, both sound good to me.
diff mbox series

Patch

diff --git a/net/core/dev.c b/net/core/dev.c
index ea5fbcd133ae..199831d86ec1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3639,6 +3639,9 @@  int skb_csum_hwoffload_help(struct sk_buff *skb,
 		return 0;

 	if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
+		if (ip_hdr(skb)->version == 6 &&
+		    skb_network_header_len(skb) != sizeof(struct ipv6hdr))
+			goto sw_checksum;
 		switch (skb->csum_offset) {
 		case offsetof(struct tcphdr, check):
 		case offsetof(struct udphdr, check):
@@ -3646,6 +3649,7 @@  int skb_csum_hwoffload_help(struct sk_buff *skb,
 		}
 	}

+sw_checksum:
 	return skb_checksum_help(skb);
 }
 EXPORT_SYMBOL(skb_csum_hwoffload_help);