bpf: allow BPF programs access 'protocol' and 'vlan_tci' fields
authorAlexei Starovoitov <ast@plumgrid.com>
Tue, 17 Mar 2015 01:06:02 +0000 (18:06 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 17 Mar 2015 19:06:31 +0000 (15:06 -0400)
as a follow on to patch 70006af95515 ("bpf: allow eBPF access skb fields")
this patch allows 'protocol' and 'vlan_tci' fields to be accessible
from extended BPF programs.

The usage of 'protocol', 'vlan_present' and 'vlan_tci' fields is the same as
corresponding SKF_AD_PROTOCOL, SKF_AD_VLAN_TAG_PRESENT and SKF_AD_VLAN_TAG
accesses in classic BPF.

Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/bpf.h
net/core/filter.c
samples/bpf/test_verifier.c

index 929545a275460e491a8fe754bc07fa4417e08a18..1623047af463770f18e4e941af8624b78f49b135 100644 (file)
@@ -178,6 +178,9 @@ struct __sk_buff {
        __u32 pkt_type;
        __u32 mark;
        __u32 queue_mapping;
+       __u32 protocol;
+       __u32 vlan_present;
+       __u32 vlan_tci;
 };
 
 #endif /* _UAPI__LINUX_BPF_H__ */
index 4e9dd0ad0d5bce4ef3a206c5fcb92a38513bc79f..b95ae7fe7e4fc380957a45894f9057e5a00eed6a 100644 (file)
@@ -177,6 +177,35 @@ static u32 convert_skb_access(int skb_field, int dst_reg, int src_reg,
                *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
                                      offsetof(struct sk_buff, queue_mapping));
                break;
+
+       case SKF_AD_PROTOCOL:
+               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
+
+               /* dst_reg = *(u16 *) (src_reg + offsetof(protocol)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
+                                     offsetof(struct sk_buff, protocol));
+               /* dst_reg = ntohs(dst_reg) [emitting a nop or swap16] */
+               *insn++ = BPF_ENDIAN(BPF_FROM_BE, dst_reg, 16);
+               break;
+
+       case SKF_AD_VLAN_TAG:
+       case SKF_AD_VLAN_TAG_PRESENT:
+               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
+               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
+
+               /* dst_reg = *(u16 *) (src_reg + offsetof(vlan_tci)) */
+               *insn++ = BPF_LDX_MEM(BPF_H, dst_reg, src_reg,
+                                     offsetof(struct sk_buff, vlan_tci));
+               if (skb_field == SKF_AD_VLAN_TAG) {
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg,
+                                               ~VLAN_TAG_PRESENT);
+               } else {
+                       /* dst_reg >>= 12 */
+                       *insn++ = BPF_ALU32_IMM(BPF_RSH, dst_reg, 12);
+                       /* dst_reg &= 1 */
+                       *insn++ = BPF_ALU32_IMM(BPF_AND, dst_reg, 1);
+               }
+               break;
        }
 
        return insn - insn_buf;
@@ -190,13 +219,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
 
        switch (fp->k) {
        case SKF_AD_OFF + SKF_AD_PROTOCOL:
-               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
-
-               /* A = *(u16 *) (CTX + offsetof(protocol)) */
-               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
-                                     offsetof(struct sk_buff, protocol));
-               /* A = ntohs(A) [emitting a nop or swap16] */
-               *insn = BPF_ENDIAN(BPF_FROM_BE, BPF_REG_A, 16);
+               cnt = convert_skb_access(SKF_AD_PROTOCOL, BPF_REG_A, BPF_REG_CTX, insn);
+               insn += cnt - 1;
                break;
 
        case SKF_AD_OFF + SKF_AD_PKTTYPE:
@@ -242,22 +266,15 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
                break;
 
        case SKF_AD_OFF + SKF_AD_VLAN_TAG:
-       case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:
-               BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, vlan_tci) != 2);
-               BUILD_BUG_ON(VLAN_TAG_PRESENT != 0x1000);
+               cnt = convert_skb_access(SKF_AD_VLAN_TAG,
+                                        BPF_REG_A, BPF_REG_CTX, insn);
+               insn += cnt - 1;
+               break;
 
-               /* A = *(u16 *) (CTX + offsetof(vlan_tci)) */
-               *insn++ = BPF_LDX_MEM(BPF_H, BPF_REG_A, BPF_REG_CTX,
-                                     offsetof(struct sk_buff, vlan_tci));
-               if (fp->k == SKF_AD_OFF + SKF_AD_VLAN_TAG) {
-                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A,
-                                             ~VLAN_TAG_PRESENT);
-               } else {
-                       /* A >>= 12 */
-                       *insn++ = BPF_ALU32_IMM(BPF_RSH, BPF_REG_A, 12);
-                       /* A &= 1 */
-                       *insn = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 1);
-               }
+       case SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:
+               cnt = convert_skb_access(SKF_AD_VLAN_TAG_PRESENT,
+                                        BPF_REG_A, BPF_REG_CTX, insn);
+               insn += cnt - 1;
                break;
 
        case SKF_AD_OFF + SKF_AD_PAY_OFFSET:
@@ -1215,6 +1232,17 @@ static u32 sk_filter_convert_ctx_access(int dst_reg, int src_reg, int ctx_off,
 
        case offsetof(struct __sk_buff, queue_mapping):
                return convert_skb_access(SKF_AD_QUEUE, dst_reg, src_reg, insn);
+
+       case offsetof(struct __sk_buff, protocol):
+               return convert_skb_access(SKF_AD_PROTOCOL, dst_reg, src_reg, insn);
+
+       case offsetof(struct __sk_buff, vlan_present):
+               return convert_skb_access(SKF_AD_VLAN_TAG_PRESENT,
+                                         dst_reg, src_reg, insn);
+
+       case offsetof(struct __sk_buff, vlan_tci):
+               return convert_skb_access(SKF_AD_VLAN_TAG,
+                                         dst_reg, src_reg, insn);
        }
 
        return insn - insn_buf;
index df6dbb6576f663719eb3c5b926b257b21dcb3910..75d561f9fd6ae430b49248214608c00e588dd594 100644 (file)
@@ -658,6 +658,15 @@ static struct bpf_test tests[] = {
                        BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
                                    offsetof(struct __sk_buff, queue_mapping)),
                        BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, protocol)),
+                       BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, vlan_present)),
+                       BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, vlan_tci)),
+                       BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,