2019-02-08 04:12:56

by Callum Sinclair

[permalink] [raw]
Subject: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

Created a way to clear the multicast forwarding cache on a socket
without having to either remove the entries manually using the delete
entry socket option or destroy and recreate the multicast socket.

Using the flags MRT_FLUSH_ENTRIES and MRT_FLUSH_VIFS, all multicast
entries can be cleared, all multicast interfaces can be closed or both
can be cleared using one sockopt call.

Callum Sinclair (1):
ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

include/uapi/linux/mroute.h | 7 +++-
include/uapi/linux/mroute6.h | 7 +++-
net/ipv4/ipmr.c | 69 ++++++++++++++++++++-------------
net/ipv6/ip6mr.c | 74 ++++++++++++++++++++++--------------
4 files changed, 100 insertions(+), 57 deletions(-)

--
2.20.1



2019-02-08 04:11:35

by Callum Sinclair

[permalink] [raw]
Subject: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

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. The new socket option
MRT_FLUSH_ENTRIES will clear all multicast entries on the sockets table
and the MRT_FLUSH_VIFS will delete all multicast vifs on the socket
table.

Signed-off-by: Callum Sinclair <[email protected]>
---
include/uapi/linux/mroute.h | 7 +++-
include/uapi/linux/mroute6.h | 7 +++-
net/ipv4/ipmr.c | 69 ++++++++++++++++++++-------------
net/ipv6/ip6mr.c | 74 ++++++++++++++++++++++--------------
4 files changed, 100 insertions(+), 57 deletions(-)

diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index 5d37a9ccce63..673495ca3495 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h
@@ -28,12 +28,17 @@
#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_FLUSH (MRT_BASE+12) /* Flush all multicast entries and vifs */
+#define MRT_MAX (MRT_BASE+12)

#define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
#define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
#define SIOCGETRPF (SIOCPROTOPRIVATE+2)

+/* MRT_FLUSH optional flags */
+#define MRT_FLUSH_ENTRIES 1 /* For flushing all multicast entries */
+#define MRT_FLUSH_VIFS 2 /* For flushing all multicast vifs */
+
#define MAXVIFS 32
typedef unsigned long vifbitmap_t; /* User mode code depends on this lot */
typedef unsigned short vifi_t;
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index 9999cc006390..1f7ac3f0bc20 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h
@@ -31,12 +31,17 @@
#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_FLUSH (MRT6_BASE+12) /* Flush all multicast entries and vifs */
+#define MRT6_MAX (MRT6_BASE+12)

#define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */
#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1)
#define SIOCGETRPF (SIOCPROTOPRIVATE+2)

+/* MRT6_FLUSH optional flags */
+#define MRT6_FLUSH_ENTRIES 1 /* For flushing all multicast entries */
+#define MRT6_FLUSH_VIFS 2 /* For flushing all multicast vifs */
+
#define MAXMIFS 32
typedef unsigned long mifbitmap_t; /* User mode code depends on this lot */
typedef unsigned short mifi_t;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index e536970557dd..dc7a4ee1d7ca 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -110,7 +110,7 @@ static int ipmr_cache_report(struct mr_table *mrt,
static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
int cmd);
static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
-static void mroute_clean_tables(struct mr_table *mrt, bool all);
+static void mroute_clean_tables(struct mr_table *mrt, bool all, int flags);
static void ipmr_expire_process(struct timer_list *t);

#ifdef CONFIG_IP_MROUTE_MULTIPLE_TABLES
@@ -415,7 +415,7 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
static void ipmr_free_table(struct mr_table *mrt)
{
del_timer_sync(&mrt->ipmr_expire_timer);
- mroute_clean_tables(mrt, true);
+ mroute_clean_tables(mrt, true, MRT_FLUSH_VIFS | MRT_FLUSH_ENTRIES);
rhltable_destroy(&mrt->mfc_hash);
kfree(mrt);
}
@@ -1296,7 +1296,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
}

/* Close the multicast socket, and clear the vif tables etc */
-static void mroute_clean_tables(struct mr_table *mrt, bool all)
+static void mroute_clean_tables(struct mr_table *mrt, bool all, int flags)
{
struct net *net = read_pnet(&mrt->net);
struct mr_mfc *c, *tmp;
@@ -1305,35 +1305,39 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all)
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);
+ if (flags & MRT_FLUSH_VIFS) {
+ 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);
}
- 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;
- rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
- list_del_rcu(&c->list);
- cache = (struct mfc_cache *)c;
- call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,
- mrt->id);
- mroute_netlink_event(mrt, cache, RTM_DELROUTE);
- mr_cache_put(c);
- }
-
- if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
- spin_lock_bh(&mfc_unres_lock);
- list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
- list_del(&c->list);
+ if (flags & MRT_FLUSH_ENTRIES) {
+ list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
+ if (!all && (c->mfc_flags & MFC_STATIC))
+ continue;
+ rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params);
+ list_del_rcu(&c->list);
cache = (struct mfc_cache *)c;
+ call_ipmr_mfc_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, cache,
+ mrt->id);
mroute_netlink_event(mrt, cache, RTM_DELROUTE);
- ipmr_destroy_unres(mrt, cache);
+ mr_cache_put(c);
+ }
+
+ if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
+ spin_lock_bh(&mfc_unres_lock);
+ list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
+ list_del(&c->list);
+ cache = (struct mfc_cache *)c;
+ mroute_netlink_event(mrt, cache, RTM_DELROUTE);
+ ipmr_destroy_unres(mrt, cache);
+ }
+ spin_unlock_bh(&mfc_unres_lock);
}
- spin_unlock_bh(&mfc_unres_lock);
}
}

@@ -1354,7 +1358,7 @@ static void mrtsock_destruct(struct sock *sk)
NETCONFA_IFINDEX_ALL,
net->ipv4.devconf_all);
RCU_INIT_POINTER(mrt->mroute_sk, NULL);
- mroute_clean_tables(mrt, false);
+ mroute_clean_tables(mrt, false, MRT_FLUSH_VIFS | MRT_FLUSH_ENTRIES);
}
}
rtnl_unlock();
@@ -1479,6 +1483,17 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval,
sk == rtnl_dereference(mrt->mroute_sk),
parent);
break;
+ case MRT_FLUSH:
+ if (optlen != sizeof(val)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)optval)) {
+ ret = -EFAULT;
+ break;
+ }
+ mroute_clean_tables(mrt, true, val);
+ break;
/* Control PIM assert. */
case MRT_ASSERT:
if (optlen != sizeof(val)) {
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index cc01aa3f2b5e..01f015fb33f0 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -97,7 +97,7 @@ static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc,
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);
-static void mroute_clean_tables(struct mr_table *mrt, bool all);
+static void mroute_clean_tables(struct mr_table *mrt, bool all, int flags);
static void ipmr_expire_process(struct timer_list *t);

#ifdef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
@@ -393,7 +393,7 @@ static struct mr_table *ip6mr_new_table(struct net *net, u32 id)
static void ip6mr_free_table(struct mr_table *mrt)
{
del_timer_sync(&mrt->ipmr_expire_timer);
- mroute_clean_tables(mrt, true);
+ mroute_clean_tables(mrt, true, MRT6_FLUSH_VIFS | MRT6_FLUSH_ENTRIES);
rhltable_destroy(&mrt->mfc_hash);
kfree(mrt);
}
@@ -1496,42 +1496,46 @@ static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt,
* Close the multicast socket, and clear the vif tables etc
*/

-static void mroute_clean_tables(struct mr_table *mrt, bool all)
+static void mroute_clean_tables(struct mr_table *mrt, bool all, int flags)
{
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);
+ if (flags & MRT6_FLUSH_VIFS) {
+ 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);
}
- 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;
- rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
- list_del_rcu(&c->list);
- call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
- FIB_EVENT_ENTRY_DEL,
- (struct mfc6_cache *)c, mrt->id);
- mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
- mr_cache_put(c);
- }
+ if (flags & MRT6_FLUSH_ENTRIES) {
+ list_for_each_entry_safe(c, tmp, &mrt->mfc_cache_list, list) {
+ if (!all && (c->mfc_flags & MFC_STATIC))
+ continue;
+ rhltable_remove(&mrt->mfc_hash, &c->mnode, ip6mr_rht_params);
+ list_del_rcu(&c->list);
+ call_ip6mr_mfc_entry_notifiers(read_pnet(&mrt->net),
+ FIB_EVENT_ENTRY_DEL,
+ (struct mfc6_cache *)c, mrt->id);
+ mr6_netlink_event(mrt, (struct mfc6_cache *)c, RTM_DELROUTE);
+ mr_cache_put(c);
+ }

- if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
- spin_lock_bh(&mfc_unres_lock);
- list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
- list_del(&c->list);
- mr6_netlink_event(mrt, (struct mfc6_cache *)c,
- RTM_DELROUTE);
- ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
+ if (atomic_read(&mrt->cache_resolve_queue_len) != 0) {
+ spin_lock_bh(&mfc_unres_lock);
+ list_for_each_entry_safe(c, tmp, &mrt->mfc_unres_queue, list) {
+ list_del(&c->list);
+ mr6_netlink_event(mrt, (struct mfc6_cache *)c,
+ RTM_DELROUTE);
+ ip6mr_destroy_unres(mrt, (struct mfc6_cache *)c);
+ }
+ spin_unlock_bh(&mfc_unres_lock);
}
- spin_unlock_bh(&mfc_unres_lock);
}
}

@@ -1587,7 +1591,7 @@ int ip6mr_sk_done(struct sock *sk)
NETCONFA_IFINDEX_ALL,
net->ipv6.devconf_all);

- mroute_clean_tables(mrt, false);
+ mroute_clean_tables(mrt, false, MRT6_FLUSH_VIFS | MRT6_FLUSH_ENTRIES);
err = 0;
break;
}
@@ -1703,6 +1707,20 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
rtnl_unlock();
return ret;

+ case MRT6_FLUSH:
+ {
+ int flags;
+
+ if (optlen != sizeof(flags))
+ return -EINVAL;
+ if (get_user(flags, (int __user *)optval))
+ return -EFAULT;
+ rtnl_lock();
+ mroute_clean_tables(mrt, true, flags);
+ rtnl_unlock();
+ return 0;
+ }
+
/*
* Control PIM assert (to activate pim will activate assert)
*/
--
2.20.1


2019-02-08 14:18:49

by Nicolas Dichtel

[permalink] [raw]
Subject: Re: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

Le 08/02/2019 à 05:11, Callum Sinclair a écrit :
> Currently the only way to clear the mfc cache was to delete the entries
mfc stands for 'multicast forwarding cache', so 'mfc cache' is a bit strange.

> one by one using the MRT_DEL_MFC socket option or to destroy and
> recreate the socket.
Note that if entries were added with MRT_ADD_MFC_PROXY, they will survive to the
socket destruction. This is not the case with your new cmd. Is it intended?
Maybe a third option (something like MRT_FLUSH_MFC_PROXY) would be useful to
avoid confusion?

>
> Create a new socket option which will clear the multicast forwarding
> cache on the socket without destroying the socket. The new socket option
> MRT_FLUSH_ENTRIES will clear all multicast entries on the sockets table
> and the MRT_FLUSH_VIFS will delete all multicast vifs on the socket
> table.
>
> Signed-off-by: Callum Sinclair <[email protected]>
> ---
> include/uapi/linux/mroute.h | 7 +++-
> include/uapi/linux/mroute6.h | 7 +++-
> net/ipv4/ipmr.c | 69 ++++++++++++++++++++-------------
> net/ipv6/ip6mr.c | 74 ++++++++++++++++++++++--------------
> 4 files changed, 100 insertions(+), 57 deletions(-)
>
> diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
> index 5d37a9ccce63..673495ca3495 100644
> --- a/include/uapi/linux/mroute.h
> +++ b/include/uapi/linux/mroute.h
> @@ -28,12 +28,17 @@
> #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_FLUSH (MRT_BASE+12) /* Flush all multicast entries and vifs */
nit: "Flush all mfc entries and/or vifs" ?

> +#define MRT_MAX (MRT_BASE+12)
>
> #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
> #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
> #define SIOCGETRPF (SIOCPROTOPRIVATE+2)
>
> +/* MRT_FLUSH optional flags */
> +#define MRT_FLUSH_ENTRIES 1 /* For flushing all multicast entries */
Maybe MRT_FLUSH_MFC is more consistent with the previous naming (MRT_ADD_MFC, etc.)


Regards,
Nicolas

2019-02-08 14:44:46

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

On 08/02/2019 16:18, Nicolas Dichtel wrote:
> Le 08/02/2019 à 05:11, Callum Sinclair a écrit :
>> Currently the only way to clear the mfc cache was to delete the entries
> mfc stands for 'multicast forwarding cache', so 'mfc cache' is a bit strange.
>
>> one by one using the MRT_DEL_MFC socket option or to destroy and
>> recreate the socket.
> Note that if entries were added with MRT_ADD_MFC_PROXY, they will survive to the
> socket destruction. This is not the case with your new cmd. Is it intended?

I think you're referring to MFC_STATIC entries (sk != mroute_sk). It
doesn't matter how you add an entry - they all get cleaned up if added
through the mroute socket.

> Maybe a third option (something like MRT_FLUSH_MFC_PROXY) would be useful to
> avoid confusion?
>
>>
>> Create a new socket option which will clear the multicast forwarding
>> cache on the socket without destroying the socket. The new socket option
>> MRT_FLUSH_ENTRIES will clear all multicast entries on the sockets table
>> and the MRT_FLUSH_VIFS will delete all multicast vifs on the socket
>> table.
>>
>> Signed-off-by: Callum Sinclair <[email protected]>
>> ---
>> include/uapi/linux/mroute.h | 7 +++-
>> include/uapi/linux/mroute6.h | 7 +++-
>> net/ipv4/ipmr.c | 69 ++++++++++++++++++++-------------
>> net/ipv6/ip6mr.c | 74 ++++++++++++++++++++++--------------
>> 4 files changed, 100 insertions(+), 57 deletions(-)
>>
>> diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
>> index 5d37a9ccce63..673495ca3495 100644
>> --- a/include/uapi/linux/mroute.h
>> +++ b/include/uapi/linux/mroute.h
>> @@ -28,12 +28,17 @@
>> #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_FLUSH (MRT_BASE+12) /* Flush all multicast entries and vifs */
> nit: "Flush all mfc entries and/or vifs" ?
>
>> +#define MRT_MAX (MRT_BASE+12)
>>
>> #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */
>> #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
>> #define SIOCGETRPF (SIOCPROTOPRIVATE+2)
>>
>> +/* MRT_FLUSH optional flags */
>> +#define MRT_FLUSH_ENTRIES 1 /* For flushing all multicast entries */
> Maybe MRT_FLUSH_MFC is more consistent with the previous naming (MRT_ADD_MFC, etc.)
>
>
> Regards,
> Nicolas
>


2019-02-08 15:09:19

by Nicolas Dichtel

[permalink] [raw]
Subject: Re: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

Le 08/02/2019 à 15:43, Nikolay Aleksandrov a écrit :
> On 08/02/2019 16:18, Nicolas Dichtel wrote:
>> Le 08/02/2019 à 05:11, Callum Sinclair a écrit :
>>> Currently the only way to clear the mfc cache was to delete the entries
>> mfc stands for 'multicast forwarding cache', so 'mfc cache' is a bit strange.
>>
>>> one by one using the MRT_DEL_MFC socket option or to destroy and
>>> recreate the socket.
>> Note that if entries were added with MRT_ADD_MFC_PROXY, they will survive to the
>> socket destruction. This is not the case with your new cmd. Is it intended?
>
> I think you're referring to MFC_STATIC entries (sk != mroute_sk). It
> doesn't matter how you add an entry - they all get cleaned up if added
> through the mroute socket.
Yes, right.
MRT_FLUSH_MFC_STATIC ?

2019-02-08 21:41:15

by Nikolay Aleksandrov

[permalink] [raw]
Subject: Re: [PATCH net-next] ipmr: ip6mr: Create new sockopt to clear mfc cache or vifs

On 08/02/2019 17:08, Nicolas Dichtel wrote:
> Le 08/02/2019 à 15:43, Nikolay Aleksandrov a écrit :
>> On 08/02/2019 16:18, Nicolas Dichtel wrote:
>>> Le 08/02/2019 à 05:11, Callum Sinclair a écrit :
>>>> Currently the only way to clear the mfc cache was to delete the entries
>>> mfc stands for 'multicast forwarding cache', so 'mfc cache' is a bit strange.
>>>
>>>> one by one using the MRT_DEL_MFC socket option or to destroy and
>>>> recreate the socket.
>>> Note that if entries were added with MRT_ADD_MFC_PROXY, they will survive to the
>>> socket destruction. This is not the case with your new cmd. Is it intended?
>>
>> I think you're referring to MFC_STATIC entries (sk != mroute_sk). It
>> doesn't matter how you add an entry - they all get cleaned up if added
>> through the mroute socket.
> Yes, right.
> MRT_FLUSH_MFC_STATIC ?
>

Sounds good to me.