Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-fix-inet_proto_csum_replace_by_diff-for-ipv6'

Paul Chaignon says:

====================
net: Fix inet_proto_csum_replace_by_diff for IPv6

This patchset fixes a bug that causes skb->csum to hold an incorrect
value when calling inet_proto_csum_replace_by_diff for an IPv6 packet
in CHECKSUM_COMPLETE state. This bug affects BPF helper
bpf_l4_csum_replace and IPv6 ILA in adj-transport mode.

In those cases, inet_proto_csum_replace_by_diff updates the L4 checksum
field after an IPv6 address change. These two changes cancel each other
in terms of checksum, so skb->csum shouldn't be updated.

v2: https://lore.kernel.org/aCz84JU60wd8etiT@mail.gmail.com
====================

Link: https://patch.msgid.link/cover.1748509484.git.paul.chaignon@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+13 -8
+1 -1
include/net/checksum.h
··· 152 152 const __be32 *from, const __be32 *to, 153 153 bool pseudohdr); 154 154 void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb, 155 - __wsum diff, bool pseudohdr); 155 + __wsum diff, bool pseudohdr, bool ipv6); 156 156 157 157 static __always_inline 158 158 void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
+2
include/uapi/linux/bpf.h
··· 2056 2056 * for updates resulting in a null checksum the value is set to 2057 2057 * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates 2058 2058 * that the modified header field is part of the pseudo-header. 2059 + * Flag **BPF_F_IPV6** should be set for IPv6 packets. 2059 2060 * 2060 2061 * This helper works in combination with **bpf_csum_diff**\ (), 2061 2062 * which does not update the checksum in-place, but offers more ··· 6073 6072 BPF_F_PSEUDO_HDR = (1ULL << 4), 6074 6073 BPF_F_MARK_MANGLED_0 = (1ULL << 5), 6075 6074 BPF_F_MARK_ENFORCE = (1ULL << 6), 6075 + BPF_F_IPV6 = (1ULL << 7), 6076 6076 }; 6077 6077 6078 6078 /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+3 -2
net/core/filter.c
··· 1968 1968 bool is_pseudo = flags & BPF_F_PSEUDO_HDR; 1969 1969 bool is_mmzero = flags & BPF_F_MARK_MANGLED_0; 1970 1970 bool do_mforce = flags & BPF_F_MARK_ENFORCE; 1971 + bool is_ipv6 = flags & BPF_F_IPV6; 1971 1972 __sum16 *ptr; 1972 1973 1973 1974 if (unlikely(flags & ~(BPF_F_MARK_MANGLED_0 | BPF_F_MARK_ENFORCE | 1974 - BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK))) 1975 + BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK | BPF_F_IPV6))) 1975 1976 return -EINVAL; 1976 1977 if (unlikely(offset > 0xffff || offset & 1)) 1977 1978 return -EFAULT; ··· 1988 1987 if (unlikely(from != 0)) 1989 1988 return -EINVAL; 1990 1989 1991 - inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo); 1990 + inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo, is_ipv6); 1992 1991 break; 1993 1992 case 2: 1994 1993 inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
+2 -2
net/core/utils.c
··· 473 473 EXPORT_SYMBOL(inet_proto_csum_replace16); 474 474 475 475 void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb, 476 - __wsum diff, bool pseudohdr) 476 + __wsum diff, bool pseudohdr, bool ipv6) 477 477 { 478 478 if (skb->ip_summed != CHECKSUM_PARTIAL) { 479 479 csum_replace_by_diff(sum, diff); 480 - if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) 480 + if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr && !ipv6) 481 481 skb->csum = ~csum_sub(diff, skb->csum); 482 482 } else if (pseudohdr) { 483 483 *sum = ~csum_fold(csum_add(diff, csum_unfold(*sum)));
+3 -3
net/ipv6/ila/ila_common.c
··· 86 86 87 87 diff = get_csum_diff(ip6h, p); 88 88 inet_proto_csum_replace_by_diff(&th->check, skb, 89 - diff, true); 89 + diff, true, true); 90 90 } 91 91 break; 92 92 case NEXTHDR_UDP: ··· 97 97 if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { 98 98 diff = get_csum_diff(ip6h, p); 99 99 inet_proto_csum_replace_by_diff(&uh->check, skb, 100 - diff, true); 100 + diff, true, true); 101 101 if (!uh->check) 102 102 uh->check = CSUM_MANGLED_0; 103 103 } ··· 111 111 112 112 diff = get_csum_diff(ip6h, p); 113 113 inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb, 114 - diff, true); 114 + diff, true, true); 115 115 } 116 116 break; 117 117 }
+2
tools/include/uapi/linux/bpf.h
··· 2056 2056 * for updates resulting in a null checksum the value is set to 2057 2057 * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates 2058 2058 * that the modified header field is part of the pseudo-header. 2059 + * Flag **BPF_F_IPV6** should be set for IPv6 packets. 2059 2060 * 2060 2061 * This helper works in combination with **bpf_csum_diff**\ (), 2061 2062 * which does not update the checksum in-place, but offers more ··· 6073 6072 BPF_F_PSEUDO_HDR = (1ULL << 4), 6074 6073 BPF_F_MARK_MANGLED_0 = (1ULL << 5), 6075 6074 BPF_F_MARK_ENFORCE = (1ULL << 6), 6075 + BPF_F_IPV6 = (1ULL << 7), 6076 6076 }; 6077 6077 6078 6078 /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */