bpf: BPF for lightweight tunnel encapsulation
Subsystems (6)
v3 cover bpf: BPF for lightweight tunnel encapsulation
This series implements BPF program invocation from dst entries via the lightweight tunnels infrastructure. The BPF program can be attached to lwtunnel_input(), lwtunnel_output() or lwtunnel_xmit() and see an L3 skb as context. Programs attached to input and output are read-only. Programs attached to lwtunnel_xmit() can modify and redirect, push headers and redirect packets.
The facility can be used to:
- Collect statistics and generate sampling data for a subset of traffic based on the dst utilized by the packet thus allowing to extend the existing realms.
- Apply additional per route/dst filters to prohibit certain outgoing or incoming packets based on BPF filters. In particular, this allows to maintain per dst custom state across multiple packets in BPF maps and apply filters based on statistics and behaviour observed over time.
- Attachment of L2 headers at transmit where resolving the L2 address is not required.
- Possibly many more.
v2 -> v3:
- Added real world sample lwt_len_hist_kern.c which demonstrates how to collect a histogram on packet sizes for all packets flowing through a number of routes.
- Restricted output to be read-only. Since the header can no longer be modified, the rerouting functionality has been removed again.
- Added test case which cover destructive modification of packet data.
v1 -> v2:
- Added new BPF_LWT_REROUTE return code for program to indicate that new route lookup should be performed. Suggested by Tom.
- New sample to illustrate rerouting
- New patch 05: Recursion limit for lwtunnel_output for the case when user creates circular dst redirection. Also resolves the issue for ILA.
- Fix to ensure headroom for potential future L2 header is still guaranteed
Thomas Graf (4):
route: Set orig_output when redirecting to lwt on locally generated
traffic
route: Set lwtstate for local traffic and cached input dsts
bpf: BPF for lightweight tunnel infrastructure
bpf: Add tests and samples for LWT-BPF
include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 32 +++- include/uapi/linux/lwtunnel.h | 23 +++ kernel/bpf/verifier.c | 14 +- net/Kconfig | 8 + net/core/Makefile | 1 + net/core/filter.c | 148 ++++++++++++++- net/core/lwt_bpf.c | 397 ++++++++++++++++++++++++++++++++++++++++ net/core/lwtunnel.c | 2 + net/ipv4/route.c | 37 ++-- samples/bpf/Makefile | 4 + samples/bpf/bpf_helpers.h | 4 + samples/bpf/lwt_len_hist.sh | 37 ++++ samples/bpf/lwt_len_hist_kern.c | 82 +++++++++ samples/bpf/lwt_len_hist_user.c | 76 ++++++++ samples/bpf/test_lwt_bpf.c | 247 +++++++++++++++++++++++++ samples/bpf/test_lwt_bpf.sh | 385 ++++++++++++++++++++++++++++++++++++++ 17 files changed, 1482 insertions(+), 17 deletions(-) create mode 100644 net/core/lwt_bpf.c create mode 100755 samples/bpf/lwt_len_hist.sh create mode 100644 samples/bpf/lwt_len_hist_kern.c create mode 100644 samples/bpf/lwt_len_hist_user.c create mode 100644 samples/bpf/test_lwt_bpf.c create mode 100755 samples/bpf/test_lwt_bpf.sh
Signature · 1 line
2.7.4
PATCH v3 1/4 route: Set orig_output when redirecting to lwt on locally generated traffic
orig_output for IPv4 was only set for dsts which hit an input route. Set it consistently for locally generated traffic as well to allow lwt to continue the dst_output() path as configured by the nexthop.
--- net/ipv4/route.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
net/ipv4/route.c +3 -1
@@ -2154,8 +2154,10 @@ static struct rtable *__mkroute_output(const struct fib_result *res, }rt_set_nexthop (rth, fl4->daddr, res, fnhe, fi, type,0 ); -if (lwtunnel_output_redirect (rth->dst.lwtstate)) +if (lwtunnel_output_redirect (rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_output = rth->dst.output; rth->dst.output = lwtunnel_output; + }return rth; }
Signature · 1 line
2.7.4
PATCH v3 2/4 route: Set lwtstate for local traffic and cached input dsts
In reply to Thomas GrafA route on the output path hitting a RTN_LOCAL route will keep the dst associated on its way through the loopback device. On the receive path, the dst_input() call will thus invoke the input handler of the route created in the output path. Thus, lwt redirection for input must be done for dsts allocated in the otuput path as well.
Also, if a route is cached in the input path, the allocated dst should respect lwtunnel configuration on the nexthop as well.
--- net/ipv4/route.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-)
net/ipv4/route.c +26 -13
@@ -1602,6 +1602,19 @@ static void ip_del_fnhe(struct fib_nh *nh, __be32 daddr)spin_unlock_bh (&fnhe_lock); } +static void set_lwt_redirect (struct rtable *rth) +{ +if (lwtunnel_output_redirect (rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_output = rth->dst.output; + rth->dst.output = lwtunnel_output; + } + +if (lwtunnel_input_redirect (rth->dst.lwtstate)) { + rth->dst.lwtstate->orig_input = rth->dst.input; + rth->dst.input = lwtunnel_input; + } +} +/* called in rcu_read_lock() section */ static int __mkroute_input(struct sk_buff *skb, const struct fib_result *res,
@@ -1691,14 +1704,7 @@ static int __mkroute_input(struct sk_buff *skb, rth->dst.input = ip_forward;rt_set_nexthop (rth, daddr, res, fnhe, res->fi, res->type, itag); -if (lwtunnel_output_redirect (rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_output = rth->dst.output; - rth->dst.output = lwtunnel_output; - } -if (lwtunnel_input_redirect (rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_input = rth->dst.input; - rth->dst.input = lwtunnel_input; - } +set_lwt_redirect (rth);skb_dst_set (skb, &rth->dst); out: err =0 ;
@@ -1925,8 +1931,18 @@ out: return err; rth->dst.error= -err; rth->rt_flags &= ~RTCF_LOCAL ; } +if (do_cache) { -if (unlikely (!rt_cache_route (&FIB_RES_NH (res), rth))) { +struct fib_nh *nh = &FIB_RES_NH (res); + + rth->dst.lwtstate =lwtstate_get (nh->nh_lwtstate); +if (lwtunnel_input_redirect (rth->dst.lwtstate)) { +WARN_ON (rth->dst.input == lwtunnel_input); + rth->dst.lwtstate->orig_input = rth->dst.input; + rth->dst.input = lwtunnel_input; + } + +if (unlikely (!rt_cache_route (nh, rth))) { rth->dst.flags |=DST_NOCACHE ;rt_add_uncached_list (rth); }
@@ -2154,10 +2170,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, }rt_set_nexthop (rth, fl4->daddr, res, fnhe, fi, type,0 ); -if (lwtunnel_output_redirect (rth->dst.lwtstate)) { - rth->dst.lwtstate->orig_output = rth->dst.output; - rth->dst.output = lwtunnel_output; - } +set_lwt_redirect (rth);return rth; }
Signature · 1 line
2.7.4
PATCH v3 3/4 bpf: BPF for lightweight tunnel infrastructure
In reply to Thomas GrafRegisters new BPF program types which correspond to the LWT hooks:
- BPF_PROG_TYPE_LWT_IN => dst_input()
- BPF_PROG_TYPE_LWT_OUT => dst_output()
- BPF_PROG_TYPE_LWT_XMIT => lwtunnel_xmit()
The separate program types are required to differentiate between the capabilities each LWT hook allows:
* Programs attached to dst_input() or dst_output() are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled.
* Programs attached to lwtunnel_xmit() are allowed to modify packet content as well as prepending an L2 header via a newly introduced helper bpf_skb_push(). This is safe as lwtunnel_xmit() is invoked after the IP header has been assembled completely.
All BPF programs receive an skb with L3 headers attached and may return one of the following error codes:
BPF_OK - Continue routing as per nexthop
BPF_DROP - Drop skb and return EPERM
BPF_REDIRECT - Redirect skb to device as per redirect() helper.
(Only valid in lwtunnel_xmit() context)
The return codes are binary compatible with their TC_ACT_ relatives to ease compatibility.
--- include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 32 +++- include/uapi/linux/lwtunnel.h | 23 +++ kernel/bpf/verifier.c | 14 +- net/Kconfig | 8 + net/core/Makefile | 1 + net/core/filter.c | 148 +++++++++++++++- net/core/lwt_bpf.c | 397 ++++++++++++++++++++++++++++++++++++++++++ net/core/lwtunnel.c | 2 + 9 files changed, 621 insertions(+), 6 deletions(-) create mode 100644 net/core/lwt_bpf.c
include/linux/filter.h +1 -1
@@ -438,7 +438,7 @@ struct xdp_buff { };/* compute the linear packet data range [data, data_end) which -* will be accessed by cls_bpf and act_bpf programs +* will be accessed by cls_bpf, act_bpf and lwt programs */ static inline void bpf_compute_data_end (struct sk_buff *skb) {
include/uapi/linux/bpf.h +31 -1
@@ -101,6 +101,9 @@ enum bpf_prog_type {BPF_PROG_TYPE_XDP ,BPF_PROG_TYPE_PERF_EVENT ,BPF_PROG_TYPE_CGROUP_SKB , +BPF_PROG_TYPE_LWT_IN , +BPF_PROG_TYPE_LWT_OUT , + BPF_PROG_TYPE_LWT_XMIT, }; enum bpf_attach_type {
@@ -409,6 +412,16 @@ union bpf_attr {* * int bpf_get_numa_node_id() * Return: Id of current NUMA node. +* +* int bpf_skb_push() +* Add room to beginning of skb and adjusts MAC header offset accordingly. +* Extends/reallocaes for needed skb headeroom automatically. +* May change skb data pointer and will thus invalidate any check done +* for direct packet access. +* @skb: pointer to skb +* @len: length of header to be pushed in front +* @flags: Flags (unused for now) +* Return: 0 on success or negative error */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \
@@ -453,7 +466,8 @@ union bpf_attr {FN (skb_pull_data), \FN (csum_update), \FN (set_hash_invalid), \ -FN (get_numa_node_id), +FN (get_numa_node_id), \ +FN (skb_push),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
@@ -537,6 +551,22 @@ struct bpf_tunnel_key {__u32 tunnel_label; }; +/* Generic BPF return codes which all BPF program types may support. +* The values are binary compatible with their TC_ACT_* counter-part to +* provide backwards compatibility with existing SCHED_CLS and SCHED_ACT +* programs. +* +* XDP is handled seprately, see XDP_*. +*/ +enum bpf_ret_code { +BPF_OK =0 , +/* 1 reserved */ +BPF_DROP =2 , +/* 3-6 reserved */ +BPF_REDIRECT =7 , +/* >127 are reserved for prog type specific return codes */ +}; +/* User return codes for XDP prog type. * A valid XDP program must return one of these defined values. All other * return codes are reserved for future use. Unknown return codes will result
include/uapi/linux/lwtunnel.h +23 -0
@@ -10,6 +10,7 @@ enum lwtunnel_encap_types {LWTUNNEL_ENCAP_ILA ,LWTUNNEL_ENCAP_IP6 ,LWTUNNEL_ENCAP_SEG6 , +LWTUNNEL_ENCAP_BPF , __LWTUNNEL_ENCAP_MAX, };
@@ -43,4 +44,26 @@ enum lwtunnel_ip6_t {#define LWTUNNEL_IP6_MAX (__LWTUNNEL_IP6_MAX - 1) +enum { +LWT_BPF_PROG_UNSPEC , +LWT_BPF_PROG_FD , +LWT_BPF_PROG_NAME , + __LWT_BPF_PROG_MAX, +}; + +#define LWT_BPF_PROG_MAX (__LWT_BPF_PROG_MAX - 1) + +enum { +LWT_BPF_UNSPEC , +LWT_BPF_IN , +LWT_BPF_OUT , +LWT_BPF_XMIT , +LWT_BPF_XMIT_HEADROOM , + __LWT_BPF_MAX, +}; + +#define LWT_BPF_MAX (__LWT_BPF_MAX - 1) + +#define LWT_BPF_MAX_HEADROOM 128 +#endif /* _UAPI_LWTUNNEL_H_ */
kernel/bpf/verifier.c +11 -3
@@ -633,12 +633,19 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, int off,#define MAX_PACKET_OFF 0xffffstatic bool may_access_direct_pkt_data (struct bpf_verifier_env *env, -const struct bpf_call_arg_meta *meta) +const struct bpf_call_arg_meta *meta, +enum bpf_access_type t) {switch (env->prog->type) { +case BPF_PROG_TYPE_LWT_IN : +case BPF_PROG_TYPE_LWT_OUT : +/* dst_input() and dst_output() can't write for now */ +if (t ==BPF_WRITE ) +return false;case BPF_PROG_TYPE_SCHED_CLS :case BPF_PROG_TYPE_SCHED_ACT :case BPF_PROG_TYPE_XDP : +case BPF_PROG_TYPE_LWT_XMIT :if (meta)return meta->pkt_access;
@@ -837,7 +844,7 @@ static int check_mem_access(struct bpf_verifier_env *env, u32 regno, int off, err =check_stack_read (state, off, size, value_regno); } }else if (state->regs[regno].type ==PTR_TO_PACKET ) { -if (t ==BPF_WRITE && !may_access_direct_pkt_data (env,NULL )) { +if (t ==BPF_WRITE && !may_access_direct_pkt_data (env,NULL , t)) {verbose ("cannot write into packet\n" );return -EACCES ; }
@@ -970,7 +977,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,return 0 ; } -if (type ==PTR_TO_PACKET && !may_access_direct_pkt_data (env, meta)) { +if (type ==PTR_TO_PACKET && + !may_access_direct_pkt_data (env, meta,BPF_READ )) {verbose ("helper access to the packet is not allowed\n" );return -EACCES ; }
net/Kconfig +8 -0
@@ -402,6 +402,14 @@ config LWTUNNEL weight tunnel endpoint. Tunnel encapsulation parameters are stored with light weight tunnel state associated with fib routes. +config LWTUNNEL_BPF + bool "Execute BPF program as route nexthop action" + depends on LWTUNNEL + default y if LWTUNNEL=y + ---help--- + Allows to run BPF programs as a nexthop action following a route + lookup for incoming and outgoing packets. + config DST_CACHE bool default n
net/core/Makefile +1 -0
@@ -24,6 +24,7 @@ obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o obj-$(CONFIG_LWTUNNEL) += lwtunnel.o +obj-$(CONFIG_LWTUNNEL_BPF) += lwt_bpf.o obj-$(CONFIG_DST_CACHE) += dst_cache.o obj-$(CONFIG_HWBM) += hwbm.o obj-$(CONFIG_NET_DEVLINK) += devlink.o
net/core/filter.c +147 -1
184 lines not on this page · raw message
net/core/lwt_bpf.c new file +397 -0
397 lines not on this page · raw message
net/core/lwtunnel.c +2 -0
@@ -41,6 +41,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type)return "ILA" ;case LWTUNNEL_ENCAP_SEG6 :return "SEG6" ; +case LWTUNNEL_ENCAP_BPF : +return "BPF" ;case LWTUNNEL_ENCAP_IP6 :case LWTUNNEL_ENCAP_IP :case LWTUNNEL_ENCAP_NONE :
Signature · 1 line
2.7.4
Re: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 31 lines · Thomas Graf
Registers new BPF program types which correspond to the LWT hooks:
-BPF_PROG_TYPE_LWT_IN=>dst_input()
-BPF_PROG_TYPE_LWT_OUT=>dst_output()
-BPF_PROG_TYPE_LWT_XMIT=>lwtunnel_xmit()The separate program types are required to differentiate between the capabilities each LWT hook allows:
* Programs attached to
dst_input()ordst_output()are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled.* Programs attached to
lwtunnel_xmit()are allowed to modify packet content as well as prepending an L2 header via a newly introduced helperbpf_skb_push(). This is safe aslwtunnel_xmit()is invoked after the IP header has been assembled completely.All BPF programs receive an skb with L3 headers attached and may return one of the following error codes:
BPF_OK- Continue routing as per nexthop
BPF_DROP- Drop skb and return EPERM
BPF_REDIRECT- Redirect skb to device as perredirect()helper.
(Only valid inlwtunnel_xmit()context)The return codes are binary compatible with their
TC_ACT_relatives to ease compatibility.Signed-off-by Thomas Graf <[email protected]>
...
Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128
why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.
Quoted 17 lines
net/core/lwt_bpf.c new file +17
-0+static int run_lwt_bpf (struct sk_buff *skb,struct bpf_lwt_prog *lwt, +struct dst_entry *dst,bool can_redirect) +{ +int ret; + +/* Preempt disable is needed to protect per-cpu redirect_info between +* BPF prog and skb_do_redirect(). The call_rcu in bpf_prog_put() and +* access to maps strictly require a rcu_read_lock() for protection, +* mixing with BH RCU lock doesn't work. +*/ +preempt_disable (); +rcu_read_lock (); +bpf_compute_data_end (skb); + ret =BPF_PROG_RUN (lwt->prog, skb); +rcu_read_unlock (); + +switch (ret) {
net/core/lwt_bpf.c line 68
Quoted 10 lines
net/core/lwt_bpf.c new file +10
-0+case BPF_OK : +break ; + +case BPF_REDIRECT : +if (!can_redirect) { +WARN_ONCE (1 ,"Illegal redirect return code in prog %s\n" , + lwt->name ? :"<unknown>" ); + ret =BPF_OK ; + }else { + ret =skb_do_redirect (skb);
I think this assumes that program did bpf_skb_push and L2 header is present. Would it make sense to check that mac_header < network_header here to make sure that it actually happened? I think the cost of single 'if' isn't much. Also skb_do_redirect() can redirect to l3 tunnels like ipip ;) so program shouldn't be doing bpf_skb_push in such case...
May be rename bpf_skb_push to bpf_skb_push_l2 ?
since it's doing skb_reset_mac_header(skb); at the end of it?
Or it's probably better to use 'flags' argument to tell whether bpf_skb_push() should set mac_header or not ? Then this bit:
net/core/lwt_bpf.c line 167
Quoted 5 lines
net/core/lwt_bpf.c new file +5
-0+case BPF_OK : +/* If the L3 header was expanded, headroom might be too +* small for L2 header now, expand as needed. +*/ + ret =xmit_check_hhlen (skb);
will work fine as well...
which probably needs "mac_header wasn't set" check? or it's fine?
All bpf bits look great. Thanks!
John Fastabend
reviewerRe: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 31 lines · Alexei Starovoitov
Quoted 30 lines · Thomas Graf
Registers new BPF program types which correspond to the LWT hooks:
-BPF_PROG_TYPE_LWT_IN=>dst_input()
-BPF_PROG_TYPE_LWT_OUT=>dst_output()
-BPF_PROG_TYPE_LWT_XMIT=>lwtunnel_xmit()The separate program types are required to differentiate between the capabilities each LWT hook allows:
* Programs attached to
dst_input()ordst_output()are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled.* Programs attached to
lwtunnel_xmit()are allowed to modify packet content as well as prepending an L2 header via a newly introduced helperbpf_skb_push(). This is safe aslwtunnel_xmit()is invoked after the IP header has been assembled completely.All BPF programs receive an skb with L3 headers attached and may return one of the following error codes:
BPF_OK- Continue routing as per nexthop
BPF_DROP- Drop skb and return EPERM
BPF_REDIRECT- Redirect skb to device as perredirect()helper.
(Only valid inlwtunnel_xmit()context)The return codes are binary compatible with their
TC_ACT_relatives to ease compatibility.
Quoted 7 lines
Quoted 1 line
Signed-off-by Thomas Graf <[email protected]>...
Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.
hopefully not too off-topic but for XDP I would like to see this get passed down with the program. It would be more generic and drivers could configure the headroom on demand and more importantly verify that a program pushing data is not going to fail at runtime.
.John
Re: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 33 lines · John Fastabend
Quoted 32 lines · Alexei Starovoitov
Quoted 31 lines · Thomas Graf
Registers new BPF program types which correspond to the LWT hooks:
-BPF_PROG_TYPE_LWT_IN=>dst_input()
-BPF_PROG_TYPE_LWT_OUT=>dst_output()
-BPF_PROG_TYPE_LWT_XMIT=>lwtunnel_xmit()The separate program types are required to differentiate between the capabilities each LWT hook allows:
* Programs attached to
dst_input()ordst_output()are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled.* Programs attached to
lwtunnel_xmit()are allowed to modify packet content as well as prepending an L2 header via a newly introduced helperbpf_skb_push(). This is safe aslwtunnel_xmit()is invoked after the IP header has been assembled completely.All BPF programs receive an skb with L3 headers attached and may return one of the following error codes:
BPF_OK- Continue routing as per nexthop
BPF_DROP- Drop skb and return EPERM
BPF_REDIRECT- Redirect skb to device as perredirect()helper.
(Only valid inlwtunnel_xmit()context)The return codes are binary compatible with their
TC_ACT_relatives to ease compatibility.Signed-off-by Thomas Graf <[email protected]>
Quoted 8 lines
Quoted 6 lines
...
Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.hopefully not too off-topic but for XDP I would like to see this get
definitely off-topic. lwt->headroom is existing concept. Too late to do anything about it.
Quoted 3 lines
passed down with the program. It would be more generic and drivers could configure the headroom on demand and more importantly verify that a program pushing data is not going to fail at runtime.
For xdp I think it will be problematic, since we'd have to check for that value at prog array access to make sure tailcalls are not broken. Mix and match won't be possible.
So what does 'configure the headroom on demand' buys us? Isn't it much easier to tell all drivers "always reserve this much" ? We burn the page anyway.
If it's configurable per driver, then we'd need an api
to retrieve it. Yet the program author doesn't care what the value is. If program needs to do udp encap, it will try do it. No matter what. If xdp_adjust_head() helper fails, the program will likely decide to drop the packet. In some cases it may decide to punt to stack for further processing, but for high performance dataplane code it's highly unlikely.
If it's configurable to something that is not cache line boundary hw dma performance may suffer and so on.
So I see only cons in such 'configurable headroom' and propose to have fixed 256 bytes headroom for XDP
which is enough for any sensible encap and metadata.
John Fastabend
reviewerRe: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 60 lines · Alexei Starovoitov
Quoted 33 lines · John Fastabend
Quoted 32 lines · Alexei Starovoitov
Quoted 31 lines · Thomas Graf
Registers new BPF program types which correspond to the LWT hooks:
-BPF_PROG_TYPE_LWT_IN=>dst_input()
-BPF_PROG_TYPE_LWT_OUT=>dst_output()
-BPF_PROG_TYPE_LWT_XMIT=>lwtunnel_xmit()The separate program types are required to differentiate between the capabilities each LWT hook allows:
* Programs attached to
dst_input()ordst_output()are restricted and may only read the data of an skb. This prevent modification and possible invalidation of already validated packet headers on receive and the construction of illegal headers while the IP headers are still being assembled.* Programs attached to
lwtunnel_xmit()are allowed to modify packet content as well as prepending an L2 header via a newly introduced helperbpf_skb_push(). This is safe aslwtunnel_xmit()is invoked after the IP header has been assembled completely.All BPF programs receive an skb with L3 headers attached and may return one of the following error codes:
BPF_OK- Continue routing as per nexthop
BPF_DROP- Drop skb and return EPERM
BPF_REDIRECT- Redirect skb to device as perredirect()helper.
(Only valid inlwtunnel_xmit()context)The return codes are binary compatible with their
TC_ACT_relatives to ease compatibility.Signed-off-by Thomas Graf <[email protected]>Quoted 8 lines
Quoted 6 lines
...
Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.hopefully not too off-topic but for XDP I would like to see this get
definitely off-topic.
lwt->headroomis existing concept. Too late to do anything about it.Quoted 3 lines
passed down with the program. It would be more generic and drivers could configure the headroom on demand and more importantly verify that a program pushing data is not going to fail at runtime.
For xdp I think it will be problematic, since we'd have to check for that value at prog array access to make sure tailcalls are not broken. Mix and match won't be possible.
So what does 'configure the headroom on demand' buys us? Isn't it much easier to tell all drivers "always reserve this much" ? We burn the page anyway.
If it's configurable per driver, then we'd need an api
to retrieve it. Yet the program author doesn't care what the value is. If program needs to do udp encap, it will try do it. No matter what. Ifxdp_adjust_head()helper fails, the program will likely decide
Quoted 9 lines
to drop the packet. In some cases it may decide to punt to stack for further processing, but for high performance dataplane code it's highly unlikely.
If it's configurable to something that is not cache line boundary hw dma performance may suffer and so on.
So I see only cons in such 'configurable headroom' and propose to have fixed 256 bytes headroom for XDP
which is enough for any sensible encap and metadata.
OK I'm convinced let it be fixed at some conservative value.
Re: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
In reply to Alexei StarovoitovQuoted 6 lines · Alexei Starovoitov
On Tue, Nov 29, 2016 at 02:21:22PM +0100, Thomas Graf wrote:
...Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.
It's an arbitrary limit to catch obvious misconfiguration. I'm absolutely fine with bumping it to 256.
Quoted 25 lines
Quoted 25 lines
net/core/lwt_bpf.c new file +25
-0+static int run_lwt_bpf (struct sk_buff *skb,struct bpf_lwt_prog *lwt, +struct dst_entry *dst,bool can_redirect) +{ +int ret; + +/* Preempt disable is needed to protect per-cpu redirect_info between +* BPF prog and skb_do_redirect(). The call_rcu in bpf_prog_put() and +* access to maps strictly require a rcu_read_lock() for protection, +* mixing with BH RCU lock doesn't work. +*/ +preempt_disable (); +rcu_read_lock (); +bpf_compute_data_end (skb); + ret =BPF_PROG_RUN (lwt->prog, skb); +rcu_read_unlock (); + +switch (ret) { +case BPF_OK : +break ; + +case BPF_REDIRECT : +if (!can_redirect) { +WARN_ONCE (1 ,"Illegal redirect return code in prog %s\n" , + lwt->name ? :"<unknown>" ); + ret =BPF_OK ;
Quoted 8 lines
net/core/lwt_bpf.c line 68
Quoted 2 lines
net/core/lwt_bpf.c new file +2
-0+ }else { + ret =skb_do_redirect (skb);I think this assumes that program did
bpf_skb_pushand L2 header is present. Would it make sense to check thatmac_header<network_headerhere to make sure that it actually happened? I think the cost of single 'if' isn't much. Alsoskb_do_redirect()can redirect to l3 tunnels like ipip ;) so program shouldn't be doingbpf_skb_pushin such case...
We are currently guaranteeing mac_header <= network_header given that bpf_skb_push() is calling skb_reset_mac_header() unconditionally.
Even if a program were to push an L2 header and then redirect to an l3 tunnel, __bpf_redirect_no_mac will pull it off again and correct the mac_header offset.
Should we check in __bpf_redirect_common() whether mac_header < nework_header then or add it to lwt-bpf conditional on dev_is_mac_header_xmit()?
Quoted 4 lines
May be rename
bpf_skb_pushtobpf_skb_push_l2?
since it's doingskb_reset_mac_header(skb); at the end of it? Or it's probably better to use 'flags' argument to tell whetherbpf_skb_push()should setmac_headeror not ?
I added the flags for this purpose but left it unused for now. This will allow to add a flag later to extend the l3 header prior to pushing an l2 header, hence the current helper name. But even then, we would always reset the mac header as well to ensure we never leave an skb with mac_header > network_header. Are you seeing a scenario where we would want to omit the mac header reset?
Quoted 10 lines
Then this bit:
net/core/lwt_bpf.c line 167
Quoted 5 lines
net/core/lwt_bpf.c new file +5
-0+case BPF_OK : +/* If the L3 header was expanded, headroom might be too +* small for L2 header now, expand as needed. +*/ + ret =xmit_check_hhlen (skb);will work fine as well...
which probably needs "mac_headerwasn't set" check? or it's fine?
Right. The check is not strictly required right now but is a safety net to ensure that the skb passed to dst_neigh_output() which will add the l2 header in the non-redirect case to always have sufficient headroom. It will currently be a NOP as we are not allowing to extend the l3 header in bpf_skb_push() yet. I left this in there to ensure that we are not missing to add this later.
Re: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 45 lines · Thomas Graf
Quoted 6 lines · Alexei Starovoitov
On Tue, Nov 29, 2016 at 02:21:22PM +0100, Thomas Graf wrote:
...Quoted 1 line
+#define
LWT_BPF_MAX_HEADROOM128why 128?
btw I'm thinking for XDP to use 256, so metadata can be stored in there.It's an arbitrary limit to catch obvious misconfiguration. I'm absolutely fine with bumping it to 256.
Quoted 25 lines
Quoted 25 lines
net/core/lwt_bpf.c new file +25
-0+static int run_lwt_bpf (struct sk_buff *skb,struct bpf_lwt_prog *lwt, +struct dst_entry *dst,bool can_redirect) +{ +int ret; + +/* Preempt disable is needed to protect per-cpu redirect_info between +* BPF prog and skb_do_redirect(). The call_rcu in bpf_prog_put() and +* access to maps strictly require a rcu_read_lock() for protection, +* mixing with BH RCU lock doesn't work. +*/ +preempt_disable (); +rcu_read_lock (); +bpf_compute_data_end (skb); + ret =BPF_PROG_RUN (lwt->prog, skb); +rcu_read_unlock (); + +switch (ret) { +case BPF_OK : +break ; + +case BPF_REDIRECT : +if (!can_redirect) { +WARN_ONCE (1 ,"Illegal redirect return code in prog %s\n" , + lwt->name ? :"<unknown>" ); + ret =BPF_OK ;Quoted 8 lines
net/core/lwt_bpf.c line 68
Quoted 2 lines
net/core/lwt_bpf.c new file +2
-0+ }else { + ret =skb_do_redirect (skb);I think this assumes that program did
bpf_skb_pushand L2 header is present. Would it make sense to check thatmac_header<network_headerhere to make sure that it actually happened? I think the cost of single 'if' isn't much. Alsoskb_do_redirect()can redirect to l3 tunnels like ipip ;) so program shouldn't be doingbpf_skb_pushin such case...
Quoted 6 lines
We are currently guaranteeing
mac_header<=network_headergiven thatbpf_skb_push()is callingskb_reset_mac_header()unconditionally.Even if a program were to push an L2 header and then redirect to an l3 tunnel,
__bpf_redirect_no_macwill pull it off again and correct themac_headeroffset.
yes. that part is fine.
Quoted 3 lines
Should we check in
__bpf_redirect_common()whethermac_header<nework_headerthen or add it to lwt-bpf conditional ondev_is_mac_header_xmit()?
may be only extra 'if' in lwt-bpf is all we need?
I'm still missing what will happen if we 'forget' to do bpf_skb_push() inside the lwt-bpf program, but still do redirect in lwt_xmit stage to l2 netdev...
Re: [PATCH net-next v3 3/4] bpf: BPF for lightweight tunnel infrastructure
Quoted 6 lines · Alexei Starovoitov
Quoted 3 lines · Thomas Graf
Should we check in
__bpf_redirect_common()whethermac_header<nework_headerthen or add it to lwt-bpf conditional ondev_is_mac_header_xmit()?may be only extra 'if' in lwt-bpf is all we need?
Agreed, I will add a mac_header < network_header check to lwt-bpf if we redirect to an l2 device.
Quoted 3 lines
I'm still missing what will happen if we 'forget' to do
bpf_skb_push()inside the lwt-bpf program, but still do redirect inlwt_xmitstage to l2 netdev...
The same as for a AF_PACKET socket not providing an actual L2 header. I will add a test case to cover this scenario as well.
PATCH v3 4/4 bpf: Add tests and samples for LWT-BPF
In reply to Thomas GrafAdds a series of test to verify the functionality of attaching BPF programs at LWT hooks.
Also adds a sample which collects a histogram of packet sizes which pass through an LWT hook.
$ ./lwt_len_hist.sh Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.253.2 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec
87380 16384 16384 10.00 39857.69
1 -> 1 : 0 | |
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 0 | |
16 -> 31 : 0 | |
32 -> 63 : 22 | |
64 -> 127 : 98 | |
128 -> 255 : 213 | |
256 -> 511 : 1444251 |******** |
512 -> 1023 : 660610 |*** |
1024 -> 2047 : 535241 |** |
2048 -> 4095 : 19 | |
4096 -> 8191 : 180 | |
8192 -> 16383 : 5578023 |************************************* |
16384 -> 32767 : 632099 |*** |
32768 -> 65535 : 6575 | |--- samples/bpf/Makefile | 4 + samples/bpf/bpf_helpers.h | 4 + samples/bpf/lwt_len_hist.sh | 37 ++++ samples/bpf/lwt_len_hist_kern.c | 82 +++++++++ samples/bpf/lwt_len_hist_user.c | 76 ++++++++ samples/bpf/test_lwt_bpf.c | 247 ++++++++++++++++++++++++++ samples/bpf/test_lwt_bpf.sh | 385 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 835 insertions(+) create mode 100755 samples/bpf/lwt_len_hist.sh create mode 100644 samples/bpf/lwt_len_hist_kern.c create mode 100644 samples/bpf/lwt_len_hist_user.c create mode 100644 samples/bpf/test_lwt_bpf.c create mode 100755 samples/bpf/test_lwt_bpf.sh
samples/bpf/Makefile +4 -0
@@ -29,6 +29,7 @@ hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip hostprogs-y += tc_l2_redirect +hostprogs-y += lwt_len_hist test_lru_dist-objs := test_lru_dist.o libbpf.o sock_example-objs := sock_example.o libbpf.o
@@ -59,6 +60,7 @@ test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ trace_event-objs := bpf_load.o libbpf.o trace_event_user.o sampleip-objs := bpf_load.o libbpf.o sampleip_user.o tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o +lwt_len_hist-objs := bpf_load.o libbpf.o lwt_len_hist_user.o # Tell kbuild to always build the programs always := $(hostprogs-y)
@@ -89,6 +91,7 @@ always += xdp2_kern.o always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o +always += lwt_len_hist_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(objtree)/tools/testing/selftests/bpf/
@@ -117,6 +120,7 @@ HOSTLOADLIBES_test_current_task_under_cgroup += -lelf HOSTLOADLIBES_trace_event += -lelf HOSTLOADLIBES_sampleip += -lelf HOSTLOADLIBES_tc_l2_redirect += -l elf +HOSTLOADLIBES_lwt_len_hist += -l elf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
samples/bpf/bpf_helpers.h +4 -0
@@ -80,6 +80,8 @@ struct bpf_map_def {unsigned int map_flags; }; +static int (*bpf_skb_load_bytes)(void *ctx,int off,void *to,int len) = + (void *) BPF_FUNC_skb_load_bytes;static int (*bpf_skb_store_bytes)(void *ctx,int off,void *from,int len,int flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) =
@@ -88,6 +90,8 @@ static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flag (void *) BPF_FUNC_l4_csum_replace;static int (*bpf_skb_under_cgroup)(void *ctx,void *map,int index) = (void *) BPF_FUNC_skb_under_cgroup; +static int (*bpf_skb_push)(void *,int len,int flags) = + (void *) BPF_FUNC_skb_push;#if defined(__x86_64__)
samples/bpf/lwt_len_hist.sh new file +37 -0
@@ -0,0 +1,37 @@ +#!/bin/bash + +NS1=lwt_ns1 +VETH0=tst_lwt1a +VETH1=tst_lwt1b + +TRACE_ROOT=/sys/kernel/debug/tracing + +function cleanup { + ip route del 192.168.253.2/32 dev $VETH0 2> /dev/null + ip link del $VETH0 2> /dev/null + ip link del $VETH1 2> /dev/null + ip netns exec $NS1 killall netserver + ip netns delete $NS1 2> /dev/null +} + +cleanup + +ip netns add $NS1 +ip link add $VETH0 type veth peer name $VETH1 +ip link set dev $VETH0 up +ip addr add 192.168.253.1/24 dev $VETH0 +ip link set $VETH1 netns $NS1 +ip netns exec $NS1 ip link set dev $VETH1 up +ip netns exec $NS1 ip addr add 192.168.253.2/24 dev $VETH1 +ip netns exec $NS1 netserver + +echo 1 > ${TRACE_ROOT}/tracing_on +cp /dev/null ${TRACE_ROOT}/trace +ip route add 192.168.253.2/32 encap bpf out obj lwt_len_hist_kern.o section len_hist dev $VETH0 +netperf -H 192.168.253.2 -t TCP_STREAM +cat ${TRACE_ROOT}/trace | grep -v '^#' +./lwt_len_hist +cleanup +echo 0 > ${TRACE_ROOT}/tracing_on + +exit 0
samples/bpf/lwt_len_hist_kern.c new file +82 -0
82 lines not on this page · raw message
samples/bpf/lwt_len_hist_user.c new file +76 -0
76 lines not on this page · raw message
samples/bpf/test_lwt_bpf.c new file +247 -0
247 lines not on this page · raw message
samples/bpf/test_lwt_bpf.sh new file +385 -0
385 lines not on this page · raw message
Signature · 1 line
2.7.4
Re: [PATCH net-next v3 4/4] bpf: Add tests and samples for LWT-BPF
Quoted 14 lines · Thomas Graf
Adds a series of test to verify the functionality of attaching BPF programs at LWT hooks.
Also adds a sample which collects a histogram of packet sizes which pass through an LWT hook.
$ ./lwt_len_hist.sh Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.253.2 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec
Quoted 1 line
87380 16384 16384 10.00 39857.69
Nice!
samples/bpf/test_lwt_bpf.c line 193
Quoted 5 lines
samples/bpf/test_lwt_bpf.c new file +5
-0+ ret =bpf_redirect (ifindex,0 ); +if (ret <0 ) { +printk ("bpf_redirect() failed: %d\n" , ret); +return BPF_DROP ; + }
this 'if' looks a bit weird. You're passing 0 as flags, so this helper will always succeed.
Other sample code often does 'return bpf_redirect(...)' due to this reasoning.
Re: [PATCH net-next v3 4/4] bpf: Add tests and samples for LWT-BPF
Quoted 19 lines · Alexei Starovoitov
Quoted 14 lines · Thomas Graf
Adds a series of test to verify the functionality of attaching BPF programs at LWT hooks.
Also adds a sample which collects a histogram of packet sizes which pass through an LWT hook.
$ ./lwt_len_hist.sh Starting netserver with host 'IN(6)ADDR_ANY' port '12865' and family AF_UNSPEC MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 192.168.253.2 () port 0 AF_INET : demo Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/secQuoted 1 line
87380 16384 16384 10.00 39857.69
Nice!
Quoted 10 lines
samples/bpf/test_lwt_bpf.c line 193
Quoted 5 lines
samples/bpf/test_lwt_bpf.c new file +5
-0+ ret =bpf_redirect (ifindex,0 ); +if (ret <0 ) { +printk ("bpf_redirect() failed: %d\n" , ret); +return BPF_DROP ; + }this 'if' looks a bit weird. You're passing 0 as flags, so this helper will always succeed.
Other sample code often does 'returnbpf_redirect(...)' due to this reasoning.
Right, the if branch is absolutely useless. I will remove it.
Hi Thomas,
Quoted 7 lines · Thomas Graf
This series implements BPF program invocation from dst entries via the lightweight tunnels infrastructure. The BPF program can be attached to
lwtunnel_input(),lwtunnel_output()orlwtunnel_xmit()and see an L3 skb as context. Programs attached to input and output are read-only. Programs attached tolwtunnel_xmit()can modify and redirect, push headers and redirect packets.
Quoted 11 lines
The facility can be used to:
- Collect statistics and generate sampling data for a subset of traffic based on the dst utilized by the packet thus allowing to extend the existing realms.
- Apply additional per route/dst filters to prohibit certain outgoing or incoming packets based on BPF filters. In particular, this allows to maintain per dst custom state across multiple packets in BPF maps and apply filters based on statistics and behaviour observed over time.
- Attachment of L2 headers at transmit where resolving the L2 address is not required.
- Possibly many more.
Did you look at the cgroup based hooks which were added recently in ip_finish_output for cgroup ebpf support and in general the cgroup bpf subsystem. Does some of this solve the problem for you already? Would be interesting to hear your opinion on that.
Thanks,
Hannes
Hi Hannes,
Quoted 4 lines · Hannes Frederic Sowa
Did you look at the cgroup based hooks which were added recently in
ip_finish_outputfor cgroup ebpf support and in general the cgroup bpf subsystem. Does some of this solve the problem for you already? Would be interesting to hear your opinion on that.
What I'm looking for is the ability to collect statistics and generate samples for a subset of the traffic, e.g. all intra data center traffic, all packets hitting the default route in a network namespace, all packets which use a dst tying a certain endpoint to particular TCP metrics. For the examples above, LWT provides a very intuitive and natural way to do so while amortizing the cost of the route lookup which is required anyway.
The cgroup hook provides similar semantics but if the application context is of interest. Obviously, tasks in a cgroup may be sharing routes so I can't use it as a replacement. However, using the two in combination will become highly useful as it allows to gather statistics individually for both application context and routing context and then aggregate them to see how applications are using different network segments.
Aside from the different context matching, the cgroup hook will not allow to modify the packet as the lwtunnel_xmit() post ip_finish_output does.