filter: added BPF random opcode
Subsystems (6)
PATCH 1/1 filter: added BPF random opcode
This should allow random packet sampling.
--- Documentation/networking/filter.txt | 1 + include/linux/filter.h | 1 + include/uapi/linux/filter.h | 3 ++- net/core/filter.c | 12 ++++++++++++ tools/net/bpf_exp.l | 1 + tools/net/bpf_exp.y | 11 ++++++++++- tools/net/icmp_random.bpf | 12 ++++++++++++ 7 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tools/net/icmp_random.bpf
Documentation/networking/filter.txt +1 -0
@@ -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) + random prandom_u32() These extensions can also be prefixed with '#'. Examples for low-level BPF:
include/linux/filter.h +1 -0
@@ -224,6 +224,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__ */
include/uapi/linux/filter.h +2 -1
@@ -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)
net/core/filter.c +12 -0
@@ -637,6 +637,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 __skb_get_random (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
@@ -773,6 +779,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 ;
@@ -806,6 +813,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 = __skb_get_random - __bpf_call_base; +break ; }break ;
@@ -1356,6 +1366,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 */
@@ -1741,6 +1752,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,
tools/net/bpf_exp.l +1 -0
@@ -92,6 +92,7 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("random") { return K_RAND; } ":" { return ':'; } "," { return ','; }
tools/net/bpf_exp.y +10 -1
@@ -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 ']' {
tools/net/icmp_random.bpf new file +12 -0
@@ -0,0 +1,12 @@ +# icmp random packet sampling, 1 in 4 +ldh [12] +jne #0x800, drop +ldb [23] +jneq #1, drop +# get a random uint32 number +ld random +mod #4 +jneq #1, drop +ret #-1 +drop: ret #0 +
Signature · 1 line
1.9.1.423.g4596e3a
Hi Chema,
[cc'ing Alexei as well]
note, net-next is still closed, so you might need to resend this later on again when it opens up.
Quoted 70 lines · Chema Gonzalez
This should allow random packet sampling.
Signed-off-by Chema Gonzalez <[email protected]>--- Documentation/networking/filter.txt | 1 + include/linux/filter.h | 1 + include/uapi/linux/filter.h | 3 ++- net/core/filter.c | 12 ++++++++++++ tools/net/bpf_exp.l | 1 + tools/net/bpf_exp.y | 11 ++++++++++- tools/net/icmp_random.bpf | 12 ++++++++++++ 7 files changed, 39 insertions(+), 2 deletions(-)create mode 100644
tools/net/icmp_random.bpfDocumentation/networking/filter.txt +1
-0@@ -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) + random prandom_u32() These extensions can also be prefixed with '#'. Examples for low-level BPF:include/linux/filter.h +1
-0@@ -224,6 +224,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__ */ include/uapi/linux/filter.h +2
-1@@ -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)net/core/filter.c +6
-0@@ -637,6 +637,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 __skb_get_random (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
net/core/filter.c static bool convert_bpf_extensions(struct sock_filter *fp, · line 782
Quoted 4 lines
net/core/filter.c +1
-0@@ -773,6 +779,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 :
I think instead of a function call, this sould rather be modelled directly into the internal insn set and thus converted differently, so we can spare us the call.
Quoted 34 lines
net/core/filter.c +5
-0/* arg1 = ctx */ insn->code =BPF_ALU64 |BPF_MOV |BPF_X ; insn->a_reg =ARG1_REG ;@@ -806,6 +813,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 = __skb_get_random - __bpf_call_base; +break ; }break ;@@ -1356,6 +1366,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 */ @@ -1741,6 +1752,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,tools/net/bpf_exp.l +0
-0
tools/net/bpf_exp.l extern void yyerror(const char *str); · line 95
Quoted 4 lines
tools/net/bpf_exp.l +1
-0@@ -92,6 +92,7 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("random") { return K_RAND; }
Thanks for also updating bpf_asm ! :)
I think using just "rnd" is cleaner here.
Quoted 54 lines
tools/net/bpf_exp.l +0
-0":" { return ':'; } "," { return ','; }tools/net/bpf_exp.y +10
-1@@ -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 ']' {tools/net/icmp_random.bpf new file +2
-0@@ -0,0 +1,12 @@ +# icmp random packet sampling, 1 in 4 +ldh [12]
tools/net/icmp_random.bpf line 12
Quoted 10 lines
tools/net/icmp_random.bpf new file +10
-0+jne #0x800, drop +ldb [23] +jneq #1, drop +# get a random uint32 number +ld random +mod #4 +jneq #1, drop +ret #-1 +drop: ret #0 +
This example should rather go into Documentation/networking/filter.txt's example section, rather than tools/net/ .
Quoted 9 lines · Daniel Borkmann
net/core/filter.c static bool convert_bpf_extensions(struct sock_filter *fp, · line 782
Quoted 5 lines
net/core/filter.c +1
-0@@ -773,6 +779,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 :I think instead of a function call, this sould rather be modelled directly into the internal insn set and thus converted differently, so we can spare us the call.
Hmmm... this would need percpu storage, thus preempt disable/enable calls, and prandom_u32_state() is about 40 instructions.
This is really not worth the pain.
Quoted 12 lines · Eric Dumazet
Quoted 9 lines · Daniel Borkmann
net/core/filter.c static bool convert_bpf_extensions(struct sock_filter *fp, · line 782
Quoted 5 lines
net/core/filter.c +1
-0@@ -773,6 +779,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 :I think instead of a function call, this sould rather be modelled directly into the internal insn set and thus converted differently, so we can spare us the call.
Quoted 4 lines
Hmmm... this would need percpu storage, thus preempt disable/enable calls, and
prandom_u32_state()is about 40 instructions.This is really not worth the pain.
Absolutely, that was not what I meant actually. Calling to prandom_u32_state() is fine, no need to have another prng just for that. I was just wondering if it makes sense to model that directly as an instruction into a jump-table target that calls prandom_u32() from there instead 'indirectly'. Need to think about this a bit more ...
Quoted 23 lines · Daniel Borkmann
Quoted 16 lines · Eric Dumazet
Quoted 11 lines · Daniel Borkmann
net/core/filter.c static bool convert_bpf_extensions(struct · line 783
Quoted 6 lines
net/core/filter.c +1
-0@@ -773,6 +779,7 @@ static bool convert_bpf_extensions(structsock_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:I think instead of a function call, this sould rather be modelled directly into the internal insn set and thus converted differently, so we can spare us the call.
Quoted 4 lines
Hmmm... this would need percpu storage, thus preempt disable/enable calls, and
prandom_u32_state()is about 40 instructions.This is really not worth the pain.
Quoted 6 lines
Absolutely, that was not what I meant actually. Calling to
prandom_u32_state()is fine, no need to have another prng just for that. I was just wondering if it makes sense to model that directly as an instruction into a jump-table target that callsprandom_u32()from there instead 'indirectly'. Need to think about this a bit more ...
I thought about that. In fact, the original patch (written for FreeBSD) worked by extending the ISA with a new load mode that did "A = random();". After seeing your eBPF work, I got convinced that we want to get a generic ISA when possible, where random is typically not an insn. Also, the bpf_call approach makes it much easier to integrate with the different JITs.
-Chema
Quoted 6 lines · Daniel Borkmann
Hi Chema,
[cc'ing Alexei as well]
note, net-next is still closed, so you might need to resend this later on again when it opens up.
I will. Thanks!
Quoted 5 lines · Chema Gonzalez
Quoted 4 lines
tools/net/bpf_exp.l +0
-0
Quoted 8 lines
tools/net/bpf_exp.l extern void yyerror(const char *str); · line 95
Quoted 5 lines
tools/net/bpf_exp.l +1
-0@@ -92,6 +92,7 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("random") { return K_RAND; }Thanks for also updating
bpf_asm! :)
bpf_asm was key in ensuring this worked. In fact, we should modify the Makefile so that it relies on the local filter.h before the /usr/include/ one (my Ubuntu Precise /usr/include/linux/filter.h does not define BPF_MOD, BPF_XOR, SKF_AD_VLAN_TAG, SKF_AD_VLAN_TAG_PRESENT, or SKF_AD_PAY_OFFSET).
Quoted 1 line
I think using just "rnd" is cleaner here.
I think using always random (in fact I may s/K_RAND/K_RANDOM/g) allows easier grepping.
Quoted 14 lines
Quoted 14 lines
tools/net/icmp_random.bpf new file +8
-0@@ -0,0 +1,12 @@ +# icmp random packet sampling, 1 in 4 +ldh [12] +jne #0x800, drop +ldb [23] +jneq #1, drop +# get a random uint32 number +ld random +mod #4
Quoted 8 lines
tools/net/icmp_random.bpf line 12
Quoted 5 lines
tools/net/icmp_random.bpf new file +4
-0+jneq #1, drop +ret #-1 +drop: ret #0 +This example should rather go into Documentation/networking/filter.txt's example section, rather than
tools/net/.
I'll add it there.
Now, I'd like to add a new directory for BPF programs (tools/net/bpf/ ?). It allows playing with bpf_asm.
-Chema
Quoted 8 lines · Chema Gonzalez
Quoted 6 lines · Daniel Borkmann
Hi Chema,
[cc'ing Alexei as well]
note, net-next is still closed, so you might need to resend this later on again when it opens up.
I will. Thanks!
Great, thanks.
Quoted 13 lines
Quoted 5 lines · Chema Gonzalez
Quoted 4 lines
tools/net/bpf_exp.l +0
-0Quoted 8 lines
tools/net/bpf_exp.l extern void yyerror(const char *str); · line 95
Quoted 5 lines
tools/net/bpf_exp.l +1
-0@@ -92,6 +92,7 @@ extern void yyerror(const char *str); "#"?("cpu") { return K_CPU; } "#"?("vlan_tci") { return K_VLANT; } "#"?("vlan_pr") { return K_VLANP; } +"#"?("random") { return K_RAND; }Thanks for also updating
bpf_asm! :)
Quoted 5 lines
bpf_asmwas key in ensuring this worked. In fact, we should modify theMakefileso that it relies on the localfilter.hbefore the/usr/include/one (my Ubuntu Precise/usr/include/linux/filter.hdoes not defineBPF_MOD,BPF_XOR,SKF_AD_VLAN_TAG,SKF_AD_VLAN_TAG_PRESENT, orSKF_AD_PAY_OFFSET).
Good point, feel free to send a patch. :)
Quoted 3 lines
Quoted 1 line
I think using just "rnd" is cleaner here.
I think using always random (in fact I may s/K_RAND/K_RANDOM/g) allows easier grepping.
I know it's just nitpicking, but I'd be happier if that would either be rnd or rand in bpf_asm.
Quoted 22 lines
Quoted 14 lines
Quoted 14 lines
tools/net/icmp_random.bpf new file +8
-0@@ -0,0 +1,12 @@ +# icmp random packet sampling, 1 in 4 +ldh [12] +jne #0x800, drop +ldb [23] +jneq #1, drop +# get a random uint32 number +ld random +mod #4Quoted 8 lines
tools/net/icmp_random.bpf line 12
Quoted 5 lines
tools/net/icmp_random.bpf new file +4
-0+jneq #1, drop +ret #-1 +drop: ret #0 +This example should rather go into Documentation/networking/filter.txt's example section, rather than
tools/net/.
Quoted 1 line
I'll add it there.
Ok, thanks.
Quoted 2 lines
Now, I'd like to add a new directory for BPF programs (
tools/net/bpf/?). It allows playing withbpf_asm.
For 'playing', I'd rather be happy to add that to the documentation file in Documentation/networking/filter.txt where we have other examples, or alternatively, if there is really no other way around, a new folder inside of Documentation/networking/ .
Quoted 8 lines · Daniel Borkmann
Quoted 4 lines
Quoted 1 line
I think using just "rnd" is cleaner here.
I think using always random (in fact I may s/K_RAND/K_RANDOM/g) allows easier grepping.
I know it's just nitpicking, but I'd be happier if that would either be rnd or rand in
bpf_asm.
"rand" it is.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
This example should rather go into Documentation/networking/filter.txt's example section, rather than
tools/net/.I'll add it there.
Ok, thanks.
Done.
Quoted 4 lines
For 'playing', I'd rather be happy to add that to the documentation file in
Documentation/networking/filter.txtwhere we have other examples, or alternatively, if there is really no other way around, a new folder inside ofDocumentation/networking/.
I think it would be better close to the tools/net/bpf* tools. Anyway, I removed it for now.
-Chema