mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-11 21:05:02 -04:00
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>
This commit is contained in:
@@ -152,7 +152,7 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,
|
||||
const __be32 *from, const __be32 *to,
|
||||
bool pseudohdr);
|
||||
void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb,
|
||||
__wsum diff, bool pseudohdr);
|
||||
__wsum diff, bool pseudohdr, bool ipv6);
|
||||
|
||||
static __always_inline
|
||||
void inet_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
|
||||
|
||||
@@ -2056,6 +2056,7 @@ union bpf_attr {
|
||||
* for updates resulting in a null checksum the value is set to
|
||||
* **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
|
||||
* that the modified header field is part of the pseudo-header.
|
||||
* Flag **BPF_F_IPV6** should be set for IPv6 packets.
|
||||
*
|
||||
* This helper works in combination with **bpf_csum_diff**\ (),
|
||||
* which does not update the checksum in-place, but offers more
|
||||
@@ -6072,6 +6073,7 @@ enum {
|
||||
BPF_F_PSEUDO_HDR = (1ULL << 4),
|
||||
BPF_F_MARK_MANGLED_0 = (1ULL << 5),
|
||||
BPF_F_MARK_ENFORCE = (1ULL << 6),
|
||||
BPF_F_IPV6 = (1ULL << 7),
|
||||
};
|
||||
|
||||
/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
|
||||
|
||||
@@ -1968,10 +1968,11 @@ BPF_CALL_5(bpf_l4_csum_replace, struct sk_buff *, skb, u32, offset,
|
||||
bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
|
||||
bool is_mmzero = flags & BPF_F_MARK_MANGLED_0;
|
||||
bool do_mforce = flags & BPF_F_MARK_ENFORCE;
|
||||
bool is_ipv6 = flags & BPF_F_IPV6;
|
||||
__sum16 *ptr;
|
||||
|
||||
if (unlikely(flags & ~(BPF_F_MARK_MANGLED_0 | BPF_F_MARK_ENFORCE |
|
||||
BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
|
||||
BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK | BPF_F_IPV6)))
|
||||
return -EINVAL;
|
||||
if (unlikely(offset > 0xffff || offset & 1))
|
||||
return -EFAULT;
|
||||
@@ -1987,7 +1988,7 @@ BPF_CALL_5(bpf_l4_csum_replace, struct sk_buff *, skb, u32, offset,
|
||||
if (unlikely(from != 0))
|
||||
return -EINVAL;
|
||||
|
||||
inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo);
|
||||
inet_proto_csum_replace_by_diff(ptr, skb, to, is_pseudo, is_ipv6);
|
||||
break;
|
||||
case 2:
|
||||
inet_proto_csum_replace2(ptr, skb, from, to, is_pseudo);
|
||||
|
||||
@@ -473,11 +473,11 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb,
|
||||
EXPORT_SYMBOL(inet_proto_csum_replace16);
|
||||
|
||||
void inet_proto_csum_replace_by_diff(__sum16 *sum, struct sk_buff *skb,
|
||||
__wsum diff, bool pseudohdr)
|
||||
__wsum diff, bool pseudohdr, bool ipv6)
|
||||
{
|
||||
if (skb->ip_summed != CHECKSUM_PARTIAL) {
|
||||
csum_replace_by_diff(sum, diff);
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr && !ipv6)
|
||||
skb->csum = ~csum_sub(diff, skb->csum);
|
||||
} else if (pseudohdr) {
|
||||
*sum = ~csum_fold(csum_add(diff, csum_unfold(*sum)));
|
||||
|
||||
@@ -86,7 +86,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
|
||||
|
||||
diff = get_csum_diff(ip6h, p);
|
||||
inet_proto_csum_replace_by_diff(&th->check, skb,
|
||||
diff, true);
|
||||
diff, true, true);
|
||||
}
|
||||
break;
|
||||
case NEXTHDR_UDP:
|
||||
@@ -97,7 +97,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
|
||||
if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
diff = get_csum_diff(ip6h, p);
|
||||
inet_proto_csum_replace_by_diff(&uh->check, skb,
|
||||
diff, true);
|
||||
diff, true, true);
|
||||
if (!uh->check)
|
||||
uh->check = CSUM_MANGLED_0;
|
||||
}
|
||||
@@ -111,7 +111,7 @@ static void ila_csum_adjust_transport(struct sk_buff *skb,
|
||||
|
||||
diff = get_csum_diff(ip6h, p);
|
||||
inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
|
||||
diff, true);
|
||||
diff, true, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2056,6 +2056,7 @@ union bpf_attr {
|
||||
* for updates resulting in a null checksum the value is set to
|
||||
* **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
|
||||
* that the modified header field is part of the pseudo-header.
|
||||
* Flag **BPF_F_IPV6** should be set for IPv6 packets.
|
||||
*
|
||||
* This helper works in combination with **bpf_csum_diff**\ (),
|
||||
* which does not update the checksum in-place, but offers more
|
||||
@@ -6072,6 +6073,7 @@ enum {
|
||||
BPF_F_PSEUDO_HDR = (1ULL << 4),
|
||||
BPF_F_MARK_MANGLED_0 = (1ULL << 5),
|
||||
BPF_F_MARK_ENFORCE = (1ULL << 6),
|
||||
BPF_F_IPV6 = (1ULL << 7),
|
||||
};
|
||||
|
||||
/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
|
||||
|
||||
Reference in New Issue
Block a user