net: Plumb support for filtering ipv4 and ipv6 multicast route dumps
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. Signed-off-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e1cedae1ba
commit
cb167893f4
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2528,18 +2528,30 @@ errout_free:
|
|||
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
{
|
||||
struct fib_dump_filter filter = {};
|
||||
|
||||
if (cb->strict_check) {
|
||||
int err;
|
||||
|
||||
if (cb->strict_check) {
|
||||
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] = {
|
||||
|
|
|
@ -268,21 +268,45 @@ int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
|
|||
}
|
||||
EXPORT_SYMBOL(mr_fill_mroute);
|
||||
|
||||
static bool mr_mfc_uses_dev(const struct mr_table *mrt,
|
||||
const struct mr_mfc *c,
|
||||
const struct net_device *dev)
|
||||
{
|
||||
int ct;
|
||||
|
||||
for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
|
||||
if (VIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
|
||||
const struct vif_device *vif;
|
||||
|
||||
vif = &mrt->vif_table[ct];
|
||||
if (vif->dev == dev)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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)
|
||||
spinlock_t *lock, struct fib_dump_filter *filter)
|
||||
{
|
||||
unsigned int e = 0, s_e = cb->args[1];
|
||||
unsigned int flags = NLM_F_MULTI;
|
||||
struct mr_mfc *mfc;
|
||||
int err;
|
||||
|
||||
if (filter->filter_set)
|
||||
flags |= NLM_F_DUMP_FILTERED;
|
||||
|
||||
list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) {
|
||||
if (e < s_e)
|
||||
goto next_entry;
|
||||
if (filter->dev &&
|
||||
!mr_mfc_uses_dev(mrt, mfc, filter->dev))
|
||||
goto next_entry;
|
||||
|
||||
err = fill(mrt, skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, mfc, RTM_NEWROUTE, flags);
|
||||
|
@ -298,6 +322,9 @@ next_entry:
|
|||
list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) {
|
||||
if (e < s_e)
|
||||
goto next_entry2;
|
||||
if (filter->dev &&
|
||||
!mr_mfc_uses_dev(mrt, mfc, filter->dev))
|
||||
goto next_entry2;
|
||||
|
||||
err = fill(mrt, skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, mfc, RTM_NEWROUTE, flags);
|
||||
|
@ -316,6 +343,7 @@ out:
|
|||
cb->args[1] = e;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mr_table_dump);
|
||||
|
||||
int mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
|
||||
struct mr_table *(*iter)(struct net *net,
|
||||
|
@ -324,19 +352,28 @@ 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)
|
||||
{
|
||||
unsigned int t = 0, s_t = cb->args[0];
|
||||
struct net *net = sock_net(skb->sk);
|
||||
struct mr_table *mrt;
|
||||
int err;
|
||||
|
||||
/* multicast does not track protocol or have route type other
|
||||
* than RTN_MULTICAST
|
||||
*/
|
||||
if (filter->filter_set) {
|
||||
if (filter->protocol || filter->flags ||
|
||||
(filter->rt_type && filter->rt_type != RTN_MULTICAST))
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
for (mrt = iter(net, NULL); mrt; mrt = iter(net, mrt)) {
|
||||
if (t < s_t)
|
||||
goto next_table;
|
||||
|
||||
err = mr_table_dump(mrt, skb, cb, fill, lock);
|
||||
err = mr_table_dump(mrt, skb, cb, fill, lock, filter);
|
||||
if (err < 0)
|
||||
break;
|
||||
next_table:
|
||||
|
|
|
@ -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 = {};
|
||||
|
||||
if (cb->strict_check) {
|
||||
int err;
|
||||
|
||||
if (cb->strict_check) {
|
||||
err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
|
||||
&filter, cb->extack);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
|
||||
_ip6mr_fill_mroute, &mfc_unres_lock);
|
||||
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, &filter);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue