BPF updates
Subsystems (6)
Daniel Borkmann
maintainercover BPF updates
This set contains various updates for eBPF, i.e. the addition of a generic csum helper function and other misc bits that mostly improve existing helpers and ease programming with eBPF on cls_bpf. For more details, please see individual patches.
Set is rebased on top of http://patchwork.ozlabs.org/patch/584465/.
Thanks!
Daniel Borkmann (6):
bpf: add new arg_type that allows for 0 sized stack buffer
bpf: add generic bpf_csum_diff helper
bpf: remove artificial bpf_skb_{load,store}_bytes buffer limitation
bpf: try harder on clones when writing into skb
bpf: fix csum update in bpf_l4_csum_replace helper for udp
bpf: don't emit mov A,A on return
include/linux/bpf.h | 1 + include/linux/skbuff.h | 7 ++++ include/uapi/linux/bpf.h | 12 ++++++ kernel/bpf/verifier.c | 42 ++++++++++++++----- net/core/filter.c | 103 ++++++++++++++++++++++++++++++++++++++--------- net/sched/act_csum.c | 8 +--- net/sched/act_nat.c | 18 +++------ 7 files changed, 142 insertions(+), 49 deletions(-)
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 1/6 bpf: add new arg_type that allows for 0 sized stack buffer
Currently, when we pass a buffer from the eBPF stack into a helper function, the function proto indicates argument types as ARG_PTR_TO_STACK and ARG_CONST_STACK_SIZE pair. If R<X> contains the former, then R<X+1> must be of the latter type. Then, verifier checks whether the buffer points into eBPF stack, is initialized, etc. The verifier also guarantees that the constant value passed in R<X+1> is greater than 0, so helper functions don't need to test for it and can always assume a non-NULL initialized buffer as well as non-0 buffer size.
This patch adds a new argument types ARG_CONST_STACK_SIZE_OR_ZERO that allows to also pass NULL as R<X> and 0 as R<X+1> into the helper function. Such helper functions, of course, need to be able to handle these cases internally then. Verifier guarantees that either R<X> == NULL && R<X+1> == 0 or R<X> != NULL && R<X+1> != 0 (like the case of ARG_CONST_STACK_SIZE), any other combinations are not possible to load.
I went through various options of extending the verifier, and introducing the type ARG_CONST_STACK_SIZE_OR_ZERO seems to have most minimal changes needed to the verifier.
--- include/linux/bpf.h | 1 + kernel/bpf/verifier.c | 42 ++++++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-)
include/linux/bpf.h +1 -0
@@ -65,6 +65,7 @@ enum bpf_arg_type {*/ ARG_PTR_TO_STACK ,/* any pointer to eBPF program stack */ ARG_CONST_STACK_SIZE ,/* number of bytes accessed from stack */ +ARG_CONST_STACK_SIZE_OR_ZERO ,/* number of bytes accessed from stack or 0 */ ARG_PTR_TO_CTX ,/* pointer to context */ ARG_ANYTHING, /* any (initialized) argument is ok */
kernel/bpf/verifier.c +32 -10
86 lines not on this page · raw message
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 2/6 bpf: add generic bpf_csum_diff helper
In reply to Daniel BorkmannFor L4 checksums, we currently have bpf_l4_csum_replace() helper. It's currently limited to handle 2 and 4 byte changes in a header and feeds the from/to into inet_proto_csum_replace{2,4}() helpers of the kernel. When working with IPv6, for example, this makes it rather cumbersome to deal with, similarly when editing larger parts of a header.
Instead, extend the API in a more generic way: For bpf_l4_csum_replace(), add a case for header field mask of 0 to change the checksum at a given offset through inet_proto_csum_replace_by_diff(), and provide a helper bpf_csum_diff() that can generically calculate a from/to diff for arbitrary amounts of data.
This can be used in multiple ways: for the bpf_l4_csum_replace() only part, this even provides us with the option to insert precalculated diffs from user space f.e. from a map, or from bpf_csum_diff() during runtime.
bpf_csum_diff() has a optional from/to stack buffer input, so we can calculate a diff by using a scratchbuffer for scenarios where we're inserting (from is NULL), removing (to is NULL) or diffing (from/to buffers don't need to be of equal size) data. Also, bpf_csum_diff() allows to feed a previous csum into csum_partial(), so the function can also be cascaded.
--- include/uapi/linux/bpf.h | 11 ++++++++++ net/core/filter.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+)
include/uapi/linux/bpf.h +11 -0
@@ -287,6 +287,17 @@ enum bpf_func_id {* Return: >= 0 stackid on success or negative error */ BPF_FUNC_get_stackid, + +/** +* bpf_csum_diff(from, from_size, to, to_size, seed) - calculate csum diff +* @from: raw from buffer +* @from_size: length of from buffer +* @to: raw to buffer +* @to_size: length of to buffer +* @seed: optional seed +* Return: csum result +*/ + BPF_FUNC_csum_diff, __BPF_FUNC_MAX_ID, };
net/core/filter.c +53 -0
71 lines not on this page · raw message
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 3/6 bpf: remove artificial bpf_skb_{load,store}_bytes buffer limitation
In reply to Daniel BorkmannWe currently limit bpf_skb_store_bytes() and bpf_skb_load_bytes() helpers to only store or load a maximum buffer of 16 bytes. Thus, loading, rewriting and storing headers require several bpf_skb_load_bytes() and bpf_skb_store_bytes() calls.
Also here we can use a per-cpu scratch buffer instead in order to not pressure stack space any further. I do suspect that this limit was mainly set in place for this particular reason. So, ease program development by removing this limitation and make the scratchpad generic, so it can be reused.
--- net/core/filter.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-)
net/core/filter.c +14 -13
@@ -1333,15 +1333,22 @@ int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)return 0 ; } -#define BPF_LDST_LEN 16U +struct bpf_scratchpad { +union { +__be32 diff[MAX_BPF_STACK /sizeof (__be32)]; +u8 buff[MAX_BPF_STACK ]; + }; +}; + +static DEFINE_PER_CPU(struct bpf_scratchpad, bpf_sp);static u64 bpf_skb_store_bytes (u64 r1,u64 r2,u64 r3,u64 r4,u64 flags) { +struct bpf_scratchpad *sp =this_cpu_ptr (&bpf_sp);struct sk_buff *skb = (struct sk_buff *) (long ) r1;int offset = (int ) r2;void *from = (void *) (long ) r3;unsigned int len = (unsigned int ) r4; -char buf[BPF_LDST_LEN ];void *ptr;if (unlikely (flags & ~(BPF_F_RECOMPUTE_CSUM )))
@@ -1355,14 +1362,14 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)* * so check for invalid 'offset' and too large 'len' */ -if (unlikely ((u32 ) offset >0xffff || len >sizeof (buf))) +if (unlikely ((u32 ) offset >0xffff || len >sizeof (sp->buff)))return -EFAULT ;if (unlikely (skb_cloned (skb) && !skb_clone_writable (skb, offset + len)))return -EFAULT ; - ptr =skb_header_pointer (skb, offset, len, buf); + ptr =skb_header_pointer (skb, offset, len, sp->buff);if (unlikely (!ptr))return -EFAULT ;
@@ -1371,7 +1378,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)memcpy (ptr, from, len); -if (ptr == buf) +if (ptr == sp->buff)/* skb_store_bits cannot return -EFAULT here */ skb_store_bits (skb, offset, ptr, len);
@@ -1400,7 +1407,7 @@ static u64 bpf_skb_load_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)unsigned int len = (unsigned int ) r4;void *ptr; -if (unlikely ((u32 ) offset >0xffff || len >BPF_LDST_LEN )) +if (unlikely ((u32 ) offset >0xffff || len >MAX_BPF_STACK ))return -EFAULT ; ptr =skb_header_pointer (skb, offset, len, to);
@@ -1525,15 +1532,9 @@ const struct bpf_func_proto bpf_l4_csum_replace_proto = { .arg5_type = ARG_ANYTHING, }; -struct bpf_csum_scratchpad { -__be32 diff[128 ]; -}; - -static DEFINE_PER_CPU(struct bpf_csum_scratchpad, bpf_csum_sp); -static u64 bpf_csum_diff (u64 r1,u64 from_size,u64 r3,u64 to_size,u64 seed) { -struct bpf_csum_scratchpad *sp =this_cpu_ptr (&bpf_csum_sp); +struct bpf_scratchpad *sp =this_cpu_ptr (&bpf_sp);u64 diff_size = from_size + to_size;__be32 *from = (__be32 *) (long ) r1;__be32 *to = (__be32 *) (long ) r3;
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 4/6 bpf: try harder on clones when writing into skb
In reply to Daniel BorkmannWhen we're dealing with clones and the area is not writeable, try harder and get a copy via pskb_expand_head(). Replace also other occurences in tc actions with the new skb_try_make_writable().
--- include/linux/skbuff.h | 7 +++++++ net/core/filter.c | 19 ++++++++++--------- net/sched/act_csum.c | 8 ++------ net/sched/act_nat.c | 18 +++++------------- 4 files changed, 24 insertions(+), 28 deletions(-)
include/linux/skbuff.h +7 -0
@@ -2630,6 +2630,13 @@ static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int lenskb_headroom (skb) + len <= skb->hdr_len; } +static inline int skb_try_make_writable (struct sk_buff *skb, +unsigned int write_len) +{ +return skb_cloned (skb) && !skb_clone_writable (skb, write_len) && +pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC ); +} +static inline int __skb_cow (struct sk_buff *skb,unsigned int headroom,int cloned) {
net/core/filter.c +10 -9
@@ -1364,9 +1364,7 @@ static u64 bpf_skb_store_bytes(u64 r1, u64 r2, u64 r3, u64 r4, u64 flags)*/ if (unlikely ((u32 ) offset >0xffff || len >sizeof (sp->buff)))return -EFAULT ; - -if (unlikely (skb_cloned (skb) && - !skb_clone_writable (skb, offset + len))) +if (unlikely (skb_try_make_writable (skb, offset + len)))return -EFAULT ; ptr =skb_header_pointer (skb, offset, len, sp->buff);
@@ -1439,9 +1437,7 @@ static u64 bpf_l3_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)return -EINVAL ;if (unlikely ((u32 ) offset >0xffff ))return -EFAULT ; - -if (unlikely (skb_cloned (skb) && - !skb_clone_writable (skb, offset +sizeof (sum)))) +if (unlikely (skb_try_make_writable (skb, offset +sizeof (sum))))return -EFAULT ; ptr =skb_header_pointer (skb, offset,sizeof (sum), &sum);
@@ -1488,9 +1484,7 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)return -EINVAL ;if (unlikely ((u32 ) offset >0xffff ))return -EFAULT ; - -if (unlikely (skb_cloned (skb) && - !skb_clone_writable (skb, offset +sizeof (sum)))) +if (unlikely (skb_try_make_writable (skb, offset +sizeof (sum))))return -EFAULT ; ptr =skb_header_pointer (skb, offset,sizeof (sum), &sum);
@@ -1734,6 +1728,13 @@ bool bpf_helper_changes_skb_data(void *func)return true;if (func == bpf_skb_vlan_pop)return true; +if (func == bpf_skb_store_bytes) +return true; +if (func == bpf_l3_csum_replace) +return true; +if (func == bpf_l4_csum_replace) +return true; +return false; }
net/sched/act_csum.c +2 -6
@@ -105,9 +105,7 @@ static void *tcf_csum_skb_nextlayer(struct sk_buff *skb,int hl = ihl + jhl;if (!pskb_may_pull (skb, ipl + ntkoff) || (ipl < hl) || - (skb_cloned (skb) && - !skb_clone_writable (skb, hl + ntkoff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC ))) +skb_try_make_writable (skb, hl + ntkoff))return NULL ;else return (void *)(skb_network_header (skb) + ihl);
@@ -365,9 +363,7 @@ static int tcf_csum_ipv4(struct sk_buff *skb, u32 update_flags) }if (update_flags &TCA_CSUM_UPDATE_FLAG_IPV4HDR ) { -if (skb_cloned (skb) && - !skb_clone_writable (skb,sizeof (*iph) + ntkoff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC )) +if (skb_try_make_writable (skb,sizeof (*iph) + ntkoff)) goto fail;ip_send_check (ip_hdr (skb));
net/sched/act_nat.c +5 -13
@@ -126,9 +126,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a, addr = iph->daddr;if (!((old_addr ^ addr) & mask)) { -if (skb_cloned (skb) && - !skb_clone_writable (skb,sizeof (*iph) + noff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC )) +if (skb_try_make_writable (skb,sizeof (*iph) + noff)) goto drop; new_addr &= mask;
@@ -156,9 +154,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,struct tcphdr *tcph;if (!pskb_may_pull (skb, ihl +sizeof (*tcph) + noff) || - (skb_cloned (skb) && - !skb_clone_writable (skb, ihl +sizeof (*tcph) + noff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC ))) +skb_try_make_writable (skb, ihl +sizeof (*tcph) + noff)) goto drop; tcph = (void *)(skb_network_header (skb) + ihl);
@@ -171,9 +167,7 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,struct udphdr *udph;if (!pskb_may_pull (skb, ihl +sizeof (*udph) + noff) || - (skb_cloned (skb) && - !skb_clone_writable (skb, ihl +sizeof (*udph) + noff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC ))) +skb_try_make_writable (skb, ihl +sizeof (*udph) + noff)) goto drop; udph = (void *)(skb_network_header (skb) + ihl);
@@ -213,10 +207,8 @@ static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,if ((old_addr ^ addr) & mask)break ; -if (skb_cloned (skb) && - !skb_clone_writable (skb, ihl +sizeof (*icmph) + -sizeof (*iph) + noff) && -pskb_expand_head (skb,0 ,0 ,GFP_ATOMIC )) +if (skb_try_make_writable (skb, ihl +sizeof (*icmph) + +sizeof (*iph) + noff)) goto drop; icmph = (void *)(skb_network_header (skb) + ihl);
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 5/6 bpf: fix csum update in bpf_l4_csum_replace helper for udp
In reply to Daniel BorkmannWhen using this helper for updating UDP checksums, we need to extend this in order to write CSUM_MANGLED_0 for csum computations that result into 0 as sum. Reason we need this is because packets with a checksum could otherwise become incorrectly marked as a packet without a checksum. Likewise, if the user indicates BPF_F_MARK_MANGLED_0, then we should not turn packets without a checksum into ones with a checksum.
--- include/uapi/linux/bpf.h | 1 + net/core/filter.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-)
include/uapi/linux/bpf.h +1 -0
@@ -313,6 +313,7 @@ enum bpf_func_id {/* BPF_FUNC_l4_csum_replace flags. */ #define BPF_F_PSEUDO_HDR (1ULL << 4) +#define BPF_F_MARK_MANGLED_0 (1ULL << 5)/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ #define BPF_F_INGRESS (1ULL << 0)
net/core/filter.c +7 -1
@@ -1477,10 +1477,12 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags) {struct sk_buff *skb = (struct sk_buff *) (long ) r1;bool is_pseudo = flags &BPF_F_PSEUDO_HDR ; +bool is_mmzero = flags &BPF_F_MARK_MANGLED_0 ;int offset = (int ) r2;__sum16 sum, *ptr; -if (unlikely (flags & ~(BPF_F_PSEUDO_HDR |BPF_F_HDR_FIELD_MASK ))) +if (unlikely (flags & ~(BPF_F_MARK_MANGLED_0 |BPF_F_PSEUDO_HDR | +BPF_F_HDR_FIELD_MASK )))return -EINVAL ;if (unlikely ((u32 ) offset >0xffff ))return -EFAULT ;
@@ -1490,6 +1492,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags) ptr =skb_header_pointer (skb, offset,sizeof (sum), &sum);if (unlikely (!ptr))return -EFAULT ; +if (is_mmzero && !*ptr) +return 0 ;switch (flags &BPF_F_HDR_FIELD_MASK ) {case 0 :
@@ -1508,6 +1512,8 @@ static u64 bpf_l4_csum_replace(u64 r1, u64 r2, u64 from, u64 to, u64 flags)return -EINVAL ; } +if (is_mmzero && !*ptr) + *ptr =CSUM_MANGLED_0 ;if (ptr == &sum)/* skb_store_bits guaranteed to not return -EFAULT here */ skb_store_bits (skb, offset, ptr,sizeof (sum));
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 6/6 bpf: don't emit mov A,A on return
In reply to Daniel BorkmannWhile debugging with bpf_jit_disasm I noticed emissions of 'mov %eax,%eax', and found that this comes from BPF_RET | BPF_A translations from classic BPF. Emitting this is unnecessary as BPF_REG_A is mapped into BPF_REG_0 already, therefore only emit a mov when immediates are used as return value.
--- net/core/filter.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
net/core/filter.c +6 -4
@@ -530,12 +530,14 @@ do_pass: *insn =BPF_MOV64_REG (BPF_REG_A ,BPF_REG_TMP );break ; -/* RET_K, RET_A are remaped into 2 insns. */ +/* RET_K is remaped into 2 insns. RET_A case doesn't need an +* extra mov as BPF_REG_0 is already mapped into BPF_REG_A. +*/ case BPF_RET |BPF_A :case BPF_RET |BPF_K : - *insn++ =BPF_MOV32_RAW (BPF_RVAL (fp->code) ==BPF_K ? -BPF_K :BPF_X ,BPF_REG_0 , -BPF_REG_A , fp->k); +if (BPF_RVAL (fp->code) ==BPF_K ) + *insn++ =BPF_MOV32_RAW (BPF_K ,BPF_REG_0 , +0 , fp->k); *insn =BPF_EXIT_INSN ();break ;
Signature · 1 line
1.9.3
David Miller
maintainerFrom: Daniel Borkmann <[email protected]>
Date: Fri, 19 Feb 2016 23:05:21 +0100
Quoted 6 lines
This set contains various updates for eBPF, i.e. the addition of a generic csum helper function and other misc bits that mostly improve existing helpers and ease programming with eBPF on
cls_bpf. For more details, please see individual patches.Set is rebased on top of http://patchwork.ozlabs.org/patch/584465/.
Series applied, thanks Daniel.