Skip to content

Commit

Permalink
net/ipv6: Plumb support for filtering route dumps
Browse files Browse the repository at this point in the history
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.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David Ahern authored and David S. Miller committed Oct 16, 2018
1 parent 18a8021 commit 13e3890
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 14 deletions.
28 changes: 22 additions & 6 deletions net/ipv6/ip6_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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);
Expand Down
40 changes: 32 additions & 8 deletions net/ipv6/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 13e3890

Please sign in to comment.