vrf: add sysctl parameter for strict mode
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> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
c8baec3857
commit
33306f1aaf
@ -103,6 +103,7 @@ struct netns_vrf {
|
|||||||
bool add_fib_rules;
|
bool add_fib_rules;
|
||||||
|
|
||||||
struct vrf_map vmap;
|
struct vrf_map vmap;
|
||||||
|
struct ctl_table_header *ctl_hdr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct net_vrf {
|
struct net_vrf {
|
||||||
@ -246,6 +247,52 @@ static void vrf_map_unlock(struct vrf_map *vmap) __releases(&vmap->vmap_lock)
|
|||||||
spin_unlock(&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 */
|
/* called with rtnl lock held */
|
||||||
static int
|
static int
|
||||||
vrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack)
|
vrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack)
|
||||||
@ -1702,19 +1749,90 @@ static int vrf_map_init(struct vrf_map *vmap)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vrf_shared_table_handler(struct ctl_table *table, int write,
|
||||||
|
void *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 */
|
/* Initialize per network namespace state */
|
||||||
static int __net_init vrf_netns_init(struct net *net)
|
static int __net_init vrf_netns_init(struct net *net)
|
||||||
{
|
{
|
||||||
struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
|
struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
|
||||||
|
struct ctl_table *table;
|
||||||
|
int res;
|
||||||
|
|
||||||
nn_vrf->add_fib_rules = true;
|
nn_vrf->add_fib_rules = true;
|
||||||
vrf_map_init(&nn_vrf->vmap);
|
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;
|
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 = {
|
static struct pernet_operations vrf_net_ops __net_initdata = {
|
||||||
.init = vrf_netns_init,
|
.init = vrf_netns_init,
|
||||||
|
.exit = vrf_netns_exit,
|
||||||
.id = &vrf_net_id,
|
.id = &vrf_net_id,
|
||||||
.size = sizeof(struct netns_vrf),
|
.size = sizeof(struct netns_vrf),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user