[RFC PATCH net-next] ipmr: ip6mr: APIs to support adding more than MAXVIFS/MAXMIFS
From: Stephen Suryaputra <hidden>
Date: 2021-09-17 22:41:48
MAXVIFS and MAXMIFS are too small (32) for certain applications. But they are defined in user header files So, use a different definition CONFIG_IP_MROUTE_EXT_MAXVIFS that is configurable and different ioctl requests (MRT_xyz_EXT and MRT6_xyz_EXT) as well as a different structure for adding MFC (mfcctl_ext). CONFIG_IP_MROUTE_EXT_MAXVIFS is bounded by the IF_SETSIZE (256) in mroute6.h. This patch is extending the following RFC: http://patchwork.ozlabs.org/project/netdev/patch/m1eiis8uc6.fsf@fess.ebiederm.org/ Signed-off-by: Stephen Suryaputra <redacted> --- include/linux/mroute_base.h | 11 +--- include/uapi/linux/mroute.h | 14 ++++- include/uapi/linux/mroute6.h | 5 +- net/ipv4/Kconfig | 11 ++++ net/ipv4/ipmr.c | 115 +++++++++++++++++++++++++++++------ net/ipv4/ipmr_base.c | 2 +- net/ipv6/ip6mr.c | 52 +++++++++++----- 7 files changed, 163 insertions(+), 47 deletions(-)
diff --git a/include/linux/mroute_base.h b/include/linux/mroute_base.h
index 8071148f29a6..85f3a8864044 100644
--- a/include/linux/mroute_base.h
+++ b/include/linux/mroute_base.h@@ -89,13 +89,6 @@ static inline int mr_call_vif_notifiers(struct net *net, return call_fib_notifiers(net, event_type, &info.info); } -#ifndef MAXVIFS -/* This one is nasty; value is defined in uapi using different symbols for - * mroute and morute6 but both map into same 32. - */ -#define MAXVIFS 32 -#endif - #define VIF_EXISTS(_mrt, _idx) (!!((_mrt)->vif_table[_idx].dev)) /* mfc_flags:
@@ -145,7 +138,7 @@ struct mr_mfc { unsigned long pkt; unsigned long wrong_if; unsigned long lastuse; - unsigned char ttls[MAXVIFS]; + unsigned char ttls[CONFIG_IP_MROUTE_EXT_MAXVIFS]; refcount_t refcount; } res; } mfc_un;
@@ -246,7 +239,7 @@ struct mr_table { struct sock __rcu *mroute_sk; struct timer_list ipmr_expire_timer; struct list_head mfc_unres_queue; - struct vif_device vif_table[MAXVIFS]; + struct vif_device vif_table[CONFIG_IP_MROUTE_EXT_MAXVIFS]; struct rhltable mfc_hash; struct list_head mfc_cache_list; int maxvif;
diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index 1a42f5f9b31b..b28f46565a43 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h@@ -29,7 +29,10 @@ #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_FLUSH (MRT_BASE+12) /* Flush all mfc entries and/or vifs */ -#define MRT_MAX (MRT_BASE+12) +#define MRT_ADD_VIF_EXT (MRT_BASE+13) /* Add a virtual interface */ +#define MRT_ADD_MFC_EXT (MRT_BASE+14) /* Add a multicast forwarding entry */ +#define MRT_ADD_MFC_PROXY_EXT (MRT_BASE+15) /* Add a (*,*|G) mfc entry */ +#define MRT_MAX (MRT_BASE+15) #define SIOCGETVIFCNT SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT (SIOCPROTOPRIVATE+1)
@@ -88,6 +91,15 @@ struct mfcctl { int mfcc_expire; }; +struct mfcctl_ext { + /* Need to be the same as mfcctl */ + struct in_addr mfcc_origin; /* Origin of mcast */ + struct in_addr mfcc_mcastgrp; /* Group in question */ + vifi_t mfcc_parent; /* Where it arrived */ + unsigned char mfcc_ttls[]; /* Where it is going */ + /* Don't put anything here as mfcc_ttls should grow into here */ +}; + /* Group count retrieval for mrouted */ struct sioc_sg_req { struct in_addr src;
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index a1fd6173e2db..1d15af3e0011 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h@@ -32,7 +32,10 @@ #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_FLUSH (MRT6_BASE+12) /* Flush all mfc entries and/or vifs */ -#define MRT6_MAX (MRT6_BASE+12) +#define MRT6_ADD_MIF_EXT (MRT6_BASE+13) /* Add a virtual interface */ +#define MRT6_ADD_MFC_EXT (MRT6_BASE+14) /* Add a multicast forwarding entry */ +#define MRT6_ADD_MFC_PROXY_EXT (MRT6_BASE+15) /* Add a (*,*|G) mfc entry */ +#define MRT6_MAX (MRT6_BASE+15) #define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */ #define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1)
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 87983e70f03f..34ff677a4dca 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig@@ -243,6 +243,17 @@ config IP_MROUTE_MULTIPLE_TABLES If unsure, say N. +config IP_MROUTE_EXT_MAXVIFS + int "IP: extended maximum multicast routing vif" + depends on IP_MROUTE || IPV6_MROUTE + range 32 256 + default 32 + help + Maximum multicast routing vif is set at 32 and it is defined in the + user API header file, so it is fixed. There are cases where more + vifs are needed. This config is used to get more vifs. The default + is set to be the same as MAXVIFS (32). + config IP_PIMSM_V1 bool "IP: PIM-SM version 1 support" depends on IP_MROUTE
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 2dda856ca260..50c81c4c347a 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c@@ -788,9 +788,9 @@ static void ipmr_update_thresholds(struct mr_table *mrt, struct mr_mfc *cache, { int vifi; - cache->mfc_un.res.minvif = MAXVIFS; + cache->mfc_un.res.minvif = CONFIG_IP_MROUTE_EXT_MAXVIFS; cache->mfc_un.res.maxvif = 0; - memset(cache->mfc_un.res.ttls, 255, MAXVIFS); + memset(cache->mfc_un.res.ttls, 255, CONFIG_IP_MROUTE_EXT_MAXVIFS); for (vifi = 0; vifi < mrt->maxvif; vifi++) { if (VIF_EXISTS(mrt, vifi) &&
@@ -952,7 +952,7 @@ static struct mfc_cache *ipmr_cache_alloc(void) if (c) { c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; - c->_c.mfc_un.res.minvif = MAXVIFS; + c->_c.mfc_un.res.minvif = CONFIG_IP_MROUTE_EXT_MAXVIFS; c->_c.free = ipmr_cache_free_rcu; refcount_set(&c->_c.mfc_un.res.refcount, 1); }
@@ -1185,15 +1185,15 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) return 0; } -static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, - struct mfcctl *mfc, int mrtsock, int parent) +static int ipmr_mfc_add_ext(struct net *net, struct mr_table *mrt, + struct mfcctl_ext *mfc, int mrtsock, int parent) { struct mfc_cache *uc, *c; struct mr_mfc *_uc; bool found; int ret; - if (mfc->mfcc_parent >= MAXVIFS) + if (mfc->mfcc_parent >= CONFIG_IP_MROUTE_EXT_MAXVIFS) return -ENFILE; /* The entries are added/deleted only under RTNL */
@@ -1265,6 +1265,36 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, return 0; } +static int ipmr_mfc_add(struct net *net, struct mr_table *mrt, + struct mfcctl *mfc, int mrtsock, int parent) +{ + unsigned int ext_maxvifs = CONFIG_IP_MROUTE_EXT_MAXVIFS; + struct mfcctl_ext *mfc_ext; + int i, ret; + + /* Maintain the behavior for MRT_ADD_MFC. */ + if (mfc->mfcc_parent >= MAXVIFS) + return -ENFILE; + + mfc_ext = kzalloc(sizeof(*mfc_ext) + + sizeof(mfc_ext->mfcc_ttls[0]) * ext_maxvifs, + GFP_KERNEL); + if (!mfc_ext) + return -ENOMEM; + memcpy(mfc_ext, mfc, sizeof(*mfc_ext)); + for (i = 0; i < MAXVIFS; i++) + mfc_ext->mfcc_ttls[i] = mfc->mfcc_ttls[i]; + /* Prevent processing ttls for vifs over MAXVIFS. Also vif above MAXVIFS + * shouldn't exist. So, ipmr_update_thresholds() will not process. + */ + for (; i < ext_maxvifs; i++) + mfc_ext->mfcc_ttls[i] = 0; + + ret = ipmr_mfc_add_ext(net, mrt, mfc_ext, mrtsock, parent); + kfree(mfc_ext); + return ret; +} + /* Close the multicast socket, and clear the vif tables etc */ static void mroute_clean_tables(struct mr_table *mrt, int flags) {
@@ -1348,8 +1378,11 @@ static void mrtsock_destruct(struct sock *sk) int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, unsigned int optlen) { + unsigned int ext_maxvifs = CONFIG_IP_MROUTE_EXT_MAXVIFS; struct net *net = sock_net(sk); int val, ret = 0, parent = 0; + struct mfcctl_ext *mfc_ext; + unsigned int mfc_ext_size; struct mr_table *mrt; struct vifctl vif; struct mfcctl mfc;
@@ -1413,6 +1446,7 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, break; case MRT_ADD_VIF: case MRT_DEL_VIF: + case MRT_ADD_VIF_EXT: if (optlen != sizeof(vif)) { ret = -EINVAL; break;
@@ -1421,11 +1455,18 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, ret = -EFAULT; break; } - if (vif.vifc_vifi >= MAXVIFS) { - ret = -ENFILE; - break; - } if (optname == MRT_ADD_VIF) { + if (vif.vifc_vifi >= MAXVIFS) { + ret = -ENFILE; + break; + } + } else { + if (vif.vifc_vifi >= CONFIG_IP_MROUTE_EXT_MAXVIFS) { + ret = -ENFILE; + break; + } + } + if (optname == MRT_ADD_VIF || optname == MRT_ADD_VIF_EXT) { ret = vif_add(net, mrt, &vif, sk == rtnl_dereference(mrt->mroute_sk)); } else {
@@ -1458,6 +1499,38 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, sk == rtnl_dereference(mrt->mroute_sk), parent); break; + case MRT_ADD_MFC_EXT: + parent = -1; + fallthrough; + case MRT_ADD_MFC_PROXY_EXT: + if (optlen < sizeof(*mfc_ext)) { + ret = -EINVAL; + break; + } + /* If userspace passes less than CONFIG_IP_MROUTE_EXT_MAXVIFS + * ttls, make sure to allocate to the max and initialize to + * zeros. If userspace passes more, the rest of the code will + * process up to CONFIG_IP_MROUTE_EXT_MAXVIFS. MRT_ADD_VIF_EXT + * only accepts up to CONFIG_IP_MROUTE_EXT_MAXVIFS + */ + mfc_ext_size = (unsigned int)(sizeof(*mfc_ext) + + sizeof(mfc_ext->mfcc_ttls[0]) * ext_maxvifs); + mfc_ext = kzalloc(max(optlen, mfc_ext_size), GFP_KERNEL); + if (!mfc_ext) { + ret = -ENOMEM; + break; + } + if (copy_from_sockptr(mfc_ext, optval, optlen)) { + ret = -EFAULT; + break; + } + if (parent == 0) + parent = mfc_ext->mfcc_parent; + ret = ipmr_mfc_add_ext(net, mrt, mfc_ext, + sk == rtnl_dereference(mrt->mroute_sk), + parent); + kfree(mfc_ext); + break; case MRT_FLUSH: if (optlen != sizeof(val)) { ret = -EINVAL;
@@ -2369,13 +2442,15 @@ static size_t mroute_msgsize(bool unresolved, int maxvif) static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc, int cmd) { + bool unresolved = mfc->_c.mfc_parent >= CONFIG_IP_MROUTE_EXT_MAXVIFS; struct net *net = read_pnet(&mrt->net); struct sk_buff *skb; int err = -ENOBUFS; - skb = nlmsg_new(mroute_msgsize(mfc->_c.mfc_parent >= MAXVIFS, - mrt->maxvif), - GFP_ATOMIC); + /* Unresolved cache has the mfc_parent being set to -1, so checking + * with CONFIG_IP_MROUTE_EXT_MAXVIFS should be ok. + */ + skb = nlmsg_new(mroute_msgsize(unresolved, mrt->maxvif), GFP_ATOMIC); if (!skb) goto errout;
@@ -2619,14 +2694,14 @@ static bool ipmr_rtm_validate_proto(unsigned char rtm_protocol) return false; } -static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) +static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl_ext *mfcc) { struct rtnexthop *rtnh = nla_data(nla); int remaining = nla_len(nla), vifi = 0; while (rtnh_ok(rtnh, remaining)) { mfcc->mfcc_ttls[vifi] = rtnh->rtnh_hops; - if (++vifi == MAXVIFS) + if (++vifi == CONFIG_IP_MROUTE_EXT_MAXVIFS) break; rtnh = rtnh_next(rtnh, &remaining); }
@@ -2636,7 +2711,7 @@ static int ipmr_nla_get_ttls(const struct nlattr *nla, struct mfcctl *mfcc) /* returns < 0 on error, 0 for ADD_MFC and 1 for ADD_MFC_PROXY */ static int rtm_to_ipmr_mfcc(struct net *net, struct nlmsghdr *nlh, - struct mfcctl *mfcc, int *mrtsock, + struct mfcctl_ext *mfcc, int *mrtsock, struct mr_table **mrtret, struct netlink_ext_ack *extack) {
@@ -2713,7 +2788,7 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, struct net *net = sock_net(skb->sk); int ret, mrtsock, parent; struct mr_table *tbl; - struct mfcctl mfcc; + struct mfcctl_ext mfcc; mrtsock = 0; tbl = NULL;
@@ -2723,9 +2798,9 @@ static int ipmr_rtm_route(struct sk_buff *skb, struct nlmsghdr *nlh, parent = ret ? mfcc.mfcc_parent : -1; if (nlh->nlmsg_type == RTM_NEWROUTE) - return ipmr_mfc_add(net, tbl, &mfcc, mrtsock, parent); + return ipmr_mfc_add_ext(net, tbl, &mfcc, mrtsock, parent); else - return ipmr_mfc_delete(tbl, &mfcc, parent); + return ipmr_mfc_delete(tbl, (struct mfcctl *)&mfcc, parent); } static bool ipmr_fill_table(struct mr_table *mrt, struct sk_buff *skb)
@@ -3106,6 +3181,8 @@ int __init ip_mr_init(void) { int err; + BUILD_BUG_ON(CONFIG_IP_MROUTE_EXT_MAXVIFS < MAXVIFS); + mrt_cachep = kmem_cache_create("ip_mrt_cache", sizeof(struct mfc_cache), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC,
diff --git a/net/ipv4/ipmr_base.c b/net/ipv4/ipmr_base.c
index aa8738a91210..ce109643ce7b 100644
--- a/net/ipv4/ipmr_base.c
+++ b/net/ipv4/ipmr_base.c@@ -215,7 +215,7 @@ int mr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, int ct; /* If cache is unresolved, don't try to parse IIF and OIF */ - if (c->mfc_parent >= MAXVIFS) { + if (c->mfc_parent >= CONFIG_IP_MROUTE_EXT_MAXVIFS) { rtm->rtm_flags |= RTNH_F_UNRESOLVED; return -ENOENT; }
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 36ed9efb8825..1310a896191e 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c@@ -50,6 +50,8 @@ #include <linux/nospec.h> +#define EXT_MAXMIFS CONFIG_IP_MROUTE_EXT_MAXVIFS + struct ip6mr_rule { struct fib_rule common; };
@@ -839,9 +841,9 @@ static void ip6mr_update_thresholds(struct mr_table *mrt, { int vifi; - cache->mfc_un.res.minvif = MAXMIFS; + cache->mfc_un.res.minvif = EXT_MAXMIFS; cache->mfc_un.res.maxvif = 0; - memset(cache->mfc_un.res.ttls, 255, MAXMIFS); + memset(cache->mfc_un.res.ttls, 255, EXT_MAXMIFS); for (vifi = 0; vifi < mrt->maxvif; vifi++) { if (VIF_EXISTS(mrt, vifi) &&
@@ -980,7 +982,7 @@ static struct mfc6_cache *ip6mr_cache_alloc(void) if (!c) return NULL; c->_c.mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; - c->_c.mfc_un.res.minvif = MAXMIFS; + c->_c.mfc_un.res.minvif = EXT_MAXMIFS; c->_c.free = ip6mr_cache_free_rcu; refcount_set(&c->_c.mfc_un.res.refcount, 1); return c;
@@ -1303,6 +1305,9 @@ static int __net_init ip6mr_net_init(struct net *net) { int err; + BUILD_BUG_ON(EXT_MAXMIFS < MAXMIFS); + BUILD_BUG_ON(EXT_MAXMIFS > IF_SETSIZE); + err = ip6mr_notifier_init(net); if (err) return err;
@@ -1405,17 +1410,18 @@ void ip6_mr_cleanup(void) static int ip6mr_mfc_add(struct net *net, struct mr_table *mrt, struct mf6cctl *mfc, int mrtsock, int parent) { - unsigned char ttls[MAXMIFS]; + unsigned char ttls[EXT_MAXMIFS]; struct mfc6_cache *uc, *c; struct mr_mfc *_uc; bool found; int i, err; - if (mfc->mf6cc_parent >= MAXMIFS) + /* Check for the case of >= MAXMIFS is done outside of this func */ + if (mfc->mf6cc_parent >= EXT_MAXMIFS) return -ENFILE; - memset(ttls, 255, MAXMIFS); - for (i = 0; i < MAXMIFS; i++) { + memset(ttls, 255, EXT_MAXMIFS); + for (i = 0; i < EXT_MAXMIFS; i++) { if (IF_ISSET(i, &mfc->mf6cc_ifset)) ttls[i] = 1; }
@@ -1663,12 +1669,18 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, return ip6mr_sk_done(sk); case MRT6_ADD_MIF: + case MRT6_ADD_MIF_EXT: if (optlen < sizeof(vif)) return -EINVAL; if (copy_from_sockptr(&vif, optval, sizeof(vif))) return -EFAULT; - if (vif.mif6c_mifi >= MAXMIFS) - return -ENFILE; + if (optname == MRT6_ADD_MIF) { + if (vif.mif6c_mifi >= MAXMIFS) + return -ENFILE; + } else { + if (vif.mif6c_mifi >= EXT_MAXMIFS) + return -ENFILE; + } rtnl_lock(); ret = mif6_add(net, mrt, &vif, sk == rtnl_dereference(mrt->mroute_sk));
@@ -1690,10 +1702,12 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, * in a sort of kernel/user symbiosis. */ case MRT6_ADD_MFC: + case MRT6_ADD_MFC_EXT: case MRT6_DEL_MFC: parent = -1; fallthrough; case MRT6_ADD_MFC_PROXY: + case MRT6_ADD_MFC_PROXY_EXT: case MRT6_DEL_MFC_PROXY: if (optlen < sizeof(mfc)) return -EINVAL;
@@ -1702,13 +1716,19 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, if (parent == 0) parent = mfc.mf6cc_parent; rtnl_lock(); - if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY) + if (optname == MRT6_DEL_MFC || optname == MRT6_DEL_MFC_PROXY) { ret = ip6mr_mfc_delete(mrt, &mfc, parent); - else - ret = ip6mr_mfc_add(net, mrt, &mfc, - sk == - rtnl_dereference(mrt->mroute_sk), - parent); + } else { + if ((optname == MRT6_ADD_MFC || + optname == MRT6_ADD_MFC_PROXY) && + mfc.mf6cc_parent >= MAXMIFS) + ret = -ENFILE; + else + ret = ip6mr_mfc_add(net, mrt, &mfc, + sk == + rtnl_dereference(mrt->mroute_sk), + parent); + } rtnl_unlock(); return ret;
@@ -2402,7 +2422,7 @@ static void mr6_netlink_event(struct mr_table *mrt, struct mfc6_cache *mfc, struct sk_buff *skb; int err = -ENOBUFS; - skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= MAXMIFS, mrt->maxvif), + skb = nlmsg_new(mr6_msgsize(mfc->_c.mfc_parent >= EXT_MAXMIFS, mrt->maxvif), GFP_ATOMIC); if (!skb) goto errout;
--
2.25.1