[RFC,net-next,3/5] vrf: add sysctl parameter for strict mode
diff mbox series

Message ID 20200612164937.5468-4-andrea.mayer@uniroma2.it
State New
Headers show
Series
  • Strict mode for VRF
Related show

Commit Message

Andrea Mayer June 12, 2020, 4:49 p.m. UTC
Add net.vrf.strict_mode sysctl parameter.

When net.vrf.strict_mode=0 (default) it is possible to associate multiple
VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a
table can be associated to a single VRF device.

When switching from net.vrf.strict_mode=0 to net.vrf.strict_mode=1, a check
is performed to verify that all tables have at most one VRF associated,
otherwise the switch is not allowed.

The net.vrf.strict_mode parameter is per network namespace.

Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>
---
 drivers/net/vrf.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 119 insertions(+)

Comments

Jakub Kicinski June 12, 2020, 5:52 p.m. UTC | #1
On Fri, 12 Jun 2020 18:49:35 +0200 Andrea Mayer wrote:
> Add net.vrf.strict_mode sysctl parameter.
> 
> When net.vrf.strict_mode=0 (default) it is possible to associate multiple
> VRF devices to the same table. Conversely, when net.vrf.strict_mode=1 a
> table can be associated to a single VRF device.
> 
> When switching from net.vrf.strict_mode=0 to net.vrf.strict_mode=1, a check
> is performed to verify that all tables have at most one VRF associated,
> otherwise the switch is not allowed.
> 
> The net.vrf.strict_mode parameter is per network namespace.
> 
> Signed-off-by: Andrea Mayer <andrea.mayer@uniroma2.it>

drivers/net/vrf.c:1771:49: warning: incorrect type in argument 3 (different address spaces)
drivers/net/vrf.c:1771:49:    expected void *
drivers/net/vrf.c:1771:49:    got void [noderef] <asn:1> *buffer
drivers/net/vrf.c:1785:35: warning: incorrect type in initializer (incompatible argument 3 (different address spaces))
drivers/net/vrf.c:1785:35:    expected int ( [usertype] *proc_handler )( ... )
drivers/net/vrf.c:1785:35:    got int ( * )( ... )
Andrea Mayer June 13, 2020, 10:40 p.m. UTC | #2
On Fri, 12 Jun 2020 10:52:27 -0700
Jakub Kicinski <kuba@kernel.org> wrote:

> drivers/net/vrf.c:1771:49: warning: incorrect type in argument 3 (different address spaces)
> drivers/net/vrf.c:1771:49:    expected void *
> drivers/net/vrf.c:1771:49:    got void [noderef] <asn:1> *buffer
> drivers/net/vrf.c:1785:35: warning: incorrect type in initializer (incompatible argument 3 (different address spaces))
> drivers/net/vrf.c:1785:35:    expected int ( [usertype] *proc_handler )( ... )
> drivers/net/vrf.c:1785:35:    got int ( * )( ... )

Hi Jakub,
the fix will be in the next version of this patch set.

Thank you,
Andrea

Patch
diff mbox series

diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index f772aac6a04c..bac118b615bc 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -102,6 +102,7 @@  struct netns_vrf {
 	bool add_fib_rules;
 
 	struct vrf_map vmap;
+	struct ctl_table_header	*ctl_hdr;
 };
 
 struct net_vrf {
@@ -245,6 +246,52 @@  static void vrf_map_unlock(struct vrf_map *vmap) __releases(&vmap->vmap_lock)
 	spin_unlock(&vmap->vmap_lock);
 }
 
+static bool vrf_strict_mode(struct vrf_map *vmap)
+{
+	bool strict_mode;
+
+	vrf_map_lock(vmap);
+	strict_mode = vmap->strict_mode;
+	vrf_map_unlock(vmap);
+
+	return strict_mode;
+}
+
+static int vrf_strict_mode_change(struct vrf_map *vmap, bool new_mode)
+{
+	bool *cur_mode;
+	int res = 0;
+
+	vrf_map_lock(vmap);
+
+	cur_mode = &vmap->strict_mode;
+	if (*cur_mode == new_mode)
+		goto unlock;
+
+	if (*cur_mode) {
+		/* disable strict mode */
+		*cur_mode = false;
+	} else {
+		if (vmap->shared_tables) {
+			/* we cannot allow strict_mode because there are some
+			 * vrfs that share one or more tables.
+			 */
+			res = -EBUSY;
+			goto unlock;
+		}
+
+		/* no tables are shared among vrfs, so we can go back
+		 * to 1:1 association between a vrf with its table.
+		 */
+		*cur_mode = true;
+	}
+
+unlock:
+	vrf_map_unlock(vmap);
+
+	return res;
+}
+
 /* called with rtnl lock held */
 static int
 vrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack)
@@ -1701,19 +1748,91 @@  static int vrf_map_init(struct vrf_map *vmap)
 	return 0;
 }
 
+static int vrf_shared_table_handler(struct ctl_table *table, int write,
+				    void __user *buffer, size_t *lenp,
+				    loff_t *ppos)
+{
+	struct net *net = (struct net *)table->extra1;
+	struct vrf_map *vmap = netns_vrf_map(net);
+	int proc_strict_mode = 0;
+	struct ctl_table tmp = {
+		.procname	= table->procname,
+		.data		= &proc_strict_mode,
+		.maxlen		= sizeof(int),
+		.mode		= table->mode,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= SYSCTL_ONE,
+	};
+	int ret;
+
+	if (!write)
+		proc_strict_mode = vrf_strict_mode(vmap);
+
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+	if (write && ret == 0)
+		ret = vrf_strict_mode_change(vmap, (bool)proc_strict_mode);
+
+	return ret;
+}
+
+static const struct ctl_table vrf_table[] = {
+	{
+		.procname	= "strict_mode",
+		.data		= NULL,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= vrf_shared_table_handler,
+		/* set by the vrf_netns_init */
+		.extra1		= NULL,
+	},
+	{ },
+};
+
 /* Initialize per network namespace state */
 static int __net_init vrf_netns_init(struct net *net)
 {
 	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
+	struct ctl_table *table;
+	int res;
 
 	nn_vrf->add_fib_rules = true;
 	vrf_map_init(&nn_vrf->vmap);
 
+	table = kmemdup(vrf_table, sizeof(vrf_table), GFP_KERNEL);
+	if (!table)
+		return -ENOMEM;
+
+	/* init the extra1 parameter with the reference to current netns */
+	table[0].extra1 = net;
+
+	nn_vrf->ctl_hdr = register_net_sysctl(net, "net/vrf", table);
+	if (!nn_vrf->ctl_hdr) {
+		res = -ENOMEM;
+		goto free_table;
+	}
+
 	return 0;
+
+free_table:
+	kfree(table);
+
+	return res;
+}
+
+static void __net_exit vrf_netns_exit(struct net *net)
+{
+	struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
+	struct ctl_table *table;
+
+	table = nn_vrf->ctl_hdr->ctl_table_arg;
+	unregister_net_sysctl_table(nn_vrf->ctl_hdr);
+	kfree(table);
 }
 
 static struct pernet_operations vrf_net_ops __net_initdata = {
 	.init = vrf_netns_init,
+	.exit = vrf_netns_exit,
 	.id   = &vrf_net_id,
 	.size = sizeof(struct netns_vrf),
 };