Commit 4cd3675e authored by Chema Gonzalez's avatar Chema Gonzalez Committed by David S. Miller

filter: added BPF random opcode

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: default avatarChema Gonzalez <chema@google.com>
Acked-by: default avatarAlexei Starovoitov <ast@plumgrid.com>
Acked-by: default avatarDaniel Borkmann <dborkman@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5a4ae5f6
...@@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table: ...@@ -281,6 +281,7 @@ Possible BPF extensions are shown in the following table:
cpu raw_smp_processor_id() cpu raw_smp_processor_id()
vlan_tci vlan_tx_tag_get(skb) vlan_tci vlan_tx_tag_get(skb)
vlan_pr vlan_tx_tag_present(skb) vlan_pr vlan_tx_tag_present(skb)
rand prandom_u32()
These extensions can also be prefixed with '#'. These extensions can also be prefixed with '#'.
Examples for low-level BPF: Examples for low-level BPF:
...@@ -308,6 +309,18 @@ Examples for low-level BPF: ...@@ -308,6 +309,18 @@ Examples for low-level BPF:
ret #-1 ret #-1
drop: ret #0 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: ** SECCOMP filter example:
ld [4] /* offsetof(struct seccomp_data, arch) */ ld [4] /* offsetof(struct seccomp_data, arch) */
......
...@@ -223,6 +223,7 @@ enum { ...@@ -223,6 +223,7 @@ enum {
BPF_S_ANC_VLAN_TAG, BPF_S_ANC_VLAN_TAG,
BPF_S_ANC_VLAN_TAG_PRESENT, BPF_S_ANC_VLAN_TAG_PRESENT,
BPF_S_ANC_PAY_OFFSET, BPF_S_ANC_PAY_OFFSET,
BPF_S_ANC_RANDOM,
}; };
#endif /* __LINUX_FILTER_H__ */ #endif /* __LINUX_FILTER_H__ */
...@@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */ ...@@ -130,7 +130,8 @@ struct sock_fprog { /* Required for SO_ATTACH_FILTER. */
#define SKF_AD_VLAN_TAG 44 #define SKF_AD_VLAN_TAG 44
#define SKF_AD_VLAN_TAG_PRESENT 48 #define SKF_AD_VLAN_TAG_PRESENT 48
#define SKF_AD_PAY_OFFSET 52 #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_NET_OFF (-0x100000)
#define SKF_LL_OFF (-0x200000) #define SKF_LL_OFF (-0x200000)
......
...@@ -643,6 +643,12 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 A, u64 X, u64 r4, u64 r5) ...@@ -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(); 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. */ /* Register mappings for user programs. */
#define A_REG 0 #define A_REG 0
#define X_REG 7 #define X_REG 7
...@@ -779,6 +785,7 @@ static bool convert_bpf_extensions(struct sock_filter *fp, ...@@ -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:
case SKF_AD_OFF + SKF_AD_NLATTR_NEST: case SKF_AD_OFF + SKF_AD_NLATTR_NEST:
case SKF_AD_OFF + SKF_AD_CPU: case SKF_AD_OFF + SKF_AD_CPU:
case SKF_AD_OFF + SKF_AD_RANDOM:
/* arg1 = ctx */ /* arg1 = ctx */
insn->code = BPF_ALU64 | BPF_MOV | BPF_X; insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
insn->a_reg = ARG1_REG; insn->a_reg = ARG1_REG;
...@@ -812,6 +819,9 @@ static bool convert_bpf_extensions(struct sock_filter *fp, ...@@ -812,6 +819,9 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
case SKF_AD_OFF + SKF_AD_CPU: case SKF_AD_OFF + SKF_AD_CPU:
insn->imm = __get_raw_cpu_id - __bpf_call_base; insn->imm = __get_raw_cpu_id - __bpf_call_base;
break; break;
case SKF_AD_OFF + SKF_AD_RANDOM:
insn->imm = __get_random_u32 - __bpf_call_base;
break;
} }
break; break;
...@@ -1362,6 +1372,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) ...@@ -1362,6 +1372,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
ANCILLARY(VLAN_TAG); ANCILLARY(VLAN_TAG);
ANCILLARY(VLAN_TAG_PRESENT); ANCILLARY(VLAN_TAG_PRESENT);
ANCILLARY(PAY_OFFSET); ANCILLARY(PAY_OFFSET);
ANCILLARY(RANDOM);
} }
/* ancillary operation unknown or unsupported */ /* ancillary operation unknown or unsupported */
...@@ -1746,6 +1757,7 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) ...@@ -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] = BPF_LD|BPF_B|BPF_ABS,
[BPF_S_ANC_VLAN_TAG_PRESENT] = 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_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_LEN] = BPF_LD|BPF_W|BPF_LEN,
[BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND, [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND,
[BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND, [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND,
......
...@@ -92,6 +92,7 @@ extern void yyerror(const char *str); ...@@ -92,6 +92,7 @@ extern void yyerror(const char *str);
"#"?("cpu") { return K_CPU; } "#"?("cpu") { return K_CPU; }
"#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_tci") { return K_VLANT; }
"#"?("vlan_pr") { return K_VLANP; } "#"?("vlan_pr") { return K_VLANP; }
"#"?("rand") { return K_RAND; }
":" { return ':'; } ":" { return ':'; }
"," { return ','; } "," { return ','; }
......
...@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type); ...@@ -56,7 +56,7 @@ static void bpf_set_jmp_label(char *label, enum jmp_type type);
%token OP_LDXI %token OP_LDXI
%token K_PKT_LEN K_PROTO K_TYPE K_NLATTR K_NLATTR_NEST K_MARK K_QUEUE K_HATYPE %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' '*' '&' '#' '%' %token ':' ',' '[' ']' '(' ')' 'x' 'a' '+' 'M' '*' '&' '#' '%'
...@@ -164,6 +164,9 @@ ldb ...@@ -164,6 +164,9 @@ ldb
| OP_LDB K_POFF { | OP_LDB K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0, bpf_set_curr_instr(BPF_LD | BPF_B | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); } 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 ldh
...@@ -212,6 +215,9 @@ ldh ...@@ -212,6 +215,9 @@ ldh
| OP_LDH K_POFF { | OP_LDH K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0, bpf_set_curr_instr(BPF_LD | BPF_H | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); } 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 ldi
...@@ -265,6 +271,9 @@ ld ...@@ -265,6 +271,9 @@ ld
| OP_LD K_POFF { | OP_LD K_POFF {
bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0, bpf_set_curr_instr(BPF_LD | BPF_W | BPF_ABS, 0, 0,
SKF_AD_OFF + SKF_AD_PAY_OFFSET); } 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 ']' { | OP_LD 'M' '[' number ']' {
bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); } bpf_set_curr_instr(BPF_LD | BPF_MEM, 0, 0, $4); }
| OP_LD '[' 'x' '+' number ']' { | OP_LD '[' 'x' '+' number ']' {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment