net: Kernel side filtering for route dumps
Subsystems (3)
David Ahern
maintainerv2 cover net: Kernel side filtering for route dumps
From: David Ahern <[email protected]>
Implement kernel side filtering of route dumps by protocol (e.g., which routing daemon installed the route), route type (e.g., unicast), table id and nexthop device.
iproute2 has been doing this filtering in userspace for years; pushing the filters to the kernel side reduces the amount of data the kernel sends and reduces wasted cycles on both sides processing unwanted data. These initial options provide a huge improvement for efficiently examining routes on large scale systems.
v2
- better handling of requests for a specific table. Rather than walking the hash of all tables, lookup the specific table and dump it
- refactor mr_rtm_dumproute moving the loop over the table into a helper that can be invoked directly
- add hook to return NLM_F_DUMP_FILTERED in DONE message to ensure it is returned even when the dump returns nothing
David Ahern (11):
netlink: Add answer_flags to netlink_callback
net: Add struct for fib dump filter
net/ipv4: Plumb support for filtering route dumps net/ipv6: Plumb support for filtering route dumps net/mpls: Plumb support for filtering route dumps
ipmr: Refactor mr_rtm_dumproute
net: Plumb support for filtering ipv4 and ipv6 multicast route dumps
net: Enable kernel side filtering of route dumps net/mpls: Handle kernel side filtering of route dumps net/ipv6: Bail early if user only wants cloned entries net/ipv4: Bail early if user only wants prefix entries
include/linux/mroute_base.h | 11 +++- include/linux/netlink.h | 1 + include/net/ip6_route.h | 1 + include/net/ip_fib.h | 17 ++++-- net/ipv4/fib_frontend.c | 76 ++++++++++++++++++++++---- net/ipv4/fib_trie.c | 37 +++++++++---- net/ipv4/ipmr.c | 22 ++++++-- net/ipv4/ipmr_base.c | 126 ++++++++++++++++++++++++++++++++------------ net/ipv6/ip6_fib.c | 34 +++++++++--- net/ipv6/ip6mr.c | 21 ++++++-- net/ipv6/route.c | 40 +++++++++++--- net/mpls/af_mpls.c | 92 +++++++++++++++++++++++++++----- net/netlink/af_netlink.c | 3 +- 13 files changed, 386 insertions(+), 95 deletions(-)
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 1/11 netlink: Add answer_flags to netlink_callback
From: David Ahern <[email protected]>
With dump filtering we need a way to ensure the NLM_F_DUMP_FILTERED flag is set on a message back to the user if the data returned is influenced by some input attributes. Normally this can be done as messages are added to the skb, but if the filter results in no data being returned, the user could be confused as to why.
This patch adds answer_flags to the netlink_callback allowing dump handlers to set the NLM_F_DUMP_FILTERED at a minimum in the NLMSG_DONE message ensuring the flag gets back to the user.
The netlink_callback space is initialized to 0 via a memset in __netlink_dump_start, so init of the new answer_flags is covered.
--- include/linux/netlink.h | 1 + net/netlink/af_netlink.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
include/linux/netlink.h +1 -0
@@ -180,6 +180,7 @@ struct netlink_callback {u16 family;u16 min_dump_alloc;bool strict_check; +u16 answer_flags;unsigned int prev_seq, seq;long args[6 ]; };
net/netlink/af_netlink.c +2 -1
@@ -2257,7 +2257,8 @@ static int netlink_dump(struct sock *sk) } nlh =nlmsg_put_answer (skb, cb,NLMSG_DONE , -sizeof (nlk->dump_done_errno),NLM_F_MULTI ); +sizeof (nlk->dump_done_errno), +NLM_F_MULTI | cb->answer_flags);if (WARN_ON (!nlh)) goto errout_skb;
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 2/11 net: Add struct for fib dump filter
In reply to David AhernFrom: David Ahern <[email protected]>
Add struct fib_dump_filter for options on limiting which routes are returned in a dump request. The current list is table id, protocol, route type, rtm_flags and nexthop device index. struct net is needed to lookup the net_device from the index.
Declare the filter for each route dump handler and plumb the new arguments from dump handlers to ip_valid_fib_dump_req.
--- include/net/ip6_route.h | 1 + include/net/ip_fib.h | 13 ++++++++++++- net/ipv4/fib_frontend.c | 6 ++++-- net/ipv4/ipmr.c | 6 +++++- net/ipv6/ip6_fib.c | 5 +++-- net/ipv6/ip6mr.c | 5 ++++- net/mpls/af_mpls.c | 12 ++++++++---- 7 files changed, 37 insertions(+), 11 deletions(-)
include/net/ip6_route.h +1 -0
@@ -174,6 +174,7 @@ struct rt6_rtnl_dump_arg {struct sk_buff *skb;struct netlink_callback *cb;struct net *net; +struct fib_dump_filter filter; };int rt6_dump_route (struct fib6_info *f6i,void *p_arg);
include/net/ip_fib.h +12 -1
@@ -222,6 +222,16 @@ struct fib_table {unsigned long __data[0 ]; }; +struct fib_dump_filter { +u32 table_id; +/* filter_set is an optimization that an entry is set */ +bool filter_set; +unsigned char protocol; +unsigned char rt_type; +unsigned int flags; +struct net_device *dev; +}; +int fib_table_lookup (struct fib_table *tb,const struct flowi4 *flp,struct fib_result *res,int fib_flags); int fib_table_insert(struct net *, struct fib_table *, struct fib_config *,
@@ -453,6 +463,7 @@ static inline void fib_proc_exit(struct net *net)u32 ip_mtu_from_fib_result (struct fib_result *res,__be32 daddr); -int ip_valid_fib_dump_req (const struct nlmsghdr *nlh, +int ip_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh, +struct fib_dump_filter *filter,struct netlink_ext_ack *extack);#endif /* _NET_FIB_H */
net/ipv4/fib_frontend.c +4 -2
@@ -802,7 +802,8 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,return err; } -int ip_valid_fib_dump_req (const struct nlmsghdr *nlh, +int ip_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh, +struct fib_dump_filter *filter,struct netlink_ext_ack *extack) {struct rtmsg *rtm;
@@ -837,6 +838,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) {const struct nlmsghdr *nlh = cb->nlh;struct net *net =sock_net (skb->sk); +struct fib_dump_filter filter = {};unsigned int h, s_h;unsigned int e =0 , s_e;struct fib_table *tb;
@@ -844,7 +846,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)int dumped =0 , err;if (cb->strict_check) { - err =ip_valid_fib_dump_req (nlh, cb->extack); + err =ip_valid_fib_dump_req (net, nlh, &filter, cb->extack);if (err <0 )return err; }
net/ipv4/ipmr.c +5 -1
@@ -2527,9 +2527,13 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,static int ipmr_rtm_dumproute (struct sk_buff *skb,struct netlink_callback *cb) { +struct fib_dump_filter filter = {}; +if (cb->strict_check) { -int err =ip_valid_fib_dump_req (cb->nlh, cb->extack); +int err; + err =ip_valid_fib_dump_req (sock_net (skb->sk), cb->nlh, + &filter, cb->extack);if (err <0 )return err; }
net/ipv6/ip6_fib.c +3 -2
@@ -569,17 +569,18 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) {const struct nlmsghdr *nlh = cb->nlh;struct net *net =sock_net (skb->sk); +struct rt6_rtnl_dump_arg arg = {};unsigned int h, s_h;unsigned int e =0 , s_e; -struct rt6_rtnl_dump_arg arg;struct fib6_walker *w;struct fib6_table *tb;struct hlist_head *head;int res =0 ;if (cb->strict_check) { -int err =ip_valid_fib_dump_req (nlh, cb->extack); +int err; + err =ip_valid_fib_dump_req (net, nlh, &arg.filter, cb->extack);if (err <0 )return err; }
net/ipv6/ip6mr.c +4 -1
@@ -2458,10 +2458,13 @@ static void mrt6msg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)static int ip6mr_rtm_dumproute (struct sk_buff *skb,struct netlink_callback *cb) {const struct nlmsghdr *nlh = cb->nlh; +struct fib_dump_filter filter = {};if (cb->strict_check) { -int err =ip_valid_fib_dump_req (nlh, cb->extack); +int err; + err =ip_valid_fib_dump_req (sock_net (skb->sk), nlh, + &filter, cb->extack);if (err <0 )return err; }
net/mpls/af_mpls.c +8 -4
@@ -2032,13 +2032,15 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event, }#if IS_ENABLED (CONFIG_INET ) -static int mpls_valid_fib_dump_req (const struct nlmsghdr *nlh, +static int mpls_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh, +struct fib_dump_filter *filter,struct netlink_ext_ack *extack) { -return ip_valid_fib_dump_req (nlh, extack); +return ip_valid_fib_dump_req (net, nlh, filter, extack); }#else -static int mpls_valid_fib_dump_req (const struct nlmsghdr *nlh, +static int mpls_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh, +struct fib_dump_filter *filter,struct netlink_ext_ack *extack) {struct rtmsg *rtm;
@@ -2070,14 +2072,16 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)const struct nlmsghdr *nlh = cb->nlh;struct net *net =sock_net (skb->sk);struct mpls_route __rcu **platform_label; +struct fib_dump_filter filter = {};size_t platform_labels;unsigned int index;ASSERT_RTNL ();if (cb->strict_check) { -int err =mpls_valid_fib_dump_req (nlh, cb->extack); +int err; + err =mpls_valid_fib_dump_req (net, nlh, &filter, cb->extack);if (err <0 )return err; }
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 3/11 net/ipv4: Plumb support for filtering route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Implement kernel side filtering of routes by table id, egress device index, protocol and route type. If the table id is given in the filter, lookup the table and call fib_table_dump directly for it.
--- include/net/ip_fib.h | 2 +- net/ipv4/fib_frontend.c | 13 ++++++++++++- net/ipv4/fib_trie.c | 37 ++++++++++++++++++++++++++----------- 3 files changed, 39 insertions(+), 13 deletions(-)
include/net/ip_fib.h +1 -1
@@ -239,7 +239,7 @@ int fib_table_insert(struct net *, struct fib_table *, struct fib_config *,int fib_table_delete (struct net *,struct fib_table *,struct fib_config *,struct netlink_ext_ack *extack);int fib_table_dump (struct fib_table *table,struct sk_buff *skb, -struct netlink_callback *cb); +struct netlink_callback *cb,struct fib_dump_filter *filter);int fib_table_flush (struct net *net,struct fib_table *table);struct fib_table *fib_trie_unmerge (struct fib_table *main_tb);void fib_table_flush_external (struct fib_table *table);
net/ipv4/fib_frontend.c +12 -1
@@ -855,6 +855,17 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) ((struct rtmsg *)nlmsg_data(nlh))->rtm_flags & RTM_F_CLONED)return skb->len; +if (filter.table_id) { + tb =fib_get_table (net, filter.table_id); +if (!tb) { +NL_SET_ERR_MSG (cb->extack,"ipv4: FIB table does not exist" ); +return -ENOENT ; + } + + err =fib_table_dump (tb, skb, cb, &filter); +return skb->len ? : err; + } + s_h = cb->args[0 ]; s_e = cb->args[1 ];
@@ -869,7 +880,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)if (dumped)memset (&cb->args[2 ],0 ,sizeof (cb->args) -2 *sizeof (cb->args[0 ])); - err =fib_table_dump (tb, skb, cb); + err =fib_table_dump (tb, skb, cb, &filter);if (err <0 ) {if (likely (skb->len)) goto out;
net/ipv4/fib_trie.c +26 -11
@@ -2003,12 +2003,17 @@ void fib_free_table(struct fib_table *tb) }static int fn_trie_dump_leaf (struct key_vector *l,struct fib_table *tb, -struct sk_buff *skb,struct netlink_callback *cb) +struct sk_buff *skb,struct netlink_callback *cb, +struct fib_dump_filter *filter) { +unsigned int flags =NLM_F_MULTI ;__be32 xkey =htonl (l->key);struct fib_alias *fa;int i, s_i; +if (filter->filter_set) + flags |=NLM_F_DUMP_FILTERED ; + s_i = cb->args[4 ]; i =0 ;
@@ -2016,25 +2021,35 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,hlist_for_each_entry_rcu (fa, &l->leaf, fa_list) {int err; -if (i < s_i) { - i++; -continue ; - } +if (i < s_i) + goto next; -if (tb->tb_id != fa->tb_id) { - i++; -continue ; +if (tb->tb_id != fa->tb_id) + goto next; + +if (filter->filter_set) { +if (filter->rt_type && fa->fa_type != filter->rt_type) + goto next; + +if ((filter->protocol && + fa->fa_info->fib_protocol != filter->protocol)) + goto next; + +if (filter->dev && + !fib_info_nh_uses_dev (fa->fa_info, filter->dev)) + goto next; } err =fib_dump_info (skb,NETLINK_CB (cb->skb).portid, cb->nlh->nlmsg_seq,RTM_NEWROUTE , tb->tb_id, fa->fa_type, xkey,KEYLENGTH - fa->fa_slen, - fa->fa_tos, fa->fa_info,NLM_F_MULTI ); + fa->fa_tos, fa->fa_info, flags);if (err <0 ) { cb->args[4 ] = i;return err; } +next: i++; }
@@ -2044,7 +2059,7 @@ static int fn_trie_dump_leaf(struct key_vector *l, struct fib_table *tb,/* rcu_read_lock needs to be hold by caller from readside */ int fib_table_dump (struct fib_table *tb,struct sk_buff *skb, -struct netlink_callback *cb) +struct netlink_callback *cb,struct fib_dump_filter *filter) {struct trie *t = (struct trie *)tb->tb_data;struct key_vector *l, *tp = t->kv;
@@ -2057,7 +2072,7 @@ int fib_table_dump(struct fib_table *tb, struct sk_buff *skb,while ((l =leaf_walk_rcu (&tp, key)) !=NULL ) {int err; - err =fn_trie_dump_leaf (l, tb, skb, cb); + err =fn_trie_dump_leaf (l, tb, skb, cb, filter);if (err <0 ) { cb->args[3 ] = key; cb->args[2 ] = count;
Signature · 1 line
2.11.0
Re: [PATCH v2 net-next 03/11] net/ipv4: Plumb support for filtering route dumps
Hi David,
Before commit 18a8021a7be3 ("net/ipv4: Plumb support for filtering route dumps"), when we dump a non-exist table, ip cmd exits silently.
# ip -4 route list table 1 # echo $? 0
After commit 18a8021a7be3 ("net/ipv4: Plumb support for filtering route dumps"). When we dump a non-exist table, as we returned -ENOENT, ip route shows:
# ip -4 route show table 1 Error: ipv4: FIB table does not exist. Dump terminated # echo $? 2
For me it looks make sense to return -ENOENT if we do not have the route table. But this changes the userspace behavior. Do you think if we need to keep backward compatible or just let it do as it is right now?
Thanks
Hangbin
Re: [PATCH v2 net-next 03/11] net/ipv4: Plumb support for filtering route dumps
Hi:
Quoted 19 lines · Hangbin Liu
Hi David,
Before commit
18a8021a7be3("net/ipv4: Plumb support for filtering route dumps"), when we dump a non-exist table, ip cmd exits silently.# ip -4 route list table 1 # echo $? 0After commit
18a8021a7be3("net/ipv4: Plumb support for filtering route dumps"). When we dump a non-exist table, as we returned-ENOENT, ip route shows:# ip -4 route show table 1 Error: ipv4: FIB table does not exist. Dump terminated # echo $? 2
Quoted 4 lines
For me it looks make sense to return
-ENOENTif we do not have the route table. But this changes the userspace behavior. Do you think if we need to keep backward compatible or just let it do as it is right now?
It is not change in userspace behavior; ip opted into the strict checking. The impact is to 'ip' users.
A couple of people have asked about this, and I am curious as to why people run a route dump for a table that does not exist and do not like being told that it does not exist.
Re: [PATCH v2 net-next 03/11] net/ipv4: Plumb support for filtering route dumps
Quoted 27 lines · David Ahern
Hi:
Quoted 19 lines · Hangbin Liu
Hi David,
Before commit
18a8021a7be3("net/ipv4: Plumb support for filtering route dumps"), when we dump a non-exist table, ip cmd exits silently.# ip -4 route list table 1 # echo $? 0After commit
18a8021a7be3("net/ipv4: Plumb support for filtering route dumps"). When we dump a non-exist table, as we returned-ENOENT, ip route shows:# ip -4 route show table 1 Error: ipv4: FIB table does not exist. Dump terminated # echo $? 2Quoted 4 lines
For me it looks make sense to return
-ENOENTif we do not have the route table. But this changes the userspace behavior. Do you think if we need to keep backward compatible or just let it do as it is right now?
Quoted 6 lines
It is not change in userspace behavior; ip opted into the strict checking. The impact is to 'ip' users.
A couple of people have asked about this, and I am curious as to why people run a route dump for a table that does not exist and do not like being told that it does not exist.
Hi David,
Thanks for the reply. We have some route function tests and the new behavior break the test. I just want to make sure this is expected so we can change our tests to match the new behavior.
Cheers
Hangbin
David Ahern
maintainerPATCH v2 4/11 net/ipv6: Plumb support for filtering route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Implement kernel side filtering of routes by table id, egress device index, protocol, and route type. If the table id is given in the filter, lookup the table and call fib6_dump_table directly for it.
Move the existing route flags check for prefix only routes to the new filter.
--- net/ipv6/ip6_fib.c | 28 ++++++++++++++++++++++------ net/ipv6/route.c | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 14 deletions(-)
net/ipv6/ip6_fib.c +22 -6
@@ -583,10 +583,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) err =ip_valid_fib_dump_req (net, nlh, &arg.filter, cb->extack);if (err <0 )return err; - } + }else if (nlmsg_len (nlh) >=sizeof (struct rtmsg )) { +struct rtmsg *rtm =nlmsg_data (nlh); - s_h = cb->args[0 ]; - s_e = cb->args[1 ]; +if (rtm->rtm_flags &RTM_F_PREFIX ) + arg.filter.flags =RTM_F_PREFIX ; + } w = (void *)cb->args[2 ];if (!w) {
@@ -612,6 +614,20 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) arg.net = net; w->args = &arg; +if (arg.filter.table_id) { + tb =fib6_get_table (net, arg.filter.table_id); +if (!tb) { +NL_SET_ERR_MSG_MOD (cb->extack,"FIB table does not exist" ); +return -ENOENT ; + } + + res =fib6_dump_table (tb, skb, cb); + goto out; + } + + s_h = cb->args[0 ]; + s_e = cb->args[1 ]; +rcu_read_lock ();for (h = s_h; h <FIB6_TABLE_HASHSZ ; h++, s_e =0 ) { e =0 ;
@@ -621,16 +637,16 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) goto next; res =fib6_dump_table (tb, skb, cb);if (res !=0 ) - goto out; + goto out_unlock; next: e++; } } -out: +out_unlock:rcu_read_unlock (); cb->args[1 ] = e; cb->args[0 ] = h; - +out: res = res <0 ? res : skb->len;if (res <=0 )fib6_dump_end (cb);
net/ipv6/route.c +32 -8
@@ -4767,28 +4767,52 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,return -EMSGSIZE ; } +static bool fib6_info_uses_dev (const struct fib6_info *f6i, +const struct net_device *dev) +{ +if (f6i->fib6_nh.nh_dev == dev) +return true; + +if (f6i->fib6_nsiblings) { +struct fib6_info *sibling, *next_sibling; + +list_for_each_entry_safe (sibling, next_sibling, + &f6i->fib6_siblings, fib6_siblings) { +if (sibling->fib6_nh.nh_dev == dev) +return true; + } + } + +return false; +} +int rt6_dump_route (struct fib6_info *rt,void *p_arg) {struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; +struct fib_dump_filter *filter = &arg->filter; +unsigned int flags =NLM_F_MULTI ;struct net *net = arg->net;if (rt == net->ipv6.fib6_null_entry)return 0 ; -if (nlmsg_len (arg->cb->nlh) >=sizeof (struct rtmsg )) { -struct rtmsg *rtm =nlmsg_data (arg->cb->nlh); - -/* user wants prefix routes only */ -if (rtm->rtm_flags &RTM_F_PREFIX && - !(rt->fib6_flags &RTF_PREFIX_RT )) { -/* success since this is not a prefix route */ +if ((filter->flags &RTM_F_PREFIX ) && + !(rt->fib6_flags &RTF_PREFIX_RT )) { +/* success since this is not a prefix route */ +return 1 ; + } +if (filter->filter_set) { +if ((filter->rt_type && rt->fib6_type != filter->rt_type) || + (filter->dev && !fib6_info_uses_dev (rt, filter->dev)) || + (filter->protocol && rt->fib6_protocol != filter->protocol)) {return 1 ; } + flags |=NLM_F_DUMP_FILTERED ; }return rt6_fill_node (net, arg->skb, rt,NULL ,NULL ,NULL ,0 ,RTM_NEWROUTE ,NETLINK_CB (arg->cb->skb).portid, - arg->cb->nlh->nlmsg_seq,NLM_F_MULTI ); + arg->cb->nlh->nlmsg_seq, flags); } static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 5/11 net/mpls: Plumb support for filtering route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Implement kernel side filtering of routes by egress device index and protocol. MPLS uses only a single table and route type.
--- net/mpls/af_mpls.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-)
net/mpls/af_mpls.c +41 -1
@@ -2067,12 +2067,35 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, }#endif +static bool mpls_rt_uses_dev (struct mpls_route *rt, +const struct net_device *dev) +{ +struct net_device *nh_dev; + +if (rt->rt_nhn ==1 ) { +struct mpls_nh *nh = rt->rt_nh; + + nh_dev =rtnl_dereference (nh->nh_dev); +if (dev == nh_dev) +return true; + }else { +for_nexthops (rt) { + nh_dev =rtnl_dereference (nh->nh_dev); +if (nh_dev == dev) +return true; + }endfor_nexthops (rt); + } + +return false; +} +static int mpls_dump_routes (struct sk_buff *skb,struct netlink_callback *cb) {const struct nlmsghdr *nlh = cb->nlh;struct net *net =sock_net (skb->sk);struct mpls_route __rcu **platform_label;struct fib_dump_filter filter = {}; +unsigned int flags =NLM_F_MULTI ;size_t platform_labels;unsigned int index;
@@ -2084,6 +2107,14 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) err =mpls_valid_fib_dump_req (net, nlh, &filter, cb->extack);if (err <0 )return err; + +/* for MPLS, there is only 1 table with fixed type and flags. +* If either are set in the filter then return nothing. +*/ +if ((filter.table_id && filter.table_id !=RT_TABLE_MAIN ) || + (filter.rt_type && filter.rt_type !=RTN_UNICAST ) || + filter.flags) +return skb->len; } index = cb->args[0 ];
@@ -2092,15 +2123,24 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb) platform_label =rtnl_dereference (net->mpls.platform_label); platform_labels = net->mpls.platform_labels; + +if (filter.filter_set) + flags |=NLM_F_DUMP_FILTERED ; +for (; index < platform_labels; index++) {struct mpls_route *rt; + rt =rtnl_dereference (platform_label[index]);if (!rt)continue ; +if ((filter.dev && !mpls_rt_uses_dev (rt, filter.dev)) || + (filter.protocol && rt->rt_protocol != filter.protocol)) +continue ; +if (mpls_dump_route (skb,NETLINK_CB (cb->skb).portid, cb->nlh->nlmsg_seq,RTM_NEWROUTE , - index, rt,NLM_F_MULTI ) <0 ) + index, rt, flags) <0 )break ; } cb->args[0 ] = index;
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 6/11 ipmr: Refactor mr_rtm_dumproute
In reply to David AhernFrom: David Ahern <[email protected]>
Move per-table loops from mr_rtm_dumproute to mr_table_dump and export mr_table_dump for dumps by specific table id.
--- include/linux/mroute_base.h | 6 ++++ net/ipv4/ipmr_base.c | 88 ++++++++++++++++++++++++++++----------------- 2 files changed, 61 insertions(+), 33 deletions(-)
include/linux/mroute_base.h +6 -0
@@ -283,6 +283,12 @@ void *mr_mfc_find_any(struct mr_table *mrt, int vifi, void *hasharg);int mr_fill_mroute (struct mr_table *mrt,struct sk_buff *skb,struct mr_mfc *c,struct rtmsg *rtm); +int mr_table_dump (struct mr_table *mrt,struct sk_buff *skb, +struct netlink_callback *cb, +int (*fill)(struct mr_table *mrt,struct sk_buff *skb, +u32 portid,u32 seq,struct mr_mfc *c, +int cmd,int flags), +spinlock_t *lock); int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, struct mr_table *(*iter)(struct net *net, struct mr_table *mrt),
net/ipv4/ipmr_base.c +55 -33
112 lines not on this page · raw message
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 7/11 net: Plumb support for filtering ipv4 and ipv6 multicast route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Implement kernel side filtering of routes by egress device index and table id. If the table id is given in the filter, lookup table and call mr_table_dump directly for it.
--- include/linux/mroute_base.h | 7 ++++--- net/ipv4/ipmr.c | 18 +++++++++++++++--- net/ipv4/ipmr_base.c | 42 +++++++++++++++++++++++++++++++++++++++--- net/ipv6/ip6mr.c | 18 +++++++++++++++--- 4 files changed, 73 insertions(+), 12 deletions(-)
include/linux/mroute_base.h +4 -3
@@ -7,6 +7,7 @@#include <net/net_namespace.h> #include <net/sock.h> #include <net/fib_notifier.h> +#include <net/ip_fib.h> /** * struct vif_device - interface representor for multicast routing
@@ -288,7 +289,7 @@ int mr_table_dump(struct mr_table *mrt, struct sk_buff *skb,int (*fill)(struct mr_table *mrt,struct sk_buff *skb,u32 portid,u32 seq,struct mr_mfc *c,int cmd,int flags), - spinlock_t *lock); + spinlock_t *lock, struct fib_dump_filter *filter); int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb, struct mr_table *(*iter)(struct net *net, struct mr_table *mrt),
@@ -296,7 +297,7 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,struct sk_buff *skb, u32 portid, u32 seq, struct mr_mfc *c, int cmd, int flags), - spinlock_t *lock); + spinlock_t *lock, struct fib_dump_filter *filter); int mr_dump(struct net *net, struct notifier_block *nb, unsigned short family, int (*rules_dump)(struct net *net,
@@ -346,7 +347,7 @@ mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,struct sk_buff *skb, u32 portid, u32 seq, struct mr_mfc *c, int cmd, int flags), - spinlock_t *lock) + spinlock_t *lock, struct fib_dump_filter *filter) { return -EINVAL; }
net/ipv4/ipmr.c +15 -3
@@ -2528,18 +2528,30 @@ static int ipmr_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,static int ipmr_rtm_dumproute (struct sk_buff *skb,struct netlink_callback *cb) {struct fib_dump_filter filter = {}; +int err;if (cb->strict_check) { -int err; - err =ip_valid_fib_dump_req (sock_net (skb->sk), cb->nlh, &filter, cb->extack);if (err <0 )return err; } +if (filter.table_id) { +struct mr_table *mrt; + + mrt =ipmr_get_table (sock_net (skb->sk), filter.table_id); +if (!mrt) { +NL_SET_ERR_MSG (cb->extack,"ipv4: MR table does not exist" ); +return -ENOENT ; + } + err =mr_table_dump (mrt, skb, cb, _ipmr_fill_mroute, + &mfc_unres_lock, &filter); +return skb->len ? : err; + } +return mr_rtm_dumproute (skb, cb, ipmr_mr_table_iter, - _ipmr_fill_mroute, &mfc_unres_lock); + _ipmr_fill_mroute, &mfc_unres_lock, &filter); }static const struct nla_policy rtm_ipmr_policy[RTA_MAX +1 ] = {
net/ipv4/ipmr_base.c +39 -3
85 lines not on this page · raw message
net/ipv6/ip6mr.c +15 -3
@@ -2459,16 +2459,28 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb) {const struct nlmsghdr *nlh = cb->nlh;struct fib_dump_filter filter = {}; +int err;if (cb->strict_check) { -int err; - err =ip_valid_fib_dump_req (sock_net (skb->sk), nlh, &filter, cb->extack);if (err <0 )return err; } +if (filter.table_id) { +struct mr_table *mrt; + + mrt =ip6mr_get_table (sock_net (skb->sk), filter.table_id); +if (!mrt) { +NL_SET_ERR_MSG_MOD (cb->extack,"MR table does not exist" ); +return -ENOENT ; + } + err =mr_table_dump (mrt, skb, cb, _ip6mr_fill_mroute, + &mfc_unres_lock, &filter); +return skb->len ? : err; + } +return mr_rtm_dumproute (skb, cb, ip6mr_mr_table_iter, - _ip6mr_fill_mroute, &mfc_unres_lock); + _ip6mr_fill_mroute, &mfc_unres_lock, &filter); }
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 8/11 net: Enable kernel side filtering of route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Update parsing of route dump request to enable kernel side filtering. Allow filtering results by protocol (e.g., which routing daemon installed the route), route type (e.g., unicast), table id and nexthop device. These amount to the low hanging fruit, yet a huge improvement, for dumping routes.
ip_valid_fib_dump_req is called with RTNL held, so __dev_get_by_index can be used to look up the device index without taking a reference. From there filter->dev is only used during dump loops with the lock still held.
Set NLM_F_DUMP_FILTERED in the answer_flags so the user knows the results have been filtered should no entries be returned.
--- include/net/ip_fib.h | 2 +- net/ipv4/fib_frontend.c | 51 ++++++++++++++++++++++++++++++++++++++++++------- net/ipv4/ipmr.c | 2 +- net/ipv6/ip6_fib.c | 2 +- net/ipv6/ip6mr.c | 2 +- net/mpls/af_mpls.c | 9 +++++---- 6 files changed, 53 insertions(+), 15 deletions(-)
include/net/ip_fib.h +1 -1
@@ -465,5 +465,5 @@ u32 ip_mtu_from_fib_result(struct fib_result *res, __be32 daddr);int ip_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh,struct fib_dump_filter *filter, -struct netlink_ext_ack *extack); +struct netlink_callback *cb);#endif /* _NET_FIB_H */
net/ipv4/fib_frontend.c +44 -7
77 lines not on this page · raw message
net/ipv4/ipmr.c +1 -1
@@ -2532,7 +2532,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)if (cb->strict_check) { err =ip_valid_fib_dump_req (sock_net (skb->sk), cb->nlh, - &filter, cb->extack); + &filter, cb);if (err <0 )return err; }
net/ipv6/ip6_fib.c +1 -1
@@ -580,7 +580,7 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)if (cb->strict_check) {int err; - err =ip_valid_fib_dump_req (net, nlh, &arg.filter, cb->extack); + err =ip_valid_fib_dump_req (net, nlh, &arg.filter, cb);if (err <0 )return err; }else if (nlmsg_len (nlh) >=sizeof (struct rtmsg )) {
net/ipv6/ip6mr.c +1 -1
@@ -2463,7 +2463,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)if (cb->strict_check) { err =ip_valid_fib_dump_req (sock_net (skb->sk), nlh, - &filter, cb->extack); + &filter, cb);if (err <0 )return err; }
net/mpls/af_mpls.c +5 -4
@@ -2034,15 +2034,16 @@ static int mpls_dump_route(struct sk_buff *skb, u32 portid, u32 seq, int event,#if IS_ENABLED (CONFIG_INET )static int mpls_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh,struct fib_dump_filter *filter, -struct netlink_ext_ack *extack) +struct netlink_callback *cb) { -return ip_valid_fib_dump_req (net, nlh, filter, extack); +return ip_valid_fib_dump_req (net, nlh, filter, cb); }#else static int mpls_valid_fib_dump_req (struct net *net,const struct nlmsghdr *nlh,struct fib_dump_filter *filter, -struct netlink_ext_ack *extack) +struct netlink_callback *cb) { +struct netlink_ext_ack *extack = cb->extack;struct rtmsg *rtm;if (nlh->nlmsg_len <nlmsg_msg_size (sizeof (*rtm))) {
@@ -2104,7 +2105,7 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)if (cb->strict_check) {int err; - err =mpls_valid_fib_dump_req (net, nlh, &filter, cb->extack); + err =mpls_valid_fib_dump_req (net, nlh, &filter, cb);if (err <0 )return err;
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 9/11 net/mpls: Handle kernel side filtering of route dumps
In reply to David AhernFrom: David Ahern <[email protected]>
Update the dump request parsing in MPLS for the non-INET case to enable kernel side filtering. If INET is disabled the only filters that make sense for MPLS are protocol and nexthop device.
--- net/mpls/af_mpls.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-)
net/mpls/af_mpls.c +28 -5
@@ -2044,7 +2044,9 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, struct netlink_callback *cb) {struct netlink_ext_ack *extack = cb->extack; +struct nlattr *tb[RTA_MAX +1 ];struct rtmsg *rtm; +int err, i;if (nlh->nlmsg_len <nlmsg_msg_size (sizeof (*rtm))) {NL_SET_ERR_MSG_MOD (extack,"Invalid header for FIB dump request" );
@@ -2053,15 +2055,36 @@ static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, rtm =nlmsg_data (nlh);if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || - rtm->rtm_table || rtm->rtm_protocol || rtm->rtm_scope || - rtm->rtm_type || rtm->rtm_flags) { + rtm->rtm_table || rtm->rtm_scope || rtm->rtm_type || + rtm->rtm_flags) {NL_SET_ERR_MSG_MOD (extack,"Invalid values in header for FIB dump request" );return -EINVAL ; } -if (nlmsg_attrlen (nlh,sizeof (*rtm))) { -NL_SET_ERR_MSG_MOD (extack,"Invalid data after header in FIB dump request" ); -return -EINVAL ; +if (rtm->rtm_protocol) { + filter->protocol = rtm->rtm_protocol; + filter->filter_set =1 ; + cb->answer_flags =NLM_F_DUMP_FILTERED ; + } + + err =nlmsg_parse_strict (nlh,sizeof (*rtm), tb,RTA_MAX , + rtm_mpls_policy, extack); +if (err <0 ) +return err; + +for (i =0 ; i <=RTA_MAX ; ++i) { +int ifindex; + +if (i ==RTA_OIF ) { + ifindex =nla_get_u32 (tb[i]); + filter->dev =__dev_get_by_index (net, ifindex); +if (!filter->dev) +return -ENODEV ; + filter->filter_set =1 ; + }else if (tb[i]) { +NL_SET_ERR_MSG_MOD (extack,"Unsupported attribute in dump request" ); +return -EINVAL ; + } }return 0 ;
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 10/11 net/ipv6: Bail early if user only wants cloned entries
In reply to David AhernFrom: David Ahern <[email protected]>
Similar to IPv4, IPv6 fib no longer contains cloned routes. If a user requests a route dump for only cloned entries, no sense walking the FIB and returning everything.
--- net/ipv6/ip6_fib.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/ipv6/ip6_fib.c +5 -2
@@ -586,10 +586,13 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) }else if (nlmsg_len (nlh) >=sizeof (struct rtmsg )) {struct rtmsg *rtm =nlmsg_data (nlh); -if (rtm->rtm_flags &RTM_F_PREFIX ) - arg.filter.flags =RTM_F_PREFIX ; + arg.filter.flags = rtm->rtm_flags & (RTM_F_PREFIX |RTM_F_CLONED ); } +/* fib entries are never clones */ +if (arg.filter.flags &RTM_F_CLONED ) +return skb->len; + w = (void *)cb->args[2 ];if (!w) {/* New dump:
Signature · 1 line
2.11.0
David Ahern
maintainerPATCH v2 11/11 net/ipv4: Bail early if user only wants prefix entries
In reply to David AhernFrom: David Ahern <[email protected]>
Unlike IPv6, IPv4 does not have routes marked with RTF_PREFIX_RT. If the flag is set in the dump request, just return.
In the process of this change, move the CLONE check to use the new filter flags.
--- net/ipv4/fib_frontend.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
net/ipv4/fib_frontend.c +6 -2
@@ -886,10 +886,14 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) err =ip_valid_fib_dump_req (net, nlh, &filter, cb);if (err <0 )return err; + }else if (nlmsg_len (nlh) >=sizeof (struct rtmsg )) { +struct rtmsg *rtm =nlmsg_data (nlh); + + filter.flags = rtm->rtm_flags & (RTM_F_PREFIX |RTM_F_CLONED ); } -if (nlmsg_len (nlh) >=sizeof (struct rtmsg ) && - ((struct rtmsg *)nlmsg_data (nlh))->rtm_flags &RTM_F_CLONED ) +/* fib entries are never clones and ipv4 does not use prefix flag */ +if (filter.flags & (RTM_F_PREFIX |RTM_F_CLONED ))return skb->len;if (filter.table_id) {
Signature · 1 line
2.11.0
David Miller
maintainerFrom: David Ahern <[email protected]>
Date: Mon, 15 Oct 2018 18:56:40 -0700
Quoted 12 lines
From: David Ahern <[email protected]>
Implement kernel side filtering of route dumps by protocol (e.g., which routing daemon installed the route), route type (e.g., unicast), table id and nexthop device.
iproute2 has been doing this filtering in userspace for years; pushing the filters to the kernel side reduces the amount of data the kernel sends and reduces wasted cycles on both sides processing unwanted data. These initial options provide a huge improvement for efficiently examining routes on large scale systems.
Quoted 7 lines
v2
- better handling of requests for a specific table. Rather than walking the hash of all tables, lookup the specific table and dump it
- refactormr_rtm_dumproutemoving the loop over the table into a helper that can be invoked directly
- add hook to returnNLM_F_DUMP_FILTEREDin DONE message to ensure it is returned even when the dump returns nothing
Looks great David, I'll push this out to net-next after my build tests finish.
Thanks.