net, xdp: Allow metadata > 32
authorAleksander Lobakin <aleksander.lobakin@intel.com>
Wed, 6 Dec 2023 20:59:19 +0000 (21:59 +0100)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 11 Dec 2023 15:09:36 +0000 (16:09 +0100)
32 bytes may be not enough for some custom metadata. Relax the restriction,
allow metadata larger than 32 bytes and make __skb_metadata_differs() work
with bigger lengths.

Now size of metadata is only limited by the fact it is stored as u8 in
skb_shared_info, so maximum possible value is 255. Size still has to be
aligned to 4, so the actual upper limit becomes 252. Most driver
implementations will offer less, none can offer more.

Other important conditions, such as having enough space for xdp_frame
building, are already checked in bpf_xdp_adjust_meta().

Signed-off-by: Aleksander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: Larysa Zaremba <larysa.zaremba@intel.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/eb87653c-8ff8-447d-a7a1-25961f60518a@kernel.org
Link: https://lore.kernel.org/bpf/20231206205919.404415-3-larysa.zaremba@intel.com
include/linux/skbuff.h
include/net/xdp.h

index b370eb8d70f7f849f2968539ba822c47edcc3149..df6ef42639d8bf1f8d9a3d641e8f0f8d3f0b17d9 100644 (file)
@@ -4247,10 +4247,13 @@ static inline bool __skb_metadata_differs(const struct sk_buff *skb_a,
 {
        const void *a = skb_metadata_end(skb_a);
        const void *b = skb_metadata_end(skb_b);
-       /* Using more efficient varaiant than plain call to memcmp(). */
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64
        u64 diffs = 0;
 
+       if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
+           BITS_PER_LONG != 64)
+               goto slow;
+
+       /* Using more efficient variant than plain call to memcmp(). */
        switch (meta_len) {
 #define __it(x, op) (x -= sizeof(u##op))
 #define __it_diff(a, b, op) (*(u##op *)__it(a, op)) ^ (*(u##op *)__it(b, op))
@@ -4270,11 +4273,11 @@ static inline bool __skb_metadata_differs(const struct sk_buff *skb_a,
                fallthrough;
        case  4: diffs |= __it_diff(a, b, 32);
                break;
+       default:
+slow:
+               return memcmp(a - meta_len, b - meta_len, meta_len);
        }
        return diffs;
-#else
-       return memcmp(a - meta_len, b - meta_len, meta_len);
-#endif
 }
 
 static inline bool skb_metadata_differs(const struct sk_buff *skb_a,
index 349c36fb5fd8f75d9c38da795c8bc1f787c79d2f..5d3673afc037a63aef2efac381d8143340eb6964 100644 (file)
@@ -369,7 +369,12 @@ xdp_data_meta_unsupported(const struct xdp_buff *xdp)
 
 static inline bool xdp_metalen_invalid(unsigned long metalen)
 {
-       return (metalen & (sizeof(__u32) - 1)) || (metalen > 32);
+       unsigned long meta_max;
+
+       meta_max = type_max(typeof_member(struct skb_shared_info, meta_len));
+       BUILD_BUG_ON(!__builtin_constant_p(meta_max));
+
+       return !IS_ALIGNED(metalen, sizeof(u32)) || metalen > meta_max;
 }
 
 struct xdp_attachment_info {