diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst index e586e17fc7a5..fe43e8595958 100644 --- a/Documentation/admin-guide/sysctl/net.rst +++ b/Documentation/admin-guide/sysctl/net.rst @@ -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 -------- diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 6c14246befb1..dfee72c8e46a 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1378,7 +1378,8 @@ out_unlock: 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; } diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index 0c4f462baa6e..ba45b61b09bb 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -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; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c index 648f4521c096..6a8d8aac7d16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c @@ -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(); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c index 8b827201935e..67c12ca19d59 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c @@ -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; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index db260e3d1412..37a8ddee3ea1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -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); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 3d6fdbab05e0..f4a435885ba4 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -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); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c index ae63d549b542..1cd8347c274d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c @@ -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 diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c index ca30702f8878..d650d33e3709 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c @@ -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; diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index 84a55f2b48ff..0fcd65fc0af7 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -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)) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 15d19a8a1710..ead8b447b89c 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -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); } diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index b84235e93ffe..793a562fc1f7 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -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 diff --git a/drivers/net/ethernet/sfc/tc_encap_actions.c b/drivers/net/ethernet/sfc/tc_encap_actions.c index c2ad3a358d20..152c4639ecc6 100644 --- a/drivers/net/ethernet/sfc/tc_encap_actions.c +++ b/drivers/net/ethernet/sfc/tc_encap_actions.c @@ -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 diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index a0557a3a7026..50ec04cc0213 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -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); diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index 459f19f7071e..0f0dfb4e176a 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -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 @@ out: 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, diff --git a/include/net/arp.h b/include/net/arp.h index e8747e0713c7..e932def63d62 100644 --- a/include/net/arp.h +++ b/include/net/arp.h @@ -7,8 +7,10 @@ #include #include - -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 diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 9e5379ad2d8e..96e3bb6e83af 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -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 diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 8860cc2175fc..ed9d9ae6d3a6 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -31,6 +31,8 @@ #include #include +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 { diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index e5ee673b9fcf..46b4c67e2966 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/include/net/route.h b/include/net/route.h index 45290177a33c..6b55de2e4df8 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -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; } diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c index 87a40e2f1c50..09458935a49e 100644 --- a/net/bridge/br_arp_nd_proxy.c +++ b/net/bridge/br_arp_nd_proxy.c @@ -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); diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 1349c0eedb64..27602bcbeb69 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -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 @@ do_alloc: 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 @@ out: 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 @@ out_kfree_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 @@ out: ++(*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 @@ free: 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 */ diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index eb35da3556f4..23310581f494 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -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[] = { diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c index 4df76ff50699..4f511476b992 100644 --- a/net/ieee802154/6lowpan/tx.c +++ b/net/ieee802154/6lowpan/tx.c @@ -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); diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index d409f606aec0..90bc53fb8090 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -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); } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index a90be57c63be..247d666824a4 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -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 @@ out_kfree: 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); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 7a362f2e2c2b..50e96f86ca59 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -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; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 37674d76f90f..d7da2f1acbb5 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -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); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9d89be7e0544..3dba94bd2ba0 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -376,6 +376,8 @@ err_ip: 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 @@ restart: /* 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; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 8fc4766c8da9..96ee3de55f93 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -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); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 75515fd99383..90cd5d852569 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -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 @@ have_ifp: 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 @@ have_ifp: */ 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 @@ have_ifp: } 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 @@ out: 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 @@ errout: 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 @@ skip_linkparms: */ 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); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9658939511e0..37e7cbbf49f2 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -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 { diff --git a/tools/testing/selftests/net/test_neigh.sh b/tools/testing/selftests/net/test_neigh.sh index 7c594bf6ead0..e70e31edf633 100755 --- a/tools/testing/selftests/net/test_neigh.sh +++ b/tools/testing/selftests/net/test_neigh.sh @@ -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()