bpf: Add gso_size to __sk_buff
authorWillem de Bruijn <willemb@google.com>
Tue, 3 Mar 2020 20:05:01 +0000 (15:05 -0500)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 4 Mar 2020 00:23:59 +0000 (16:23 -0800)
BPF programs may want to know whether an skb is gso. The canonical
answer is skb_is_gso(skb), which tests that gso_size != 0.

Expose this field in the same manner as gso_segs. That field itself
is not a sufficient signal, as the comment in skb_shared_info makes
clear: gso_segs may be zero, e.g., from dodgy sources.

Also prepare net/bpf/test_run for upcoming BPF_PROG_TEST_RUN tests
of the feature.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200303200503.226217-2-willemdebruijn.kernel@gmail.com
include/uapi/linux/bpf.h
net/bpf/test_run.c
net/core/filter.c

index 8e98ced0963b52009c4754efce7ec8a4e06b9a68..180337fae97e166fea7b161b16cb22b3bfc7af1d 100644 (file)
@@ -3176,6 +3176,7 @@ struct __sk_buff {
        __u32 wire_len;
        __u32 gso_segs;
        __bpf_md_ptr(struct bpf_sock *, sk);
+       __u32 gso_size;
 };
 
 struct bpf_tunnel_key {
index 562443f94133b31bb96ffc1ebeeb25c513134e7e..1cd7a1c2f8b20069ea1137d4326fb3347d923065 100644 (file)
@@ -277,6 +277,12 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
        /* gso_segs is allowed */
 
        if (!range_is_zero(__skb, offsetofend(struct __sk_buff, gso_segs),
+                          offsetof(struct __sk_buff, gso_size)))
+               return -EINVAL;
+
+       /* gso_size is allowed */
+
+       if (!range_is_zero(__skb, offsetofend(struct __sk_buff, gso_size),
                           sizeof(struct __sk_buff)))
                return -EINVAL;
 
@@ -297,6 +303,7 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb)
        if (__skb->gso_segs > GSO_MAX_SEGS)
                return -EINVAL;
        skb_shinfo(skb)->gso_segs = __skb->gso_segs;
+       skb_shinfo(skb)->gso_size = __skb->gso_size;
 
        return 0;
 }
index 4a08c9fb2be7753d839a4e405537b090af4f9fcd..cd0a532db4e7189637f8aac07cf165ba7351eac3 100644 (file)
@@ -7139,6 +7139,27 @@ static u32 flow_dissector_convert_ctx_access(enum bpf_access_type type,
        return insn - insn_buf;
 }
 
+static struct bpf_insn *bpf_convert_shinfo_access(const struct bpf_insn *si,
+                                                 struct bpf_insn *insn)
+{
+       /* si->dst_reg = skb_shinfo(SKB); */
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+       *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end),
+                             BPF_REG_AX, si->src_reg,
+                             offsetof(struct sk_buff, end));
+       *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, head),
+                             si->dst_reg, si->src_reg,
+                             offsetof(struct sk_buff, head));
+       *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX);
+#else
+       *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end),
+                             si->dst_reg, si->src_reg,
+                             offsetof(struct sk_buff, end));
+#endif
+
+       return insn;
+}
+
 static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                                  const struct bpf_insn *si,
                                  struct bpf_insn *insn_buf,
@@ -7461,26 +7482,21 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type,
                break;
 
        case offsetof(struct __sk_buff, gso_segs):
-               /* si->dst_reg = skb_shinfo(SKB); */
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end),
-                                     BPF_REG_AX, si->src_reg,
-                                     offsetof(struct sk_buff, end));
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, head),
-                                     si->dst_reg, si->src_reg,
-                                     offsetof(struct sk_buff, head));
-               *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX);
-#else
-               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end),
-                                     si->dst_reg, si->src_reg,
-                                     offsetof(struct sk_buff, end));
-#endif
+               insn = bpf_convert_shinfo_access(si, insn);
                *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_segs),
                                      si->dst_reg, si->dst_reg,
                                      bpf_target_off(struct skb_shared_info,
                                                     gso_segs, 2,
                                                     target_size));
                break;
+       case offsetof(struct __sk_buff, gso_size):
+               insn = bpf_convert_shinfo_access(si, insn);
+               *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_size),
+                                     si->dst_reg, si->dst_reg,
+                                     bpf_target_off(struct skb_shared_info,
+                                                    gso_size, 2,
+                                                    target_size));
+               break;
        case offsetof(struct __sk_buff, wire_len):
                BUILD_BUG_ON(sizeof_field(struct qdisc_skb_cb, pkt_len) != 4);