From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom
---
kernel/drivers/net/vrf.c | 590 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 539 insertions(+), 51 deletions(-)
diff --git a/kernel/drivers/net/vrf.c b/kernel/drivers/net/vrf.c
index 857b6f8..8ab0b5a 100644
--- a/kernel/drivers/net/vrf.c
+++ b/kernel/drivers/net/vrf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* vrf.c: device driver to encapsulate a VRF space
*
@@ -6,11 +7,6 @@
* Copyright (c) 2015 David Ahern <dsa@cumulusnetworks.com>
*
* Based on dummy, team and ipvlan drivers
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
*/
#include <linux/module.h>
@@ -25,6 +21,7 @@
#include <net/rtnetlink.h>
#include <linux/u64_stats_sync.h>
#include <linux/hashtable.h>
+#include <linux/spinlock_types.h>
#include <linux/inetdevice.h>
#include <net/arp.h>
@@ -37,13 +34,78 @@
#include <net/l3mdev.h>
#include <net/fib_rules.h>
#include <net/netns/generic.h>
+#include <net/netfilter/nf_conntrack.h>
#define DRV_NAME "vrf"
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "1.1"
#define FIB_RULE_PREF 1000 /* default preference for FIB rules */
+#define HT_MAP_BITS 4
+#define HASH_INITVAL ((u32)0xcafef00d)
+
+struct vrf_map {
+ DECLARE_HASHTABLE(ht, HT_MAP_BITS);
+ spinlock_t vmap_lock;
+
+ /* shared_tables:
+ * count how many distinct tables do not comply with the strict mode
+ * requirement.
+ * shared_tables value must be 0 in order to enable the strict mode.
+ *
+ * example of the evolution of shared_tables:
+ * | time
+ * add vrf0 --> table 100 shared_tables = 0 | t0
+ * add vrf1 --> table 101 shared_tables = 0 | t1
+ * add vrf2 --> table 100 shared_tables = 1 | t2
+ * add vrf3 --> table 100 shared_tables = 1 | t3
+ * add vrf4 --> table 101 shared_tables = 2 v t4
+ *
+ * shared_tables is a "step function" (or "staircase function")
+ * and it is increased by one when the second vrf is associated to a
+ * table.
+ *
+ * at t2, vrf0 and vrf2 are bound to table 100: shared_tables = 1.
+ *
+ * at t3, another dev (vrf3) is bound to the same table 100 but the
+ * value of shared_tables is still 1.
+ * This means that no matter how many new vrfs will register on the
+ * table 100, the shared_tables will not increase (considering only
+ * table 100).
+ *
+ * at t4, vrf4 is bound to table 101, and shared_tables = 2.
+ *
+ * Looking at the value of shared_tables we can immediately know if
+ * the strict_mode can or cannot be enforced. Indeed, strict_mode
+ * can be enforced iff shared_tables = 0.
+ *
+ * Conversely, shared_tables is decreased when a vrf is de-associated
+ * from a table with exactly two associated vrfs.
+ */
+ u32 shared_tables;
+
+ bool strict_mode;
+};
+
+struct vrf_map_elem {
+ struct hlist_node hnode;
+ struct list_head vrf_list; /* VRFs registered to this table */
+
+ u32 table_id;
+ int users;
+ int ifindex;
+};
+
static unsigned int vrf_net_id;
+
+/* per netns vrf data */
+struct netns_vrf {
+ /* protected by rtnl lock */
+ bool add_fib_rules;
+
+ struct vrf_map vmap;
+ struct ctl_table_header *ctl_hdr;
+};
struct net_vrf {
struct rtable __rcu *rth;
@@ -52,6 +114,9 @@
struct fib6_table *fib6_table;
#endif
u32 tb_id;
+
+ struct list_head me_list; /* entry in vrf_map_elem */
+ int ifindex;
};
struct pcpu_dstats {
@@ -107,6 +172,214 @@
}
}
+static struct vrf_map *netns_vrf_map(struct net *net)
+{
+ struct netns_vrf *nn_vrf = net_generic(net, vrf_net_id);
+
+ return &nn_vrf->vmap;
+}
+
+static struct vrf_map *netns_vrf_map_by_dev(struct net_device *dev)
+{
+ return netns_vrf_map(dev_net(dev));
+}
+
+static int vrf_map_elem_get_vrf_ifindex(struct vrf_map_elem *me)
+{
+ struct list_head *me_head = &me->vrf_list;
+ struct net_vrf *vrf;
+
+ if (list_empty(me_head))
+ return -ENODEV;
+
+ vrf = list_first_entry(me_head, struct net_vrf, me_list);
+
+ return vrf->ifindex;
+}
+
+static struct vrf_map_elem *vrf_map_elem_alloc(gfp_t flags)
+{
+ struct vrf_map_elem *me;
+
+ me = kmalloc(sizeof(*me), flags);
+ if (!me)
+ return NULL;
+
+ return me;
+}
+
+static void vrf_map_elem_free(struct vrf_map_elem *me)
+{
+ kfree(me);
+}
+
+static void vrf_map_elem_init(struct vrf_map_elem *me, int table_id,
+ int ifindex, int users)
+{
+ me->table_id = table_id;
+ me->ifindex = ifindex;
+ me->users = users;
+ INIT_LIST_HEAD(&me->vrf_list);
+}
+
+static struct vrf_map_elem *vrf_map_lookup_elem(struct vrf_map *vmap,
+ u32 table_id)
+{
+ struct vrf_map_elem *me;
+ u32 key;
+
+ key = jhash_1word(table_id, HASH_INITVAL);
+ hash_for_each_possible(vmap->ht, me, hnode, key) {
+ if (me->table_id == table_id)
+ return me;
+ }
+
+ return NULL;
+}
+
+static void vrf_map_add_elem(struct vrf_map *vmap, struct vrf_map_elem *me)
+{
+ u32 table_id = me->table_id;
+ u32 key;
+
+ key = jhash_1word(table_id, HASH_INITVAL);
+ hash_add(vmap->ht, &me->hnode, key);
+}
+
+static void vrf_map_del_elem(struct vrf_map_elem *me)
+{
+ hash_del(&me->hnode);
+}
+
+static void vrf_map_lock(struct vrf_map *vmap) __acquires(&vmap->vmap_lock)
+{
+ spin_lock(&vmap->vmap_lock);
+}
+
+static void vrf_map_unlock(struct vrf_map *vmap) __releases(&vmap->vmap_lock)
+{
+ spin_unlock(&vmap->vmap_lock);
+}
+
+/* called with rtnl lock held */
+static int
+vrf_map_register_dev(struct net_device *dev, struct netlink_ext_ack *extack)
+{
+ struct vrf_map *vmap = netns_vrf_map_by_dev(dev);
+ struct net_vrf *vrf = netdev_priv(dev);
+ struct vrf_map_elem *new_me, *me;
+ u32 table_id = vrf->tb_id;
+ bool free_new_me = false;
+ int users;
+ int res;
+
+ /* we pre-allocate elements used in the spin-locked section (so that we
+ * keep the spinlock as short as possibile).
+ */
+ new_me = vrf_map_elem_alloc(GFP_KERNEL);
+ if (!new_me)
+ return -ENOMEM;
+
+ vrf_map_elem_init(new_me, table_id, dev->ifindex, 0);
+
+ vrf_map_lock(vmap);
+
+ me = vrf_map_lookup_elem(vmap, table_id);
+ if (!me) {
+ me = new_me;
+ vrf_map_add_elem(vmap, me);
+ goto link_vrf;
+ }
+
+ /* we already have an entry in the vrf_map, so it means there is (at
+ * least) a vrf registered on the specific table.
+ */
+ free_new_me = true;
+ if (vmap->strict_mode) {
+ /* vrfs cannot share the same table */
+ NL_SET_ERR_MSG(extack, "Table is used by another VRF");
+ res = -EBUSY;
+ goto unlock;
+ }
+
+link_vrf:
+ users = ++me->users;
+ if (users == 2)
+ ++vmap->shared_tables;
+
+ list_add(&vrf->me_list, &me->vrf_list);
+
+ res = 0;
+
+unlock:
+ vrf_map_unlock(vmap);
+
+ /* clean-up, if needed */
+ if (free_new_me)
+ vrf_map_elem_free(new_me);
+
+ return res;
+}
+
+/* called with rtnl lock held */
+static void vrf_map_unregister_dev(struct net_device *dev)
+{
+ struct vrf_map *vmap = netns_vrf_map_by_dev(dev);
+ struct net_vrf *vrf = netdev_priv(dev);
+ u32 table_id = vrf->tb_id;
+ struct vrf_map_elem *me;
+ int users;
+
+ vrf_map_lock(vmap);
+
+ me = vrf_map_lookup_elem(vmap, table_id);
+ if (!me)
+ goto unlock;
+
+ list_del(&vrf->me_list);
+
+ users = --me->users;
+ if (users == 1) {
+ --vmap->shared_tables;
+ } else if (users == 0) {
+ vrf_map_del_elem(me);
+
+ /* no one will refer to this element anymore */
+ vrf_map_elem_free(me);
+ }
+
+unlock:
+ vrf_map_unlock(vmap);
+}
+
+/* return the vrf device index associated with the table_id */
+static int vrf_ifindex_lookup_by_table_id(struct net *net, u32 table_id)
+{
+ struct vrf_map *vmap = netns_vrf_map(net);
+ struct vrf_map_elem *me;
+ int ifindex;
+
+ vrf_map_lock(vmap);
+
+ if (!vmap->strict_mode) {
+ ifindex = -EPERM;
+ goto unlock;
+ }
+
+ me = vrf_map_lookup_elem(vmap, table_id);
+ if (!me) {
+ ifindex = -ENODEV;
+ goto unlock;
+ }
+
+ ifindex = vrf_map_elem_get_vrf_ifindex(me);
+
+unlock:
+ vrf_map_unlock(vmap);
+
+ return ifindex;
+}
+
/* by default VRF devices do not have a qdisc and are expected
* to be created with only a single queue.
*/
@@ -151,11 +424,25 @@
return NETDEV_TX_OK;
}
+static void vrf_nf_set_untracked(struct sk_buff *skb)
+{
+ if (skb_get_nfct(skb) == 0)
+ nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
+}
+
+static void vrf_nf_reset_ct(struct sk_buff *skb)
+{
+ if (skb_get_nfct(skb) == IP_CT_UNTRACKED)
+ nf_reset_ct(skb);
+}
+
#if IS_ENABLED(CONFIG_IPV6)
static int vrf_ip6_local_out(struct net *net, struct sock *sk,
struct sk_buff *skb)
{
int err;
+
+ vrf_nf_reset_ct(skb);
err = nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net,
sk, skb, NULL, skb_dst(skb)->dev, dst_output);
@@ -236,6 +523,8 @@
struct sk_buff *skb)
{
int err;
+
+ vrf_nf_reset_ct(skb);
err = nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, net, sk,
skb, NULL, skb_dst(skb)->dev, dst_output);
@@ -357,8 +646,7 @@
skb_pull(skb, ETH_HLEN);
}
- /* reset skb device */
- nf_reset(skb);
+ vrf_nf_reset_ct(skb);
}
#if IS_ENABLED(CONFIG_IPV6)
@@ -368,11 +656,11 @@
{
struct dst_entry *dst = skb_dst(skb);
struct net_device *dev = dst->dev;
+ const struct in6_addr *nexthop;
struct neighbour *neigh;
- struct in6_addr *nexthop;
int ret;
- nf_reset(skb);
+ vrf_nf_reset_ct(skb);
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
@@ -384,7 +672,7 @@
neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
if (!IS_ERR(neigh)) {
sock_confirm_neigh(skb, neigh);
- ret = neigh_output(neigh, skb);
+ ret = neigh_output(neigh, skb, false);
rcu_read_unlock_bh();
return ret;
}
@@ -503,6 +791,8 @@
if (rt6_need_strict(&ipv6_hdr(skb)->daddr))
return skb;
+ vrf_nf_set_untracked(skb);
+
if (qdisc_tx_is_default(vrf_dev) ||
IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED)
return vrf_ip6_out_direct(vrf_dev, sk, skb);
@@ -534,7 +824,7 @@
static int vrf_rt6_create(struct net_device *dev)
{
- int flags = DST_HOST | DST_NOPOLICY | DST_NOXFRM;
+ int flags = DST_NOPOLICY | DST_NOXFRM;
struct net_vrf *vrf = netdev_priv(dev);
struct net *net = dev_net(dev);
struct rt6_info *rt6;
@@ -587,10 +877,10 @@
struct net_device *dev = dst->dev;
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
- u32 nexthop;
+ bool is_v6gw = false;
int ret = -EINVAL;
- nf_reset(skb);
+ vrf_nf_reset_ct(skb);
/* Be paranoid, rather than too clever. */
if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) {
@@ -610,13 +900,11 @@
rcu_read_lock_bh();
- nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr);
- neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
- if (unlikely(!neigh))
- neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
+ neigh = ip_neigh_for_gw(rt, skb, &is_v6gw);
if (!IS_ERR(neigh)) {
sock_confirm_neigh(skb, neigh);
- ret = neigh_output(neigh, skb);
+ /* if crossing protocols, can not use the cached header */
+ ret = neigh_output(neigh, skb, is_v6gw);
rcu_read_unlock_bh();
return ret;
}
@@ -741,6 +1029,8 @@
ipv4_is_lbcast(ip_hdr(skb)->daddr))
return skb;
+ vrf_nf_set_untracked(skb);
+
if (qdisc_tx_is_default(vrf_dev) ||
IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED)
return vrf_ip_out_direct(vrf_dev, sk, skb);
@@ -795,7 +1085,7 @@
return -ENOMEM;
/* create a dst for routing packets out through a VRF device */
- rth = rt_dst_alloc(dev, 0, RTN_UNICAST, 1, 1, 0);
+ rth = rt_dst_alloc(dev, 0, RTN_UNICAST, 1, 1);
if (!rth)
return -ENOMEM;
@@ -809,7 +1099,8 @@
/**************************** device handling ********************/
/* cycle interface to flush neighbor cache and move routes across tables */
-static void cycle_netdev(struct net_device *dev)
+static void cycle_netdev(struct net_device *dev,
+ struct netlink_ext_ack *extack)
{
unsigned int flags = dev->flags;
int ret;
@@ -817,9 +1108,9 @@
if (!netif_running(dev))
return;
- ret = dev_change_flags(dev, flags & ~IFF_UP);
+ ret = dev_change_flags(dev, flags & ~IFF_UP, extack);
if (ret >= 0)
- ret = dev_change_flags(dev, flags);
+ ret = dev_change_flags(dev, flags, extack);
if (ret < 0) {
netdev_err(dev,
@@ -847,7 +1138,7 @@
if (ret < 0)
goto err;
- cycle_netdev(port_dev);
+ cycle_netdev(port_dev, extack);
return 0;
@@ -877,7 +1168,7 @@
netdev_upper_dev_unlink(port_dev, dev);
port_dev->priv_flags &= ~IFF_L3MDEV_SLAVE;
- cycle_netdev(port_dev);
+ cycle_netdev(port_dev, NULL);
return 0;
}
@@ -915,9 +1206,6 @@
dev->flags = IFF_MASTER | IFF_NOARP;
- /* MTU is irrelevant for VRF device; set to 64k similar to lo */
- dev->mtu = 64 * 1024;
-
/* similarly, oper state is irrelevant; set to up to avoid confusion */
dev->operstate = IF_OPER_UP;
netdev_lockdep_set_classes(dev);
@@ -936,6 +1224,7 @@
.ndo_init = vrf_dev_init,
.ndo_uninit = vrf_dev_uninit,
.ndo_start_xmit = vrf_xmit,
+ .ndo_set_mac_address = eth_mac_addr,
.ndo_get_stats64 = vrf_get_stats64,
.ndo_add_slave = vrf_add_slave,
.ndo_del_slave = vrf_del_slave,
@@ -1043,24 +1332,29 @@
struct sk_buff *skb)
{
int orig_iif = skb->skb_iif;
- bool need_strict;
+ bool need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
+ bool is_ndisc = ipv6_ndisc_frame(skb);
- /* loopback traffic; do not push through packet taps again.
- * Reset pkt_type for upper layers to process skb
+ /* loopback, multicast & non-ND link-local traffic; do not push through
+ * packet taps again. Reset pkt_type for upper layers to process skb.
+ * For strict packets with a source LLA, determine the dst using the
+ * original ifindex.
*/
- if (skb->pkt_type == PACKET_LOOPBACK) {
+ if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
IP6CB(skb)->flags |= IP6SKB_L3SLAVE;
- skb->pkt_type = PACKET_HOST;
+
+ if (skb->pkt_type == PACKET_LOOPBACK)
+ skb->pkt_type = PACKET_HOST;
+ else if (ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)
+ vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
+
goto out;
}
- /* if packet is NDISC or addressed to multicast or link-local
- * then keep the ingress interface
- */
- need_strict = rt6_need_strict(&ipv6_hdr(skb)->daddr);
- if (!ipv6_ndisc_frame(skb) && !need_strict) {
+ /* if packet is NDISC then keep the ingress interface */
+ if (!is_ndisc) {
vrf_rx_stats(vrf_dev, skb->len);
skb->dev = vrf_dev;
skb->skb_iif = vrf_dev->ifindex;
@@ -1139,12 +1433,14 @@
#if IS_ENABLED(CONFIG_IPV6)
/* send to link-local or multicast address via interface enslaved to
* VRF device. Force lookup to VRF table without changing flow struct
+ * Note: Caller to this function must hold rcu_read_lock() and no refcnt
+ * is taken on the dst by this function.
*/
static struct dst_entry *vrf_link_scope_lookup(const struct net_device *dev,
struct flowi6 *fl6)
{
struct net *net = dev_net(dev);
- int flags = RT6_LOOKUP_F_IFACE;
+ int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_DST_NOREF;
struct dst_entry *dst = NULL;
struct rt6_info *rt;
@@ -1154,7 +1450,6 @@
*/
if (fl6->flowi6_oif == dev->ifindex) {
dst = &net->ipv6.ip6_null_entry->dst;
- dst_hold(dst);
return dst;
}
@@ -1208,7 +1503,8 @@
struct sk_buff *skb;
int err;
- if (family == AF_INET6 && !ipv6_mod_enabled())
+ if ((family == AF_INET6 || family == RTNL_FAMILY_IP6MR) &&
+ !ipv6_mod_enabled())
return 0;
skb = nlmsg_new(vrf_fib_rule_nl_size(), GFP_KERNEL);
@@ -1277,7 +1573,18 @@
goto ipmr_err;
#endif
+#if IS_ENABLED(CONFIG_IPV6_MROUTE_MULTIPLE_TABLES)
+ err = vrf_fib_rule(dev, RTNL_FAMILY_IP6MR, true);
+ if (err < 0)
+ goto ip6mr_err;
+#endif
+
return 0;
+
+#if IS_ENABLED(CONFIG_IPV6_MROUTE_MULTIPLE_TABLES)
+ip6mr_err:
+ vrf_fib_rule(dev, RTNL_FAMILY_IPMR, false);
+#endif
#if IS_ENABLED(CONFIG_IP_MROUTE_MULTIPLE_TABLES)
ipmr_err:
@@ -1325,6 +1632,15 @@
/* default to no qdisc; user can add if desired */
dev->priv_flags |= IFF_NO_QUEUE;
dev->priv_flags |= IFF_NO_RX_HANDLER;
+ dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+
+ /* VRF devices do not care about MTU, but if the MTU is set
+ * too low then the ipv4 and ipv6 protocols are disabled
+ * which breaks networking.
+ */
+ dev->min_mtu = IPV6_MIN_MTU;
+ dev->max_mtu = IP6_MAX_MTU;
+ dev->mtu = dev->max_mtu;
}
static int vrf_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1351,6 +1667,8 @@
netdev_for_each_lower_dev(dev, port_dev, iter)
vrf_del_slave(dev, port_dev);
+ vrf_map_unregister_dev(dev);
+
unregister_netdevice_queue(dev, head);
}
@@ -1359,6 +1677,7 @@
struct netlink_ext_ack *extack)
{
struct net_vrf *vrf = netdev_priv(dev);
+ struct netns_vrf *nn_vrf;
bool *add_fib_rules;
struct net *net;
int err;
@@ -1381,11 +1700,26 @@
if (err)
goto out;
+ /* mapping between table_id and vrf;
+ * note: such binding could not be done in the dev init function
+ * because dev->ifindex id is not available yet.
+ */
+ vrf->ifindex = dev->ifindex;
+
+ err = vrf_map_register_dev(dev, extack);
+ if (err) {
+ unregister_netdevice(dev);
+ goto out;
+ }
+
net = dev_net(dev);
- add_fib_rules = net_generic(net, vrf_net_id);
+ nn_vrf = net_generic(net, vrf_net_id);
+
+ add_fib_rules = &nn_vrf->add_fib_rules;
if (*add_fib_rules) {
err = vrf_add_fib_rules(dev);
if (err) {
+ vrf_map_unregister_dev(dev);
unregister_netdevice(dev);
goto out;
}
@@ -1472,20 +1806,164 @@
.notifier_call = vrf_device_event,
};
-/* Initialize per network namespace state */
-static int __net_init vrf_netns_init(struct net *net)
+static int vrf_map_init(struct vrf_map *vmap)
{
- bool *add_fib_rules = net_generic(net, vrf_net_id);
+ spin_lock_init(&vmap->vmap_lock);
+ hash_init(vmap->ht);
- *add_fib_rules = true;
+ vmap->strict_mode = false;
return 0;
}
+#ifdef CONFIG_SYSCTL
+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;
+}
+
+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,
+ },
+ { },
+};
+
+static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
+{
+ struct ctl_table *table;
+
+ 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) {
+ kfree(table);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void vrf_netns_exit_sysctl(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);
+}
+#else
+static int vrf_netns_init_sysctl(struct net *net, struct netns_vrf *nn_vrf)
+{
+ return 0;
+}
+
+static void vrf_netns_exit_sysctl(struct net *net)
+{
+}
+#endif
+
+/* 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);
+
+ nn_vrf->add_fib_rules = true;
+ vrf_map_init(&nn_vrf->vmap);
+
+ return vrf_netns_init_sysctl(net, nn_vrf);
+}
+
+static void __net_exit vrf_netns_exit(struct net *net)
+{
+ vrf_netns_exit_sysctl(net);
+}
+
static struct pernet_operations vrf_net_ops __net_initdata = {
.init = vrf_netns_init,
+ .exit = vrf_netns_exit,
.id = &vrf_net_id,
- .size = sizeof(bool),
+ .size = sizeof(struct netns_vrf),
};
static int __init vrf_init_module(void)
@@ -1498,14 +1976,24 @@
if (rc < 0)
goto error;
+ rc = l3mdev_table_lookup_register(L3MDEV_TYPE_VRF,
+ vrf_ifindex_lookup_by_table_id);
+ if (rc < 0)
+ goto unreg_pernet;
+
rc = rtnl_link_register(&vrf_link_ops);
- if (rc < 0) {
- unregister_pernet_subsys(&vrf_net_ops);
- goto error;
- }
+ if (rc < 0)
+ goto table_lookup_unreg;
return 0;
+table_lookup_unreg:
+ l3mdev_table_lookup_unregister(L3MDEV_TYPE_VRF,
+ vrf_ifindex_lookup_by_table_id);
+
+unreg_pernet:
+ unregister_pernet_subsys(&vrf_net_ops);
+
error:
unregister_netdevice_notifier(&vrf_notifier_block);
return rc;
--
Gitblit v1.6.2