Merge branch 'neighbour-namespacify-arp_tbl-and-nd_tbl'

Kuniyuki Iwashima says:

====================
neighbour: Namespacify arp_tbl and nd_tbl.

The neighbour subsystem is almost ready to drop RTNL.

However, the control paths are serialised by the global
per-table lock.

This series converts arp_tbl and nd_tbl to per-netns table.

With the series, /proc/sys/net/ipv{4,6}/neigh/default/ can
be configured per netns, which was only configurable in
init_net.

To avoid potential regression, all the settings are inherited
from init_net by default, and this behaviour is controlled by
a new sysctl knob, net.core.neigh_inherit_init_net:

  # sysctl net.core.neigh_inherit_init_net
  net.core.neigh_inherit_init_net = 1
  # sysctl net.ipv4.neigh.default.gc_thresh1
  net.ipv4.neigh.default.gc_thresh1 = 128
  # sysctl net.ipv4.neigh.default.gc_thresh1=129
  net.ipv4.neigh.default.gc_thresh1 = 129
  # unshare -n sysctl net.ipv4.neigh.default.gc_thresh1
  net.ipv4.neigh.default.gc_thresh1 = 129

If it is turned off, all settings are reset in the new netns:

  # sysctl net.core.neigh_inherit_init_net=0
  net.core.neigh_inherit_init_net = 0
  # unshare -n sysctl net.ipv4.neigh.default.gc_thresh1
  net.ipv4.neigh.default.gc_thresh1 = 128

Series overview:

  Patch 1 deflakes test_neigh.sh.

  Patch 2 ~ 3 are misc cleanup.

  Patch 4 ~ 7 store arp_tbl/nd_tbl to net->neigh_tables[] and
  remove the global neigh_tables[].

  Patch 8 ~ 9 replace the direct access to arp_tbl/nd_tbl to
  net->neigh_tables[] using new helpers.

  Patch 10 ~ 12 finally replace the global table with per-netns
  table.

  Patch 13 ~ 14 clean up unnecessary net_eq().

  Patch 15 updates test_neigh.sh.

Note that some buggy drivers access nd_tbl without checking
disable_ipv6_mod, so nd_tbl's extern definition is still left.
====================

Link: https://patch.msgid.link/20260902203722.926528-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-04 15:03:00 -07:00
commit a753848a32
33 changed files with 637 additions and 486 deletions

View File

@ -413,6 +413,20 @@ new netns has been created.
Default : 0 (for compatibility reasons)
neigh_inherit_init_net
----------------------
Controls if a new network namespace should inherit all current
settings under /proc/sys/net/ipv{4,6}/neigh/default/.
By default, the value is set to 1, and all settings are inherited
from init_net.
If set to 0, both IPv4 and IPv6 settings are reset to their default
values.
Default : 1 (for compatibility reasons)
txrehash
--------

View File

@ -1378,7 +1378,8 @@ struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
return neigh;
}
static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv,
int gc_interval)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
@ -1397,7 +1398,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
goto out_unlock;
/* neigh is obsolete if it was idle for two GC periods */
dt = 2 * arp_tbl.gc_interval;
dt = 2 * gc_interval;
neigh_obsolete = jiffies - dt;
for (i = 0; i < htbl->size; i++) {
@ -1433,11 +1434,16 @@ static void ipoib_reap_neigh(struct work_struct *work)
{
struct ipoib_dev_priv *priv =
container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
struct net_device *dev = priv->dev;
struct net *net = dev_net(dev);
struct neigh_table *tbl;
int gc_interval;
__ipoib_reap_neigh(priv);
tbl = arp_table(net);
gc_interval = tbl->gc_interval;
__ipoib_reap_neigh(priv, gc_interval);
queue_delayed_work(priv->wq, &priv->neigh_reap_task,
arp_tbl.gc_interval);
queue_delayed_work(priv->wq, &priv->neigh_reap_task, gc_interval);
}
@ -1590,8 +1596,11 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
struct ipoib_neigh __rcu **buckets;
struct net_device *dev = priv->dev;
struct net *net = dev_net(dev);
struct ipoib_neigh_hash *htbl;
struct neigh_table *tbl;
u32 size;
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
@ -1599,7 +1608,9 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl = kzalloc_obj(*htbl);
if (!htbl)
return -ENOMEM;
size = roundup_pow_of_two(arp_tbl.gc_thresh3);
tbl = arp_table(net);
size = roundup_pow_of_two(tbl->gc_thresh3);
buckets = kvzalloc_objs(*buckets, size);
if (!buckets) {
kfree(htbl);
@ -1614,7 +1625,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
/* start garbage collection */
queue_delayed_work(priv->wq, &priv->neigh_reap_task,
arp_tbl.gc_interval);
tbl->gc_interval);
return 0;
}

View File

@ -683,7 +683,7 @@ __prestera_k_arb_n_offload_set(struct prestera_switch *sw,
{
struct neighbour *n;
n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,
n = neigh_lookup(arp_table(&init_net), &nc->key.addr.u.ipv4,
nc->key.dev);
if (!n)
return;
@ -790,7 +790,7 @@ __prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw,
int err;
memset(&nc->nh_neigh_info, 0, sizeof(nc->nh_neigh_info));
n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4, nc->key.dev);
n = neigh_lookup(arp_table(&init_net), &nc->key.addr.u.ipv4, nc->key.dev);
if (!n)
goto out;
@ -1052,10 +1052,12 @@ static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw,
#endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */
if (nc->key.addr.v == PRESTERA_IPV4) {
n = neigh_lookup(&arp_tbl, &nc->key.addr.u.ipv4,
struct neigh_table *tbl = arp_table(&init_net);
n = neigh_lookup(tbl, &nc->key.addr.u.ipv4,
nc->key.dev);
if (!n)
n = neigh_create(&arp_tbl, &nc->key.addr.u.ipv4,
n = neigh_create(tbl, &nc->key.addr.u.ipv4,
nc->key.dev);
} else {
n = NULL;

View File

@ -17,20 +17,27 @@
#include "fs_core.h"
#include "diag/en_rep_tracepoint.h"
static unsigned long mlx5e_rep_ipv6_interval(void)
static unsigned long mlx5e_rep_ipv6_interval(struct net *net)
{
if (IS_ENABLED(CONFIG_IPV6) && ipv6_mod_enabled())
return NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME);
return NEIGH_VAR(&nd_table(net)->parms, DELAY_PROBE_TIME);
return ~0UL;
}
static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv)
{
unsigned long ipv4_interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
unsigned long ipv6_interval = mlx5e_rep_ipv6_interval();
struct net_device *netdev = rpriv->netdev;
struct mlx5e_priv *priv = netdev_priv(netdev);
struct net *net = dev_net(netdev);
unsigned long ipv4_interval;
unsigned long ipv6_interval;
struct neigh_table *tbl;
struct mlx5e_priv *priv;
priv = netdev_priv(netdev);
tbl = arp_table(net);
ipv4_interval = NEIGH_VAR(&tbl->parms, DELAY_PROBE_TIME);
ipv6_interval = mlx5e_rep_ipv6_interval(net);
rpriv->neigh_update.min_interval = min_t(unsigned long, ipv6_interval, ipv4_interval);
mlx5_fc_update_sampling_interval(priv->mdev, rpriv->neigh_update.min_interval);
@ -217,12 +224,6 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,
switch (event) {
case NETEVENT_NEIGH_UPDATE:
n = ptr;
#if IS_ENABLED(CONFIG_IPV6)
if (n->tbl != &nd_tbl && n->tbl != &arp_tbl)
#else
if (n->tbl != &arp_tbl)
#endif
return NOTIFY_DONE;
update_work = mlx5e_alloc_neigh_update_work(priv, n);
if (!update_work)
@ -238,11 +239,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,
* changes in the default table, we only care about changes
* done per device delay prob time parameter.
*/
#if IS_ENABLED(CONFIG_IPV6)
if (!p->dev || (p->tbl != &nd_tbl && p->tbl != &arp_tbl))
#else
if (!p->dev || p->tbl != &arp_tbl)
#endif
if (!p->dev)
return NOTIFY_DONE;
rcu_read_lock();

View File

@ -393,20 +393,10 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
struct mlx5e_encap_entry *e = NULL;
struct mlx5e_tc_flow *flow;
struct mlx5_fc *counter;
struct neigh_table *tbl;
bool neigh_used = false;
struct neighbour *n;
u64 lastuse;
if (m_neigh->family == AF_INET)
tbl = &arp_tbl;
#if IS_ENABLED(CONFIG_IPV6)
else if (m_neigh->family == AF_INET6)
tbl = &nd_tbl;
#endif
else
return;
/* mlx5e_get_next_valid_encap() releases previous encap before returning
* next one.
*/
@ -447,12 +437,23 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)
trace_mlx5e_tc_update_neigh_used_value(nhe, neigh_used);
if (neigh_used) {
struct net_device *dev = READ_ONCE(nhe->neigh_dev);
struct net *net = dev_net(dev);
struct neigh_table *tbl;
nhe->reported_lastuse = jiffies;
#if IS_ENABLED(CONFIG_IPV6)
if (m_neigh->family != AF_INET)
tbl = nd_table(net);
else
#endif
tbl = arp_table(net);
/* find the relevant neigh according to the cached device and
* dst ip pair
*/
n = neigh_lookup(tbl, &m_neigh->dst_ip, READ_ONCE(nhe->neigh_dev));
n = neigh_lookup(tbl, &m_neigh->dst_ip, dev);
if (!n)
return;

View File

@ -262,6 +262,7 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,
struct net_device *netdev = sa_entry->dev;
struct xfrm_state *x = sa_entry->x;
struct dst_entry *rt_dst_entry;
struct neigh_table *tbl;
struct flowi4 fl4 = {};
struct flowi6 fl6 = {};
struct neighbour *n;
@ -364,9 +365,10 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,
return;
neigh:
n = neigh_lookup(&arp_tbl, pkey, netdev);
tbl = arp_table(dev_net(netdev));
n = neigh_lookup(tbl, pkey, netdev);
if (!n) {
n = neigh_create(&arp_tbl, pkey, netdev);
n = neigh_create(tbl, pkey, netdev);
if (IS_ERR(n))
return;
neigh_event_send(n, NULL);

View File

@ -2397,14 +2397,15 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)
static void
mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)
{
struct net *net = mlxsw_sp_net(mlxsw_sp);
unsigned long interval;
#if IS_ENABLED(CONFIG_IPV6)
interval = min_t(unsigned long,
NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME),
NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME));
NEIGH_VAR(&arp_table(net)->parms, DELAY_PROBE_TIME),
NEIGH_VAR(&nd_table(net)->parms, DELAY_PROBE_TIME));
#else
interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME);
interval = NEIGH_VAR(&arp_table(net)->parms, DELAY_PROBE_TIME);
#endif
mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval);
}
@ -2414,6 +2415,8 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
int ent_index)
{
u64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS);
struct net *net = mlxsw_sp_net(mlxsw_sp);
struct neigh_table *tbl;
struct net_device *dev;
struct neighbour *n;
__be32 dipn;
@ -2429,9 +2432,10 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,
return;
}
tbl = arp_table(net);
dipn = htonl(dip);
dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
n = neigh_lookup(&arp_tbl, &dipn, dev);
n = neigh_lookup(tbl, &dipn, dev);
if (!n)
return;
@ -2445,6 +2449,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
char *rauhtd_pl,
int rec_index)
{
struct net *net = mlxsw_sp_net(mlxsw_sp);
struct neigh_table *tbl;
struct net_device *dev;
struct neighbour *n;
struct in6_addr dip;
@ -2458,8 +2464,9 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,
return;
}
tbl = nd_table(net);
dev = mlxsw_sp_rif_dev(mlxsw_sp->router->rifs[rif]);
n = neigh_lookup(&nd_tbl, &dip, dev);
n = neigh_lookup(tbl, &dip, dev);
if (!n)
return;
@ -3014,16 +3021,17 @@ static int mlxsw_sp_neigh_rif_made_sync(struct mlxsw_sp *mlxsw_sp,
.mlxsw_sp = mlxsw_sp,
.rif = rif,
};
struct net *net = mlxsw_sp_net(mlxsw_sp);
if (!mlxsw_sp_dev_lower_is_port(mlxsw_sp_rif_dev(rif)))
return 0;
neigh_for_each(&arp_tbl, mlxsw_sp_neigh_rif_made_sync_each, &rms);
neigh_for_each(arp_table(net), mlxsw_sp_neigh_rif_made_sync_each, &rms);
if (rms.err)
goto err_arp;
#if IS_ENABLED(CONFIG_IPV6)
neigh_for_each(&nd_tbl, mlxsw_sp_neigh_rif_made_sync_each, &rms);
neigh_for_each(nd_table(net), mlxsw_sp_neigh_rif_made_sync_each, &rms);
#endif
if (rms.err)
goto err_nd;
@ -4622,7 +4630,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,
nh->nh_weight = 1;
#endif
memcpy(&nh->gw_addr, &fib_nh->fib_nh_gw4, sizeof(fib_nh->fib_nh_gw4));
nh->neigh_tbl = &arp_tbl;
nh->neigh_tbl = arp_table(mlxsw_sp_net(mlxsw_sp));
err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);
if (err)
return err;
@ -5112,6 +5120,7 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,
struct nh_notifier_single_info *nh_obj, int weight)
{
struct net_device *dev = nh_obj->dev;
struct net *net = dev_net(dev);
int err;
nh->nhgi = nh_grp->nhgi;
@ -5120,12 +5129,12 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,
switch (nh_obj->gw_family) {
case AF_INET:
memcpy(&nh->gw_addr, &nh_obj->ipv4, sizeof(nh_obj->ipv4));
nh->neigh_tbl = &arp_tbl;
nh->neigh_tbl = arp_table(net);
break;
case AF_INET6:
memcpy(&nh->gw_addr, &nh_obj->ipv6, sizeof(nh_obj->ipv6));
#if IS_ENABLED(CONFIG_IPV6)
nh->neigh_tbl = &nd_tbl;
nh->neigh_tbl = nd_table(net);
#endif
break;
}
@ -6981,7 +6990,7 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,
nh->nh_weight = rt->fib6_nh->fib_nh_weight;
memcpy(&nh->gw_addr, &rt->fib6_nh->fib_nh_gw6, sizeof(nh->gw_addr));
#if IS_ENABLED(CONFIG_IPV6)
nh->neigh_tbl = &nd_tbl;
nh->neigh_tbl = nd_table(mlxsw_sp_net(mlxsw_sp));
#endif
err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);

View File

@ -456,6 +456,7 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
bool inherit_tos = tparm.iph.tos & 0x1;
bool inherit_ttl = !tparm.iph.ttl;
union mlxsw_sp_l3addr gw = daddr;
struct neigh_table *tbl = NULL;
struct net_device *l3edev;
if (!(to_dev->flags & IFF_UP) ||
@ -469,9 +470,11 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
l3edev = mlxsw_sp_span_gretap4_route(to_dev, &saddr.addr4, &gw.addr4);
if (l3edev)
tbl = arp_table(dev_net(l3edev));
return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
tparm.iph.ttl,
&arp_tbl, sparmsp);
tbl, sparmsp);
}
static int
@ -561,6 +564,7 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
union mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };
bool inherit_ttl = !tparm.hop_limit;
union mlxsw_sp_l3addr gw = daddr;
struct neigh_table *tbl = NULL;
struct net_device *l3edev;
if (!(to_dev->flags & IFF_UP) ||
@ -574,9 +578,11 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,
return mlxsw_sp_span_entry_unoffloadable(sparmsp);
l3edev = mlxsw_sp_span_gretap6_route(to_dev, &saddr.addr6, &gw.addr6);
if (l3edev)
tbl = nd_table(dev_net(l3edev));
return mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,
tparm.hop_limit,
&nd_tbl, sparmsp);
tbl, sparmsp);
}
static int

View File

@ -209,6 +209,7 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
{
struct nfp_tun_active_tuns *payload;
struct net_device *netdev;
struct neigh_table *tbl;
int count, i, pay_len;
struct neighbour *n;
__be32 ipv4_addr;
@ -235,7 +236,8 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
if (!netdev)
continue;
n = neigh_lookup(&arp_tbl, &ipv4_addr, netdev);
tbl = arp_table(dev_net(netdev));
n = neigh_lookup(tbl, &ipv4_addr, netdev);
if (!n)
continue;
@ -251,6 +253,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
#if IS_ENABLED(CONFIG_IPV6)
struct nfp_tun_active_tuns_v6 *payload;
struct net_device *netdev;
struct neigh_table *tbl;
int count, i, pay_len;
struct neighbour *n;
void *ipv6_add;
@ -277,7 +280,8 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
if (!netdev)
continue;
n = neigh_lookup(&nd_tbl, ipv6_add, netdev);
tbl = nd_table(dev_net(netdev));
n = neigh_lookup(tbl, ipv6_add, netdev);
if (!n)
continue;
@ -729,12 +733,6 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,
default:
return NOTIFY_DONE;
}
#if IS_ENABLED(CONFIG_IPV6)
if (n->tbl != &nd_tbl && n->tbl != &arp_tbl)
#else
if (n->tbl != &arp_tbl)
#endif
return NOTIFY_DONE;
app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb);
app = app_priv->app;

View File

@ -3134,7 +3134,7 @@ static int rocker_netevent_event(struct notifier_block *unused,
switch (event) {
case NETEVENT_NEIGH_UPDATE:
if (n->tbl != &arp_tbl)
if (n->tbl != arp_table(&init_net))
return NOTIFY_DONE;
dev = n->dev;
if (!rocker_port_dev_check(dev))

View File

@ -1336,7 +1336,7 @@ static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port,
int err = 0;
if (!n) {
n = neigh_create(&arp_tbl, &ip_addr, dev);
n = neigh_create(arp_table(&init_net), &ip_addr, dev);
if (IS_ERR(n))
return PTR_ERR(n);
}

View File

@ -91,6 +91,7 @@ static void efx_tc_counter_work(struct work_struct *work)
struct efx_tc_action_set *act;
unsigned long touched;
struct neighbour *n;
struct net *net;
spin_lock_bh(&cnt->lock);
touched = READ_ONCE(cnt->touched);
@ -103,16 +104,19 @@ static void efx_tc_counter_work(struct work_struct *work)
continue;
if (time_after_eq(encap->neigh->used, touched))
continue;
encap->neigh->used = touched;
net = encap->neigh->net;
/* We have passed traffic using this ARP entry, so
* indicate to the ARP cache that it's still active
*/
if (encap->neigh->dst_ip)
n = neigh_lookup(&arp_tbl, &encap->neigh->dst_ip,
n = neigh_lookup(arp_table(net), &encap->neigh->dst_ip,
encap->neigh->egdev);
else
#if IS_ENABLED(CONFIG_IPV6)
n = neigh_lookup(&nd_tbl,
n = neigh_lookup(nd_table(net),
&encap->neigh->dst_ip6,
encap->neigh->egdev);
#else

View File

@ -527,10 +527,10 @@ static int efx_neigh_event(struct efx_nic *efx, struct neighbour *n)
if (WARN_ON(!efx->tc))
return NOTIFY_DONE;
if (n->tbl == &arp_tbl) {
if (n->tbl->family == AF_INET) {
keysize = sizeof(keys.dst_ip);
#if IS_ENABLED(CONFIG_IPV6)
} else if (n->tbl == &nd_tbl) {
} else if (n->tbl->family == AF_INET6) {
ipv6 = true;
keysize = sizeof(keys.dst_ip6);
#endif

View File

@ -616,7 +616,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,
nexthop = rt6_nexthop(dst_rt6_info(dst), &ipv6_hdr(skb)->daddr);
neigh = __ipv6_neigh_lookup_noref(dst->dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&nd_tbl, nexthop, dst->dev, false);
neigh = __neigh_create(nd_table(net), nexthop, dst->dev, false);
if (!IS_ERR(neigh)) {
sock_confirm_neigh(skb, neigh);
ret = neigh_output(neigh, skb, false);

View File

@ -1841,11 +1841,12 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)
static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
{
struct neigh_table *tbl = arp_table(dev_net(dev));
struct vxlan_dev *vxlan = netdev_priv(dev);
struct neighbour *n;
struct arphdr *parp;
u8 *arpptr, *sha;
__be32 sip, tip;
struct neighbour *n;
if (dev->flags & IFF_NOARP)
goto out;
@ -1877,7 +1878,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
ipv4_is_multicast(tip))
goto out;
n = neigh_lookup(&arp_tbl, &tip, dev);
n = neigh_lookup(tbl, &tip, dev);
if (n) {
struct vxlan_rdst *rdst = NULL;
@ -2051,7 +2052,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
ipv6_addr_is_multicast(&msg->target))
goto out;
n = neigh_lookup(&nd_tbl, &msg->target, dev);
n = neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev);
if (n) {
struct vxlan_rdst *rdst = NULL;
@ -2106,6 +2107,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
struct neigh_table *tbl;
struct neighbour *n;
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@ -2119,8 +2121,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
return false;
tbl = arp_table(dev_net(dev));
pip = ip_hdr(skb);
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
n = neigh_lookup(tbl, &pip->daddr, dev);
if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin.sin_addr.s_addr = pip->daddr,
@ -2145,8 +2149,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
return false;
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
return false;
tbl = nd_table(dev_net(dev));
pip6 = ipv6_hdr(skb);
n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
n = neigh_lookup(tbl, &pip6->daddr, dev);
if (!n && (vxlan->cfg.flags & VXLAN_F_L3MISS)) {
union vxlan_addr ipa = {
.sin6.sin6_addr = pip6->daddr,

View File

@ -7,8 +7,10 @@
#include <linux/hash.h>
#include <net/neighbour.h>
extern struct neigh_table arp_tbl;
static inline struct neigh_table *arp_table(struct net *net)
{
return net->neigh_tables[NEIGH_ARP_TABLE];
}
static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 *hash_rnd)
{
@ -21,10 +23,12 @@ static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32
#ifdef CONFIG_INET
static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
{
struct neigh_table *tbl = arp_table(dev_net(dev));
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
key = INADDR_ANY;
return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev);
return ___neigh_lookup_noref(tbl, neigh_key_eq32, arp_hashfn, &key, dev);
}
#else
static inline

View File

@ -67,6 +67,15 @@ struct prefix_info;
extern struct neigh_table nd_tbl;
static inline struct neigh_table *nd_table(struct net *net)
{
#if IS_ENABLED(CONFIG_IPV6)
if (disable_ipv6_mod)
return &nd_tbl;
#endif
return net->neigh_tables[NEIGH_ND_TABLE];
}
struct nd_msg {
struct icmp6hdr icmph;
struct in6_addr target;
@ -354,7 +363,9 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _
static inline struct neighbour *__ipv6_neigh_lookup_noref(struct net_device *dev, const void *pkey)
{
return ___neigh_lookup_noref(&nd_tbl, neigh_key_eq128, ndisc_hashfn, pkey, dev);
struct neigh_table *tbl = nd_table(dev_net(dev));
return ___neigh_lookup_noref(tbl, neigh_key_eq128, ndisc_hashfn, pkey, dev);
}
static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, const void *pkey)
@ -388,8 +399,11 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,
struct neighbour *neigh;
neigh = __ipv6_neigh_lookup_noref(dev, addr);
if (unlikely(!neigh))
neigh = __neigh_create(&nd_tbl, addr, dev, false);
if (unlikely(!neigh)) {
struct neigh_table *tbl = nd_table(dev_net(dev));
neigh = __neigh_create(tbl, addr, dev, false);
}
return neigh;
#else

View File

@ -31,6 +31,8 @@
#include <net/rtnetlink.h>
#include <net/neighbour_tables.h>
extern int sysctl_neigh_inherit_init_net;
/*
* NUD stands for "neighbor unreachability detection"
*/
@ -179,7 +181,6 @@ struct neigh_ops {
struct pneigh_entry {
struct pneigh_entry __rcu *next;
possible_net_t net;
struct net_device *dev;
netdevice_tracker dev_tracker;
union {
@ -234,7 +235,7 @@ struct neigh_table {
struct delayed_work managed_work;
struct timer_list proxy_timer;
struct sk_buff_head proxy_queue;
atomic_t entries;
refcount_t entries;
atomic_t gc_entries;
struct list_head gc_list;
struct list_head managed_list;
@ -339,8 +340,8 @@ static inline void neigh_confirm(struct neighbour *n)
}
}
void neigh_table_init(int index, struct neigh_table *tbl);
int neigh_table_clear(int index, struct neigh_table *tbl);
int neigh_table_register(struct net *net, struct neigh_table *tbl, int index);
void neigh_table_unregister(struct net *net, int index);
struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
struct net_device *dev);
struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
@ -389,24 +390,17 @@ static inline void neigh_set_reach_time(struct neigh_parms *p)
void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
struct sk_buff *skb);
struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net,
struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
const void *key, struct net_device *dev);
int pneigh_create(struct neigh_table *tbl, struct net *net, const void *key,
int pneigh_create(struct neigh_table *tbl, const void *key,
struct net_device *dev, u32 flags, u8 protocol,
bool permanent);
int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key,
int pneigh_delete(struct neigh_table *tbl, const void *key,
struct net_device *dev);
static inline struct net *pneigh_net(const struct pneigh_entry *pneigh)
{
return read_pnet(&pneigh->net);
}
void neigh_app_ns(struct neighbour *n);
void neigh_for_each(struct neigh_table *tbl,
void (*cb)(struct neighbour *, void *), void *cookie);
void __neigh_for_each_release(struct neigh_table *tbl,
int (*cb)(struct neighbour *));
int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *);
struct neigh_seq_state {

View File

@ -39,6 +39,7 @@
#include <net/netns/mctp.h>
#include <net/netns/vsock.h>
#include <net/net_trackers.h>
#include <net/neighbour_tables.h>
#include <linux/ns_common.h>
#include <linux/idr.h>
#include <linux/skbuff.h>
@ -47,6 +48,7 @@
struct user_namespace;
struct proc_dir_entry;
struct neigh_table;
struct net_device;
struct sock;
struct ctl_table_header;
@ -103,6 +105,8 @@ struct net {
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
struct neigh_table *neigh_tables[NEIGH_NR_TABLES];
#ifdef CONFIG_SYSCTL
struct ctl_table_set sysctls;
#endif

View File

@ -405,8 +405,11 @@ static inline struct neighbour *ip_neigh_gw4(struct net_device *dev,
struct neighbour *neigh;
neigh = __ipv4_neigh_lookup_noref(dev, (__force u32)daddr);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &daddr, dev, false);
if (unlikely(!neigh)) {
struct neigh_table *tbl = arp_table(dev_net(dev));
neigh = __neigh_create(tbl, &daddr, dev, false);
}
return neigh;
}

View File

@ -193,7 +193,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
return;
}
n = neigh_lookup(&arp_tbl, &tip, vlandev);
n = neigh_lookup(arp_table(dev_net(vlandev)), &tip, vlandev);
if (n) {
struct net_bridge_fdb_entry *f;
u8 ha[ETH_ALEN] __aligned(2);
@ -470,7 +470,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
return;
}
n = neigh_lookup(&nd_tbl, &msg->target, vlandev);
n = neigh_lookup(nd_table(dev_net(vlandev)), &msg->target, vlandev);
if (n) {
struct net_bridge_fdb_entry *f;
u8 ha[ETH_ALEN] __aligned(2);

View File

@ -55,6 +55,23 @@ static void neigh_notify(struct neighbour *n, int type, int flags, u32 pid);
static void __neigh_notify(struct neighbour *n, int type, int flags, u32 pid);
static void pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
bool skip_perm);
static void neigh_table_free(struct neigh_table *tbl);
static void neigh_table_get(struct neigh_table *tbl)
{
refcount_inc(&tbl->entries);
}
static void neigh_table_put(struct neigh_table *tbl)
{
if (refcount_dec_and_test(&tbl->entries))
neigh_table_free(tbl);
}
static int neigh_table_entries(struct neigh_table *tbl)
{
return refcount_read(&tbl->entries) - 1;
}
#ifdef CONFIG_PROC_FS
static const struct seq_operations neigh_stat_seq_ops;
@ -131,10 +148,11 @@ unsigned long neigh_rand_reach_time(unsigned long base)
{
return base ? get_random_u32_below(base) + (base >> 1) : 0;
}
EXPORT_SYMBOL(neigh_rand_reach_time);
static void neigh_mark_dead(struct neighbour *n)
{
lockdep_assert_held(&n->tbl->lock);
n->dead = 1;
if (!list_empty(&n->gc_list)) {
list_del_init(&n->gc_list);
@ -148,11 +166,6 @@ static void neigh_update_gc_list(struct neighbour *n)
{
bool on_gc_list, exempt_from_gc;
spin_lock_bh(&n->tbl->lock);
write_lock(&n->lock);
if (n->dead)
goto out;
/* remove from the gc list if new state is permanent or if neighbor is
* externally learned / validated; otherwise entry should be on the gc
* list
@ -169,20 +182,12 @@ static void neigh_update_gc_list(struct neighbour *n)
list_add_tail(&n->gc_list, &n->tbl->gc_list);
atomic_inc(&n->tbl->gc_entries);
}
out:
write_unlock(&n->lock);
spin_unlock_bh(&n->tbl->lock);
}
static void neigh_update_managed_list(struct neighbour *n)
{
bool on_managed_list, add_to_managed;
spin_lock_bh(&n->tbl->lock);
write_lock(&n->lock);
if (n->dead)
goto out;
add_to_managed = n->flags & NTF_MANAGED;
on_managed_list = !list_empty(&n->managed_list);
@ -190,9 +195,6 @@ static void neigh_update_managed_list(struct neighbour *n)
list_del_init(&n->managed_list);
else if (add_to_managed && !on_managed_list)
list_add_tail(&n->managed_list, &n->tbl->managed_list);
out:
write_unlock(&n->lock);
spin_unlock_bh(&n->tbl->lock);
}
static void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,
@ -349,8 +351,7 @@ static void neigh_parms_qlen_dec(struct net_device *dev, int family)
rcu_read_unlock();
}
static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
int family)
static void pneigh_queue_purge(struct sk_buff_head *list, int family)
{
struct sk_buff_head tmp;
unsigned long flags;
@ -361,13 +362,11 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
skb = skb_peek(list);
while (skb != NULL) {
struct sk_buff *skb_next = skb_peek_next(skb, list);
struct net_device *dev = skb->dev;
if (net == NULL || net_eq(dev_net(dev), net)) {
neigh_parms_qlen_dec(dev, family);
__skb_unlink(skb, list);
__skb_queue_tail(&tmp, skb);
}
neigh_parms_qlen_dec(skb->dev, family);
__skb_unlink(skb, list);
__skb_queue_tail(&tmp, skb);
skb = skb_next;
}
spin_unlock_irqrestore(&list->lock, flags);
@ -471,8 +470,7 @@ static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
spin_unlock_bh(&tbl->lock);
pneigh_ifdown(tbl, dev, skip_perm);
pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
tbl->family);
pneigh_queue_purge(&tbl->proxy_queue, tbl->family);
if (skb_queue_empty_lockless(&tbl->proxy_queue))
timer_delete_sync(&tbl->proxy_timer);
return 0;
@ -537,7 +535,7 @@ static struct neighbour *neigh_alloc(struct neigh_table *tbl,
INIT_LIST_HEAD(&n->gc_list);
INIT_LIST_HEAD(&n->managed_list);
atomic_inc(&tbl->entries);
neigh_table_get(tbl);
out:
return n;
@ -687,7 +685,7 @@ ___neigh_create(struct neigh_table *tbl, const void *pkey,
nht = rcu_dereference_protected(tbl->nht,
lockdep_is_held(&tbl->lock));
if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
if (neigh_table_entries(tbl) > (1 << nht->hash_shift))
nht = neigh_hash_grow(tbl, nht->hash_shift + 1);
hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
@ -752,8 +750,7 @@ static u32 pneigh_hash(const void *pkey, unsigned int key_len)
}
struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
struct net *net, const void *pkey,
struct net_device *dev)
const void *pkey, struct net_device *dev)
{
struct pneigh_entry *n;
unsigned int key_len;
@ -766,7 +763,6 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
while (n) {
if (!memcmp(n->key, pkey, key_len) &&
net_eq(pneigh_net(n), net) &&
(n->dev == dev || !n->dev))
return n;
@ -776,7 +772,7 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,
return NULL;
}
int pneigh_create(struct neigh_table *tbl, struct net *net,
int pneigh_create(struct neigh_table *tbl,
const void *pkey, struct net_device *dev,
u32 flags, u8 protocol, bool permanent)
{
@ -787,7 +783,7 @@ int pneigh_create(struct neigh_table *tbl, struct net *net,
mutex_lock(&tbl->phash_lock);
n = pneigh_lookup(tbl, net, pkey, dev);
n = pneigh_lookup(tbl, pkey, dev);
if (n)
goto update;
@ -798,7 +794,6 @@ int pneigh_create(struct neigh_table *tbl, struct net *net,
goto out;
}
write_pnet(&n->net, net);
memcpy(n->key, pkey, key_len);
n->dev = dev;
netdev_hold(dev, &n->dev_tracker, GFP_KERNEL);
@ -831,7 +826,7 @@ static void pneigh_destroy(struct rcu_head *rcu)
kfree(n);
}
int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
int pneigh_delete(struct neigh_table *tbl, const void *pkey,
struct net_device *dev)
{
struct pneigh_entry *n, __rcu **np;
@ -846,8 +841,7 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
for (np = &tbl->phash_buckets[hash_val];
(n = rcu_dereference_protected(*np, 1)) != NULL;
np = &n->next) {
if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
net_eq(pneigh_net(n), net)) {
if (!memcmp(n->key, pkey, key_len) && n->dev == dev) {
rcu_assign_pointer(*np, n->next);
mutex_unlock(&tbl->phash_lock);
@ -939,7 +933,7 @@ void neigh_destroy(struct neighbour *neigh)
neigh_dbg(2, "neigh %p is destroyed\n", neigh);
atomic_dec(&neigh->tbl->entries);
neigh_table_put(neigh->tbl);
kfree_rcu(neigh, rcu);
}
EXPORT_SYMBOL(neigh_destroy);
@ -994,7 +988,7 @@ static void neigh_periodic_work(struct work_struct *work)
neigh_set_reach_time(p);
}
if (atomic_read(&tbl->entries) < READ_ONCE(tbl->gc_thresh1))
if (neigh_table_entries(tbl) < READ_ONCE(tbl->gc_thresh1))
goto out;
for (i = 0 ; i < (1 << nht->hash_shift); i++) {
@ -1523,10 +1517,23 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
write_unlock_bh(&neigh->lock);
if (((new ^ old) & NUD_PERMANENT) || gc_update)
neigh_update_gc_list(neigh);
if (managed_update)
neigh_update_managed_list(neigh);
gc_update |= !!((new ^ old) & NUD_PERMANENT);
if (gc_update || managed_update) {
spin_lock_bh(&neigh->tbl->lock);
if (!neigh->dead) {
write_lock(&neigh->lock);
if (gc_update)
neigh_update_gc_list(neigh);
if (managed_update)
neigh_update_managed_list(neigh);
write_unlock(&neigh->lock);
}
spin_unlock_bh(&neigh->tbl->lock);
}
if (notify)
call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
@ -1540,7 +1547,6 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
{
return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);
}
EXPORT_SYMBOL(neigh_update);
/* Update the neigh to listen temporarily for probe responses, even if it is
* in a NUD_FAILED state. The caller has to hold neigh->lock for writing.
@ -1558,7 +1564,6 @@ void __neigh_set_probe_once(struct neighbour *neigh)
jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
HZ/100));
}
EXPORT_SYMBOL(__neigh_set_probe_once);
struct neighbour *neigh_event_ns(struct neigh_table *tbl,
u8 *lladdr, void *saddr,
@ -1571,7 +1576,6 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,
NEIGH_UPDATE_F_OVERRIDE, 0);
return neigh;
}
EXPORT_SYMBOL(neigh_event_ns);
/* called with read_lock_bh(&n->lock); */
static void neigh_hh_init(struct neighbour *n)
@ -1624,7 +1628,6 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL);
goto out;
}
EXPORT_SYMBOL(neigh_resolve_output);
/* As fast as possible without hh cache */
@ -1741,16 +1744,15 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
mod_timer(&tbl->proxy_timer, sched_next);
spin_unlock(&tbl->proxy_queue.lock);
}
EXPORT_SYMBOL(pneigh_enqueue);
static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
struct net *net, int ifindex)
int ifindex)
{
struct neigh_parms *p;
list_for_each_entry(p, &tbl->parms_list, list) {
if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
(!p->dev && !ifindex && net_eq(net, &init_net)))
if ((p->dev && p->dev->ifindex == ifindex) ||
(!p->dev && !ifindex))
return p;
}
@ -1789,7 +1791,6 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
}
return p;
}
EXPORT_SYMBOL(neigh_parms_alloc);
static void neigh_rcu_free_parms(struct rcu_head *head)
{
@ -1812,113 +1813,171 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
netdev_put(parms->dev, &parms->dev_tracker);
call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
}
EXPORT_SYMBOL(neigh_parms_release);
static struct lock_class_key neigh_table_proxy_queue_class;
static struct neigh_table __rcu *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
void neigh_table_init(int index, struct neigh_table *tbl)
static int neigh_table_init(struct net *net, struct neigh_table *tbl)
{
unsigned long now = jiffies;
unsigned long phsize;
INIT_LIST_HEAD(&tbl->parms_list);
INIT_LIST_HEAD(&tbl->gc_list);
INIT_LIST_HEAD(&tbl->managed_list);
list_add(&tbl->parms.list, &tbl->parms_list);
write_pnet(&tbl->parms.net, &init_net);
refcount_set(&tbl->parms.refcnt, 1);
neigh_set_reach_time(&tbl->parms);
tbl->parms.qlen = 0;
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
panic("cannot create neighbour cache statistics");
#ifdef CONFIG_PROC_FS
if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat,
&neigh_stat_seq_ops, tbl))
panic("cannot create neighbour proc dir entry");
#endif
RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
if (!tbl->nht)
goto err_hash;
phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
if (!tbl->phash_buckets)
goto err_phash;
if (!tbl->nht || !tbl->phash_buckets)
panic("cannot allocate neighbour cache hashes");
tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
tbl->key_len, NEIGH_PRIV_ALIGN);
if (!tbl->entry_size)
tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
tbl->key_len, NEIGH_PRIV_ALIGN);
else
WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
goto err_stats;
spin_lock_init(&tbl->lock);
mutex_init(&tbl->phash_lock);
INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
tbl->parms.reachable_time);
INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work);
queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0);
timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
skb_queue_head_init_class(&tbl->proxy_queue,
&neigh_table_proxy_queue_class);
#ifdef CONFIG_PROC_FS
if (!proc_create_net_data(tbl->id, 0, net->proc_net_stat,
&neigh_stat_seq_ops,
sizeof(struct seq_net_private), tbl))
goto err_proc;
#endif
tbl->parms.tbl = tbl;
tbl->parms.qlen = 0;
INIT_LIST_HEAD(&tbl->parms_list);
list_add(&tbl->parms.list, &tbl->parms_list);
write_pnet(&tbl->parms.net, net);
refcount_set(&tbl->parms.refcnt, 1);
neigh_set_reach_time(&tbl->parms);
tbl->last_flush = now;
tbl->last_rand = now + tbl->parms.reachable_time * 20;
rcu_assign_pointer(neigh_tables[index], tbl);
refcount_set(&tbl->entries, 1);
spin_lock_init(&tbl->lock);
mutex_init(&tbl->phash_lock);
skb_queue_head_init_class(&tbl->proxy_queue,
&neigh_table_proxy_queue_class);
timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
INIT_LIST_HEAD(&tbl->gc_list);
INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
tbl->parms.reachable_time);
INIT_LIST_HEAD(&tbl->managed_list);
INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work);
queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0);
return 0;
#ifdef CONFIG_PROC_FS
err_proc:
free_percpu(tbl->stats);
#endif
err_stats:
kfree(tbl->phash_buckets);
err_phash:
neigh_hash_free_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu);
err_hash:
return -ENOMEM;
}
/*
* Only called from ndisc_cleanup(), which means this is dead code
* because we no longer can unload IPv6 module.
*/
int neigh_table_clear(int index, struct neigh_table *tbl)
static void neigh_table_free(struct neigh_table *tbl)
{
RCU_INIT_POINTER(neigh_tables[index], NULL);
synchronize_rcu();
/* It is not clean... Fix it to unload IPv6 module safely */
cancel_delayed_work_sync(&tbl->managed_work);
cancel_delayed_work_sync(&tbl->gc_work);
timer_delete_sync(&tbl->proxy_timer);
pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family);
neigh_ifdown(tbl, NULL);
if (atomic_read(&tbl->entries))
pr_crit("neighbour leakage\n");
call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
neigh_hash_free_rcu);
tbl->nht = NULL;
kfree(tbl->phash_buckets);
tbl->phash_buckets = NULL;
remove_proc_entry(tbl->id, init_net.proc_net_stat);
struct neigh_hash_table *nht;
free_percpu(tbl->stats);
tbl->stats = NULL;
return 0;
kfree(tbl->phash_buckets);
tbl->phash_buckets = NULL;
nht = rcu_dereference_protected(tbl->nht, 1);
tbl->nht = NULL;
neigh_hash_free_rcu(&nht->rcu);
kfree(tbl);
}
static struct neigh_table *neigh_find_table(int family)
static void neigh_table_clear(struct net *net, struct neigh_table *tbl)
{
cancel_delayed_work_sync(&tbl->managed_work);
cancel_delayed_work_sync(&tbl->gc_work);
timer_shutdown_sync(&tbl->proxy_timer);
neigh_ifdown(tbl, NULL);
remove_proc_entry(tbl->id, net->proc_net_stat);
neigh_table_put(tbl);
}
int sysctl_neigh_inherit_init_net __read_mostly = 1;
static void neigh_table_inherit(struct net *net, struct neigh_table *tbl,
int index)
{
const struct neigh_table *init_tbl = init_net.neigh_tables[index];
bool inherit = READ_ONCE(sysctl_neigh_inherit_init_net);
int i;
if (net_eq(net, &init_net) || !inherit)
return;
tbl->gc_interval = READ_ONCE(init_tbl->gc_interval);
tbl->gc_thresh1 = READ_ONCE(init_tbl->gc_thresh1);
tbl->gc_thresh2 = READ_ONCE(init_tbl->gc_thresh2);
tbl->gc_thresh3 = READ_ONCE(init_tbl->gc_thresh3);
for (i = 0; i < NEIGH_VAR_DATA_MAX; i++)
tbl->parms.data[i] = READ_ONCE(init_tbl->parms.data[i]);
}
int neigh_table_register(struct net *net, struct neigh_table *tbl, int index)
{
int err;
tbl = kmemdup(tbl, sizeof(*tbl), GFP_KERNEL);
if (!tbl) {
err = -ENOMEM;
goto err;
}
neigh_table_inherit(net, tbl, index);
err = neigh_table_init(net, tbl);
if (err)
goto free_table;
net->neigh_tables[index] = tbl;
return 0;
free_table:
kfree(tbl);
err:
return err;
}
void neigh_table_unregister(struct net *net, int index)
{
struct neigh_table *tbl = net->neigh_tables[index];
net->neigh_tables[index] = NULL;
neigh_table_clear(net, tbl);
}
static struct neigh_table *neigh_find_table(struct net *net, int family)
{
struct neigh_table *tbl = NULL;
switch (family) {
case AF_INET:
tbl = rcu_dereference_rtnl(neigh_tables[NEIGH_ARP_TABLE]);
tbl = net->neigh_tables[NEIGH_ARP_TABLE];
break;
case AF_INET6:
tbl = rcu_dereference_rtnl(neigh_tables[NEIGH_ND_TABLE]);
tbl = net->neigh_tables[NEIGH_ND_TABLE];
break;
}
@ -1972,7 +2031,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
tbl = neigh_find_table(ndm->ndm_family);
tbl = neigh_find_table(net, ndm->ndm_family);
if (tbl == NULL)
return -EAFNOSUPPORT;
@ -1982,7 +2041,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (ndm->ndm_flags & NTF_PROXY) {
err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
err = pneigh_delete(tbl, nla_data(dst_attr), dev);
goto out;
}
@ -2058,7 +2117,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
tbl = neigh_find_table(ndm->ndm_family);
tbl = neigh_find_table(net, ndm->ndm_family);
if (tbl == NULL)
return -EAFNOSUPPORT;
@ -2078,7 +2137,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
}
err = pneigh_create(tbl, net, dst, dev, ndm_flags, protocol,
err = pneigh_create(tbl, dst, dev, ndm_flags, protocol,
!!(ndm->ndm_state & NUD_PERMANENT));
goto out;
}
@ -2267,7 +2326,7 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
struct ndt_config ndc = {
.ndtc_key_len = tbl->key_len,
.ndtc_entry_size = tbl->entry_size,
.ndtc_entries = atomic_read(&tbl->entries),
.ndtc_entries = neigh_table_entries(tbl),
.ndtc_last_flush = jiffies_to_msecs(flush_delta),
.ndtc_last_rand = jiffies_to_msecs(rand_delta),
.ndtc_proxy_qlen = READ_ONCE(tbl->proxy_queue.qlen),
@ -2400,10 +2459,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
ndtmsg = nlmsg_data(nlh);
rcu_read_lock();
for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
tbl = rcu_dereference(neigh_tables[tidx]);
tbl = net->neigh_tables[tidx];
if (!tbl)
continue;
@ -2417,7 +2474,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (!found) {
rcu_read_unlock();
err = -ENOENT;
goto errout;
}
@ -2442,8 +2498,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tbp[NDTPA_IFINDEX])
ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
p = lookup_neigh_parms(tbl, net, ifindex);
if (p == NULL) {
p = lookup_neigh_parms(tbl, ifindex);
if (!p) {
err = -ENOENT;
goto errout_tbl_lock;
}
@ -2524,12 +2580,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
}
}
err = -ENOENT;
if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] ||
tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) &&
!net_eq(net, &init_net))
goto errout_tbl_lock;
if (tb[NDTA_THRESH1])
WRITE_ONCE(tbl->gc_thresh1, nla_get_u32(tb[NDTA_THRESH1]));
@ -2546,7 +2596,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
errout_tbl_lock:
spin_unlock_bh(&tbl->lock);
rcu_read_unlock();
errout:
return err;
}
@ -2598,7 +2647,7 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
struct neigh_parms *p;
tbl = rcu_dereference(neigh_tables[tidx]);
tbl = net->neigh_tables[tidx];
if (!tbl)
continue;
@ -2613,9 +2662,6 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
nidx = 0;
p = list_next_entry(&tbl->parms, list);
list_for_each_entry_from_rcu(p, &tbl->parms_list, list) {
if (!net_eq(neigh_parms_net(p), net))
continue;
if (nidx < neigh_skip)
goto next;
@ -2793,12 +2839,11 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
struct netlink_callback *cb,
struct neigh_dump_filter *filter)
{
struct net *net = sock_net(skb->sk);
struct neighbour *n;
int err = 0, h, s_h = cb->args[1];
int idx, s_idx = idx = cb->args[2];
struct neigh_hash_table *nht;
int err = 0, h, s_h = cb->args[1];
unsigned int flags = NLM_F_MULTI;
struct neigh_hash_table *nht;
struct neighbour *n;
if (filter->dev_idx || filter->master_idx)
flags |= NLM_F_DUMP_FILTERED;
@ -2810,7 +2855,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
s_idx = 0;
idx = 0;
neigh_for_each_in_bucket_rcu(n, &nht->hash_heads[h]) {
if (idx < s_idx || !net_eq(dev_net(n->dev), net))
if (idx < s_idx)
goto next;
if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
neigh_master_filtered(n->dev, filter->master_idx))
@ -2834,11 +2879,10 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
struct netlink_callback *cb,
struct neigh_dump_filter *filter)
{
struct pneigh_entry *n;
struct net *net = sock_net(skb->sk);
int err = 0, h, s_h = cb->args[3];
int idx, s_idx = idx = cb->args[4];
int err = 0, h, s_h = cb->args[3];
unsigned int flags = NLM_F_MULTI;
struct pneigh_entry *n;
if (filter->dev_idx || filter->master_idx)
flags |= NLM_F_DUMP_FILTERED;
@ -2849,7 +2893,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
for (n = rcu_dereference(tbl->phash_buckets[h]), idx = 0;
n;
n = rcu_dereference(n->next)) {
if (idx < s_idx || pneigh_net(n) != net)
if (idx < s_idx)
goto next;
if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
neigh_master_filtered(n->dev, filter->master_idx))
@ -2935,6 +2979,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
{
const struct nlmsghdr *nlh = cb->nlh;
struct neigh_dump_filter filter = {};
struct net *net = sock_net(skb->sk);
struct neigh_table *tbl;
int t, family, s_t;
int proxy = 0;
@ -2958,8 +3003,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock();
for (t = 0; t < NEIGH_NR_TABLES; t++) {
tbl = rcu_dereference(neigh_tables[t]);
tbl = net->neigh_tables[t];
if (!tbl)
continue;
if (t < s_t || (family && tbl->family != family))
@ -3081,7 +3125,7 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
rcu_read_lock();
tbl = neigh_find_table(ndm->ndm_family);
tbl = neigh_find_table(net, ndm->ndm_family);
if (!tbl) {
NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
err = -EAFNOSUPPORT;
@ -3108,7 +3152,7 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
if (ndm->ndm_flags & NTF_PROXY) {
struct pneigh_entry *pn;
pn = pneigh_lookup(tbl, net, dst, dev);
pn = pneigh_lookup(tbl, dst, dev);
if (!pn) {
NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
err = -ENOENT;
@ -3161,37 +3205,6 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void
}
EXPORT_SYMBOL(neigh_for_each);
/* The tbl->lock must be held as a writer and BH disabled. */
void __neigh_for_each_release(struct neigh_table *tbl,
int (*cb)(struct neighbour *))
{
struct neigh_hash_table *nht;
int chain;
nht = rcu_dereference_protected(tbl->nht,
lockdep_is_held(&tbl->lock));
for (chain = 0; chain < (1 << nht->hash_shift); chain++) {
struct hlist_node *tmp;
struct neighbour *n;
neigh_for_each_in_bucket_safe(n, tmp, &nht->hash_heads[chain]) {
int release;
write_lock(&n->lock);
release = cb(n);
if (release) {
hlist_del_rcu(&n->hash);
hlist_del_rcu(&n->dev_list);
neigh_mark_dead(n);
}
write_unlock(&n->lock);
if (release)
neigh_cleanup_and_release(n);
}
}
}
EXPORT_SYMBOL(__neigh_for_each_release);
int neigh_xmit(int index, struct net_device *dev,
const void *addr, struct sk_buff *skb)
{
@ -3200,9 +3213,11 @@ int neigh_xmit(int index, struct net_device *dev,
if (likely(index < NEIGH_NR_TABLES)) {
struct neigh_table *tbl;
struct neighbour *neigh;
struct net *net;
rcu_read_lock();
tbl = rcu_dereference(neigh_tables[index]);
net = dev_net_rcu(dev);
tbl = net->neigh_tables[index];
if (!tbl) {
rcu_read_unlock();
goto out_kfree_skb;
@ -3245,10 +3260,6 @@ static struct neighbour *neigh_get_valid(struct seq_file *seq,
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
struct net *net = seq_file_net(seq);
if (!net_eq(dev_net(n->dev), net))
return NULL;
if (state->neigh_sub_iter) {
loff_t fakep = 0;
@ -3337,7 +3348,6 @@ static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
{
struct neigh_seq_state *state = seq->private;
struct net *net = seq_file_net(seq);
struct neigh_table *tbl = state->tbl;
struct pneigh_entry *pn = NULL;
int bucket;
@ -3345,9 +3355,6 @@ static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
state->flags |= NEIGH_SEQ_IS_PNEIGH;
for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
pn = rcu_dereference(tbl->phash_buckets[bucket]);
while (pn && !net_eq(pneigh_net(pn), net))
pn = rcu_dereference(pn->next);
if (pn)
break;
}
@ -3361,21 +3368,15 @@ static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
loff_t *pos)
{
struct neigh_seq_state *state = seq->private;
struct net *net = seq_file_net(seq);
struct neigh_table *tbl = state->tbl;
do {
pn = rcu_dereference(pn->next);
} while (pn && !net_eq(pneigh_net(pn), net));
pn = rcu_dereference(pn->next);
while (!pn) {
if (++state->bucket > PNEIGH_HASHMASK)
break;
pn = rcu_dereference(tbl->phash_buckets[state->bucket]);
while (pn && !net_eq(pneigh_net(pn), net))
pn = rcu_dereference(pn->next);
if (pn)
break;
}
@ -3430,7 +3431,6 @@ void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl
return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;
}
EXPORT_SYMBOL(neigh_seq_start);
void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
@ -3457,7 +3457,6 @@ void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
++(*pos);
return rc;
}
EXPORT_SYMBOL(neigh_seq_next);
void neigh_seq_stop(struct seq_file *seq, void *v)
__releases(tbl->lock)
@ -3469,7 +3468,6 @@ void neigh_seq_stop(struct seq_file *seq, void *v)
spin_unlock_bh(&tbl->lock);
rcu_read_unlock();
}
EXPORT_SYMBOL(neigh_seq_stop);
/* statistics via seq_file */
@ -3523,7 +3521,7 @@ static int neigh_stat_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx "
"%08lx %08lx %08lx "
"%08lx %08lx %08lx\n",
atomic_read(&tbl->entries),
neigh_table_entries(tbl),
st->allocs,
st->destroys,
@ -3593,7 +3591,6 @@ void neigh_app_ns(struct neighbour *n)
{
neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0);
}
EXPORT_SYMBOL(neigh_app_ns);
#ifdef CONFIG_SYSCTL
static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
@ -3689,7 +3686,6 @@ int neigh_proc_dointvec(const struct ctl_table *ctl, int write, void *buffer,
neigh_proc_update(ctl, write);
return ret;
}
EXPORT_SYMBOL(neigh_proc_dointvec);
int neigh_proc_dointvec_jiffies(const struct ctl_table *ctl, int write, void *buffer,
size_t *lenp, loff_t *ppos)
@ -3699,7 +3695,6 @@ int neigh_proc_dointvec_jiffies(const struct ctl_table *ctl, int write, void *bu
neigh_proc_update(ctl, write);
return ret;
}
EXPORT_SYMBOL(neigh_proc_dointvec_jiffies);
static int neigh_proc_dointvec_userhz_jiffies(const struct ctl_table *ctl, int write,
void *buffer, size_t *lenp,
@ -3719,7 +3714,6 @@ int neigh_proc_dointvec_ms_jiffies(const struct ctl_table *ctl, int write,
neigh_proc_update(ctl, write);
return ret;
}
EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);
static int neigh_proc_dointvec_unres_qlen(const struct ctl_table *ctl, int write,
void *buffer, size_t *lenp,
@ -3926,7 +3920,6 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
err:
return -ENOBUFS;
}
EXPORT_SYMBOL(neigh_sysctl_register);
void neigh_sysctl_unregister(struct neigh_parms *p)
{
@ -3937,7 +3930,6 @@ void neigh_sysctl_unregister(struct neigh_parms *p)
kfree(t);
}
}
EXPORT_SYMBOL(neigh_sysctl_unregister);
#endif /* CONFIG_SYSCTL */

View File

@ -21,6 +21,7 @@
#include <net/ip.h>
#include <net/sock.h>
#include <net/neighbour.h>
#include <net/net_ratelimit.h>
#include <net/busy_poll.h>
#include <net/pkt_sched.h>
@ -676,6 +677,15 @@ static struct ctl_table net_core_table[] = {
.proc_handler = proc_do_skb_defer_max,
.extra1 = SYSCTL_ZERO,
},
{
.procname = "neigh_inherit_init_net",
.data = &sysctl_neigh_inherit_init_net,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
};
static const struct ctl_table netns_core_table[] = {

View File

@ -58,8 +58,9 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
info->daddr.mode = IEEE802154_ADDR_SHORT;
} else {
__le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
struct neigh_table *tbl = nd_table(dev_net(ldev));
n = neigh_lookup(&nd_tbl, &hdr->daddr, ldev);
n = neigh_lookup(tbl, &hdr->daddr, ldev);
if (n) {
llneigh = lowpan_802154_neigh(neighbour_priv(n));
read_lock_bh(&n->lock);

View File

@ -149,7 +149,7 @@ static const struct neigh_ops arp_direct_ops = {
.connected_output = neigh_direct_output,
};
struct neigh_table arp_tbl = {
static struct neigh_table arp_tbl = {
.family = AF_INET,
.key_len = 4,
.protocol = cpu_to_be16(ETH_P_IP),
@ -182,7 +182,6 @@ struct neigh_table arp_tbl = {
.gc_thresh2 = 512,
.gc_thresh3 = 1024,
};
EXPORT_SYMBOL(arp_tbl);
int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)
{
@ -222,14 +221,18 @@ static bool arp_key_eq(const struct neighbour *neigh, const void *pkey)
static int arp_constructor(struct neighbour *neigh)
{
__be32 addr;
struct net_device *dev = neigh->dev;
struct in_device *in_dev;
struct neigh_parms *parms;
struct net *net = dev_net(dev);
u32 inaddr_any = INADDR_ANY;
struct neigh_parms *parms;
struct in_device *in_dev;
struct neigh_table *tbl;
__be32 addr;
tbl = arp_table(net);
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
memcpy(neigh->primary_key, &inaddr_any, arp_tbl.key_len);
memcpy(neigh->primary_key, &inaddr_any, tbl->key_len);
addr = *(__be32 *)neigh->primary_key;
rcu_read_lock();
@ -239,7 +242,7 @@ static int arp_constructor(struct neighbour *neigh)
return -EINVAL;
}
neigh->type = inet_addr_type_dev_table(dev_net(dev), dev, addr);
neigh->type = inet_addr_type_dev_table(net, dev, addr);
parms = in_dev->arp_parms;
__neigh_parms_put(neigh->parms);
@ -701,24 +704,24 @@ static bool arp_is_garp(struct net *net, struct net_device *dev,
static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in_device *in_dev = __in_dev_get_rcu(dev);
struct arphdr *arp;
unsigned char *arp_ptr;
struct rtable *rt;
unsigned char *sha;
unsigned char *tha = NULL;
__be32 sip, tip;
u16 dev_type = dev->type;
int addr_type;
struct neighbour *n;
struct neigh_table *tbl = arp_table(net);
struct dst_entry *reply_dst = NULL;
struct net_device *dev = skb->dev;
unsigned char *sha, *tha = NULL;
struct in_device *in_dev;
u16 dev_type = dev->type;
unsigned char *arp_ptr;
bool is_garp = false;
struct neighbour *n;
struct arphdr *arp;
struct rtable *rt;
__be32 sip, tip;
int addr_type;
/* arp_rcv below verifies the ARP header and verifies the device
* is ARP'able.
*/
in_dev = __in_dev_get_rcu(dev);
if (!in_dev)
goto out_free_skb;
@ -850,7 +853,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
if (!dont_send && IN_DEV_ARPFILTER(in_dev))
dont_send = arp_filter(sip, tip, dev);
if (!dont_send) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
n = neigh_event_ns(tbl, sha, &sip, dev);
if (n) {
arp_send_dst(ARPOP_REPLY, ETH_P_ARP,
sip, dev, tip, sha,
@ -865,8 +868,8 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
(arp_fwd_proxy(in_dev, dev, rt) ||
arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||
(rt->dst.dev != dev &&
pneigh_lookup(&arp_tbl, net, &tip, dev)))) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
pneigh_lookup(tbl, &tip, dev)))) {
n = neigh_event_ns(tbl, sha, &sip, dev);
if (n)
neigh_release(n);
@ -878,7 +881,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
dev->dev_addr, sha,
reply_dst);
} else {
pneigh_enqueue(&arp_tbl,
pneigh_enqueue(tbl,
in_dev->arp_parms, skb);
goto out_free_dst;
}
@ -889,7 +892,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
/* Update our ARP tables */
n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
n = __neigh_lookup(tbl, &sip, dev, 0);
addr_type = -1;
if (n || arp_accept(in_dev, sip)) {
@ -910,7 +913,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
/* postpone calculation to as late as possible */
inet_addr_type_dev_table(net, dev, sip) ==
RTN_UNICAST)))))
n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
n = __neigh_lookup(tbl, &sip, dev, 1);
}
if (n) {
@ -1077,9 +1080,10 @@ static int arp_req_set_proxy(struct net *net, struct net_device *dev, int on)
}
static int arp_req_set_public(struct net *net, struct arpreq *r,
struct net_device *dev)
struct net_device *dev)
{
__be32 mask = ((struct sockaddr_in *)&r->arp_netmask)->sin_addr.s_addr;
struct neigh_table *tbl = arp_table(net);
if (!dev && (r->arp_flags & ATF_COM)) {
dev = dev_getbyhwaddr(net, r->arp_ha.sa_family,
@ -1090,7 +1094,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
if (mask) {
__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
return pneigh_create(&arp_tbl, net, &ip, dev, 0, 0, false);
return pneigh_create(tbl, &ip, dev, 0, 0, false);
}
return arp_req_set_proxy(net, dev, 1);
@ -1098,6 +1102,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
static int arp_req_set(struct net *net, struct arpreq *r)
{
struct neigh_table *tbl = arp_table(net);
struct neighbour *neigh;
struct net_device *dev;
__be32 ip;
@ -1133,7 +1138,7 @@ static int arp_req_set(struct net *net, struct arpreq *r)
ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
neigh = __neigh_lookup_errno(&arp_tbl, &ip, dev);
neigh = __neigh_lookup_errno(tbl, &ip, dev);
err = PTR_ERR(neigh);
if (!IS_ERR(neigh)) {
unsigned int state = NUD_STALE;
@ -1169,6 +1174,7 @@ static unsigned int arp_state_to_flags(struct neighbour *neigh)
static int arp_req_get(struct net *net, struct arpreq *r)
{
__be32 ip = ((struct sockaddr_in *) &r->arp_pa)->sin_addr.s_addr;
struct neigh_table *tbl = arp_table(net);
struct neighbour *neigh;
struct net_device *dev;
@ -1179,7 +1185,7 @@ static int arp_req_get(struct net *net, struct arpreq *r)
if (IS_ERR(dev))
return PTR_ERR(dev);
neigh = neigh_lookup(&arp_tbl, &ip, dev);
neigh = neigh_lookup(tbl, &ip, dev);
if (!neigh)
return -ENXIO;
@ -1204,10 +1210,11 @@ static int arp_req_get(struct net *net, struct arpreq *r)
int arp_invalidate(struct net_device *dev, __be32 ip, bool force)
{
struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev);
struct neigh_table *tbl = arp_table(dev_net(dev));
struct neighbour *neigh;
int err = -ENXIO;
struct neigh_table *tbl = &arp_tbl;
neigh = neigh_lookup(tbl, &ip, dev);
if (neigh) {
if ((READ_ONCE(neigh->nud_state) & NUD_VALID) && !force) {
neigh_release(neigh);
@ -1228,14 +1235,15 @@ int arp_invalidate(struct net_device *dev, __be32 ip, bool force)
}
static int arp_req_delete_public(struct net *net, struct arpreq *r,
struct net_device *dev)
struct net_device *dev)
{
__be32 mask = ((struct sockaddr_in *)&r->arp_netmask)->sin_addr.s_addr;
struct neigh_table *tbl = arp_table(net);
if (mask) {
__be32 ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
return pneigh_delete(&arp_tbl, net, &ip, dev);
return pneigh_delete(tbl, &ip, dev);
}
return arp_req_set_proxy(net, dev, 0);
@ -1325,18 +1333,22 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_change_info *change_info;
struct net *net = dev_net(dev);
struct in_device *in_dev;
struct neigh_table *tbl;
bool evict_nocarrier;
tbl = arp_table(net);
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&arp_tbl, dev);
rt_cache_flush(dev_net(dev));
neigh_changeaddr(tbl, dev);
rt_cache_flush(net);
break;
case NETDEV_CHANGE:
change_info = ptr;
if (change_info->flags_changed & IFF_NOARP)
neigh_changeaddr(&arp_tbl, dev);
neigh_changeaddr(tbl, dev);
in_dev = __in_dev_get_rtnl(dev);
if (!in_dev)
@ -1345,7 +1357,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
evict_nocarrier = IN_DEV_ARP_EVICT_NOCARRIER(in_dev);
if (evict_nocarrier && !netif_carrier_ok(dev))
neigh_carrier_down(&arp_tbl, dev);
neigh_carrier_down(tbl, dev);
break;
default:
break;
@ -1364,7 +1376,9 @@ static struct notifier_block arp_netdev_notifier = {
*/
void arp_ifdown(struct net_device *dev)
{
neigh_ifdown(&arp_tbl, dev);
struct neigh_table *tbl = arp_table(dev_net(dev));
neigh_ifdown(tbl, dev);
}
@ -1479,10 +1493,12 @@ static int arp_seq_show(struct seq_file *seq, void *v)
static void *arp_seq_start(struct seq_file *seq, loff_t *pos)
{
struct neigh_table *tbl = arp_table(seq_file_net(seq));
/* Don't want to confuse "arp -a" w/ magic entries,
* so we tell the generic iterator to skip NUD_NOARP.
*/
return neigh_seq_start(seq, pos, &arp_tbl, NEIGH_SEQ_SKIP_NOARP);
return neigh_seq_start(seq, pos, tbl, NEIGH_SEQ_SKIP_NOARP);
}
static const struct seq_operations arp_seq_ops = {
@ -1495,15 +1511,47 @@ static const struct seq_operations arp_seq_ops = {
static int __net_init arp_net_init(struct net *net)
{
int err;
err = neigh_table_register(net, &arp_tbl, NEIGH_ARP_TABLE);
if (err)
goto err;
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_SYSCTL
err = neigh_sysctl_register(NULL, &arp_table(net)->parms, NULL);
if (err)
goto err_sysctl;
#endif
if (!proc_create_net("arp", 0444, net->proc_net, &arp_seq_ops,
sizeof(struct neigh_seq_state)))
return -ENOMEM;
sizeof(struct neigh_seq_state))) {
err = -ENOMEM;
goto err_proc_create;
}
#endif
return 0;
#ifdef CONFIG_PROC_FS
err_proc_create:
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&arp_table(net)->parms);
err_sysctl:
#endif
neigh_table_unregister(net, NEIGH_ARP_TABLE);
#endif
err:
return err;
}
static void __net_exit arp_net_exit(struct net *net)
{
remove_proc_entry("arp", net->proc_net);
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&arp_table(net)->parms);
#endif
neigh_table_unregister(net, NEIGH_ARP_TABLE);
}
static struct pernet_operations arp_net_ops = {
@ -1513,12 +1561,9 @@ static struct pernet_operations arp_net_ops = {
void __init arp_init(void)
{
neigh_table_init(NEIGH_ARP_TABLE, &arp_tbl);
if (register_pernet_subsys(&arp_net_ops))
panic("Cannot allocate arp table\n");
dev_add_pack(&arp_packet_type);
register_pernet_subsys(&arp_net_ops);
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);
}

View File

@ -265,7 +265,9 @@ EXPORT_SYMBOL(in_dev_finish_destroy);
static struct in_device *inetdev_init(struct net_device *dev)
{
struct net *net = dev_net(dev);
struct in_device *in_dev;
struct neigh_table *tbl;
int err = -ENOMEM;
ASSERT_RTNL();
@ -273,11 +275,12 @@ static struct in_device *inetdev_init(struct net_device *dev)
in_dev = kzalloc_obj(*in_dev);
if (!in_dev)
goto out;
memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
sizeof(in_dev->cnf));
tbl = arp_table(net);
memcpy(&in_dev->cnf, net->ipv4.devconf_dflt, sizeof(in_dev->cnf));
in_dev->cnf.sysctl = NULL;
in_dev->dev = dev;
in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
in_dev->arp_parms = neigh_parms_alloc(dev, tbl);
if (!in_dev->arp_parms)
goto out_kfree;
if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
@ -291,7 +294,7 @@ static struct in_device *inetdev_init(struct net_device *dev)
err = devinet_sysctl_register(in_dev);
if (err) {
in_dev->dead = 1;
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
neigh_parms_release(tbl, in_dev->arp_parms);
in_dev_put(in_dev);
in_dev = NULL;
goto out;
@ -313,13 +316,12 @@ static struct in_device *inetdev_init(struct net_device *dev)
static void inetdev_destroy(struct in_device *in_dev)
{
struct net_device *dev;
struct net_device *dev = in_dev->dev;
struct net *net = dev_net(dev);
struct in_ifaddr *ifa;
ASSERT_RTNL();
dev = in_dev->dev;
in_dev->dead = 1;
RCU_INIT_POINTER(dev->ip_ptr, NULL);
@ -332,7 +334,7 @@ static void inetdev_destroy(struct in_device *in_dev)
}
devinet_sysctl_unregister(in_dev);
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
neigh_parms_release(arp_table(net), in_dev->arp_parms);
arp_ifdown(dev);
in_dev_put(in_dev);

View File

@ -610,13 +610,14 @@ static int fib_detect_death(struct fib_info *fi, int order,
int dflt)
{
const struct fib_nh_common *nhc = fib_info_nhc(fi, 0);
struct neighbour *n;
struct net *net = fi->fib_net;
int state = NUD_NONE;
struct neighbour *n;
if (likely(nhc->nhc_gw_family == AF_INET))
n = neigh_lookup(&arp_tbl, &nhc->nhc_gw.ipv4, nhc->nhc_dev);
n = neigh_lookup(arp_table(net), &nhc->nhc_gw.ipv4, nhc->nhc_dev);
else if (IS_ENABLED(CONFIG_IPV6) && nhc->nhc_gw_family == AF_INET6)
n = neigh_lookup(&nd_tbl, &nhc->nhc_gw.ipv6, nhc->nhc_dev);
n = neigh_lookup(nd_table(net), &nhc->nhc_gw.ipv6, nhc->nhc_dev);
else
n = NULL;

View File

@ -817,7 +817,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow
n = __ipv4_neigh_lookup(rt->dst.dev, (__force u32)new_gw);
if (!n)
n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev);
n = neigh_create(arp_table(net), &new_gw, rt->dst.dev);
if (!IS_ERR(n)) {
if (!(READ_ONCE(n->nud_state) & NUD_VALID)) {
neigh_event_send(n, NULL);

View File

@ -376,6 +376,8 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{
struct net *net = dev_net(dev);
struct neigh_table *tbl;
struct inet6_dev *ndev;
int err = -ENOMEM;
@ -393,14 +395,15 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
ndev->dev = dev;
INIT_LIST_HEAD(&ndev->addr_list);
timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0);
memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
memcpy(&ndev->cnf, net->ipv6.devconf_dflt, sizeof(ndev->cnf));
if (ndev->cnf.stable_secret.initialized)
ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
tbl = nd_table(net);
ndev->cnf.mtu6 = dev->mtu;
ndev->ra_mtu = 0;
ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
ndev->nd_parms = neigh_parms_alloc(dev, tbl);
if (!ndev->nd_parms) {
kfree(ndev);
return ERR_PTR(err);
@ -413,7 +416,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
if (snmp6_alloc_dev(ndev) < 0) {
netdev_dbg(dev, "%s: cannot allocate memory for statistics\n",
__func__);
neigh_parms_release(&nd_tbl, ndev->nd_parms);
neigh_parms_release(tbl, ndev->nd_parms);
netdev_put(dev, &ndev->dev_tracker);
kfree(ndev);
return ERR_PTR(err);
@ -481,7 +484,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
return ndev;
err_release:
neigh_parms_release(&nd_tbl, ndev->nd_parms);
neigh_parms_release(tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
return ERR_PTR(err);
@ -4039,9 +4042,11 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
/* Last: Shot the device (if unregistered) */
if (unregister) {
struct neigh_table *tbl = nd_table(net);
addrconf_sysctl_unregister(idev);
neigh_parms_release(&nd_tbl, idev->nd_parms);
neigh_ifdown(&nd_tbl, dev);
neigh_parms_release(tbl, idev->nd_parms);
neigh_ifdown(tbl, dev);
in6_dev_put(idev);
}
return 0;

View File

@ -127,7 +127,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
if (IS_ERR_OR_NULL(neigh)) {
if (unlikely(!neigh))
neigh = __neigh_create(&nd_tbl, nexthop, dev, false);
neigh = __neigh_create(nd_table(net), nexthop, dev, false);
if (IS_ERR(neigh)) {
IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);
kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);
@ -583,7 +583,7 @@ int ip6_forward(struct sk_buff *skb)
/* XXX: idev->cnf.proxy_ndp? */
if (READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev)) {
pneigh_lookup(nd_table(net), &hdr->daddr, skb->dev)) {
int proxied = ip6_forward_proxy_check(skb);
hdr = ipv6_hdr(skb);

View File

@ -767,10 +767,11 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
static int pndisc_is_router(const void *pkey,
struct net_device *dev)
{
struct net *net = dev_net(dev);
struct pneigh_entry *n;
int ret = -1;
n = pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
n = pneigh_lookup(nd_table(net), pkey, dev);
if (n)
ret = !!(READ_ONCE(n->flags) & NTF_ROUTER);
@ -788,19 +789,21 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
{
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
offsetof(struct nd_msg, opt));
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
u8 *lladdr = NULL;
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
offsetof(struct nd_msg, opt));
struct ndisc_options ndopts;
struct net_device *dev = skb->dev;
struct inet6_ifaddr *ifp;
struct inet6_dev *idev = NULL;
struct neighbour *neigh;
struct net *net = dev_net(dev);
int dad = ipv6_addr_any(saddr);
struct inet6_dev *idev = NULL;
struct ndisc_options ndopts;
struct inet6_ifaddr *ifp;
struct neigh_table *tbl;
struct neighbour *neigh;
int is_router = -1;
u8 *lladdr = NULL;
SKB_DR(reason);
u64 nonce = 0;
bool inc;
@ -845,9 +848,10 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1)
memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);
inc = ipv6_addr_is_multicast(daddr);
tbl = nd_table(net);
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
inc = ipv6_addr_is_multicast(daddr);
ifp = ipv6_get_ifaddr(net, &msg->target, dev, 1);
if (ifp) {
have_ifp:
if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
@ -880,8 +884,6 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
idev = ifp->idev;
} else {
struct net *net = dev_net(dev);
/* perhaps an address on the master device */
if (netif_is_l3_slave(dev)) {
struct net_device *mdev;
@ -918,7 +920,7 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
*/
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
if (n)
pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
pneigh_enqueue(tbl, idev->nd_parms, n);
goto out;
}
} else {
@ -937,15 +939,15 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
}
if (inc)
NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
NEIGH_CACHE_STAT_INC(tbl, rcv_probes_mcast);
else
NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
NEIGH_CACHE_STAT_INC(tbl, rcv_probes_ucast);
/*
* update / create cache entry
* for the source address
*/
neigh = __neigh_lookup(&nd_tbl, saddr, dev,
neigh = __neigh_lookup(tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
if (neigh)
ndisc_update(dev, neigh, lladdr, NUD_STALE,
@ -987,17 +989,19 @@ static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr)
static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
u8 *lladdr = NULL;
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
offsetof(struct nd_msg, opt));
struct ndisc_options ndopts;
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
struct net_device *dev = skb->dev;
struct inet6_dev *idev = __in6_dev_get(dev);
struct net *net = dev_net(dev);
struct ndisc_options ndopts;
struct inet6_ifaddr *ifp;
struct neigh_table *tbl;
struct neighbour *neigh;
struct inet6_dev *idev;
u8 *lladdr = NULL;
SKB_DR(reason);
u8 new_state;
@ -1020,6 +1024,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
* and thus should not be accepted.
* drop_unsolicited_na takes precedence over accept_untracked_na
*/
idev = __in6_dev_get(dev);
if (!msg->icmph.icmp6_solicited && idev &&
READ_ONCE(idev->cnf.drop_unsolicited_na))
return reason;
@ -1034,7 +1039,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
return reason;
}
}
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
ifp = ipv6_get_ifaddr(net, &msg->target, dev, 1);
if (ifp) {
if (skb->pkt_type != PACKET_LOOPBACK
&& (ifp->flags & IFA_F_TENTATIVE)) {
@ -1058,7 +1063,8 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
return reason;
}
neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
tbl = nd_table(net);
neigh = neigh_lookup(tbl, &msg->target, dev);
/* RFC 9131 updates original Neighbour Discovery RFC 4861.
* NAs with Target LL Address option without a corresponding
@ -1078,14 +1084,13 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE;
if (!neigh && lladdr && idev && READ_ONCE(idev->cnf.forwarding)) {
if (accept_untracked_na(idev, saddr)) {
neigh = neigh_create(&nd_tbl, &msg->target, dev);
neigh = neigh_create(tbl, &msg->target, dev);
new_state = NUD_STALE;
}
}
if (neigh && !IS_ERR(neigh)) {
u8 old_flags = neigh->flags;
struct net *net = dev_net(dev);
if (READ_ONCE(neigh->nud_state) & NUD_FAILED)
goto out;
@ -1098,7 +1103,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
READ_ONCE(net->ipv6.devconf_all->forwarding) &&
READ_ONCE(net->ipv6.devconf_all->proxy_ndp) &&
pneigh_lookup(&nd_tbl, net, &msg->target, dev)) {
pneigh_lookup(tbl, &msg->target, dev)) {
/* XXX: idev->cnf.proxy_ndp */
goto out;
}
@ -1127,18 +1132,20 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
{
struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
struct net_device *dev = skb->dev;
struct ndisc_options ndopts;
struct neigh_table *tbl;
struct neighbour *neigh;
struct inet6_dev *idev;
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
struct ndisc_options ndopts;
u8 *lladdr = NULL;
SKB_DR(reason);
if (skb->len < sizeof(*rs_msg))
return SKB_DROP_REASON_PKT_TOO_SMALL;
idev = __in6_dev_get(skb->dev);
idev = __in6_dev_get(dev);
if (!idev) {
net_err_ratelimited("RS: can't find in6 device\n");
return reason;
@ -1156,19 +1163,19 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
goto out;
/* Parse ND options */
if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts))
if (!ndisc_parse_options(dev, rs_msg->opt, ndoptlen, &ndopts))
return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
if (ndopts.nd_opts_src_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
skb->dev);
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
if (!lladdr)
goto out;
}
neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
tbl = nd_table(dev_net(dev));
neigh = __neigh_lookup(tbl, saddr, dev, 1);
if (neigh) {
ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
ndisc_update(dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
@ -1232,6 +1239,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
struct net *net = dev_net(skb->dev);
bool send_ifinfo_notify = false;
struct neighbour *neigh = NULL;
struct ndisc_options ndopts;
@ -1241,7 +1249,6 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
u32 defrtr_usr_metric;
unsigned int pref = 0;
__u32 old_if_flags;
struct net *net;
SKB_DR(reason);
int lifetime;
int optlen;
@ -1330,7 +1337,6 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
/* Do not accept RA with source-addr found on local machine unless
* accept_ra_from_local is set to true.
*/
net = dev_net(in6_dev->dev);
if (!READ_ONCE(in6_dev->cnf.accept_ra_from_local) &&
ipv6_chk_addr(net, &ipv6_hdr(skb)->saddr, in6_dev->dev, 0)) {
net_dbg_ratelimited("RA from local address detected on dev: %s: default router ignored\n",
@ -1468,7 +1474,7 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
*/
if (!neigh)
neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
skb->dev, 1);
if (neigh) {
u8 *lladdr = NULL;
@ -1861,12 +1867,15 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct netdev_notifier_change_info *change_info;
struct net *net = dev_net(dev);
struct neigh_table *tbl;
struct inet6_dev *idev;
bool evict_nocarrier;
tbl = nd_table(net);
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&nd_tbl, dev);
neigh_changeaddr(tbl, dev);
fib6_run_gc(0, net, false);
fallthrough;
case NETDEV_UP:
@ -1890,12 +1899,12 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
change_info = ptr;
if (change_info->flags_changed & IFF_NOARP)
neigh_changeaddr(&nd_tbl, dev);
neigh_changeaddr(tbl, dev);
if (evict_nocarrier && !netif_carrier_ok(dev))
neigh_carrier_down(&nd_tbl, dev);
neigh_carrier_down(tbl, dev);
break;
case NETDEV_DOWN:
neigh_ifdown(&nd_tbl, dev);
neigh_ifdown(tbl, dev);
fib6_run_gc(0, net, false);
break;
case NETDEV_NOTIFY_PEERS:
@ -1974,12 +1983,23 @@ static int __net_init ndisc_net_init(struct net *net)
struct sock *sk;
int err;
err = neigh_table_register(net, &nd_tbl, NEIGH_ND_TABLE);
if (err)
goto err;
#ifdef CONFIG_SYSCTL
err = neigh_sysctl_register(NULL, &nd_table(net)->parms,
ndisc_ifinfo_sysctl_change);
if (err)
goto err_sysctl;
#endif
err = inet_ctl_sock_create(&sk, PF_INET6,
SOCK_RAW, IPPROTO_ICMPV6, net);
if (err < 0) {
net_err_ratelimited("NDISC: Failed to initialize the control socket (err %d)\n",
err);
return err;
goto err_sock_create;
}
net->ipv6.ndisc_sk = sk;
@ -1990,11 +2010,24 @@ static int __net_init ndisc_net_init(struct net *net)
inet6_clear_bit(MC6_LOOP, sk);
return 0;
err_sock_create:
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&nd_table(net)->parms);
err_sysctl:
#endif
neigh_table_unregister(net, NEIGH_ND_TABLE);
err:
return err;
}
static void __net_exit ndisc_net_exit(struct net *net)
{
inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&nd_table(net)->parms);
#endif
neigh_table_unregister(net, NEIGH_ND_TABLE);
}
static struct pernet_operations ndisc_net_ops = {
@ -2004,30 +2037,7 @@ static struct pernet_operations ndisc_net_ops = {
int __init ndisc_init(void)
{
int err;
err = register_pernet_subsys(&ndisc_net_ops);
if (err)
return err;
/*
* Initialize the neighbour table
*/
neigh_table_init(NEIGH_ND_TABLE, &nd_tbl);
#ifdef CONFIG_SYSCTL
err = neigh_sysctl_register(NULL, &nd_tbl.parms,
ndisc_ifinfo_sysctl_change);
if (err)
goto out_unregister_pernet;
out:
#endif
return err;
#ifdef CONFIG_SYSCTL
out_unregister_pernet:
unregister_pernet_subsys(&ndisc_net_ops);
goto out;
#endif
return register_pernet_subsys(&ndisc_net_ops);
}
int __init ndisc_late_init(void)
@ -2042,9 +2052,5 @@ void ndisc_late_cleanup(void)
void ndisc_cleanup(void)
{
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&nd_tbl.parms);
#endif
neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl);
unregister_pernet_subsys(&ndisc_net_ops);
}

View File

@ -217,7 +217,7 @@ struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,
if (n)
return n;
n = neigh_create(&nd_tbl, daddr, dev);
n = neigh_create(nd_table(dev_net(dev)), daddr, dev);
return IS_ERR(n) ? NULL : n;
}
@ -4241,6 +4241,7 @@ static int ip6_route_del(struct fib6_config *cfg,
static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)
{
struct netevent_redirect netevent;
struct net_device *dev = skb->dev;
struct rt6_info *rt, *nrt = NULL;
struct fib6_result res = {};
struct ndisc_options ndopts;
@ -4274,7 +4275,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
return;
}
in6_dev = __in6_dev_get(skb->dev);
in6_dev = __in6_dev_get(dev);
if (!in6_dev)
return;
if (READ_ONCE(in6_dev->cnf.forwarding) ||
@ -4286,15 +4287,14 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
* first-hop router for the specified ICMP Destination Address.
*/
if (!ndisc_parse_options(skb->dev, msg->opt, optlen, &ndopts)) {
if (!ndisc_parse_options(dev, msg->opt, optlen, &ndopts)) {
net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
return;
}
lladdr = NULL;
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
skb->dev);
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
if (!lladdr) {
net_dbg_ratelimited("rt6_redirect: invalid link-layer address length\n");
return;
@ -4313,7 +4313,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
*/
dst_confirm_neigh(&rt->dst, &ipv6_hdr(skb)->saddr);
neigh = __neigh_lookup(&nd_tbl, &msg->target, skb->dev, 1);
neigh = __neigh_lookup(nd_table(dev_net(dev)), &msg->target, dev, 1);
if (!neigh)
return;
@ -4321,7 +4321,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
* We have finally decided to accept it.
*/
ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
ndisc_update(dev, neigh, lladdr, NUD_STALE,
NEIGH_UPDATE_F_WEAK_OVERRIDE|
NEIGH_UPDATE_F_OVERRIDE|
(on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
@ -5042,9 +5042,11 @@ void rt6_sync_down_dev(struct net_device *dev, unsigned long event)
void rt6_disable_ip(struct net_device *dev, unsigned long event)
{
struct net *net = dev_net(dev);
rt6_sync_down_dev(dev, event);
rt6_uncached_list_flush_dev(dev);
neigh_ifdown(&nd_tbl, dev);
neigh_ifdown(nd_table(net), dev);
}
struct rt6_mtu_change_arg {

View File

@ -63,6 +63,15 @@ exit_cleanup_all()
exit "${EXIT_STATUS}"
}
get_periodic_gc_runs()
{
local ns=$1
local tbl_name=$2
ip -n "$ns" -j -s ntable show name "$tbl_name" | \
jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]'
}
################################################################################
# Tests
@ -231,9 +240,6 @@ extern_valid_common()
# Check that an "extern_valid" entry survives a forced garbage
# collection. Add an entry, wait 5 seconds and add more entries than
# "thresh3" so that forced garbage collection will run.
#
# Note that the garbage collection thresholds are global resources and
# that changes in the initial namespace affect all the namespaces.
local forced_gc_runs_t0
local forced_gc_runs_t1
local orig_thresh1
@ -241,18 +247,23 @@ extern_valid_common()
local orig_thresh3
run_cmd "ip -n $ns1 neigh flush dev veth0"
orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]')
orig_thresh2=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh2")) | .["thresh2"]')
orig_thresh3=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh3")) | .["thresh3"]')
run_cmd "ip ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8"
orig_thresh1=$(ip -n "$ns1" -j ntable show name "$tbl_name" | \
jq '.[] | select(has("thresh1")) | .["thresh1"]')
orig_thresh2=$(ip -n "$ns1" -j ntable show name "$tbl_name" | \
jq '.[] | select(has("thresh2")) | .["thresh2"]')
orig_thresh3=$(ip -n "$ns1" -j ntable show name "$tbl_name" | \
jq '.[] | select(has("thresh3")) | .["thresh3"]')
run_cmd "ip -n $ns1 ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8"
run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0"
run_cmd "sleep 5"
forced_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
forced_gc_runs_t0=$(ip -n "$ns1" -j -s ntable show name "$tbl_name" | \
jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
for i in {1..20}; do
run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0"
done
forced_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
forced_gc_runs_t1=$(ip -n "$ns1" -j -s ntable show name "$tbl_name" | \
jq '.[] | select(has("forced_gc_runs")) | .["forced_gc_runs"]')
if [[ $forced_gc_runs_t1 -eq $forced_gc_runs_t0 ]]; then
check_err 1 "Forced garbage collection did not run"
fi
@ -263,7 +274,8 @@ extern_valid_common()
log_test "$af_str \"extern_valid\" flag: Forced garbage collection"
run_cmd "ip ntable change name $tbl_name thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1"
run_cmd "ip -n $ns1 ntable change name $tbl_name \
thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1"
RET=0
@ -275,31 +287,36 @@ extern_valid_common()
# collection. Add an "extern_valid" entry, add more than "thresh1"
# regular entries, wait "base_reachable" (longer than "gc_stale")
# seconds and check that the "extern_valid" entry was not deleted.
#
# Note that the garbage collection thresholds and "base_reachable" are
# global resources and that changes in the initial namespace affect all
# the namespaces.
local periodic_gc_runs_t0
local periodic_gc_runs_t1
local orig_base_reachable
local base_reachable=10000
local orig_gc_stale
local timeout
run_cmd "ip -n $ns1 neigh flush dev veth0"
orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]')
orig_base_reachable=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["base_reachable"]')
run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable 10000"
orig_gc_stale=$(ip -n "$ns1" -j ntable show name "$tbl_name" dev veth0 | jq '.[]["gc_stale"]')
orig_thresh1=$(ip -n "$ns1" -j ntable show name "$tbl_name" | \
jq '.[] | select(has("thresh1")) | .["thresh1"]')
orig_base_reachable=$(ip -n "$ns1" -j ntable show name "$tbl_name" | \
jq '.[] | select(has("thresh1")) | .["base_reachable"]')
run_cmd "ip -n $ns1 ntable change name $tbl_name thresh1 10 base_reachable $base_reachable"
orig_gc_stale=$(ip -n "$ns1" -j ntable show name "$tbl_name" dev veth0 | \
jq '.[]["gc_stale"]')
run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale 1000"
run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0"
# Wait orig_base_reachable/2 for the new interval to take effect.
run_cmd "sleep $(((orig_base_reachable / 1000) / 2 + 2))"
# Wait up to neigh_set_reach_time(BASE_REACHABLE_TIME) (~45s) + 5s
# for the new interval to take effect, see neigh_table_init().
timeout=$(((orig_base_reachable / 1000) * 3 / 2 + 5))
slowwait_for_counter "$timeout" 1 get_periodic_gc_runs "$ns1" "$tbl_name" >/dev/null
for i in {1..20}; do
run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0"
done
periodic_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
run_cmd "sleep 10"
periodic_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
periodic_gc_runs_t0=$(get_periodic_gc_runs "$ns1" "$tbl_name")
# Wait up to BASE_REACHABLE_TIME / 2 + 5s, see neigh_periodic_work().
timeout=$(((base_reachable / 1000) / 2 + 5))
slowwait_for_counter "$timeout" 1 get_periodic_gc_runs "$ns1" "$tbl_name" >/dev/null
periodic_gc_runs_t1=$(get_periodic_gc_runs "$ns1" "$tbl_name")
[[ $periodic_gc_runs_t1 -ne $periodic_gc_runs_t0 ]]
check_err $? "Periodic garbage collection did not run"
run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
@ -310,7 +327,8 @@ extern_valid_common()
log_test "$af_str \"extern_valid\" flag: Periodic garbage collection"
run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale $orig_gc_stale"
run_cmd "ip ntable change name $tbl_name thresh1 $orig_thresh1 base_reachable $orig_base_reachable"
run_cmd "ip -n $ns1 ntable change name $tbl_name \
thresh1 $orig_thresh1 base_reachable $orig_base_reachable"
}
extern_valid_ipv4()