bpf: fix csum update in bpf_l4_csum_replace helper for udp
authorDaniel Borkmann <daniel@iogearbox.net>
Fri, 19 Feb 2016 22:05:26 +0000 (23:05 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Feb 2016 03:07:10 +0000 (22:07 -0500)
When using this helper for updating UDP checksums, we need to extend
this in order to write CSUM_MANGLED_0 for csum computations that result
into 0 as sum. Reason we need this is because packets with a checksum
could otherwise become incorrectly marked as a packet without a checksum.
Likewise, if the user indicates BPF_F_MARK_MANGLED_0, then we should
not turn packets without a checksum into ones with a checksum.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/bpf.h
net/core/filter.c

index 48d0a6c546091c974ae6182a9621e814f96bde85..6496f98d3d681f5434950a294a1174ac4a275840 100644 (file)
@@ -313,6 +313,7 @@ enum bpf_func_id {
 
 /* BPF_FUNC_l4_csum_replace flags. */
 #define BPF_F_PSEUDO_HDR               (1ULL << 4)
+#define BPF_F_MARK_MANGLED_0           (1ULL << 5)
 
 /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
 #define BPF_F_INGRESS                  (1ULL << 0)
index f031b82128f3f276810402fee2b1fd3dbe1c6c5e..8a0b8c3eb189fc2d95f123f2b2005547a1f0a938 100644 (file)
@@ -1477,10 +1477,12 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
 {
        struct sk_buff *skb = (struct sk_buff *) (long) r1;
        bool is_pseudo = flags & BPF_F_PSEUDO_HDR;
+       bool is_mmzero = flags & BPF_F_MARK_MANGLED_0;
        int offset = (int) r2;
        __sum16 sum, *ptr;
 
-       if (unlikely(flags & ~(BPF_F_PSEUDO_HDR | BPF_F_HDR_FIELD_MASK)))
+       if (unlikely(flags & ~(BPF_F_MARK_MANGLED_0 | BPF_F_PSEUDO_HDR |
+                              BPF_F_HDR_FIELD_MASK)))
                return -EINVAL;
        if (unlikely((u32) offset > 0xffff))
                return -EFAULT;
@@ -1490,6 +1492,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
        ptr = skb_header_pointer(skb, offset, sizeof(sum), &sum);
        if (unlikely(!ptr))
                return -EFAULT;
+       if (is_mmzero && !*ptr)
+               return 0;
 
        switch (flags & BPF_F_HDR_FIELD_MASK) {
        case 0:
@@ -1508,6 +1512,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)
                return -EINVAL;
        }
 
+       if (is_mmzero && !*ptr)
+               *ptr = CSUM_MANGLED_0;
        if (ptr == &sum)
                /* skb_store_bits guaranteed to not return -EFAULT here */
                skb_store_bits(skb, offset, ptr, sizeof(sum));