From: David Ahern <dsah...@gmail.com>

Implement kernel side filtering of routes by egress device index and
table id.

Signed-off-by: David Ahern <dsah...@gmail.com>
---
 include/linux/mroute_base.h |  5 +++--
 net/ipv4/ipmr.c             |  2 +-
 net/ipv4/ipmr_base.c        | 42 +++++++++++++++++++++++++++++++++++++++++-
 net/ipv6/ip6mr.c            |  2 +-
 4 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/include/linux/mroute_base.h b/include/linux/mroute_base.h
index 6675b9f81979..8fc516c47a64 100644
--- a/include/linux/mroute_base.h
+++ b/include/linux/mroute_base.h
@@ -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
@@ -290,7 +291,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,
@@ -340,7 +341,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;
 }
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 9e9ad60dff6b..2fe24009439a 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2538,7 +2538,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct 
netlink_callback *cb)
        }
 
        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] = {
diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c
index 1ad9aa62a97b..a4f83cbf033d 100644
--- a/net/ipv4/ipmr_base.c
+++ b/net/ipv4/ipmr_base.c
@@ -268,6 +268,24 @@ 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_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb,
                     struct mr_table *(*iter)(struct net *net,
                                              struct mr_table *mrt),
@@ -275,17 +293,35 @@ 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, e = 0, s_t = cb->args[0], s_e = cb->args[1];
        struct net *net = sock_net(skb->sk);
        struct mr_table *mrt;
        struct mr_mfc *mfc;
 
+       /* multicast does not use tos or scope, track protocol or have
+        * route type other than RTN_MULTICAST
+        */
+       if (filter->tos || filter->protocol || filter->scope || filter->flags ||
+           (filter->rt_type && filter->rt_type != RTN_MULTICAST))
+               return 0;
+
        rcu_read_lock();
+
+       if (filter->ifindex) {
+               filter->dev = dev_get_by_index_rcu(net, filter->ifindex);
+               if (!filter->dev) {
+                       rcu_read_unlock();
+                       return -ENODEV;
+               }
+       }
+
        for (mrt = iter(net, NULL); mrt; mrt = iter(net, mrt)) {
                if (t < s_t)
                        goto next_table;
+               if (filter->table_id && filter->table_id != mrt->id)
+                       goto next_table;
                list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) {
                        if (e < s_e)
                                goto next_entry;
@@ -303,6 +339,10 @@ int mr_rtm_dumproute(struct sk_buff *skb, struct 
netlink_callback *cb,
                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;
+
                        if (fill(mrt, skb, NETLINK_CB(cb->skb).portid,
                                 cb->nlh->nlmsg_seq, mfc,
                                 RTM_NEWROUTE, NLM_F_MULTI) < 0) {
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index b3084b2c8f88..08e2443ca0cc 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2444,5 +2444,5 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, 
struct netlink_callback *cb)
        }
 
        return mr_rtm_dumproute(skb, cb, ip6mr_mr_table_iter,
-                               _ip6mr_fill_mroute, &mfc_unres_lock);
+                               _ip6mr_fill_mroute, &mfc_unres_lock, &filter);
 }
-- 
2.11.0

Reply via email to