bpf: Add helper to do FIB lookups
Subsystems (9)
v2 cover bpf: Add helper to do FIB lookups
Provide a helper for doing a FIB and neighbor lookups in the kernel tables from an XDP program. The helper provides a fastpath for forwarding packets. If the packet is a local delivery or for any reason is not a simple lookup and forward, the packet is expected to continue up the stack for full processing.
Patches 1-6 do some more refactoring to IPv6 with the end goal of extracting a FIB lookup function that aligns with fib_lookup for IPv4, basically returning a fib6_info without creating a dst based entry.
Patch 7 adds lookup functions to the ipv6 stub. These are needed since bpf is built into the kernel and ipv6 may not be built or loaded.
Patch 8 adds the bpf helper and 9 adds a sample program.
v2
- fixed use of foward helper from cls_act as noted by Daniel
- in patch 1 rename fib6_lookup_1 as well for consistency
David Ahern (9):
net/ipv6: Rename fib6_lookup to fib6_node_lookup net/ipv6: Rename rt6_multipath_select
net/ipv6: Extract table lookup from ip6_pol_route net/ipv6: Refactor fib6_rule_action
net/ipv6: Add fib6_lookup
net/ipv6: Update fib6 tracepoint to take fib6_info net/ipv6: Add fib lookup stubs for use in bpf helper
bpf: Provide helper to do lookups in kernel FIB table samples/bpf: Add examples of ipv4 and ipv6 forwarding in XDP
include/net/addrconf.h | 14 ++ include/net/ip6_fib.h | 21 ++- include/trace/events/fib6.h | 14 +- include/uapi/linux/bpf.h | 83 +++++++++- net/core/filter.c | 263 ++++++++++++++++++++++++++++++ net/ipv6/addrconf_core.c | 33 +++- net/ipv6/af_inet6.c | 6 +- net/ipv6/fib6_rules.c | 138 +++++++++++++--- net/ipv6/ip6_fib.c | 21 ++- net/ipv6/route.c | 76 +++++---- samples/bpf/Makefile | 4 + samples/bpf/xdp_fwd_kern.c | 110 +++++++++++++ samples/bpf/xdp_fwd_user.c | 136 +++++++++++++++ tools/testing/selftests/bpf/bpf_helpers.h | 3 + 14 files changed, 847 insertions(+), 75 deletions(-) create mode 100644 samples/bpf/xdp_fwd_kern.c create mode 100644 samples/bpf/xdp_fwd_user.c
Signature · 1 line
2.11.0
RFC v2 1/9 net/ipv6: Rename fib6_lookup to fib6_node_lookup
Rename fib6_lookup to fib6_node_lookup to better reflect what it returns. The fib6_lookup name will be used in a later patch for an IPv6 equivalent to IPv4's fib_lookup.
--- include/net/ip6_fib.h | 6 +++--- net/ipv6/ip6_fib.c | 14 ++++++++------ net/ipv6/route.c | 8 ++++---- 3 files changed, 15 insertions(+), 13 deletions(-)
include/net/ip6_fib.h +3 -3
@@ -376,9 +376,9 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,const struct sk_buff *skb, int flags, pol_lookup_t lookup); -struct fib6_node *fib6_lookup (struct fib6_node *root, -const struct in6_addr *daddr, -const struct in6_addr *saddr); +struct fib6_node *fib6_node_lookup (struct fib6_node *root, +const struct in6_addr *daddr, +const struct in6_addr *saddr); struct fib6_node *fib6_locate(struct fib6_node *root, const struct in6_addr *daddr, int dst_len,
net/ipv6/ip6_fib.c +8 -6
@@ -1354,8 +1354,8 @@ struct lookup_args {const struct in6_addr *addr;/* search key */ }; -static struct fib6_node *fib6_lookup_1 (struct fib6_node *root, -struct lookup_args *args) +static struct fib6_node *fib6_node_lookup_1 (struct fib6_node *root, +struct lookup_args *args) {struct fib6_node *fn;__be32 dir;
@@ -1400,7 +1400,8 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,#ifdef CONFIG_IPV6_SUBTREES if (subtree) {struct fib6_node *sfn; - sfn =fib6_lookup_1 (subtree, args +1 ); + sfn =fib6_node_lookup_1 (subtree, + args +1 );if (!sfn) goto backtrack; fn = sfn;
@@ -1422,8 +1423,9 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,/* called with rcu_read_lock() held */ -struct fib6_node *fib6_lookup (struct fib6_node *root,const struct in6_addr *daddr, -const struct in6_addr *saddr) +struct fib6_node *fib6_node_lookup (struct fib6_node *root, +const struct in6_addr *daddr, +const struct in6_addr *saddr) {struct fib6_node *fn;struct lookup_args args[] = {
@@ -1442,7 +1444,7 @@ struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *dad } }; - fn =fib6_lookup_1 (root, daddr ? args : args +1 ); + fn =fib6_node_lookup_1 (root, daddr ? args : args +1 );if (!fn || fn->fn_flags &RTN_TL_ROOT ) fn = root;
net/ipv6/route.c +4 -4
@@ -1006,7 +1006,7 @@ static struct fib6_node* fib6_backtrack(struct fib6_node *fn, pn =rcu_dereference (fn->parent); sn =FIB6_SUBTREE (pn);if (sn && sn != fn) - fn =fib6_lookup (sn,NULL , saddr); + fn =fib6_node_lookup (sn,NULL , saddr);else fn = pn;if (fn->fn_flags &RTN_RTINFO )
@@ -1059,7 +1059,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, flags &= ~RT6_LOOKUP_F_IFACE ;rcu_read_lock (); - fn =fib6_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); + fn =fib6_node_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); restart: f6i =rcu_dereference (fn->leaf);if (!f6i) {
@@ -1815,7 +1815,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,rcu_read_lock (); - fn =fib6_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); + fn =fib6_node_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn;if (fl6->flowi6_flags &FLOWI_FLAG_SKIP_NH_OIF )
@@ -2420,7 +2420,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,*/ rcu_read_lock (); - fn =fib6_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); + fn =fib6_node_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); restart:for_each_fib6_node_rt_rcu (fn) {if (rt->fib6_nh.nh_flags &RTNH_F_DEAD )
Signature · 1 line
2.11.0
RFC v2 2/9 net/ipv6: Rename rt6_multipath_select
In reply to David AhernRename rt6_multipath_select to fib6_multipath_select and export it. A later patch wants access to it similar to IPv4's fib_select_path.
--- include/net/ip6_fib.h | 5 +++++ net/ipv6/route.c | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-)
include/net/ip6_fib.h +5 -0
@@ -376,6 +376,11 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,const struct sk_buff *skb, int flags, pol_lookup_t lookup); +struct fib6_info *fib6_multipath_select (const struct net *net, +struct fib6_info *match, +struct flowi6 *fl6,int oif, +const struct sk_buff *skb,int strict); +struct fib6_node *fib6_node_lookup (struct fib6_node *root,const struct in6_addr *daddr,const struct in6_addr *saddr);
net/ipv6/route.c +9 -8
@@ -419,11 +419,11 @@ static bool rt6_check_expired(const struct rt6_info *rt)return false; } -static struct fib6_info *rt6_multipath_select (const struct net *net, -struct fib6_info *match, -struct flowi6 *fl6,int oif, -const struct sk_buff *skb, -int strict) +struct fib6_info *fib6_multipath_select (const struct net *net, +struct fib6_info *match, +struct flowi6 *fl6,int oif, +const struct sk_buff *skb, +int strict) {struct fib6_info *sibling, *next_sibling;
@@ -1068,8 +1068,9 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, f6i =rt6_device_match (net, f6i, &fl6->saddr, fl6->flowi6_oif, flags);if (f6i->fib6_nsiblings && fl6->flowi6_oif ==0 ) - f6i =rt6_multipath_select (net, f6i, fl6, - fl6->flowi6_oif, skb, flags); + f6i =fib6_multipath_select (net, f6i, fl6, + fl6->flowi6_oif, skb, + flags); }if (f6i == net->ipv6.fib6_null_entry) { fn =fib6_backtrack (fn, &fl6->saddr);
@@ -1824,7 +1825,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, redo_rt6_select: f6i =rt6_select (net, fn, oif, strict);if (f6i->fib6_nsiblings) - f6i =rt6_multipath_select (net, f6i, fl6, oif, skb, strict); + f6i =fib6_multipath_select (net, f6i, fl6, oif, skb, strict);if (f6i == net->ipv6.fib6_null_entry) { fn =fib6_backtrack (fn, &fl6->saddr);if (fn)
Signature · 1 line
2.11.0
RFC v2 3/9 net/ipv6: Extract table lookup from ip6_pol_route
In reply to David Ahernip6_pol_route is used for ingress and egress FIB lookups. Refactor it moving the table lookup into a separate fib6_table_lookup that can be invoked separately and export the new function.
ip6_pol_route now calls fib6_table_lookup and uses the result to generate a dst based rt6_info.
--- include/net/ip6_fib.h | 4 ++++ net/ipv6/route.c | 39 +++++++++++++++++++++++++-------------- 2 files changed, 29 insertions(+), 14 deletions(-)
include/net/ip6_fib.h +4 -0
@@ -376,6 +376,10 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,const struct sk_buff *skb, int flags, pol_lookup_t lookup); +/* called with rcu lock held; caller needs to select path */ +struct fib6_info *fib6_table_lookup (struct net *net,struct fib6_table *table, +int oif,struct flowi6 *fl6,int strict); + struct fib6_info *fib6_multipath_select(const struct net *net, struct fib6_info *match, struct flowi6 *fl6, int oif,
net/ipv6/route.c +25 -14
@@ -1800,21 +1800,12 @@ void rt6_age_exceptions(struct fib6_info *rt,rcu_read_unlock_bh (); } -struct rt6_info *ip6_pol_route (struct net *net,struct fib6_table *table, -int oif,struct flowi6 *fl6, -const struct sk_buff *skb,int flags) +/* must be called with rcu lock held */ +struct fib6_info *fib6_table_lookup (struct net *net,struct fib6_table *table, +int oif,struct flowi6 *fl6,int strict) {struct fib6_node *fn, *saved_fn;struct fib6_info *f6i; -struct rt6_info *rt; -int strict =0 ; - - strict |= flags &RT6_LOOKUP_F_IFACE ; - strict |= flags &RT6_LOOKUP_F_IGNORE_LINKSTATE ; -if (net->ipv6.devconf_all->forwarding ==0 ) - strict |=RT6_LOOKUP_F_REACHABLE ; - -rcu_read_lock (); fn =fib6_node_lookup (&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn;
@@ -1824,8 +1815,6 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, redo_rt6_select: f6i =rt6_select (net, fn, oif, strict); -if (f6i->fib6_nsiblings) - f6i =fib6_multipath_select (net, f6i, fl6, oif, skb, strict);if (f6i == net->ipv6.fib6_null_entry) { fn =fib6_backtrack (fn, &fl6->saddr);if (fn)
@@ -1838,6 +1827,28 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, } } +return f6i; +} + +struct rt6_info *ip6_pol_route (struct net *net,struct fib6_table *table, +int oif,struct flowi6 *fl6, +const struct sk_buff *skb,int flags) +{ +struct fib6_info *f6i; +struct rt6_info *rt; +int strict =0 ; + + strict |= flags &RT6_LOOKUP_F_IFACE ; + strict |= flags &RT6_LOOKUP_F_IGNORE_LINKSTATE ; +if (net->ipv6.devconf_all->forwarding ==0 ) + strict |=RT6_LOOKUP_F_REACHABLE ; + +rcu_read_lock (); + + f6i =fib6_table_lookup (net, table, oif, fl6, strict); +if (f6i->fib6_nsiblings) + f6i =fib6_multipath_select (net, f6i, fl6, oif, skb, strict); +if (f6i == net->ipv6.fib6_null_entry) { rt = net->ipv6.ip6_null_entry;rcu_read_unlock ();
Signature · 1 line
2.11.0
RFC v2 4/9 net/ipv6: Refactor fib6_rule_action
In reply to David AhernMove source address lookup from fib6_rule_action to a helper. It will be used in a later patch by a second variant for fib6_rule_action.
--- net/ipv6/fib6_rules.c | 52 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-)
net/ipv6/fib6_rules.c +31 -21
@@ -96,6 +96,31 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,return &net->ipv6.ip6_null_entry->dst; } +static int fib6_rule_saddr (struct net *net,struct fib_rule *rule,int flags, +struct flowi6 *flp6,const struct net_device *dev) +{ +struct fib6_rule *r = (struct fib6_rule *)rule; + +/* If we need to find a source address for this traffic, +* we check the result if it meets requirement of the rule. +*/ +if ((rule->flags &FIB_RULE_FIND_SADDR ) && + r->src.plen && !(flags &RT6_LOOKUP_F_HAS_SADDR )) { +struct in6_addr saddr; + +if (ipv6_dev_get_saddr (net, dev, &flp6->daddr, +rt6_flags2srcprefs (flags), &saddr)) +return -EAGAIN ; + +if (!ipv6_prefix_equal (&saddr, &r->src.addr, r->src.plen)) +return -EAGAIN ; + + flp6->saddr = saddr; + } + +return 0 ; +} +static int fib6_rule_action (struct fib_rule *rule,struct flowi *flp,int flags,struct fib_lookup_arg *arg) {
@@ -134,27 +159,12 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp, rt =lookup (net, table, flp6, arg->lookup_data, flags);if (rt != net->ipv6.ip6_null_entry) { -struct fib6_rule *r = (struct fib6_rule *)rule; - -/* -* If we need to find a source address for this traffic, -* we check the result if it meets requirement of the rule. -*/ -if ((rule->flags &FIB_RULE_FIND_SADDR ) && - r->src.plen && !(flags &RT6_LOOKUP_F_HAS_SADDR )) { -struct in6_addr saddr; - -if (ipv6_dev_get_saddr (net, -ip6_dst_idev (&rt->dst)->dev, - &flp6->daddr, -rt6_flags2srcprefs (flags), - &saddr)) - goto again; -if (!ipv6_prefix_equal (&saddr, &r->src.addr, - r->src.plen)) - goto again; - flp6->saddr = saddr; - } + err =fib6_rule_saddr (net, rule, flags, flp6, +ip6_dst_idev (&rt->dst)->dev); + +if (err == -EAGAIN ) + goto again; + err = rt->dst.error;if (err != -EAGAIN ) goto out;
Signature · 1 line
2.11.0
RFC v2 5/9 net/ipv6: Add fib6_lookup
In reply to David AhernAdd IPv6 equivalent to fib_lookup. Does a fib lookup, including rules, but returns a FIB entry, fib6_info, rather than a dst based rt6_info. fib6_lookup is on the order of 40% faster than any of the dst based lookup methods without custom rules and 25% faster with custom rules.
Since the lookup function has a completely different signature, fib6_rule_action is split into 2 paths: the existing one is renamed __fib6_rule_action and a new one for the fib6_info path is added. fib6_rule_action decides which to call based on the lookup_ptr. If it is fib6_table_lookup then the new path is taken.
Caller must hold rcu lock as no reference is taken on the returned fib entry.
--- include/net/ip6_fib.h | 6 ++++ net/ipv6/fib6_rules.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++-- net/ipv6/ip6_fib.c | 7 +++++ 3 files changed, 97 insertions(+), 2 deletions(-)
include/net/ip6_fib.h +6 -0
@@ -376,6 +376,12 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,const struct sk_buff *skb, int flags, pol_lookup_t lookup); +/* called with rcu lock held; can return error pointer +* caller needs to select path +*/ +struct fib6_info *fib6_lookup (struct net *net,int oif,struct flowi6 *fl6, +int flags); +/* called with rcu lock held; caller needs to select path */ struct fib6_info *fib6_table_lookup (struct net *net,struct fib6_table *table,int oif,struct flowi6 *fl6,int strict);
net/ipv6/fib6_rules.c +84 -2
104 lines not on this page · raw message
net/ipv6/ip6_fib.c +7 -0
@@ -354,6 +354,13 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6,return &rt->dst; } +/* called with rcu lock held; no reference taken on fib6_info */ +struct fib6_info *fib6_lookup (struct net *net,int oif,struct flowi6 *fl6, +int flags) +{ +return fib6_table_lookup (net, net->ipv6.fib6_main_tbl, oif, fl6, flags); +} +static void __net_init fib6_tables_init (struct net *net) {fib6_link_table (net, net->ipv6.fib6_main_tbl);
Signature · 1 line
2.11.0
Re: [RFC v2 bpf-next 5/9] net/ipv6: Add fib6_lookup
❦ 29 avril 2018 11:07 -0700, David Ahern <[email protected]> :
Review
Quoted 2 lines
(quoted diff) +2
-0+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, + int flags)
Maybe an EXPORT_SYMBOL_GPL? There is one for __fib_lookup (fib_lookup is an inline function).
Signature · 2 lines
Use the "telephone test" for readability.
- The Elements of Programming Style (Kernighan & Plauger)Re: [RFC v2 bpf-next 5/9] net/ipv6: Add fib6_lookup
Quoted 8 lines · Vincent Bernat
❦ 29 avril 2018 11:07 -0700, David Ahern <[email protected]> :
Review
Quoted 2 lines
(quoted diff) +2
-0+struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, + int flags)Maybe an
EXPORT_SYMBOL_GPL? There is one for__fib_lookup(fib_lookupis an inline function).
There needs to be an in-tree user relying on it. My intention is to convert rt6_lookup to fib6_lookup; as I recall all of those just want the egress device and do not need a dst based response. That might provide the in-tree user - depending on how the conversion is done.
And I presume you are asking for your benchmark modules. I can send you my diffs and results.
RFC v2 6/9 net/ipv6: Update fib6 tracepoint to take fib6_info
In reply to David AhernSimilar to IPv4, IPv6 should use the FIB lookup result in the tracepoint.
--- include/trace/events/fib6.h | 14 +++++++------- net/ipv6/route.c | 14 ++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-)
include/trace/events/fib6.h +7 -7
@@ -12,10 +12,10 @@ TRACE_EVENT(fib6_table_lookup, - TP_PROTO(const struct net *net, const struct rt6_info *rt, + TP_PROTO(const struct net *net, const struct fib6_info *f6i, struct fib6_table *table, const struct flowi6 *flp), - TP_ARGS(net, rt, table, flp), + TP_ARGS(net, f6i, table, flp), TP_STRUCT__entry( __field( u32, tb_id )
@@ -48,20 +48,20 @@ TRACE_EVENT(fib6_table_lookup, in6 = (struct in6_addr *)__entry->dst; *in6 = flp->daddr; -if (rt->rt6i_idev) { -__assign_str (name, rt->rt6i_idev->dev->name); +if (f6i->fib6_nh.nh_dev) { +__assign_str (name, f6i->fib6_nh.nh_dev); }else {__assign_str (name,"" ); } -if (rt == net->ipv6.ip6_null_entry) { +if (f6i == net->ipv6.fib6_null_entry) {struct in6_addr in6_zero = {}; in6 = (struct in6_addr *)__entry->gw; *in6 = in6_zero; - }else if (rt) { + }else if (f6i) { in6 = (struct in6_addr *)__entry->gw; - *in6 = rt->rt6i_gateway; + *in6 = f6i->fib6_nh.nh_gw; } ),
net/ipv6/route.c +6 -8
@@ -1078,6 +1078,8 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, goto restart; } +trace_fib6_table_lookup (net, f6i, table, fl6); +/* Search through exception table */ rt =rt6_find_cached_rt (f6i, &fl6->daddr, &fl6->saddr);if (rt) {
@@ -1096,8 +1098,6 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,rcu_read_unlock (); -trace_fib6_table_lookup (net, rt, table, fl6); -return rt; }
@@ -1827,6 +1827,8 @@ struct fib6_info *fib6_table_lookup(struct net *net, struct fib6_table *table, } } +trace_fib6_table_lookup (net, f6i, table, fl6); +return f6i; }
@@ -1853,7 +1855,6 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, rt = net->ipv6.ip6_null_entry;rcu_read_unlock ();dst_hold (&rt->dst); -trace_fib6_table_lookup (net, rt, table, fl6);return rt; }
@@ -1864,7 +1865,6 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,dst_use_noref (&rt->dst, jiffies);rcu_read_unlock (); -trace_fib6_table_lookup (net, rt, table, fl6);return rt; }else if (unlikely ((fl6->flowi6_flags &FLOWI_FLAG_KNOWN_NH ) && !(f6i->fib6_flags &RTF_GATEWAY ))) {
@@ -1890,9 +1890,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,dst_hold (&uncached_rt->dst); } -trace_fib6_table_lookup (net, uncached_rt, table, fl6);return uncached_rt; - }else {/* Get a percpu copy */
@@ -1906,7 +1904,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,local_bh_enable ();rcu_read_unlock (); -trace_fib6_table_lookup (net, pcpu_rt, table, fl6); +return pcpu_rt; } }
@@ -2486,7 +2484,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,rcu_read_unlock (); -trace_fib6_table_lookup (net, ret, table, fl6); +trace_fib6_table_lookup (net, rt, table, fl6);return ret; };
Signature · 1 line
2.11.0
RFC v2 7/9 net/ipv6: Add fib lookup stubs for use in bpf helper
In reply to David AhernAdd stubs to retrieve a handle to an IPv6 FIB table, fib6_get_table, a stub to do a lookup in a specific table, fib6_table_lookup, and a stub for a full route lookup.
The stubs are needed for core bpf code to handle the case when the IPv6 module is not builtin.
--- include/net/addrconf.h | 14 ++++++++++++++ net/ipv6/addrconf_core.c | 33 ++++++++++++++++++++++++++++++++- net/ipv6/af_inet6.c | 6 +++++- 3 files changed, 51 insertions(+), 2 deletions(-)
include/net/addrconf.h +14 -0
@@ -223,6 +223,20 @@ struct ipv6_stub { const struct in6_addr *addr);int (*ipv6_dst_lookup)(struct net *net,struct sock *sk,struct dst_entry **dst,struct flowi6 *fl6); + +struct fib6_table *(*fib6_get_table)(struct net *net,u32 id); +struct fib6_info *(*fib6_lookup)(struct net *net,int oif, +struct flowi6 *fl6,int flags); +struct fib6_info *(*fib6_table_lookup)(struct net *net, +struct fib6_table *table, +int oif,struct flowi6 *fl6, +int flags); +struct fib6_info *(*fib6_multipath_select)(const struct net *net, +struct fib6_info *f6i, +struct flowi6 *fl6,int oif, +const struct sk_buff *skb, +int strict); +void (*udpv6_encap_enable)(void ); void (*ndisc_send_na)(struct net_device *dev, const struct in6_addr *daddr, const struct in6_addr *solicited_addr,
net/ipv6/addrconf_core.c +32 -1
@@ -134,8 +134,39 @@ static int eafnosupport_ipv6_dst_lookup(struct net *net, struct sock *u1,return -EAFNOSUPPORT ; } +static struct fib6_table *eafnosupport_fib6_get_table (struct net *net,u32 id) +{ +return NULL ; +} + +static struct fib6_info * +eafnosupport_fib6_table_lookup (struct net *net,struct fib6_table *table, +int oif,struct flowi6 *fl6,int flags) +{ +return NULL ; +} + +static struct fib6_info * +eafnosupport_fib6_lookup (struct net *net,int oif,struct flowi6 *fl6, +int flags) +{ +return NULL ; +} + +static struct fib6_info * +eafnosupport_fib6_multipath_select (const struct net *net,struct fib6_info *f6i, +struct flowi6 *fl6,int oif, +const struct sk_buff *skb,int strict) +{ +return f6i; +} +const struct ipv6_stub *ipv6_stub__read_mostly = &(struct ipv6_stub ) { - .ipv6_dst_lookup = eafnosupport_ipv6_dst_lookup, + .ipv6_dst_lookup = eafnosupport_ipv6_dst_lookup, + .fib6_get_table = eafnosupport_fib6_get_table, + .fib6_table_lookup = eafnosupport_fib6_table_lookup, + .fib6_lookup = eafnosupport_fib6_lookup, + .fib6_multipath_select = eafnosupport_fib6_multipath_select, };EXPORT_SYMBOL_GPL (ipv6_stub);
net/ipv6/af_inet6.c +5 -1
@@ -887,7 +887,11 @@ static struct pernet_operations inet6_net_ops = {static const struct ipv6_stub ipv6_stub_impl = { .ipv6_sock_mc_join = ipv6_sock_mc_join, .ipv6_sock_mc_drop = ipv6_sock_mc_drop, - .ipv6_dst_lookup = ip6_dst_lookup, + .ipv6_dst_lookup = ip6_dst_lookup, + .fib6_get_table = fib6_get_table, + .fib6_table_lookup = fib6_table_lookup, + .fib6_lookup = fib6_lookup, + .fib6_multipath_select = fib6_multipath_select, .udpv6_encap_enable = udpv6_encap_enable, .ndisc_send_na = ndisc_send_na, .nd_tbl = &nd_tbl,
Signature · 1 line
2.11.0
RFC v2 8/9 bpf: Provide helper to do lookups in kernel FIB table
In reply to David AhernProvide a helper for doing a FIB and neighbor lookup in the kernel tables from an XDP program. The helper provides a fastpath for forwarding packets. If the packet is a local delivery or for any reason is not a simple lookup and forward, the packet continues up the stack.
If it is to be forwarded, the forwarding can be done directly if the neighbor is already known. If the neighbor does not exist, the first few packets go up the stack for neighbor resolution. Once resolved, the xdp program provides the fast path.
On successful lookup the nexthop dmac, current device smac and egress device index are returned.
The API supports IPv4, IPv6 and MPLS protocols, but only IPv4 and IPv6 are implemented in this patch. The API includes layer 4 parameters if the XDP program chooses to do deep packet inspection to allow compare against ACLs implemented as FIB rules.
Header rewrite is left to the XDP program.
The lookup takes 2 flags:
- BPF_FIB_LOOKUP_DIRECT to do a lookup that bypasses FIB rules and goes straight to the table associated with the device (expert setting for those looking to maximize throughput)
- BPF_FIB_LOOKUP_OUTPUT to do a lookup from the egress perspective. Default is an ingress lookup.
Initial performance numbers collected by Jesper, forwarded packets/sec:
Full stack XDP FIB lookup XDP Direct lookup
IPv4 1,947,969 7,074,156 7,415,333 IPv6 1,728,000 6,165,504 7,262,720
--- include/uapi/linux/bpf.h | 83 ++++++++++++++- net/core/filter.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+), 1 deletion(-)
include/uapi/linux/bpf.h +82 -1
@@ -10,6 +10,8 @@#include <linux/types.h> #include <linux/bpf_common.h> +#include <linux/if_ether.h> +#include <linux/in6.h> /* Extended instruction set based on top of classic BPF */
@@ -1801,6 +1803,33 @@ union bpf_attr {* Return * a non-negative value equal to or less than size on success, or * a negative error in case of failure. +* +* int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) +* Description +* Do FIB lookup in kernel tables using parameters in *params*. +* If lookup is successful and result shows packets is to be +* forwarded, the neighbor tables are searched for the nexthop. +* If successful (ie., FIB lookup shows forwarding and nexthop +* is resolved), the nexthop address is returned in ipv4_dst, +* ipv6_dst or mpls_out based on family, smac is set to mac +* address of egress device, dmac is set to nexthop mac address, +* rt_metric is set to metric from route. +* +* *plen* argument is the size of the passed in struct. +* *flags* argument can be one or more BPF_FIB_LOOKUP_ flags: +* +* **BPF_FIB_LOOKUP_DIRECT** means do a direct table lookup vs +* full lookup using FIB rules +* **BPF_FIB_LOOKUP_OUTPUT** mmeans do lookup from an egress +* perspective (default is ingress) +* +* *ctx* is either **struct xdp_md** for XDP programs or +* **struct sk_buff** tc cls_act programs. +* +* Return +* Egress device index on success, 0 if packet needs to continue +* up the stack for further processing or a negative error in case +* of failure. */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \
@@ -1870,7 +1899,8 @@ union bpf_attr {FN (bind), \FN (xdp_adjust_tail), \FN (skb_get_xfrm_state), \ -FN (get_stack), +FN (get_stack), \ +FN (fib_lookup),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
@@ -2278,4 +2308,55 @@ struct bpf_raw_tracepoint_args { __u64 args[0]; }; +/* DIRECT: Skip the FIB rules and go to FIB table associated with device +* OUTPUT: Do lookup from egress perspective; default is ingress +*/ +#define BPF_FIB_LOOKUP_DIRECT BIT(0) +#define BPF_FIB_LOOKUP_OUTPUT BIT(1) + +struct bpf_fib_lookup { +/* input */ +__u8 family;/* network family, AF_INET, AF_INET6, AF_MPLS */ + +/* set if lookup is to consider L4 data - e.g., FIB rules */ +__u8 l4_protocol; +__be16 sport; +__be16 dport; + +/* total length of packet from network header - used for MTU check */ +__u16 tot_len; +__u32 ifindex;/* L3 device index for lookup */ + +union { +/* inputs to lookup */ +__u8 tos;/* AF_INET */ +__be32 flowlabel;/* AF_INET6 */ + +/* output: metric of fib result */ +__u32 rt_metric; + }; + +union { +__be32 mpls_in; +__be32 ipv4_src; +struct in6_addr ipv6_src; + }; + +/* input to bpf_fib_lookup, *dst is destination address. +* output: bpf_fib_lookup sets to gateway address +*/ +union { +/* return for MPLS lookups */ +__be32 mpls_out[4 ];/* support up to 4 labels */ +__be32 ipv4_dst; +struct in6_addr ipv6_dst; + }; + +/* output */ +__be16 h_vlan_proto; +__be16 h_vlan_TCI; +__u8 smac[ETH_ALEN ]; +__u8 dmac[ETH_ALEN ]; +}; +#endif /* _UAPI__LINUX_BPF_H__ */
net/core/filter.c +263 -0
287 lines not on this page · raw message
Signature · 1 line
2.11.0
Re: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
Quoted 234 lines · David Ahern
Provide a helper for doing a FIB and neighbor lookup in the kernel tables from an XDP program. The helper provides a fastpath for forwarding packets. If the packet is a local delivery or for any reason is not a simple lookup and forward, the packet continues up the stack.
If it is to be forwarded, the forwarding can be done directly if the neighbor is already known. If the neighbor does not exist, the first few packets go up the stack for neighbor resolution. Once resolved, the xdp program provides the fast path.
On successful lookup the nexthop dmac, current device smac and egress device index are returned.
The API supports IPv4, IPv6 and MPLS protocols, but only IPv4 and IPv6 are implemented in this patch. The API includes layer 4 parameters if the XDP program chooses to do deep packet inspection to allow compare against ACLs implemented as FIB rules.
Header rewrite is left to the XDP program.
The lookup takes 2 flags:
-BPF_FIB_LOOKUP_DIRECTto do a lookup that bypasses FIB rules and goes straight to the table associated with the device (expert setting for those looking to maximize throughput)-
BPF_FIB_LOOKUP_OUTPUTto do a lookup from the egress perspective. Default is an ingress lookup.Initial performance numbers collected by Jesper, forwarded packets/sec:
Full stack XDP FIB lookup XDP Direct lookup
IPv4 1,947,969 7,074,156 7,415,333 IPv6 1,728,000 6,165,504 7,262,720Signed-off-by David Ahern <[email protected]>--- include/uapi/linux/bpf.h | 83 ++++++++++++++- net/core/filter.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 345 insertions(+), 1 deletion(-)include/uapi/linux/bpf.h +82
-1@@ -10,6 +10,8 @@#include <linux/types.h> #include <linux/bpf_common.h> +#include <linux/if_ether.h> +#include <linux/in6.h> /* Extended instruction set based on top of classic BPF */ @@ -1801,6 +1803,33 @@ union bpf_attr {* Return * a non-negative value equal to or less than size on success, or * a negative error in case of failure. +* +* int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags) +* Description +* Do FIB lookup in kernel tables using parameters in *params*. +* If lookup is successful and result shows packets is to be +* forwarded, the neighbor tables are searched for the nexthop. +* If successful (ie., FIB lookup shows forwarding and nexthop +* is resolved), the nexthop address is returned in ipv4_dst, +* ipv6_dst or mpls_out based on family, smac is set to mac +* address of egress device, dmac is set to nexthop mac address, +* rt_metric is set to metric from route. +* +* *plen* argument is the size of the passed in struct. +* *flags* argument can be one or more BPF_FIB_LOOKUP_ flags: +* +* **BPF_FIB_LOOKUP_DIRECT** means do a direct table lookup vs +* full lookup using FIB rules +* **BPF_FIB_LOOKUP_OUTPUT** mmeans do lookup from an egress +* perspective (default is ingress) +* +* *ctx* is either **struct xdp_md** for XDP programs or +* **struct sk_buff** tc cls_act programs. +* +* Return +* Egress device index on success, 0 if packet needs to continue +* up the stack for further processing or a negative error in case +* of failure. */ #define __BPF_FUNC_MAPPER (FN ) \ FN(unspec), \@@ -1870,7 +1899,8 @@ union bpf_attr {FN (bind), \FN (xdp_adjust_tail), \FN (skb_get_xfrm_state), \ -FN (get_stack), +FN (get_stack), \ +FN (fib_lookup),/* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -2278,4 +2308,55 @@ struct bpf_raw_tracepoint_args { __u64 args[0]; }; +/* DIRECT: Skip the FIB rules and go to FIB table associated with device +* OUTPUT: Do lookup from egress perspective; default is ingress +*/ +#define BPF_FIB_LOOKUP_DIRECT BIT(0) +#define BPF_FIB_LOOKUP_OUTPUT BIT(1) + +struct bpf_fib_lookup { +/* input */ +__u8 family;/* network family, AF_INET, AF_INET6, AF_MPLS */ + +/* set if lookup is to consider L4 data - e.g., FIB rules */ +__u8 l4_protocol; +__be16 sport; +__be16 dport; + +/* total length of packet from network header - used for MTU check */ +__u16 tot_len; +__u32 ifindex;/* L3 device index for lookup */ + +union { +/* inputs to lookup */ +__u8 tos;/* AF_INET */ +__be32 flowlabel;/* AF_INET6 */ + +/* output: metric of fib result */ +__u32 rt_metric; + }; + +union { +__be32 mpls_in; +__be32 ipv4_src; +struct in6_addr ipv6_src; + }; + +/* input to bpf_fib_lookup, *dst is destination address. +* output: bpf_fib_lookup sets to gateway address +*/ +union { +/* return for MPLS lookups */ +__be32 mpls_out[4 ];/* support up to 4 labels */ +__be32 ipv4_dst; +struct in6_addr ipv6_dst; + }; + +/* output */ +__be16 h_vlan_proto; +__be16 h_vlan_TCI; +__u8 smac[ETH_ALEN ]; +__u8 dmac[ETH_ALEN ]; +}; +#endif /* _UAPI__LINUX_BPF_H__ */ net/core/filter.c +66
-0@@ -59,6 +59,10 @@#include <net/tcp.h> #include <net/xfrm.h> #include <linux/bpf_trace.h> +#include <linux/inetdevice.h> +#include <net/ip_fib.h> +#include <net/flow.h> +#include <net/arp.h> /** * sk_filter_trim_cap - run a packet through a socket filter @@ -3788,6 +3792,261 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { };#endif +#if IS_ENABLED (CONFIG_INET ) ||IS_ENABLED (CONFIG_IPV6 ) +static int bpf_fib_set_fwd_params (struct bpf_fib_lookup *params, +const struct neighbour *neigh, +const struct net_device *dev) +{ +memcpy (params->dmac, neigh->ha,ETH_ALEN ); +memcpy (params->smac, dev->dev_addr,ETH_ALEN ); + params->h_vlan_TCI =0 ; + params->h_vlan_proto =0 ; + +return dev->ifindex; +} +#endif + +#if IS_ENABLED (CONFIG_INET ) +static int bpf_ipv4_fib_lookup (struct net *net,struct bpf_fib_lookup *params, +u32 flags) +{ +struct in_device *in_dev; +struct neighbour *neigh; +struct net_device *dev; +struct fib_result res; +struct fib_nh *nh; +struct flowi4 fl4; +int err; + + dev =dev_get_by_index_rcu (net, params->ifindex); +if (unlikely (!dev)) +return -ENODEV ; + +/* verify forwarding is enabled on this interface */ + in_dev =__in_dev_get_rcu (dev); +if (unlikely (!in_dev || !IN_DEV_FORWARD (in_dev))) +return 0 ; + +if (flags &BPF_FIB_LOOKUP_OUTPUT ) { + fl4.flowi4_iif =1 ; + fl4.flowi4_oif = params->ifindex; + }else { + fl4.flowi4_iif = params->ifindex; + fl4.flowi4_oif =0 ; + } + fl4.flowi4_tos = params->tos &IPTOS_RT_MASK ; + fl4.flowi4_scope =RT_SCOPE_UNIVERSE ; + fl4.flowi4_flags =0 ; + + fl4.flowi4_proto = params->l4_protocol; + fl4.daddr = params->ipv4_dst; + fl4.saddr = params->ipv4_src; + fl4.fl4_sport = params->sport; + fl4.fl4_dport = params->dport; + +if (flags &BPF_FIB_LOOKUP_DIRECT ) { +u32 tbid =l3mdev_fib_table_rcu (dev) ? :RT_TABLE_MAIN ; +struct fib_table *tb; + + tb =fib_get_table (net, tbid); +if (unlikely (!tb)) +return 0 ; + + err =fib_table_lookup (tb, &fl4, &res,FIB_LOOKUP_NOREF ); + }else {
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3866
Quoted 10 lines
net/core/filter.c +10
-0+ fl4.flowi4_mark =0 ; + fl4.flowi4_secid =0 ; + fl4.flowi4_tun_key.tun_id =0 ; + fl4.flowi4_uid =sock_net_uid (net,NULL ); + + err =fib_lookup (net, &fl4, &res,FIB_LOOKUP_NOREF ); + } + +if (err || res.type !=RTN_UNICAST ) +return 0 ;
should this be an error returned to the user instead of zero? Seems useful to indicate.
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3875
Quoted 9 lines
net/core/filter.c +9
-0+ +if (res.fi->fib_nhs >1 ) +fib_select_path (net, &res, &fl4,NULL ); + + nh = &res.fi->fib_nh[res.nh_sel]; + +/* do not handle lwt encaps right now */ +if (nh->nh_lwtstate) +return 0 ;
adn return enotsupp here?
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3879
Quoted 4 lines
net/core/filter.c +4
-0+ + dev = nh->nh_dev; +if (unlikely (!dev)) +return 0 ;
enodev ?
Quoted 4 lines
net/core/filter.c +4
-0+ +if (nh->nh_gw) + params->ipv4_dst = nh->nh_gw; +
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3893
Quoted 10 lines
net/core/filter.c +10
-0+ params->rt_metric = res.fi->fib_priority; + +/* xdp and cls_bpf programs are run in RCU-bh so +* rcu_read_lock_bh is not needed here +*/ + neigh =__ipv4_neigh_lookup_noref (dev, (__force u32 )params->ipv4_dst); +if (neigh) +return bpf_fib_set_fwd_params (params, neigh, dev); + +return 0 ;
Even this return 0 doesn't quite fit to what doc says:
"0 if packet needs to continue up the stack for further processing" What stack suppose to do ?
It will hit the same condition and packet will be dropped, right? Isn't it better to report all errors back to bpf prog and let the program make decision instead of 'return 0' almost everywhere?
Quoted 89 lines
net/core/filter.c +89
-0+} +#endif + +#if IS_ENABLED (CONFIG_IPV6 ) +static int bpf_ipv6_fib_lookup (struct net *net,struct bpf_fib_lookup *params, +u32 flags) +{ +struct neighbour *neigh; +struct net_device *dev; +struct fib6_info *f6i; +struct flowi6 fl6; +int strict =0 ; +int oif; + +/* link local addresses are never forwarded */ +if (rt6_need_strict (¶ms->ipv6_dst) || +rt6_need_strict (¶ms->ipv6_src)) +return 0 ; + + dev =dev_get_by_index_rcu (net, params->ifindex); +if (unlikely (!dev)) +return -ENODEV ; + +if (flags &BPF_FIB_LOOKUP_OUTPUT ) { + fl6.flowi6_iif =1 ; + oif = fl6.flowi6_oif = params->ifindex; + }else { + oif = fl6.flowi6_iif = params->ifindex; + fl6.flowi6_oif =0 ; + strict =RT6_LOOKUP_F_HAS_SADDR ; + } + fl6.flowlabel = params->flowlabel; + fl6.flowi6_scope =0 ; + fl6.flowi6_flags =0 ; + fl6.mp_hash =0 ; + + fl6.flowi6_proto = params->l4_protocol; + fl6.daddr = params->ipv6_dst; + fl6.saddr = params->ipv6_src; + fl6.fl6_sport = params->sport; + fl6.fl6_dport = params->dport; + +if (flags &BPF_FIB_LOOKUP_DIRECT ) { +u32 tbid =l3mdev_fib_table_rcu (dev) ? :RT_TABLE_MAIN ; +struct fib6_table *tb; + + tb = ipv6_stub->fib6_get_table (net, tbid); +if (unlikely (!tb)) +return 0 ; + + f6i = ipv6_stub->fib6_table_lookup (net, tb, oif, &fl6, strict); + }else { + fl6.flowi6_mark =0 ; + fl6.flowi6_secid =0 ; + fl6.flowi6_tun_key.tun_id =0 ; + fl6.flowi6_uid =sock_net_uid (net,NULL ); + + f6i = ipv6_stub->fib6_lookup (net, oif, &fl6, strict); + } + +if (unlikely (IS_ERR_OR_NULL (f6i) || f6i == net->ipv6.fib6_null_entry)) +return 0 ; + +if (unlikely (f6i->fib6_flags &RTF_REJECT || + f6i->fib6_type !=RTN_UNICAST )) +return 0 ; + +if (f6i->fib6_nsiblings && fl6.flowi6_oif ==0 ) + f6i = ipv6_stub->fib6_multipath_select (net, f6i, &fl6, + fl6.flowi6_oif,NULL , + strict); + +if (f6i->fib6_nh.nh_lwtstate) +return 0 ; + +if (f6i->fib6_flags &RTF_GATEWAY ) + params->ipv6_dst = f6i->fib6_nh.nh_gw; + + dev = f6i->fib6_nh.nh_dev; + params->rt_metric = f6i->fib6_metric; + +/* xdp and cls_bpf programs are run in RCU-bh so rcu_read_lock_bh is +* not needed here. Can not use __ipv6_neigh_lookup_noref here +* because we need to get nd_tbl via the stub +*/ + neigh =___neigh_lookup_noref (ipv6_stub->nd_tbl, neigh_key_eq128, + ndisc_hashfn, ¶ms->ipv6_dst, dev); +if (neigh) +return bpf_fib_set_fwd_params (params, neigh, dev);
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3992
Quoted 10 lines
net/core/filter.c +10
-0+ +return 0 ; +} +#endif + +BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx, + struct bpf_fib_lookup *, params, int, plen, u32, flags) +{ +if (plen <sizeof (*params)) +return -EINVAL ;
there should be a check here that only two bits are used in 'flags' Otherwise flags will not be extendable.
Quoted 9 lines
net/core/filter.c +9
-0+ +switch (params->family) { +#if IS_ENABLED (CONFIG_INET ) +case AF_INET : +return bpf_ipv4_fib_lookup (dev_net (ctx->rxq->dev), params, + flags); +#endif +#if IS_ENABLED (CONFIG_IPV6 ) +case AF_INET6 :
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 4011
Quoted 10 lines
net/core/filter.c +10
-0+return bpf_ipv6_fib_lookup (dev_net (ctx->rxq->dev), params, + flags); +#endif + } +return -ENOTSUPP ; +} + +static const struct bpf_func_proto bpf_xdp_fib_lookup_proto = { + .func = bpf_xdp_fib_lookup, + .gpl_only = true,
do you really want to force all users of this helper to be gpl only? I don't mind at all. That's your choice as the author of this helper. Just the rest of networking helpers don't require gpl-ness.
Quoted 62 lines
net/core/filter.c +42
-0+ .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM, + .arg3_type = ARG_CONST_SIZE, + .arg4_type = ARG_ANYTHING, +}; + +BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb, + struct bpf_fib_lookup *, params, int, plen, u32, flags) +{ +if (plen <sizeof (*params)) +return -EINVAL ; + +switch (params->family) { +#if IS_ENABLED (CONFIG_INET ) +case AF_INET : +return bpf_ipv4_fib_lookup (dev_net (skb->dev), params, flags); +#endif +#if IS_ENABLED (CONFIG_IPV6 ) +case AF_INET6 : +return bpf_ipv6_fib_lookup (dev_net (skb->dev), params, flags); +#endif + } +return -ENOTSUPP ; +} + +static const struct bpf_func_proto bpf_skb_fib_lookup_proto = { + .func = bpf_skb_fib_lookup, + .gpl_only = true, + .pkt_access = true, + .ret_type =RET_INTEGER , + .arg1_type =ARG_PTR_TO_CTX , + .arg2_type =ARG_PTR_TO_MEM , + .arg3_type =ARG_CONST_SIZE , + .arg4_type =ARG_ANYTHING , +}; +static const struct bpf_func_proto *bpf_base_func_proto (enum bpf_func_id func_id) {@@ -3933,6 +4192,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)case BPF_FUNC_skb_get_xfrm_state:return &bpf_skb_get_xfrm_state_proto;#endif +case BPF_FUNC_fib_lookup: +return &bpf_skb_fib_lookup_proto;default :return bpf_base_func_proto (func_id); }@@ -3958,6 +4219,8 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)return &bpf_xdp_redirect_map_proto;case BPF_FUNC_xdp_adjust_tail:return &bpf_xdp_adjust_tail_proto; +case BPF_FUNC_fib_lookup: +return &bpf_xdp_fib_lookup_proto;default :return bpf_base_func_proto (func_id); }Signature · 1 line
2.11.0
Re: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
Quoted 15 lines · Alexei Starovoitov
Quoted 15 lines
net/core/filter.c +15
-0+if (flags &BPF_FIB_LOOKUP_DIRECT ) { +u32 tbid =l3mdev_fib_table_rcu (dev) ? :RT_TABLE_MAIN ; +struct fib_table *tb; + + tb =fib_get_table (net, tbid); +if (unlikely (!tb)) +return 0 ; + + err =fib_table_lookup (tb, &fl4, &res,FIB_LOOKUP_NOREF ); + }else { + fl4.flowi4_mark =0 ; + fl4.flowi4_secid =0 ; + fl4.flowi4_tun_key.tun_id =0 ; + fl4.flowi4_uid =sock_net_uid (net,NULL ); +
Quoted 8 lines
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3866
Quoted 5 lines
net/core/filter.c +5
-0+ err =fib_lookup (net, &fl4, &res,FIB_LOOKUP_NOREF ); + } + +if (err || res.type !=RTN_UNICAST ) +return 0 ;should this be an error returned to the user instead of zero? Seems useful to indicate.
res.type != UNICAST is not an error; it means other delivery type (e.g., local).
err < 0 means unreachable, prohibit, blackhole, etc. Arguably the error could be returned to the xdp program, but it is more complicated than that. Blackhole is a common default route or policy, but RTN_BLACKHOLE == -EINVAL which is also the error code if the user passes invalid arguments to the program.
Quoted 12 lines
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3875
Quoted 10 lines
net/core/filter.c +9
-0+ +if (res.fi->fib_nhs >1 ) +fib_select_path (net, &res, &fl4,NULL ); + + nh = &res.fi->fib_nh[res.nh_sel]; + +/* do not handle lwt encaps right now */ +if (nh->nh_lwtstate) +return 0 ;adn return enotsupp here?
see below
Quoted 7 lines
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3879
Quoted 5 lines
net/core/filter.c +4
-0+ + dev = nh->nh_dev; +if (unlikely (!dev)) +return 0 ;enodev ?
see below
Quoted 11 lines
Quoted 11 lines
net/core/filter.c +10
-0+ +if (nh->nh_gw) + params->ipv4_dst = nh->nh_gw; + + params->rt_metric = res.fi->fib_priority; + +/* xdp and cls_bpf programs are run in RCU-bh so +* rcu_read_lock_bh is not needed here +*/ + neigh =__ipv4_neigh_lookup_noref (dev, (__force u32 )params->ipv4_dst);
Quoted 8 lines
net/core/filter.c static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = { · line 3893
Quoted 4 lines
net/core/filter.c +4
-0+if (neigh) +return bpf_fib_set_fwd_params (params, neigh, dev); + +return 0 ;Even this return 0 doesn't quite fit to what doc says:
"0 if packet needs to continue up the stack for further processing" What stack suppose to do ?
First packet on a route the nexthop may not be resolved. Without punting to the stack it never has an impetus to resolve that neighbor.
Quoted 1 line
It will hit the same condition and packet will be dropped, right?
no. It can resolve the neighbor so follow up packets can be forwarded in the fast path.
Quoted 2 lines
Isn't it better to report all errors back to bpf prog and let the program make decision instead of 'return 0' almost everywhere?
The idea here is to fast pass packets that fit a supported profile and are to be forwarded. Everything else should continue up the stack as it has wider capabilities. The helper and XDP programs should make no assumptions on what the broader kernel and userspace might be monitoring or want to do with packets that can not be forwarded in the fast path. This is very similar to hardware forwarding when it punts packets to the CPU for control plane assistance.
Re: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
Quoted 9 lines · David Ahern
The idea here is to fast pass packets that fit a supported profile and are to be forwarded. Everything else should continue up the stack as it has wider capabilities. The helper and XDP programs should make no assumptions on what the broader kernel and userspace might be monitoring or want to do with packets that can not be forwarded in the fast path. This is very similar to hardware forwarding when it punts packets to the CPU for control plane assistance.
Thinking about this some more and how to return more information to the bpf program about the FIB lookup.
bpf_fib_lookup struct is 64-bytes. It can not be expanded without hurting performance. I could do another union on an input parameter and return flags indicating why the returned index is 0. Something like this:
include/uapi/linux/bpf.h +2 -0
@@ -2314,6 +2314,12 @@ struct bpf_raw_tracepoint_args {#define BPF_FIB_LOOKUP_DIRECT BIT(0)#define BPF_FIB_LOOKUP_OUTPUT BIT(1) +#define BPF_FIB_LKUP_RET_NO_FWD BIT(0)/* pkt is not fwded */ +#define BPF_FIB_LKUP_RET_UNSUPP_LWT BIT(1)/* fwd requires unsupp
encap */
+#define BPF_FIB_LKUP_RET_NO_NHDEV BIT(2) /* nh device does not exist */ +#define BPF_FIB_LKUP_RET_NO_NEIGH BIT(3) /* no neigh entry for nh */ +#define BPF_FIB_LKUP_RET_FRAG_NEEDED BIT(4) /* pkt too big to fwd */
+
struct bpf_fib_lookup {
/* input */
__u8 family; /* network family, AF_INET, AF_INET6, AF_MPLS */
@@ -2325,7 +2331,11 @@ struct bpf_fib_lookup {
/* total length of packet from network header - used for MTU
check */
__u16 tot_len;
- __u32 ifindex; /* L3 device index for lookup */
+
+ union {
+ __u32 ifindex; /* in: L3 device index for lookup */
+ __u32 ret_flags; /* out: BPF_FIB_LOOKUP_RET flags */+ }
union {
/* inputs to lookup */Similarly for the fib result, it could be returned with a union on say family:
union {
__u8 family; /* in: network family, AF_INET, AF_INET6, AF_MPLS */
__u8 rt_type; /* out: FIB lookup route type */
};
Then if the fib result is -EINVAL/-EHOSTUNREACH/-EACCES, rt_type is set to RTN_BLACKHOLE/RTN_UNREACHABLE/RTN_PROHIBIT allowing the XDP program to make an informed decision on dropping the packet.
To avoid performance hits on the forwarding path, these return values would *only* set if the ifindex returned is 0.
Re: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
In reply to David AhernQuoted 5 lines · On Sun, 29 Apr 2018 11:07:51 -0700 David Ahern <[email protected]> wrote:
Initial performance numbers collected by Jesper, forwarded packets/sec:
Full stack XDP FIB lookup XDP Direct lookup
IPv4 1,947,969 7,074,156 7,415,333 IPv6 1,728,000 6,165,504 7,262,720
Do notice these number is single CPU core forwarding performance! On a Broadwell E5-1650 v4 @ 3.60GHz.
Another interesting data point is that xdp_redirect_map performance is 13,365,161 pps, which allow us to calculate/isolate the overhead/cost of the FIB lookup.
(1/13365161-1/7074156)*10^9 = -66.5 ns
(1/13365161-1/7415333)*10^9 = -60.0 ns
Which is very close to the measured 50 ns cost of the FIB lookup, done by Vincent Bernat.
See: https://vincent.bernat.im/en/blog/2017-ipv4-route-lookup-linux
Another way I calculate this is by (ran a new benchmark):
Performance: 7641593 (7,641,593) <= tx_unicast /sec
* Packet-gap: (1/7641593*10^9) = 130.86 ns
Find all FIB related lookup functions in perf-report::
Samples: 93K of event 'cycles:ppp', Event count (approx.): 88553104553
Overhead Cost CPU Command Symbol
20.63 % 26.99 ns 002 ksoftirqd/2 [k] fib_table_lookup
12.92 % 16.90 ns 002 ksoftirqd/2 [k] bpf_fib_lookup
2.40 % 3.14 ns 002 ksoftirqd/2 [k] fib_select_path
0.83 % 1.09 ns 002 ksoftirqd/2 [k] fib_get_table
0.40 % 0.52 ns 002 ksoftirqd/2 [k] l3mdev_fib_table_rcu -----------
Tot:37.18 % (20.63+12.92+2.40+0.83+0.40)
===========
Cost of FIB lookup:
- 130.86/100*37.18 = 48.65 ns overhead by FIB lookup.
Again very close to Vincent's IPv4 measurements of ~50 ns.
Notice that the IPv6 measurements does not match up:
https://vincent.bernat.im/en/blog/2017-ipv6-route-lookup-linux
This is because, we/I'm just testing the IPv6 route cache here...
Signature · 4 lines
Best regards, Jesper Dangaard Brouer MSc.CS, Principal Kernel Engineer at Red Hat LinkedIn: http://www.linkedin.com/in/brouer
Re: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
Quoted 11 lines · Jesper Dangaard Brouer
Quoted 5 lines · On Sun, 29 Apr 2018 11:07:51 -0700 David Ahern <[email protected]> wrote:
Initial performance numbers collected by Jesper, forwarded packets/sec:
Full stack XDP FIB lookup XDP Direct lookup
IPv4 1,947,969 7,074,156 7,415,333 IPv6 1,728,000 6,165,504 7,262,720Do notice these number is single CPU core forwarding performance! On a Broadwell E5-1650 v4 @ 3.60GHz.
I'll add that context to the commit message. Thanks,
Quoted 35 lines
Another interesting data point is that
xdp_redirect_mapperformance is 13,365,161 pps, which allow us to calculate/isolate the overhead/cost of the FIB lookup.(1/13365161-1/7074156)*10^9 = -66.5 ns
(1/13365161-1/7415333)*10^9 = -60.0 nsWhich is very close to the measured 50 ns cost of the FIB lookup, done by Vincent Bernat.
See: https://vincent.bernat.im/en/blog/2017-ipv4-route-lookup-linuxAnother way I calculate this is by (ran a new benchmark):
Performance: 7641593 (7,641,593) <=
tx_unicast/sec
* Packet-gap: (1/7641593*10^9) = 130.86 nsFind all FIB related lookup functions in perf-report::
Samples: 93K of event 'cycles:ppp', Event count (approx.): 88553104553
Overhead Cost CPU Command Symbol 20.63 % 26.99 ns 002 ksoftirqd/2 [k] fib_table_lookup 12.92 % 16.90 ns 002 ksoftirqd/2 [k] bpf_fib_lookup 2.40 % 3.14 ns 002 ksoftirqd/2 [k] fib_select_path 0.83 % 1.09 ns 002 ksoftirqd/2 [k] fib_get_table 0.40 % 0.52 ns 002 ksoftirqd/2 [k] l3mdev_fib_table_rcu-----------
Tot:37.18 % (20.63+12.92+2.40+0.83+0.40)
===========Cost of FIB lookup:
- 130.86/100*37.18 = 48.65 ns overhead by FIB lookup.
Quoted 8 lines
Again very close to Vincent's IPv4 measurements of ~50 ns.
Notice that the IPv6 measurements does not match up:
https://vincent.bernat.im/en/blog/2017-ipv6-route-lookup-linux
This is because, we/I'm just testing the IPv6 route cache here...
Vincent's blog is before recent changes -- 4.15 getting the rcu locking, net-next getting separate fib entries and now this set adding a FIB lookup without the dst.
To share numbers from recent testing I did using Vincent's modules, lookup times in nsec (using local_clock) with MULTIPLE_TABLES config disabled for IPv4 and IPv6
IPv4 IPv6-dst IPv6-fib6
baseline 49 126 52
I have other cases with combinations of configs and rules, but this shows the best possible case.
IPv6 needs some more work to improve speeds with MULTIPLE_TABLES enabled (separate local and main tables unlike IPv4) and IPV6_SUBTREES enabled.
David Miller
maintainerRe: [RFC v2 bpf-next 8/9] bpf: Provide helper to do lookups in kernel FIB table
From: David Ahern <[email protected]>
Date: Wed, 2 May 2018 09:37:21 -0600
Quoted 4 lines
To share numbers from recent testing I did using Vincent's modules, lookup times in nsec (using
local_clock) withMULTIPLE_TABLESconfig disabled for IPv4 and IPv6
Quoted 8 lines
IPv4 IPv6-dst IPv6-fib6
baseline 49 126 52I have other cases with combinations of configs and rules, but this shows the best possible case.
IPv6 needs some more work to improve speeds with
MULTIPLE_TABLESenabled (separate local and main tables unlike IPv4) andIPV6_SUBTREESenabled.
Yes, like for ipv4 sharing local and main tables will help a lot.
RFC v2 9/9 samples/bpf: Add examples of ipv4 and ipv6 forwarding in XDP
In reply to David Ahern--- samples/bpf/Makefile | 4 + samples/bpf/xdp_fwd_kern.c | 110 ++++++++++++++++++++++++ samples/bpf/xdp_fwd_user.c | 136 ++++++++++++++++++++++++++++++ tools/testing/selftests/bpf/bpf_helpers.h | 3 + 4 files changed, 253 insertions(+) create mode 100644 samples/bpf/xdp_fwd_kern.c create mode 100644 samples/bpf/xdp_fwd_user.c
samples/bpf/Makefile +4 -0
@@ -45,6 +45,7 @@ hostprogs-y += xdp_rxq_info hostprogs-y += syscall_tp hostprogs-y += cpustat hostprogs-y += xdp_adjust_tail +hostprogs-y += xdp_fwd # Libbpf dependencies LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
@@ -98,6 +99,7 @@ xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o xdp_adjust_tail-objs := bpf_load.o $(LIBBPF) xdp_adjust_tail_user.o +xdp_fwd-objs := bpf_load.o $(LIBBPF) xdp_fwd_user.o # Tell kbuild to always build the programs always := $(hostprogs-y)
@@ -151,6 +153,7 @@ always += xdp2skb_meta_kern.o always += syscall_tp_kern.o always += cpustat_kern.o always += xdp_adjust_tail_kern.o +always += xdp_fwd_kern.o HOSTCFLAGS += -I$(objtree)/usr/include HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -197,6 +200,7 @@ HOSTLOADLIBES_xdp_rxq_info += -lelf HOSTLOADLIBES_syscall_tp += -lelf HOSTLOADLIBES_cpustat += -lelf HOSTLOADLIBES_xdp_adjust_tail += -lelf +HOSTLOADLIBES_xdp_fwd += -lelf # 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/xdp_fwd_kern.c new file +110 -0
110 lines not on this page · raw message
samples/bpf/xdp_fwd_user.c new file +136 -0
136 lines not on this page · raw message
tools/testing/selftests/bpf/bpf_helpers.h +3 -0
@@ -103,6 +103,9 @@ static int (*bpf_skb_get_xfrm_state)(void *ctx, int index, void *state, (void *) BPF_FUNC_skb_get_xfrm_state;static int (*bpf_get_stack)(void *ctx,void *buf,int size,int flags) = (void *) BPF_FUNC_get_stack; +static int (*bpf_fib_lookup)(void *ctx,struct bpf_fib_lookup *params, +int plen,__u32 flags) = + (void *) BPF_FUNC_fib_lookup;/* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions
Signature · 1 line
2.11.0
Re: [RFC v2 bpf-next 9/9] samples/bpf: Add examples of ipv4 and ipv6 forwarding in XDP
samples/bpf/xdp_fwd_kern.c line 88
Quoted 3 lines · On Sun, 29 Apr 2018 11:07:52 -0700 David Ahern <[email protected]> wrote:
samples/bpf/xdp_fwd_kern.c new file +3
-0+/* verify egress index has xdp support */ +// TO-DO bpf_map_lookup_elem(&tx_port, &key) fails with +// cannot pass map_type 14 into func bpf_map_lookup_elem#1:
I just want to point out that I/we are aware of this "usability" problem with the sample program, but I don't want to block the FIB helper going upstream, we can fix this problem later.
The problem is that if you load this bpf/xdp prog, then all incoming traffic (on that interface), will be forward using XDP, regardless whether the egress ifindex support XDP or not. And if not supported, then packets are dropped hard (only detectable via tracepoints).
If the bpf prog could tell/know that the egress ifindex doesn't support XDP xmit, then it could simply return XDP_PASS to fallback to the normal network stack.
Signature · 4 lines
Best regards, Jesper Dangaard Brouer MSc.CS, Principal Kernel Engineer at Red Hat LinkedIn: http://www.linkedin.com/in/brouer
Re: [RFC v2 bpf-next 9/9] samples/bpf: Add examples of ipv4 and ipv6 forwarding in XDP
Quoted 11 lines · Jesper Dangaard Brouer
samples/bpf/xdp_fwd_kern.c line 88
Quoted 3 lines · On Sun, 29 Apr 2018 11:07:52 -0700 David Ahern <[email protected]> wrote:
samples/bpf/xdp_fwd_kern.c new file +3
-0+/* verify egress index has xdp support */ +// TO-DO bpf_map_lookup_elem(&tx_port, &key) fails with +// cannot pass map_type 14 into func bpf_map_lookup_elem#1: I just want to point out that I/we are aware of this "usability" problem with the sample program, but I don't want to block the FIB helper going upstream, we can fix this problem later.
Quoted 9 lines
The problem is that if you load this bpf/xdp prog, then all incoming traffic (on that interface), will be forward using XDP, regardless whether the egress ifindex support XDP or not. And if not supported, then packets are dropped hard (only detectable via tracepoints).
If the bpf prog could tell/know that the egress ifindex doesn't support XDP xmit, then it could simply return
XDP_PASSto fallback to the normal network stack.
That's what the above lookup is intending but DEVMAP type does not handle lookups from xdp programs. Any chance of fixing that?
David Miller
maintainerFrom: David Ahern <[email protected]>
Date: Sun, 29 Apr 2018 11:07:43 -0700
Quoted 10 lines
Provide a helper for doing a FIB and neighbor lookups in the kernel tables from an XDP program. The helper provides a fastpath for forwarding packets. If the packet is a local delivery or for any reason is not a simple lookup and forward, the packet is expected to continue up the stack for full processing.
Patches 1-6 do some more refactoring to IPv6 with the end goal of extracting a FIB lookup function that aligns with
fib_lookupfor IPv4, basically returning afib6_infowithout creating a dst based entry.
Quoted 8 lines
Patch 7 adds lookup functions to the ipv6 stub. These are needed since bpf is built into the kernel and ipv6 may not be built or loaded.
Patch 8 adds the bpf helper and 9 adds a sample program.
v2
- fixed use of foward helper fromcls_actas noted by Daniel
- in patch 1 renamefib6_lookup_1as well for consistency
I've reviewed this and generally I agree with the semantic choices wrt. resolution.
We really can't do neigh resolution without an SKB, so at least in the xdp case we must push the packet up into the full stack path.
I guess we could do the neigh resolve in the cls_bpf case, but I wonder how helpful that would be.