IPv6: Set SIT tunnel hard_header_len to zero
Subsystems (2)
PATCH 1/1 IPv6: Set SIT tunnel hard_header_len to zero
Due to the legacy usage of hard_header_len for SIT tunnels while already using infrastructure from net/ipv4/ip_tunnel.c the calculation of the path MTU in tnl_update_pmtu is incorrect. This leads to unnecessary creation of MTU exceptions for any flow going over a SIT tunnel.
As SIT tunnels do not have a header themsevles other than their transport (L3, L2) headers we're leaving hard_header_len set to zero as tnl_update_pmtu is already taking care of the transport headers sizes.
This will also help avoiding unnecessary IPv6 GC runs and spinlock contention seen when using SIT tunnels and for more than net.ipv6.route.gc_thresh flows.
--- net/ipv6/sit.c | 2 -- 1 file changed, 2 deletions(-)
net/ipv6/sit.c +0 -2
@@ -1128,7 +1128,6 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)if (tdev && !netif_is_l3_master (tdev)) {int t_hlen = tunnel->hlen +sizeof (struct iphdr ); - dev->hard_header_len = tdev->hard_header_len +sizeof (struct iphdr ); dev->mtu = tdev->mtu - t_hlen;if (dev->mtu <IPV6_MIN_MTU ) dev->mtu =IPV6_MIN_MTU ;
@@ -1426,7 +1425,6 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->priv_destructor = ipip6_dev_free; dev->type =ARPHRD_SIT ; - dev->hard_header_len =LL_MAX_HEADER + t_hlen; dev->mtu =ETH_DATA_LEN - t_hlen; dev->min_mtu =IPV6_MIN_MTU ; dev->max_mtu =IP6_MAX_MTU - t_hlen;
Signature · 1 line
2.25.1
Quoted 7 lines · Oliver Herms
Due to the legacy usage of
hard_header_lenfor SIT tunnels while already using infrastructure fromnet/ipv4/ip_tunnel.cthe calculation of the path MTU intnl_update_pmtuis incorrect. This leads to unnecessary creation of MTU exceptions for any flow going over a SIT tunnel.
Quoted 8 lines
As SIT tunnels do not have a header themsevles other than their transport (L3, L2) headers we're leaving
hard_header_lenset to zero astnl_update_pmtuis already taking care of the transport headers sizes.This will also help avoiding unnecessary IPv6 GC runs and spinlock contention seen when using SIT tunnels and for more than net.ipv6.route.gc_thresh flows.
Thanks. Yes, this is long overdue.
The hard_header_len issue was also recently discussed in the context of GRE in commit fdafed459998 ("ip_gre: set dev->hard_header_len and dev->needed_headroom properly").
Question is whether we should reserve room in needed_headroom instead. AFAIK this existing update logic in ip6_tnl_xmit is sufficient
"
/* Calculate max headroom for all the headers and adjust
* needed_headroom if necessary.
*/
max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr)
+ dst->header_len + t->hlen;
if (max_headroom > dev->needed_headroom)
dev->needed_headroom = max_headroom;"
Quoted 1 line
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")
How did you arrive at this SHA1?
Quoted 28 lines
Signed-off-by Oliver Herms <[email protected]>--- net/ipv6/sit.c | 2 -- 1 file changed, 2 deletions(-)net/ipv6/sit.c +0
-3@@ -1128,7 +1128,6 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)if (tdev && !netif_is_l3_master (tdev)) {int t_hlen = tunnel->hlen +sizeof (struct iphdr ); - dev->hard_header_len = tdev->hard_header_len +sizeof (struct iphdr ); dev->mtu = tdev->mtu - t_hlen;if (dev->mtu <IPV6_MIN_MTU ) dev->mtu =IPV6_MIN_MTU ;@@ -1426,7 +1425,6 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->priv_destructor = ipip6_dev_free; dev->type =ARPHRD_SIT ; - dev->hard_header_len =LL_MAX_HEADER + t_hlen; dev->mtu =ETH_DATA_LEN - t_hlen; dev->min_mtu =IPV6_MIN_MTU ; dev->max_mtu =IP6_MAX_MTU - t_hlen; --2.25.1
Quoted 10 lines · Willem de Bruijn
Thanks. Yes, this is long overdue.
The
hard_header_lenissue was also recently discussed in the context of GRE in commitfdafed459998("ip_gre: setdev->hard_header_lenanddev->needed_headroomproperly").Question is whether we should reserve room in
needed_headroominstead. AFAIK this existing update logic inip6_tnl_xmitis sufficient"
Quoted 8 lines
/* Calculate max headroom for all the headers and adjust * needed_headroom if necessary. */ max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr) + dst->header_len + t->hlen; if (max_headroom > dev->needed_headroom) dev->needed_headroom = max_headroom;"I think that's enough. At least it definitely works in my test and prod environment.
Would be good to get another opinion on this though.
Quoted 3 lines
Quoted 1 line
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")How did you arrive at this SHA1?
I think the legacy usage of hard_header_len in ipv6/sit.c was overseen in c54419321455. Please correct me if I'm wrong.
Quoted 20 lines · Oliver Herms
Quoted 10 lines · Willem de Bruijn
Thanks. Yes, this is long overdue.
The
hard_header_lenissue was also recently discussed in the context of GRE in commitfdafed459998("ip_gre: setdev->hard_header_lenanddev->needed_headroomproperly").Question is whether we should reserve room in
needed_headroominstead. AFAIK this existing update logic inip6_tnl_xmitis sufficient"
Quoted 8 lines
/* Calculate max headroom for all the headers and adjust * needed_headroom if necessary. */ max_headroom = LL_RESERVED_SPACE(dst->dev) + sizeof(struct ipv6hdr) + dst->header_len + t->hlen; if (max_headroom > dev->needed_headroom) dev->needed_headroom = max_headroom;"I think that's enough. At least it definitely works in my test and prod environment.
Quoted 1 line
Would be good to get another opinion on this though.
Sit should behave the same as other tunnels. At least in ip6_tunnel.c and ip6_gre.c I do not see explicit initialization of needed_headroom.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")How did you arrive at this SHA1?
I think the legacy usage of
hard_header_leninipv6/sit.cwas overseen inc54419321455. Please correct me if I'm wrong.
I don't see anything in that patch assign or modify hard_header_len.
Quoted 8 lines · Willem de Bruijn
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")How did you arrive at this SHA1?
I think the legacy usage of
hard_header_leninipv6/sit.cwas overseen inc54419321455. Please correct me if I'm wrong.I don't see anything in that patch assign or modify
hard_header_len.
It's not assigning or modifying it but changing expectations about how dev->hard_header_len is to be used.
The patch changed the MTU calculation from:
mtu = dst_mtu(&rt->dst) - dev->hard_header_len - tunnel->hlen;
to this:
mtu = dst_mtu(&rt->dst) - dev->hard_header_len - sizeof(struct iphdr);
Later is became this (in patch 23a3647. This is the current implementation.):
mtu = dst_mtu(&rt->dst) - dev->hard_header_len - sizeof(struct iphdr) - tunnel_hlen;
Apparently the initial usage of dev->hard_header_len was that it contains the length of all headers before the tunnel payload. c54419321455 changed it to assuming dev->hard_header_len does not contain the tunnels outter IP header. Thus I think the bug was introduced by c54419321455.
Kind Regards
Oliver
Willem de Bruijn
Acked-byQuoted 19 lines · Oliver Herms
Quoted 8 lines · Willem de Bruijn
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")How did you arrive at this SHA1?
I think the legacy usage of
hard_header_leninipv6/sit.cwas overseen inc54419321455. Please correct me if I'm wrong.I don't see anything in that patch assign or modify
hard_header_len.It's not assigning or modifying it but changing expectations about how
dev->hard_header_lenis to be used.The patch changed the MTU calculation from:
mtu =dst_mtu(&rt->dst)-dev->hard_header_len-tunnel->hlen;to this:
mtu =dst_mtu(&rt->dst)-dev->hard_header_len- sizeof(struct iphdr);
Quoted 6 lines
Later is became this (in patch 23a3647. This is the current implementation.):
mtu =dst_mtu(&rt->dst)-dev->hard_header_len- sizeof(struct iphdr) -tunnel_hlen;Apparently the initial usage of
dev->hard_header_lenwas that it contains the length of all headers before the tunnel payload.c54419321455changed it to assumingdev->hard_header_lendoes not contain the tunnels outter IP header. Thus I think the bug was introduced byc54419321455.
And the only header in the case of SIT is that outer ip header. Got it, thanks.
Overly conservative MTU calculation is one issue. Packet sockets also expect read/write link layer access with SOCK_RAW, which does not work correctly for sit. I'm not sure that it ever did.
The chosen commit predates all stable trees, which is the most important point.
Could ip6 tunnels have the same issue? In ip6_tnl_dev_init_gen,
dev->hard_header_len = LL_MAX_HEADER + t_hlen;
Quoted 11 lines · On Tue, 3 Nov 2020 11:41:33 +0100 Oliver Herms wrote:
Due to the legacy usage of
hard_header_lenfor SIT tunnels while already using infrastructure fromnet/ipv4/ip_tunnel.cthe calculation of the path MTU intnl_update_pmtuis incorrect. This leads to unnecessary creation of MTU exceptions for any flow going over a SIT tunnel.As SIT tunnels do not have a header themsevles other than their transport (L3, L2) headers we're leaving
hard_header_lenset to zero astnl_update_pmtuis already taking care of the transport headers sizes.
Quoted 6 lines
This will also help avoiding unnecessary IPv6 GC runs and spinlock contention seen when using SIT tunnels and for more than net.ipv6.route.gc_thresh flows.
Fixes c54419321455 ("GRE: Refactor GRE tunneling code.")Signed-off-by Oliver Herms <[email protected]>
Applied, thanks! Let's carry on the discussion about ipv6 tunnels, though, it does look questionable.