Currently the only way to clear the mfc cache was to delete the entries
one by one using the MRT_DEL_MFC socket option or to destroy and
recreate the socket.

Create a new socket option which will clear the multicast forwarding
cache on the socket without destroying the socket.

Signed-off-by: Callum Sinclair <callum.sincl...@alliedtelesis.co.nz>
---
 include/uapi/linux/mroute.h  |  3 ++-
 include/uapi/linux/mroute6.h |  3 ++-
 net/ipv4/ipmr.c              | 41 +++++++++++++++++++++-----------
 net/ipv6/ip6mr.c             | 45 +++++++++++++++++++++++-------------
 4 files changed, 60 insertions(+), 32 deletions(-)

diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index 5d37a9ccce63..8a0beb885cd9 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h
@@ -28,7 +28,8 @@
 #define MRT_TABLE      (MRT_BASE+9)    /* Specify mroute table ID              
*/
 #define MRT_ADD_MFC_PROXY      (MRT_BASE+10)   /* Add a (*,*|G) mfc entry      
*/
 #define MRT_DEL_MFC_PROXY      (MRT_BASE+11)   /* Del a (*,*|G) mfc entry      
*/
-#define MRT_MAX                (MRT_BASE+11)
+#define MRT_DEL_MFC_ALL                (MRT_BASE+12)   /* Del all multicast 
entries    */
+#define MRT_MAX                (MRT_BASE+12)
 
 #define SIOCGETVIFCNT  SIOCPROTOPRIVATE        /* IP protocol privates */
 #define SIOCGETSGCNT   (SIOCPROTOPRIVATE+1)
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index 9999cc006390..7def70cdf571 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h
@@ -31,7 +31,8 @@
 #define MRT6_TABLE     (MRT6_BASE+9)   /* Specify mroute table ID              
*/
 #define MRT6_ADD_MFC_PROXY     (MRT6_BASE+10)  /* Add a (*,*|G) mfc entry      
*/
 #define MRT6_DEL_MFC_PROXY     (MRT6_BASE+11)  /* Del a (*,*|G) mfc entry      
*/
-#define MRT6_MAX       (MRT6_BASE+11)
+#define MRT6_DEL_MFC_ALL       (MRT6_BASE+12)  /* Del all multicast entries    
*/
+#define MRT6_MAX       (MRT6_BASE+12)
 
 #define SIOCGETMIFCNT_IN6      SIOCPROTOPRIVATE        /* IP protocol privates 
*/
 #define SIOCGETSGCNT_IN6       (SIOCPROTOPRIVATE+1)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index ddbf8c9a1abb..bbbce5ec8a0c 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1298,22 +1298,10 @@ static int ipmr_mfc_add(struct net *net, struct 
mr_table *mrt,
        return 0;
 }
 
-/* Close the multicast socket, and clear the vif tables etc */
-static void mroute_clean_tables(struct mr_table *mrt, bool all)
+/* Clear the vif tables */
+static void mroute_clean_cache(struct mr_table *mrt, bool all)
 {
-       struct net *net = read_pnet(&mrt->net);
        struct mr_mfc *c, *tmp;
-       struct mfc_cache *cache;
-       LIST_HEAD(list);
-       int i;
-
-       /* Shut down all active vif entries */
-       for (i = 0; i < mrt->maxvif; i++) {
-               if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
-                       continue;
-               vif_delete(mrt, i, 0, &list);
-       }
-       unregister_netdevice_many(&list);
 
        /* Wipe the cache */
        list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
@@ -1340,6 +1328,24 @@ static void mroute_clean_tables(struct mr_table *mrt, 
bool all)
        }
 }
 
+/* Close the multicast socket, and clear the vif tables etc */
+static void mroute_clean_tables(struct mr_table *mrt, bool all)
+{
+       struct net *net = read_pnet(&mrt->net);
+       LIST_HEAD(list);
+       int i;
+
+       /* Shut down all active vif entries */
+       for (i = 0; i < mrt->maxvif; i++) {
+               if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
+                       continue;
+               vif_delete(mrt, i, 0, &list);
+       }
+       unregister_netdevice_many(&list);
+
+       mroute_clean_cache (mrt, all);
+}
+
 /* called from ip_ra_control(), before an RCU grace period,
  * we dont need to call synchronize_rcu() here
  */
@@ -1482,6 +1488,13 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, 
char __user *optval,
                                           sk == 
rtnl_dereference(mrt->mroute_sk),
                                           parent);
                break;
+       case MRT_DEL_MFC_ALL:
+               rtnl_lock();
+               ipmr_for_each_table(mrt, net) {
+                       mroute_clean_cache(mrt, true);
+               }
+               rtnl_unlock();
+               break;
        /* Control PIM assert. */
        case MRT_ASSERT:
                if (optlen != sizeof(val)) {
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 30337b38274b..64631f85dcf4 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1492,25 +1492,11 @@ static int ip6mr_mfc_add(struct net *net, struct 
mr_table *mrt,
        return 0;
 }
 
-/*
- *     Close the multicast socket, and clear the vif tables etc
- */
-
-static void mroute_clean_tables(struct mr_table *mrt, bool all)
+/* Clear the vif tables */
+static void mroute_clean_cache(struct mr_table *mrt, bool all)
 {
        struct mr_mfc *c, *tmp;
-       LIST_HEAD(list);
-       int i;
-
-       /* Shut down all active vif entries */
-       for (i = 0; i < mrt->maxvif; i++) {
-               if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
-                       continue;
-               mif6_delete(mrt, i, 0, &list);
-       }
-       unregister_netdevice_many(&list);
 
-       /* Wipe the cache */
        list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
                if (!all && (c->mfc_flags & MFC_STATIC))
                        continue;
@@ -1535,6 +1521,26 @@ static void mroute_clean_tables(struct mr_table *mrt, 
bool all)
                spin_unlock_bh(&mfc_unres_lock);
        }
 }
+/*
+ *     Close the multicast socket, and clear the vif tables etc
+ */
+
+static void mroute_clean_tables(struct mr_table *mrt, bool all)
+{
+       LIST_HEAD(list);
+       int i;
+
+       /* Shut down all active vif entries */
+       for (i = 0; i < mrt->maxvif; i++) {
+               if (!all && (mrt->vif_table[i].flags & VIFF_STATIC))
+                       continue;
+               mif6_delete(mrt, i, 0, &list);
+       }
+       unregister_netdevice_many(&list);
+
+       /* Wipe the cache */
+       mroute_clean_cache(mrt, all);
+}
 
 static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk)
 {
@@ -1703,6 +1709,13 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, 
char __user *optval, uns
                                            parent);
                rtnl_unlock();
                return ret;
+       case MRT6_DEL_MFC_ALL:
+               rtnl_lock();
+               ip6mr_for_each_table(mrt, net) {
+                       mroute_clean_cache(mrt, true);
+               }
+               rtnl_unlock();
+               return 0;
 
        /*
         *      Control PIM assert (to activate pim will activate assert)
-- 
2.20.1

Reply via email to