filter: added BPF random opcode
authorChema Gonzalez <chema@google.com>
Mon, 21 Apr 2014 16:21:24 +0000 (09:21 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 Apr 2014 01:27:57 +0000 (21:27 -0400)
Added a new ancillary load (bpf call in eBPF parlance) that produces
a 32-bit random number. We are implementing it as an ancillary load
(instead of an ISA opcode) because (a) it is simpler, (b) allows easy
JITing, and (c) seems more in line with generic ISAs that do not have
"get a random number" as a instruction, but as an OS call.

The main use for this ancillary load is to perform random packet sampling.

Signed-off-by: Chema Gonzalez <chema@google.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Acked-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/filter.txt
include/linux/filter.h
include/uapi/linux/filter.h
net/core/filter.c
tools/net/bpf_exp.l
tools/net/bpf_exp.y

index 81f940f4e88480d48c35fd7707d679d646ef0af8..82e1cb0b3da8efc9c76e2a21d424d41d6d6ebe20 100644 (file)
@@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table:
   cpu                                   raw_smp_processor_id()
   vlan_tci                              vlan_tx_tag_get(skb)
   vlan_pr                               vlan_tx_tag_present(skb)
+  rand                                  prandom_u32()
 
 These extensions can also be prefixed with '#'.
 Examples for low-level BPF:
@@ -308,6 +309,18 @@ Examples for low-level BPF:
   ret #-1
   drop: ret #0
 
+** icmp random packet sampling, 1 in 4
+  ldh [12]
+  jne #0x800, drop
+  ldb [23]
+  jneq #1, drop
+  # get a random uint32 number
+  ld rand
+  mod #4
+  jneq #1, drop
+  ret #-1
+  drop: ret #0
+
 ** SECCOMP filter example:
 
   ld [4]                  /* offsetof(struct seccomp_data, arch) */
index 024fd03e5d182d5670ee2c60005cbea43f8a83e8..759abf78dd61945422e20792f50cab73dec7c622 100644 (file)
@@ -223,6 +223,7 @@ enum {
        BPF_S_ANC_VLAN_TAG,
        BPF_S_ANC_VLAN_TAG_PRESENT,
        BPF_S_ANC_PAY_OFFSET,
+       BPF_S_ANC_RANDOM,
 };
 
 #endif /* __LINUX_FILTER_H__ */
index 8eb9ccaa5b48124b716e5abe7741aef4ca711d9f..253b4d42cf2bb31517a8a159f5e1c37f13c075f2 100644 (file)
@@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
 #define SKF_AD_VLAN_TAG        44
 #define SKF_AD_VLAN_TAG_PRESENT 48
 #define SKF_AD_PAY_OFFSET      52
-#define SKF_AD_MAX     56
+#define SKF_AD_RANDOM  56
+#define SKF_AD_MAX     60
 #define SKF_NET_OFF   (-0x100000)
 #define SKF_LL_OFF    (-0x200000)
 
index cd58614660cf54e1431392c5045bbdc9478336e6..78a636e60a0b182061358a068c2c2ed4d36051cb 100644 (file)
@@ -643,6 +643,12 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
        return raw_smp_processor_id();
 }
 
+/* note that this only generates 32-bit random numbers */
+static u64 __get_random_u32(u64 ctx, u64 A, u64 X, u64 r4, u64 r5)
+{
+       return (u64)prandom_u32();
+}
+
 /* Register mappings for user programs. */
 #define A_REG          0
 #define X_REG          7
@@ -779,6 +785,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
        case SKF_AD_OFF + SKF_AD_NLATTR:
        case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
        case SKF_AD_OFF + SKF_AD_CPU:
+       case SKF_AD_OFF + SKF_AD_RANDOM:
                /* arg1 = ctx */
                insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
                insn->a_reg = ARG1_REG;
@@ -812,6 +819,9 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
                case SKF_AD_OFF + SKF_AD_CPU:
                        insn->imm = __get_raw_cpu_id - __bpf_call_base;
                        break;
+               case SKF_AD_OFF + SKF_AD_RANDOM:
+                       insn->imm = __get_random_u32 - __bpf_call_base;
+                       break;
                }
                break;
 
@@ -1362,6 +1372,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
                        ANCILLARY(VLAN_TAG);
                        ANCILLARY(VLAN_TAG_PRESENT);
                        ANCILLARY(PAY_OFFSET);
+                       ANCILLARY(RANDOM);
                        }
 
                        /* ancillary operation unknown or unsupported */
@@ -1746,6 +1757,7 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
                [BPF_S_ANC_VLAN_TAG]    = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_ANC_PAY_OFFSET]  = BPF_LD|BPF_B|BPF_ABS,
+               [BPF_S_ANC_RANDOM]      = BPF_LD|BPF_B|BPF_ABS,
                [BPF_S_LD_W_LEN]        = BPF_LD|BPF_W|BPF_LEN,
                [BPF_S_LD_W_IND]        = BPF_LD|BPF_W|BPF_IND,
                [BPF_S_LD_H_IND]        = BPF_LD|BPF_H|BPF_IND,
index bf7be77ddd621238aa2a26ca2103b309bdcd089f..833a96611da6d042e089294990801817d0f1ade9 100644 (file)
@@ -92,6 +92,7 @@ extern void yyerror(const char *str);
 "#"?("cpu")    { return K_CPU; }
 "#"?("vlan_tci") { return K_VLANT; }
 "#"?("vlan_pr")        { return K_VLANP; }
+"#"?("rand")   { return K_RAND; }
 
 ":"            { return ':'; }
 ","            { return ','; }
index d15efc989ef500ac3f9998cf660d8e2bd22627e7..e6306c51c26f9e7cb53342ad088a9db1e4e32423 100644 (file)
@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type);
 %token OP_LDXI
 
 %token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE
-%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF
+%token K_RXHASH K_CPU K_IFIDX K_VLANT K_VLANP K_POFF K_RAND
 
 %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
 
@@ -164,6 +164,9 @@ ldb
        | OP_LDB K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDB K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldh
@@ -212,6 +215,9 @@ ldh
        | OP_LDH K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LDH K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        ;
 
 ldi
@@ -265,6 +271,9 @@ ld
        | OP_LD K_POFF {
                bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
                                   SKF_AD_OFF + SKF_AD_PAY_OFFSET); }
+       | OP_LD K_RAND {
+               bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
+                                  SKF_AD_OFF + SKF_AD_RANDOM); }
        | OP_LD 'M' '[' number ']' {
                bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
        | OP_LD '[' 'x' '+' number ']' {