| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/07 23:53 | flow | patch-triage |
7mArgs:null Results: {
"EnableConfigs": [
"NET_NS",
"IPV6",
"INET"
],
"FocusSymbols": [
"neigh_table_register",
"neigh_table_unregister",
"arp_rcv",
"ndisc_rcv",
"pneigh_create",
"pneigh_delete",
"pneigh_lookup",
"neigh_xmit"
],
"KMSANReasoning": "The patch series refactors the IPv4 ARP and IPv6 NDISC neighbour tables (`arp_tbl` and `nd_tbl`) to be per-network namespace instead of global. It modifies various network drivers and core networking code to retrieve the neighbour table from the network namespace (`arp_table(net)` and `nd_table(net)`) rather than using the global variables.\n\nKey changes:\n1. `struct neigh_table` instances are now dynamically allocated per netns via `neigh_table_register()`, which uses `kmemdup()` to duplicate a statically allocated template (`arp_tbl` or `nd_tbl`). Since the templates are static globals, their memory (including padding) is zero-initialized.\n2. The `net` field is removed from `struct pneigh_entry` because the neighbour tables are now per-netns, making the field redundant.\n3. Various lookups, creations, and garbage collection logic are updated to use the per-netns tables.\n\nKMSAN vs KASAN applicability:\n- KMSAN detects uses of uninitialized memory and info-leaks. The new dynamic allocations for the neighbour tables use `kmemdup()` from zero-initialized static templates, `kzalloc()` for hash buckets, and `alloc_percpu()` for stats. No uninitialized memory is introduced.\n- The structures are not copied directly to user space; their fields are serialized into netlink messages via `nla_put_*` functions, which do not leak padding.\n- Any potential bugs introduced by this refactoring (e.g., use-after-free during netns teardown, NULL pointer dereferences, or out-of-bounds accesses) would be reliably caught by standard KASAN and other bug detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch changes the IPv4 ARP and IPv6 Neighbor Discovery tables from being global to being per-network namespace. This is a significant functional change to the core networking stack, affecting how neighbor tables are initialized, accessed, and destroyed. It impacts network namespace creation/destruction, ARP/ND packet processing, and netlink neighbor management. Fuzzing should focus on namespace operations, ARP/ND packet reception, and proxy neighbor management.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/07 23:53 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit a5688afda8d14b0a24271682d891e388f5701c90\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 7 23:53:29 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c\nindex 16a015b672063..895a3e3f7310b 100644\n--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c\n+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c\n@@ -1378,7 +1378,8 @@ struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)\n \treturn neigh;\n }\n \n-static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)\n+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv,\n+\t\t\t int gc_interval)\n {\n \tstruct ipoib_neigh_table *ntbl = \u0026priv-\u003entbl;\n \tstruct ipoib_neigh_hash *htbl;\n@@ -1397,7 +1398,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)\n \t\tgoto out_unlock;\n \n \t/* neigh is obsolete if it was idle for two GC periods */\n-\tdt = 2 * arp_tbl.gc_interval;\n+\tdt = 2 * gc_interval;\n \tneigh_obsolete = jiffies - dt;\n \n \tfor (i = 0; i \u003c htbl-\u003esize; i++) {\n@@ -1433,11 +1434,16 @@ static void ipoib_reap_neigh(struct work_struct *work)\n {\n \tstruct ipoib_dev_priv *priv =\n \t\tcontainer_of(work, struct ipoib_dev_priv, neigh_reap_task.work);\n+\tstruct net_device *dev = priv-\u003edev;\n+\tstruct net *net = dev_net(dev);\n+\tstruct neigh_table *tbl;\n+\tint gc_interval;\n \n-\t__ipoib_reap_neigh(priv);\n+\ttbl = arp_table(net);\n+\tgc_interval = tbl-\u003egc_interval;\n+\t__ipoib_reap_neigh(priv, gc_interval);\n \n-\tqueue_delayed_work(priv-\u003ewq, \u0026priv-\u003eneigh_reap_task,\n-\t\t\t arp_tbl.gc_interval);\n+\tqueue_delayed_work(priv-\u003ewq, \u0026priv-\u003eneigh_reap_task, gc_interval);\n }\n \n \n@@ -1590,8 +1596,11 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)\n static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)\n {\n \tstruct ipoib_neigh_table *ntbl = \u0026priv-\u003entbl;\n-\tstruct ipoib_neigh_hash *htbl;\n \tstruct ipoib_neigh __rcu **buckets;\n+\tstruct net_device *dev = priv-\u003edev;\n+\tstruct net *net = dev_net(dev);\n+\tstruct ipoib_neigh_hash *htbl;\n+\tstruct neigh_table *tbl;\n \tu32 size;\n \n \tclear_bit(IPOIB_NEIGH_TBL_FLUSH, \u0026priv-\u003eflags);\n@@ -1599,7 +1608,9 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)\n \thtbl = kzalloc_obj(*htbl);\n \tif (!htbl)\n \t\treturn -ENOMEM;\n-\tsize = roundup_pow_of_two(arp_tbl.gc_thresh3);\n+\n+\ttbl = arp_table(net);\n+\tsize = roundup_pow_of_two(tbl-\u003egc_thresh3);\n \tbuckets = kvzalloc_objs(*buckets, size);\n \tif (!buckets) {\n \t\tkfree(htbl);\n@@ -1614,7 +1625,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)\n \n \t/* start garbage collection */\n \tqueue_delayed_work(priv-\u003ewq, \u0026priv-\u003eneigh_reap_task,\n-\t\t\t arp_tbl.gc_interval);\n+\t\t\t tbl-\u003egc_interval);\n \n \treturn 0;\n }\ndiff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c\nindex 0c4f462baa6ef..ba45b61b09bb0 100644\n--- a/drivers/net/ethernet/marvell/prestera/prestera_router.c\n+++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c\n@@ -683,7 +683,7 @@ __prestera_k_arb_n_offload_set(struct prestera_switch *sw,\n {\n \tstruct neighbour *n;\n \n-\tn = neigh_lookup(\u0026arp_tbl, \u0026nc-\u003ekey.addr.u.ipv4,\n+\tn = neigh_lookup(arp_table(\u0026init_net), \u0026nc-\u003ekey.addr.u.ipv4,\n \t\t\t nc-\u003ekey.dev);\n \tif (!n)\n \t\treturn;\n@@ -790,7 +790,7 @@ __prestera_k_arb_nc_kern_n_fetch(struct prestera_switch *sw,\n \tint err;\n \n \tmemset(\u0026nc-\u003enh_neigh_info, 0, sizeof(nc-\u003enh_neigh_info));\n-\tn = neigh_lookup(\u0026arp_tbl, \u0026nc-\u003ekey.addr.u.ipv4, nc-\u003ekey.dev);\n+\tn = neigh_lookup(arp_table(\u0026init_net), \u0026nc-\u003ekey.addr.u.ipv4, nc-\u003ekey.dev);\n \tif (!n)\n \t\tgoto out;\n \n@@ -1052,10 +1052,12 @@ static void __prestera_k_arb_hw_state_upd(struct prestera_switch *sw,\n #endif /* PRESTERA_IMPLICITY_RESOLVE_DEAD_NEIGH */\n \n \tif (nc-\u003ekey.addr.v == PRESTERA_IPV4) {\n-\t\tn = neigh_lookup(\u0026arp_tbl, \u0026nc-\u003ekey.addr.u.ipv4,\n+\t\tstruct neigh_table *tbl = arp_table(\u0026init_net);\n+\n+\t\tn = neigh_lookup(tbl, \u0026nc-\u003ekey.addr.u.ipv4,\n \t\t\t\t nc-\u003ekey.dev);\n \t\tif (!n)\n-\t\t\tn = neigh_create(\u0026arp_tbl, \u0026nc-\u003ekey.addr.u.ipv4,\n+\t\t\tn = neigh_create(tbl, \u0026nc-\u003ekey.addr.u.ipv4,\n \t\t\t\t\t nc-\u003ekey.dev);\n \t} else {\n \t\tn = NULL;\ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c\nindex 648f4521c0964..6a8d8aac7d16f 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/neigh.c\n@@ -17,20 +17,27 @@\n #include \"fs_core.h\"\n #include \"diag/en_rep_tracepoint.h\"\n \n-static unsigned long mlx5e_rep_ipv6_interval(void)\n+static unsigned long mlx5e_rep_ipv6_interval(struct net *net)\n {\n \tif (IS_ENABLED(CONFIG_IPV6) \u0026\u0026 ipv6_mod_enabled())\n-\t\treturn NEIGH_VAR(\u0026nd_tbl.parms, DELAY_PROBE_TIME);\n+\t\treturn NEIGH_VAR(\u0026nd_table(net)-\u003eparms, DELAY_PROBE_TIME);\n \n \treturn ~0UL;\n }\n \n static void mlx5e_rep_neigh_update_init_interval(struct mlx5e_rep_priv *rpriv)\n {\n-\tunsigned long ipv4_interval = NEIGH_VAR(\u0026arp_tbl.parms, DELAY_PROBE_TIME);\n-\tunsigned long ipv6_interval = mlx5e_rep_ipv6_interval();\n \tstruct net_device *netdev = rpriv-\u003enetdev;\n-\tstruct mlx5e_priv *priv = netdev_priv(netdev);\n+\tstruct net *net = dev_net(netdev);\n+\tunsigned long ipv4_interval;\n+\tunsigned long ipv6_interval;\n+\tstruct neigh_table *tbl;\n+\tstruct mlx5e_priv *priv;\n+\n+\tpriv = netdev_priv(netdev);\n+\ttbl = arp_table(net);\n+\tipv4_interval = NEIGH_VAR(\u0026tbl-\u003eparms, DELAY_PROBE_TIME);\n+\tipv6_interval = mlx5e_rep_ipv6_interval(net);\n \n \trpriv-\u003eneigh_update.min_interval = min_t(unsigned long, ipv6_interval, ipv4_interval);\n \tmlx5_fc_update_sampling_interval(priv-\u003emdev, rpriv-\u003eneigh_update.min_interval);\n@@ -217,12 +224,6 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,\n \tswitch (event) {\n \tcase NETEVENT_NEIGH_UPDATE:\n \t\tn = ptr;\n-#if IS_ENABLED(CONFIG_IPV6)\n-\t\tif (n-\u003etbl != \u0026nd_tbl \u0026\u0026 n-\u003etbl != \u0026arp_tbl)\n-#else\n-\t\tif (n-\u003etbl != \u0026arp_tbl)\n-#endif\n-\t\t\treturn NOTIFY_DONE;\n \n \t\tupdate_work = mlx5e_alloc_neigh_update_work(priv, n);\n \t\tif (!update_work)\n@@ -238,11 +239,7 @@ static int mlx5e_rep_netevent_event(struct notifier_block *nb,\n \t\t * changes in the default table, we only care about changes\n \t\t * done per device delay prob time parameter.\n \t\t */\n-#if IS_ENABLED(CONFIG_IPV6)\n-\t\tif (!p-\u003edev || (p-\u003etbl != \u0026nd_tbl \u0026\u0026 p-\u003etbl != \u0026arp_tbl))\n-#else\n-\t\tif (!p-\u003edev || p-\u003etbl != \u0026arp_tbl)\n-#endif\n+\t\tif (!p-\u003edev)\n \t\t\treturn NOTIFY_DONE;\n \n \t\trcu_read_lock();\ndiff --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\nindex 8b827201935eb..67c12ca19d59d 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c\n@@ -393,20 +393,10 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)\n \tstruct mlx5e_encap_entry *e = NULL;\n \tstruct mlx5e_tc_flow *flow;\n \tstruct mlx5_fc *counter;\n-\tstruct neigh_table *tbl;\n \tbool neigh_used = false;\n \tstruct neighbour *n;\n \tu64 lastuse;\n \n-\tif (m_neigh-\u003efamily == AF_INET)\n-\t\ttbl = \u0026arp_tbl;\n-#if IS_ENABLED(CONFIG_IPV6)\n-\telse if (m_neigh-\u003efamily == AF_INET6)\n-\t\ttbl = \u0026nd_tbl;\n-#endif\n-\telse\n-\t\treturn;\n-\n \t/* mlx5e_get_next_valid_encap() releases previous encap before returning\n \t * next one.\n \t */\n@@ -447,12 +437,23 @@ void mlx5e_tc_update_neigh_used_value(struct mlx5e_neigh_hash_entry *nhe)\n \ttrace_mlx5e_tc_update_neigh_used_value(nhe, neigh_used);\n \n \tif (neigh_used) {\n+\t\tstruct net_device *dev = READ_ONCE(nhe-\u003eneigh_dev);\n+\t\tstruct net *net = dev_net(dev);\n+\t\tstruct neigh_table *tbl;\n+\n \t\tnhe-\u003ereported_lastuse = jiffies;\n \n+#if IS_ENABLED(CONFIG_IPV6)\n+\t\tif (m_neigh-\u003efamily != AF_INET)\n+\t\t\ttbl = nd_table(net);\n+\t\telse\n+#endif\n+\t\t\ttbl = arp_table(net);\n+\n \t\t/* find the relevant neigh according to the cached device and\n \t\t * dst ip pair\n \t\t */\n-\t\tn = neigh_lookup(tbl, \u0026m_neigh-\u003edst_ip, READ_ONCE(nhe-\u003eneigh_dev));\n+\t\tn = neigh_lookup(tbl, \u0026m_neigh-\u003edst_ip, dev);\n \t\tif (!n)\n \t\t\treturn;\n \ndiff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c\nindex db260e3d1412f..37a8ddee3ea1e 100644\n--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c\n+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c\n@@ -262,6 +262,7 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,\n \tstruct net_device *netdev = sa_entry-\u003edev;\n \tstruct xfrm_state *x = sa_entry-\u003ex;\n \tstruct dst_entry *rt_dst_entry;\n+\tstruct neigh_table *tbl;\n \tstruct flowi4 fl4 = {};\n \tstruct flowi6 fl6 = {};\n \tstruct neighbour *n;\n@@ -364,9 +365,10 @@ static void mlx5e_ipsec_init_macs(struct mlx5e_ipsec_sa_entry *sa_entry,\n \treturn;\n \n neigh:\n-\tn = neigh_lookup(\u0026arp_tbl, pkey, netdev);\n+\ttbl = arp_table(dev_net(netdev));\n+\tn = neigh_lookup(tbl, pkey, netdev);\n \tif (!n) {\n-\t\tn = neigh_create(\u0026arp_tbl, pkey, netdev);\n+\t\tn = neigh_create(tbl, pkey, netdev);\n \t\tif (IS_ERR(n))\n \t\t\treturn;\n \t\tneigh_event_send(n, NULL);\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c\nindex 3d6fdbab05e01..f4a435885ba43 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c\n@@ -2397,14 +2397,15 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n)\n static void\n mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp)\n {\n+\tstruct net *net = mlxsw_sp_net(mlxsw_sp);\n \tunsigned long interval;\n \n #if IS_ENABLED(CONFIG_IPV6)\n \tinterval = min_t(unsigned long,\n-\t\t\t NEIGH_VAR(\u0026arp_tbl.parms, DELAY_PROBE_TIME),\n-\t\t\t NEIGH_VAR(\u0026nd_tbl.parms, DELAY_PROBE_TIME));\n+\t\t\t NEIGH_VAR(\u0026arp_table(net)-\u003eparms, DELAY_PROBE_TIME),\n+\t\t\t NEIGH_VAR(\u0026nd_table(net)-\u003eparms, DELAY_PROBE_TIME));\n #else\n-\tinterval = NEIGH_VAR(\u0026arp_tbl.parms, DELAY_PROBE_TIME);\n+\tinterval = NEIGH_VAR(\u0026arp_table(net)-\u003eparms, DELAY_PROBE_TIME);\n #endif\n \tmlxsw_sp-\u003erouter-\u003eneighs_update.interval = jiffies_to_msecs(interval);\n }\n@@ -2414,6 +2415,8 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t\t\t int ent_index)\n {\n \tu64 max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp-\u003ecore, MAX_RIFS);\n+\tstruct net *net = mlxsw_sp_net(mlxsw_sp);\n+\tstruct neigh_table *tbl;\n \tstruct net_device *dev;\n \tstruct neighbour *n;\n \t__be32 dipn;\n@@ -2429,9 +2432,10 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp,\n \t\treturn;\n \t}\n \n+\ttbl = arp_table(net);\n \tdipn = htonl(dip);\n \tdev = mlxsw_sp_rif_dev(mlxsw_sp-\u003erouter-\u003erifs[rif]);\n-\tn = neigh_lookup(\u0026arp_tbl, \u0026dipn, dev);\n+\tn = neigh_lookup(tbl, \u0026dipn, dev);\n \tif (!n)\n \t\treturn;\n \n@@ -2445,6 +2449,8 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,\n \t\t\t\t\t\t char *rauhtd_pl,\n \t\t\t\t\t\t int rec_index)\n {\n+\tstruct net *net = mlxsw_sp_net(mlxsw_sp);\n+\tstruct neigh_table *tbl;\n \tstruct net_device *dev;\n \tstruct neighbour *n;\n \tstruct in6_addr dip;\n@@ -2458,8 +2464,9 @@ static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp,\n \t\treturn;\n \t}\n \n+\ttbl = nd_table(net);\n \tdev = mlxsw_sp_rif_dev(mlxsw_sp-\u003erouter-\u003erifs[rif]);\n-\tn = neigh_lookup(\u0026nd_tbl, \u0026dip, dev);\n+\tn = neigh_lookup(tbl, \u0026dip, dev);\n \tif (!n)\n \t\treturn;\n \n@@ -3014,16 +3021,17 @@ static int mlxsw_sp_neigh_rif_made_sync(struct mlxsw_sp *mlxsw_sp,\n \t\t.mlxsw_sp = mlxsw_sp,\n \t\t.rif = rif,\n \t};\n+\tstruct net *net = mlxsw_sp_net(mlxsw_sp);\n \n \tif (!mlxsw_sp_dev_lower_is_port(mlxsw_sp_rif_dev(rif)))\n \t\treturn 0;\n \n-\tneigh_for_each(\u0026arp_tbl, mlxsw_sp_neigh_rif_made_sync_each, \u0026rms);\n+\tneigh_for_each(arp_table(net), mlxsw_sp_neigh_rif_made_sync_each, \u0026rms);\n \tif (rms.err)\n \t\tgoto err_arp;\n \n #if IS_ENABLED(CONFIG_IPV6)\n-\tneigh_for_each(\u0026nd_tbl, mlxsw_sp_neigh_rif_made_sync_each, \u0026rms);\n+\tneigh_for_each(nd_table(net), mlxsw_sp_neigh_rif_made_sync_each, \u0026rms);\n #endif\n \tif (rms.err)\n \t\tgoto err_nd;\n@@ -4622,7 +4630,7 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp,\n \tnh-\u003enh_weight = 1;\n #endif\n \tmemcpy(\u0026nh-\u003egw_addr, \u0026fib_nh-\u003efib_nh_gw4, sizeof(fib_nh-\u003efib_nh_gw4));\n-\tnh-\u003eneigh_tbl = \u0026arp_tbl;\n+\tnh-\u003eneigh_tbl = arp_table(mlxsw_sp_net(mlxsw_sp));\n \terr = mlxsw_sp_nexthop_insert(mlxsw_sp, nh);\n \tif (err)\n \t\treturn err;\n@@ -5112,6 +5120,7 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,\n \t\t\t struct nh_notifier_single_info *nh_obj, int weight)\n {\n \tstruct net_device *dev = nh_obj-\u003edev;\n+\tstruct net *net = dev_net(dev);\n \tint err;\n \n \tnh-\u003enhgi = nh_grp-\u003enhgi;\n@@ -5120,12 +5129,12 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp,\n \tswitch (nh_obj-\u003egw_family) {\n \tcase AF_INET:\n \t\tmemcpy(\u0026nh-\u003egw_addr, \u0026nh_obj-\u003eipv4, sizeof(nh_obj-\u003eipv4));\n-\t\tnh-\u003eneigh_tbl = \u0026arp_tbl;\n+\t\tnh-\u003eneigh_tbl = arp_table(net);\n \t\tbreak;\n \tcase AF_INET6:\n \t\tmemcpy(\u0026nh-\u003egw_addr, \u0026nh_obj-\u003eipv6, sizeof(nh_obj-\u003eipv6));\n #if IS_ENABLED(CONFIG_IPV6)\n-\t\tnh-\u003eneigh_tbl = \u0026nd_tbl;\n+\t\tnh-\u003eneigh_tbl = nd_table(net);\n #endif\n \t\tbreak;\n \t}\n@@ -6981,7 +6990,7 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp,\n \tnh-\u003enh_weight = rt-\u003efib6_nh-\u003efib_nh_weight;\n \tmemcpy(\u0026nh-\u003egw_addr, \u0026rt-\u003efib6_nh-\u003efib_nh_gw6, sizeof(nh-\u003egw_addr));\n #if IS_ENABLED(CONFIG_IPV6)\n-\tnh-\u003eneigh_tbl = \u0026nd_tbl;\n+\tnh-\u003eneigh_tbl = nd_table(mlxsw_sp_net(mlxsw_sp));\n #endif\n \n \terr = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh);\ndiff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c\nindex ae63d549b5429..1cd8347c274d4 100644\n--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c\n+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c\n@@ -456,6 +456,7 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,\n \tbool inherit_tos = tparm.iph.tos \u0026 0x1;\n \tbool inherit_ttl = !tparm.iph.ttl;\n \tunion mlxsw_sp_l3addr gw = daddr;\n+\tstruct neigh_table *tbl = NULL;\n \tstruct net_device *l3edev;\n \n \tif (!(to_dev-\u003eflags \u0026 IFF_UP) ||\n@@ -469,9 +470,11 @@ mlxsw_sp_span_entry_gretap4_parms(struct mlxsw_sp *mlxsw_sp,\n \t\treturn mlxsw_sp_span_entry_unoffloadable(sparmsp);\n \n \tl3edev = mlxsw_sp_span_gretap4_route(to_dev, \u0026saddr.addr4, \u0026gw.addr4);\n+\tif (l3edev)\n+\t\ttbl = arp_table(dev_net(l3edev));\n \treturn mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,\n \t\t\t\t\t\t tparm.iph.ttl,\n-\t\t\t\t\t\t \u0026arp_tbl, sparmsp);\n+\t\t\t\t\t\t tbl, sparmsp);\n }\n \n static int\n@@ -561,6 +564,7 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,\n \tunion mlxsw_sp_l3addr daddr = { .addr6 = tparm.raddr };\n \tbool inherit_ttl = !tparm.hop_limit;\n \tunion mlxsw_sp_l3addr gw = daddr;\n+\tstruct neigh_table *tbl = NULL;\n \tstruct net_device *l3edev;\n \n \tif (!(to_dev-\u003eflags \u0026 IFF_UP) ||\n@@ -574,9 +578,11 @@ mlxsw_sp_span_entry_gretap6_parms(struct mlxsw_sp *mlxsw_sp,\n \t\treturn mlxsw_sp_span_entry_unoffloadable(sparmsp);\n \n \tl3edev = mlxsw_sp_span_gretap6_route(to_dev, \u0026saddr.addr6, \u0026gw.addr6);\n+\tif (l3edev)\n+\t\ttbl = nd_table(dev_net(l3edev));\n \treturn mlxsw_sp_span_entry_tunnel_parms_common(l3edev, saddr, daddr, gw,\n \t\t\t\t\t\t tparm.hop_limit,\n-\t\t\t\t\t\t \u0026nd_tbl, sparmsp);\n+\t\t\t\t\t\t tbl, sparmsp);\n }\n \n static int\ndiff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c\nindex ca30702f88780..d650d33e3709c 100644\n--- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c\n+++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c\n@@ -209,6 +209,7 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)\n {\n \tstruct nfp_tun_active_tuns *payload;\n \tstruct net_device *netdev;\n+\tstruct neigh_table *tbl;\n \tint count, i, pay_len;\n \tstruct neighbour *n;\n \t__be32 ipv4_addr;\n@@ -235,7 +236,8 @@ void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)\n \t\tif (!netdev)\n \t\t\tcontinue;\n \n-\t\tn = neigh_lookup(\u0026arp_tbl, \u0026ipv4_addr, netdev);\n+\t\ttbl = arp_table(dev_net(netdev));\n+\t\tn = neigh_lookup(tbl, \u0026ipv4_addr, netdev);\n \t\tif (!n)\n \t\t\tcontinue;\n \n@@ -251,6 +253,7 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)\n #if IS_ENABLED(CONFIG_IPV6)\n \tstruct nfp_tun_active_tuns_v6 *payload;\n \tstruct net_device *netdev;\n+\tstruct neigh_table *tbl;\n \tint count, i, pay_len;\n \tstruct neighbour *n;\n \tvoid *ipv6_add;\n@@ -277,7 +280,8 @@ void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)\n \t\tif (!netdev)\n \t\t\tcontinue;\n \n-\t\tn = neigh_lookup(\u0026nd_tbl, ipv6_add, netdev);\n+\t\ttbl = nd_table(dev_net(netdev));\n+\t\tn = neigh_lookup(tbl, ipv6_add, netdev);\n \t\tif (!n)\n \t\t\tcontinue;\n \n@@ -729,12 +733,6 @@ nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,\n \tdefault:\n \t\treturn NOTIFY_DONE;\n \t}\n-#if IS_ENABLED(CONFIG_IPV6)\n-\tif (n-\u003etbl != \u0026nd_tbl \u0026\u0026 n-\u003etbl != \u0026arp_tbl)\n-#else\n-\tif (n-\u003etbl != \u0026arp_tbl)\n-#endif\n-\t\treturn NOTIFY_DONE;\n \n \tapp_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb);\n \tapp = app_priv-\u003eapp;\ndiff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c\nindex 84a55f2b48ffe..0fcd65fc0af7f 100644\n--- a/drivers/net/ethernet/rocker/rocker_main.c\n+++ b/drivers/net/ethernet/rocker/rocker_main.c\n@@ -3134,7 +3134,7 @@ static int rocker_netevent_event(struct notifier_block *unused,\n \n \tswitch (event) {\n \tcase NETEVENT_NEIGH_UPDATE:\n-\t\tif (n-\u003etbl != \u0026arp_tbl)\n+\t\tif (n-\u003etbl != arp_table(\u0026init_net))\n \t\t\treturn NOTIFY_DONE;\n \t\tdev = n-\u003edev;\n \t\tif (!rocker_port_dev_check(dev))\ndiff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c\nindex 15d19a8a17101..ead8b447b89c4 100644\n--- a/drivers/net/ethernet/rocker/rocker_ofdpa.c\n+++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c\n@@ -1336,7 +1336,7 @@ static int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port,\n \tint err = 0;\n \n \tif (!n) {\n-\t\tn = neigh_create(\u0026arp_tbl, \u0026ip_addr, dev);\n+\t\tn = neigh_create(arp_table(\u0026init_net), \u0026ip_addr, dev);\n \t\tif (IS_ERR(n))\n \t\t\treturn PTR_ERR(n);\n \t}\ndiff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c\nindex b84235e93ffe9..793a562fc1f7c 100644\n--- a/drivers/net/ethernet/sfc/tc_counters.c\n+++ b/drivers/net/ethernet/sfc/tc_counters.c\n@@ -91,6 +91,7 @@ static void efx_tc_counter_work(struct work_struct *work)\n \tstruct efx_tc_action_set *act;\n \tunsigned long touched;\n \tstruct neighbour *n;\n+\tstruct net *net;\n \n \tspin_lock_bh(\u0026cnt-\u003elock);\n \ttouched = READ_ONCE(cnt-\u003etouched);\n@@ -103,16 +104,19 @@ static void efx_tc_counter_work(struct work_struct *work)\n \t\t\tcontinue;\n \t\tif (time_after_eq(encap-\u003eneigh-\u003eused, touched))\n \t\t\tcontinue;\n+\n \t\tencap-\u003eneigh-\u003eused = touched;\n+\t\tnet = encap-\u003eneigh-\u003enet;\n+\n \t\t/* We have passed traffic using this ARP entry, so\n \t\t * indicate to the ARP cache that it's still active\n \t\t */\n \t\tif (encap-\u003eneigh-\u003edst_ip)\n-\t\t\tn = neigh_lookup(\u0026arp_tbl, \u0026encap-\u003eneigh-\u003edst_ip,\n+\t\t\tn = neigh_lookup(arp_table(net), \u0026encap-\u003eneigh-\u003edst_ip,\n \t\t\t\t\t encap-\u003eneigh-\u003eegdev);\n \t\telse\n #if IS_ENABLED(CONFIG_IPV6)\n-\t\t\tn = neigh_lookup(\u0026nd_tbl,\n+\t\t\tn = neigh_lookup(nd_table(net),\n \t\t\t\t\t \u0026encap-\u003eneigh-\u003edst_ip6,\n \t\t\t\t\t encap-\u003eneigh-\u003eegdev);\n #else\ndiff --git a/drivers/net/ethernet/sfc/tc_encap_actions.c b/drivers/net/ethernet/sfc/tc_encap_actions.c\nindex c2ad3a358d201..152c4639ecc6f 100644\n--- a/drivers/net/ethernet/sfc/tc_encap_actions.c\n+++ b/drivers/net/ethernet/sfc/tc_encap_actions.c\n@@ -527,10 +527,10 @@ static int efx_neigh_event(struct efx_nic *efx, struct neighbour *n)\n \tif (WARN_ON(!efx-\u003etc))\n \t\treturn NOTIFY_DONE;\n \n-\tif (n-\u003etbl == \u0026arp_tbl) {\n+\tif (n-\u003etbl-\u003efamily == AF_INET) {\n \t\tkeysize = sizeof(keys.dst_ip);\n #if IS_ENABLED(CONFIG_IPV6)\n-\t} else if (n-\u003etbl == \u0026nd_tbl) {\n+\t} else if (n-\u003etbl-\u003efamily == AF_INET6) {\n \t\tipv6 = true;\n \t\tkeysize = sizeof(keys.dst_ip6);\n #endif\ndiff --git a/drivers/net/vrf.c b/drivers/net/vrf.c\nindex a0557a3a70260..50ec04cc0213f 100644\n--- a/drivers/net/vrf.c\n+++ b/drivers/net/vrf.c\n@@ -616,7 +616,7 @@ static int vrf_finish_output6(struct net *net, struct sock *sk,\n \tnexthop = rt6_nexthop(dst_rt6_info(dst), \u0026ipv6_hdr(skb)-\u003edaddr);\n \tneigh = __ipv6_neigh_lookup_noref(dst-\u003edev, nexthop);\n \tif (unlikely(!neigh))\n-\t\tneigh = __neigh_create(\u0026nd_tbl, nexthop, dst-\u003edev, false);\n+\t\tneigh = __neigh_create(nd_table(net), nexthop, dst-\u003edev, false);\n \tif (!IS_ERR(neigh)) {\n \t\tsock_confirm_neigh(skb, neigh);\n \t\tret = neigh_output(neigh, skb, false);\ndiff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c\nindex fd5505153d0b6..345d1883d993e 100644\n--- a/drivers/net/vxlan/vxlan_core.c\n+++ b/drivers/net/vxlan/vxlan_core.c\n@@ -1841,11 +1841,12 @@ static int vxlan_err_lookup(struct sock *sk, struct sk_buff *skb)\n \n static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n {\n+\tstruct neigh_table *tbl = arp_table(dev_net(dev));\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tstruct neighbour *n;\n \tstruct arphdr *parp;\n \tu8 *arpptr, *sha;\n \t__be32 sip, tip;\n-\tstruct neighbour *n;\n \n \tif (dev-\u003eflags \u0026 IFF_NOARP)\n \t\tgoto out;\n@@ -1877,7 +1878,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t ipv4_is_multicast(tip))\n \t\tgoto out;\n \n-\tn = neigh_lookup(\u0026arp_tbl, \u0026tip, dev);\n+\tn = neigh_lookup(tbl, \u0026tip, dev);\n \n \tif (n) {\n \t\tstruct vxlan_rdst *rdst = NULL;\n@@ -2047,7 +2048,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n \t ipv6_addr_is_multicast(\u0026msg-\u003etarget))\n \t\tgoto out;\n \n-\tn = neigh_lookup(\u0026nd_tbl, \u0026msg-\u003etarget, dev);\n+\tn = neigh_lookup(nd_table(dev_net(dev)), \u0026msg-\u003etarget, dev);\n \n \tif (n) {\n \t\tstruct vxlan_rdst *rdst = NULL;\n@@ -2100,6 +2101,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)\n static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n {\n \tstruct vxlan_dev *vxlan = netdev_priv(dev);\n+\tstruct neigh_table *tbl;\n \tstruct neighbour *n;\n \n \tif (is_multicast_ether_addr(eth_hdr(skb)-\u003eh_dest))\n@@ -2113,8 +2115,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \n \t\tif (!pskb_network_may_pull(skb, sizeof(struct iphdr)))\n \t\t\treturn false;\n+\n+\t\ttbl = arp_table(dev_net(dev));\n \t\tpip = ip_hdr(skb);\n-\t\tn = neigh_lookup(\u0026arp_tbl, \u0026pip-\u003edaddr, dev);\n+\t\tn = neigh_lookup(tbl, \u0026pip-\u003edaddr, dev);\n \t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin.sin_addr.s_addr = pip-\u003edaddr,\n@@ -2139,8 +2143,10 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)\n \t\t\treturn false;\n \t\tif (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))\n \t\t\treturn false;\n+\n+\t\ttbl = nd_table(dev_net(dev));\n \t\tpip6 = ipv6_hdr(skb);\n-\t\tn = neigh_lookup(\u0026nd_tbl, \u0026pip6-\u003edaddr, dev);\n+\t\tn = neigh_lookup(tbl, \u0026pip6-\u003edaddr, dev);\n \t\tif (!n \u0026\u0026 (vxlan-\u003ecfg.flags \u0026 VXLAN_F_L3MISS)) {\n \t\t\tunion vxlan_addr ipa = {\n \t\t\t\t.sin6.sin6_addr = pip6-\u003edaddr,\ndiff --git a/include/net/arp.h b/include/net/arp.h\nindex e8747e0713c79..e932def63d62f 100644\n--- a/include/net/arp.h\n+++ b/include/net/arp.h\n@@ -7,8 +7,10 @@\n #include \u003clinux/hash.h\u003e\n #include \u003cnet/neighbour.h\u003e\n \n-\n-extern struct neigh_table arp_tbl;\n+static inline struct neigh_table *arp_table(struct net *net)\n+{\n+\treturn net-\u003eneigh_tables[NEIGH_ARP_TABLE];\n+}\n \n static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 *hash_rnd)\n {\n@@ -21,10 +23,12 @@ static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32\n #ifdef CONFIG_INET\n static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)\n {\n+\tstruct neigh_table *tbl = arp_table(dev_net(dev));\n+\n \tif (dev-\u003eflags \u0026 (IFF_LOOPBACK | IFF_POINTOPOINT))\n \t\tkey = INADDR_ANY;\n \n-\treturn ___neigh_lookup_noref(\u0026arp_tbl, neigh_key_eq32, arp_hashfn, \u0026key, dev);\n+\treturn ___neigh_lookup_noref(tbl, neigh_key_eq32, arp_hashfn, \u0026key, dev);\n }\n #else\n static inline\ndiff --git a/include/net/ndisc.h b/include/net/ndisc.h\nindex 3da1a6f8d3f92..5b7a36cbda169 100644\n--- a/include/net/ndisc.h\n+++ b/include/net/ndisc.h\n@@ -67,6 +67,15 @@ struct prefix_info;\n \n extern struct neigh_table nd_tbl;\n \n+static inline struct neigh_table *nd_table(struct net *net)\n+{\n+#if IS_ENABLED(CONFIG_IPV6)\n+\tif (disable_ipv6_mod)\n+\t\treturn \u0026nd_tbl;\n+#endif\n+\treturn net-\u003eneigh_tables[NEIGH_ND_TABLE];\n+}\n+\n struct nd_msg {\n struct icmp6hdr\ticmph;\n struct in6_addr\ttarget;\n@@ -354,7 +363,9 @@ static inline u32 ndisc_hashfn(const void *pkey, const struct net_device *dev, _\n \n static inline struct neighbour *__ipv6_neigh_lookup_noref(struct net_device *dev, const void *pkey)\n {\n-\treturn ___neigh_lookup_noref(\u0026nd_tbl, neigh_key_eq128, ndisc_hashfn, pkey, dev);\n+\tstruct neigh_table *tbl = nd_table(dev_net(dev));\n+\n+\treturn ___neigh_lookup_noref(tbl, neigh_key_eq128, ndisc_hashfn, pkey, dev);\n }\n \n static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, const void *pkey)\n@@ -388,8 +399,11 @@ static inline struct neighbour *ip_neigh_gw6(struct net_device *dev,\n \tstruct neighbour *neigh;\n \n \tneigh = __ipv6_neigh_lookup_noref(dev, addr);\n-\tif (unlikely(!neigh))\n-\t\tneigh = __neigh_create(\u0026nd_tbl, addr, dev, false);\n+\tif (unlikely(!neigh)) {\n+\t\tstruct neigh_table *tbl = nd_table(dev_net(dev));\n+\n+\t\tneigh = __neigh_create(tbl, addr, dev, false);\n+\t}\n \n \treturn neigh;\n #else\ndiff --git a/include/net/neighbour.h b/include/net/neighbour.h\nindex 8860cc2175fc1..954c13b3a2b4f 100644\n--- a/include/net/neighbour.h\n+++ b/include/net/neighbour.h\n@@ -179,7 +179,6 @@ struct neigh_ops {\n \n struct pneigh_entry {\n \tstruct pneigh_entry\t__rcu *next;\n-\tpossible_net_t\t\tnet;\n \tstruct net_device\t*dev;\n \tnetdevice_tracker\tdev_tracker;\n \tunion {\n@@ -339,8 +338,8 @@ static inline void neigh_confirm(struct neighbour *n)\n \t}\n }\n \n-void neigh_table_init(int index, struct neigh_table *tbl);\n-int neigh_table_clear(int index, struct neigh_table *tbl);\n+int neigh_table_register(struct net *net, struct neigh_table *tbl, int index);\n+void neigh_table_unregister(struct net *net, int index);\n struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,\n \t\t\t struct net_device *dev);\n struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,\n@@ -389,24 +388,17 @@ static inline void neigh_set_reach_time(struct neigh_parms *p)\n \n void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,\n \t\t struct sk_buff *skb);\n-struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl, struct net *net,\n+struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\n \t\t\t\t const void *key, struct net_device *dev);\n-int pneigh_create(struct neigh_table *tbl, struct net *net, const void *key,\n+int pneigh_create(struct neigh_table *tbl, const void *key,\n \t\t struct net_device *dev, u32 flags, u8 protocol,\n \t\t bool permanent);\n-int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *key,\n+int pneigh_delete(struct neigh_table *tbl, const void *key,\n \t\t struct net_device *dev);\n \n-static inline struct net *pneigh_net(const struct pneigh_entry *pneigh)\n-{\n-\treturn read_pnet(\u0026pneigh-\u003enet);\n-}\n-\n void neigh_app_ns(struct neighbour *n);\n void neigh_for_each(struct neigh_table *tbl,\n \t\t void (*cb)(struct neighbour *, void *), void *cookie);\n-void __neigh_for_each_release(struct neigh_table *tbl,\n-\t\t\t int (*cb)(struct neighbour *));\n int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *);\n \n struct neigh_seq_state {\ndiff --git a/include/net/net_namespace.h b/include/net/net_namespace.h\nindex 501af1999fe83..f96a390ae5364 100644\n--- a/include/net/net_namespace.h\n+++ b/include/net/net_namespace.h\n@@ -39,6 +39,7 @@\n #include \u003cnet/netns/mctp.h\u003e\n #include \u003cnet/netns/vsock.h\u003e\n #include \u003cnet/net_trackers.h\u003e\n+#include \u003cnet/neighbour_tables.h\u003e\n #include \u003clinux/ns_common.h\u003e\n #include \u003clinux/idr.h\u003e\n #include \u003clinux/skbuff.h\u003e\n@@ -47,6 +48,7 @@\n \n struct user_namespace;\n struct proc_dir_entry;\n+struct neigh_table;\n struct net_device;\n struct sock;\n struct ctl_table_header;\n@@ -103,6 +105,8 @@ struct net {\n \tstruct proc_dir_entry \t*proc_net;\n \tstruct proc_dir_entry \t*proc_net_stat;\n \n+\tstruct neigh_table\t*neigh_tables[NEIGH_NR_TABLES];\n+\n #ifdef CONFIG_SYSCTL\n \tstruct ctl_table_set\tsysctls;\n #endif\ndiff --git a/include/net/route.h b/include/net/route.h\nindex f90106f383c56..38a23f28ee14a 100644\n--- a/include/net/route.h\n+++ b/include/net/route.h\n@@ -403,8 +403,11 @@ static inline struct neighbour *ip_neigh_gw4(struct net_device *dev,\n \tstruct neighbour *neigh;\n \n \tneigh = __ipv4_neigh_lookup_noref(dev, (__force u32)daddr);\n-\tif (unlikely(!neigh))\n-\t\tneigh = __neigh_create(\u0026arp_tbl, \u0026daddr, dev, false);\n+\tif (unlikely(!neigh)) {\n+\t\tstruct neigh_table *tbl = arp_table(dev_net(dev));\n+\n+\t\tneigh = __neigh_create(tbl, \u0026daddr, dev, false);\n+\t}\n \n \treturn neigh;\n }\ndiff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c\nindex 23eb6931a2b4a..3b4cd709e70ea 100644\n--- a/net/bridge/br_arp_nd_proxy.c\n+++ b/net/bridge/br_arp_nd_proxy.c\n@@ -192,7 +192,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,\n \t\treturn;\n \t}\n \n-\tn = neigh_lookup(\u0026arp_tbl, \u0026tip, vlandev);\n+\tn = neigh_lookup(arp_table(dev_net(vlandev)), \u0026tip, vlandev);\n \tif (n) {\n \t\tstruct net_bridge_fdb_entry *f;\n \n@@ -469,7 +469,7 @@ void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,\n \t\treturn;\n \t}\n \n-\tn = neigh_lookup(\u0026nd_tbl, \u0026msg-\u003etarget, vlandev);\n+\tn = neigh_lookup(nd_table(dev_net(vlandev)), \u0026msg-\u003etarget, vlandev);\n \tif (n) {\n \t\tstruct net_bridge_fdb_entry *f;\n \ndiff --git a/net/core/neighbour.c b/net/core/neighbour.c\nindex 1349c0eedb642..e22b0fe4b9521 100644\n--- a/net/core/neighbour.c\n+++ b/net/core/neighbour.c\n@@ -131,10 +131,11 @@ unsigned long neigh_rand_reach_time(unsigned long base)\n {\n \treturn base ? get_random_u32_below(base) + (base \u003e\u003e 1) : 0;\n }\n-EXPORT_SYMBOL(neigh_rand_reach_time);\n \n static void neigh_mark_dead(struct neighbour *n)\n {\n+\tlockdep_assert_held(\u0026n-\u003etbl-\u003elock);\n+\n \tn-\u003edead = 1;\n \tif (!list_empty(\u0026n-\u003egc_list)) {\n \t\tlist_del_init(\u0026n-\u003egc_list);\n@@ -148,11 +149,6 @@ static void neigh_update_gc_list(struct neighbour *n)\n {\n \tbool on_gc_list, exempt_from_gc;\n \n-\tspin_lock_bh(\u0026n-\u003etbl-\u003elock);\n-\twrite_lock(\u0026n-\u003elock);\n-\tif (n-\u003edead)\n-\t\tgoto out;\n-\n \t/* remove from the gc list if new state is permanent or if neighbor is\n \t * externally learned / validated; otherwise entry should be on the gc\n \t * list\n@@ -169,20 +165,12 @@ static void neigh_update_gc_list(struct neighbour *n)\n \t\tlist_add_tail(\u0026n-\u003egc_list, \u0026n-\u003etbl-\u003egc_list);\n \t\tatomic_inc(\u0026n-\u003etbl-\u003egc_entries);\n \t}\n-out:\n-\twrite_unlock(\u0026n-\u003elock);\n-\tspin_unlock_bh(\u0026n-\u003etbl-\u003elock);\n }\n \n static void neigh_update_managed_list(struct neighbour *n)\n {\n \tbool on_managed_list, add_to_managed;\n \n-\tspin_lock_bh(\u0026n-\u003etbl-\u003elock);\n-\twrite_lock(\u0026n-\u003elock);\n-\tif (n-\u003edead)\n-\t\tgoto out;\n-\n \tadd_to_managed = n-\u003eflags \u0026 NTF_MANAGED;\n \ton_managed_list = !list_empty(\u0026n-\u003emanaged_list);\n \n@@ -190,9 +178,6 @@ static void neigh_update_managed_list(struct neighbour *n)\n \t\tlist_del_init(\u0026n-\u003emanaged_list);\n \telse if (add_to_managed \u0026\u0026 !on_managed_list)\n \t\tlist_add_tail(\u0026n-\u003emanaged_list, \u0026n-\u003etbl-\u003emanaged_list);\n-out:\n-\twrite_unlock(\u0026n-\u003elock);\n-\tspin_unlock_bh(\u0026n-\u003etbl-\u003elock);\n }\n \n static void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,\n@@ -349,8 +334,7 @@ static void neigh_parms_qlen_dec(struct net_device *dev, int family)\n \trcu_read_unlock();\n }\n \n-static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,\n-\t\t\t int family)\n+static void pneigh_queue_purge(struct sk_buff_head *list, int family)\n {\n \tstruct sk_buff_head tmp;\n \tunsigned long flags;\n@@ -361,13 +345,11 @@ static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,\n \tskb = skb_peek(list);\n \twhile (skb != NULL) {\n \t\tstruct sk_buff *skb_next = skb_peek_next(skb, list);\n-\t\tstruct net_device *dev = skb-\u003edev;\n \n-\t\tif (net == NULL || net_eq(dev_net(dev), net)) {\n-\t\t\tneigh_parms_qlen_dec(dev, family);\n-\t\t\t__skb_unlink(skb, list);\n-\t\t\t__skb_queue_tail(\u0026tmp, skb);\n-\t\t}\n+\t\tneigh_parms_qlen_dec(skb-\u003edev, family);\n+\t\t__skb_unlink(skb, list);\n+\t\t__skb_queue_tail(\u0026tmp, skb);\n+\n \t\tskb = skb_next;\n \t}\n \tspin_unlock_irqrestore(\u0026list-\u003elock, flags);\n@@ -412,6 +394,9 @@ static void neigh_flush_one(struct neighbour *n)\n \n \twrite_unlock(\u0026n-\u003elock);\n \n+\tif (!check_net(neigh_parms_net(n-\u003eparms)))\n+\t\ttimer_shutdown_sync(\u0026n-\u003etimer);\n+\n \tneigh_cleanup_and_release(n);\n }\n \n@@ -471,8 +456,7 @@ static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,\n \tspin_unlock_bh(\u0026tbl-\u003elock);\n \n \tpneigh_ifdown(tbl, dev, skip_perm);\n-\tpneigh_queue_purge(\u0026tbl-\u003eproxy_queue, dev ? dev_net(dev) : NULL,\n-\t\t\t tbl-\u003efamily);\n+\tpneigh_queue_purge(\u0026tbl-\u003eproxy_queue, tbl-\u003efamily);\n \tif (skb_queue_empty_lockless(\u0026tbl-\u003eproxy_queue))\n \t\ttimer_delete_sync(\u0026tbl-\u003eproxy_timer);\n \treturn 0;\n@@ -752,8 +736,7 @@ static u32 pneigh_hash(const void *pkey, unsigned int key_len)\n }\n \n struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\n-\t\t\t\t struct net *net, const void *pkey,\n-\t\t\t\t struct net_device *dev)\n+\t\t\t\t const void *pkey, struct net_device *dev)\n {\n \tstruct pneigh_entry *n;\n \tunsigned int key_len;\n@@ -766,7 +749,6 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\n \n \twhile (n) {\n \t\tif (!memcmp(n-\u003ekey, pkey, key_len) \u0026\u0026\n-\t\t net_eq(pneigh_net(n), net) \u0026\u0026\n \t\t (n-\u003edev == dev || !n-\u003edev))\n \t\t\treturn n;\n \n@@ -776,7 +758,7 @@ struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\n \treturn NULL;\n }\n \n-int pneigh_create(struct neigh_table *tbl, struct net *net,\n+int pneigh_create(struct neigh_table *tbl,\n \t\t const void *pkey, struct net_device *dev,\n \t\t u32 flags, u8 protocol, bool permanent)\n {\n@@ -787,7 +769,7 @@ int pneigh_create(struct neigh_table *tbl, struct net *net,\n \n \tmutex_lock(\u0026tbl-\u003ephash_lock);\n \n-\tn = pneigh_lookup(tbl, net, pkey, dev);\n+\tn = pneigh_lookup(tbl, pkey, dev);\n \tif (n)\n \t\tgoto update;\n \n@@ -798,7 +780,6 @@ int pneigh_create(struct neigh_table *tbl, struct net *net,\n \t\tgoto out;\n \t}\n \n-\twrite_pnet(\u0026n-\u003enet, net);\n \tmemcpy(n-\u003ekey, pkey, key_len);\n \tn-\u003edev = dev;\n \tnetdev_hold(dev, \u0026n-\u003edev_tracker, GFP_KERNEL);\n@@ -831,7 +812,7 @@ static void pneigh_destroy(struct rcu_head *rcu)\n \tkfree(n);\n }\n \n-int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,\n+int pneigh_delete(struct neigh_table *tbl, const void *pkey,\n \t\t struct net_device *dev)\n {\n \tstruct pneigh_entry *n, __rcu **np;\n@@ -846,8 +827,7 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,\n \tfor (np = \u0026tbl-\u003ephash_buckets[hash_val];\n \t (n = rcu_dereference_protected(*np, 1)) != NULL;\n \t np = \u0026n-\u003enext) {\n-\t\tif (!memcmp(n-\u003ekey, pkey, key_len) \u0026\u0026 n-\u003edev == dev \u0026\u0026\n-\t\t net_eq(pneigh_net(n), net)) {\n+\t\tif (!memcmp(n-\u003ekey, pkey, key_len) \u0026\u0026 n-\u003edev == dev) {\n \t\t\trcu_assign_pointer(*np, n-\u003enext);\n \n \t\t\tmutex_unlock(\u0026tbl-\u003ephash_lock);\n@@ -1523,10 +1503,23 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,\n \n \twrite_unlock_bh(\u0026neigh-\u003elock);\n \n-\tif (((new ^ old) \u0026 NUD_PERMANENT) || gc_update)\n-\t\tneigh_update_gc_list(neigh);\n-\tif (managed_update)\n-\t\tneigh_update_managed_list(neigh);\n+\tgc_update |= !!((new ^ old) \u0026 NUD_PERMANENT);\n+\tif (gc_update || managed_update) {\n+\t\tspin_lock_bh(\u0026neigh-\u003etbl-\u003elock);\n+\n+\t\tif (!neigh-\u003edead) {\n+\t\t\twrite_lock(\u0026neigh-\u003elock);\n+\n+\t\t\tif (gc_update)\n+\t\t\t\tneigh_update_gc_list(neigh);\n+\t\t\tif (managed_update)\n+\t\t\t\tneigh_update_managed_list(neigh);\n+\n+\t\t\twrite_unlock(\u0026neigh-\u003elock);\n+\t\t}\n+\n+\t\tspin_unlock_bh(\u0026neigh-\u003etbl-\u003elock);\n+\t}\n \n \tif (notify)\n \t\tcall_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);\n@@ -1540,7 +1533,6 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,\n {\n \treturn __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);\n }\n-EXPORT_SYMBOL(neigh_update);\n \n /* Update the neigh to listen temporarily for probe responses, even if it is\n * in a NUD_FAILED state. The caller has to hold neigh-\u003elock for writing.\n@@ -1558,7 +1550,6 @@ void __neigh_set_probe_once(struct neighbour *neigh)\n \t\t\tjiffies + max(NEIGH_VAR(neigh-\u003eparms, RETRANS_TIME),\n \t\t\t\t HZ/100));\n }\n-EXPORT_SYMBOL(__neigh_set_probe_once);\n \n struct neighbour *neigh_event_ns(struct neigh_table *tbl,\n \t\t\t\t u8 *lladdr, void *saddr,\n@@ -1571,7 +1562,6 @@ struct neighbour *neigh_event_ns(struct neigh_table *tbl,\n \t\t\t NEIGH_UPDATE_F_OVERRIDE, 0);\n \treturn neigh;\n }\n-EXPORT_SYMBOL(neigh_event_ns);\n \n /* called with read_lock_bh(\u0026n-\u003elock); */\n static void neigh_hh_init(struct neighbour *n)\n@@ -1624,7 +1614,6 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)\n \tkfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL);\n \tgoto out;\n }\n-EXPORT_SYMBOL(neigh_resolve_output);\n \n /* As fast as possible without hh cache */\n \n@@ -1741,16 +1730,15 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,\n \tmod_timer(\u0026tbl-\u003eproxy_timer, sched_next);\n \tspin_unlock(\u0026tbl-\u003eproxy_queue.lock);\n }\n-EXPORT_SYMBOL(pneigh_enqueue);\n \n static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,\n-\t\t\t\t\t\t struct net *net, int ifindex)\n+\t\t\t\t\t\t int ifindex)\n {\n \tstruct neigh_parms *p;\n \n \tlist_for_each_entry(p, \u0026tbl-\u003eparms_list, list) {\n-\t\tif ((p-\u003edev \u0026\u0026 p-\u003edev-\u003eifindex == ifindex \u0026\u0026 net_eq(neigh_parms_net(p), net)) ||\n-\t\t (!p-\u003edev \u0026\u0026 !ifindex \u0026\u0026 net_eq(net, \u0026init_net)))\n+\t\tif ((p-\u003edev \u0026\u0026 p-\u003edev-\u003eifindex == ifindex) ||\n+\t\t (!p-\u003edev \u0026\u0026 !ifindex))\n \t\t\treturn p;\n \t}\n \n@@ -1789,7 +1777,6 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,\n \t}\n \treturn p;\n }\n-EXPORT_SYMBOL(neigh_parms_alloc);\n \n static void neigh_rcu_free_parms(struct rcu_head *head)\n {\n@@ -1812,113 +1799,143 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)\n \tnetdev_put(parms-\u003edev, \u0026parms-\u003edev_tracker);\n \tcall_rcu(\u0026parms-\u003ercu_head, neigh_rcu_free_parms);\n }\n-EXPORT_SYMBOL(neigh_parms_release);\n \n static struct lock_class_key neigh_table_proxy_queue_class;\n \n-static struct neigh_table __rcu *neigh_tables[NEIGH_NR_TABLES] __read_mostly;\n-\n-void neigh_table_init(int index, struct neigh_table *tbl)\n+static int neigh_table_init(struct net *net, struct neigh_table *tbl)\n {\n \tunsigned long now = jiffies;\n \tunsigned long phsize;\n \n-\tINIT_LIST_HEAD(\u0026tbl-\u003eparms_list);\n-\tINIT_LIST_HEAD(\u0026tbl-\u003egc_list);\n-\tINIT_LIST_HEAD(\u0026tbl-\u003emanaged_list);\n+\tRCU_INIT_POINTER(tbl-\u003enht, neigh_hash_alloc(3));\n+\tif (!tbl-\u003enht)\n+\t\tgoto err_hash;\n \n-\tlist_add(\u0026tbl-\u003eparms.list, \u0026tbl-\u003eparms_list);\n-\twrite_pnet(\u0026tbl-\u003eparms.net, \u0026init_net);\n-\trefcount_set(\u0026tbl-\u003eparms.refcnt, 1);\n-\tneigh_set_reach_time(\u0026tbl-\u003eparms);\n-\ttbl-\u003eparms.qlen = 0;\n+\tphsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);\n+\ttbl-\u003ephash_buckets = kzalloc(phsize, GFP_KERNEL);\n+\tif (!tbl-\u003ephash_buckets)\n+\t\tgoto err_phash;\n+\n+\ttbl-\u003eentry_size = ALIGN(offsetof(struct neighbour, primary_key) +\n+\t\t\t\ttbl-\u003ekey_len, NEIGH_PRIV_ALIGN);\n \n \ttbl-\u003estats = alloc_percpu(struct neigh_statistics);\n \tif (!tbl-\u003estats)\n-\t\tpanic(\"cannot create neighbour cache statistics\");\n+\t\tgoto err_stats;\n \n #ifdef CONFIG_PROC_FS\n-\tif (!proc_create_seq_data(tbl-\u003eid, 0, init_net.proc_net_stat,\n-\t\t\t \u0026neigh_stat_seq_ops, tbl))\n-\t\tpanic(\"cannot create neighbour proc dir entry\");\n+\tif (!proc_create_net_data(tbl-\u003eid, 0, net-\u003eproc_net_stat,\n+\t\t\t\t \u0026neigh_stat_seq_ops,\n+\t\t\t\t sizeof(struct seq_net_private), tbl))\n+\t\tgoto err_proc;\n #endif\n \n-\tRCU_INIT_POINTER(tbl-\u003enht, neigh_hash_alloc(3));\n-\n-\tphsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);\n-\ttbl-\u003ephash_buckets = kzalloc(phsize, GFP_KERNEL);\n-\n-\tif (!tbl-\u003enht || !tbl-\u003ephash_buckets)\n-\t\tpanic(\"cannot allocate neighbour cache hashes\");\n-\n-\tif (!tbl-\u003eentry_size)\n-\t\ttbl-\u003eentry_size = ALIGN(offsetof(struct neighbour, primary_key) +\n-\t\t\t\t\ttbl-\u003ekey_len, NEIGH_PRIV_ALIGN);\n-\telse\n-\t\tWARN_ON(tbl-\u003eentry_size % NEIGH_PRIV_ALIGN);\n+\ttbl-\u003eparms.tbl = tbl;\n+\ttbl-\u003eparms.qlen = 0;\n+\tINIT_LIST_HEAD(\u0026tbl-\u003eparms_list);\n+\tlist_add(\u0026tbl-\u003eparms.list, \u0026tbl-\u003eparms_list);\n+\twrite_pnet(\u0026tbl-\u003eparms.net, net);\n+\trefcount_set(\u0026tbl-\u003eparms.refcnt, 1);\n+\tneigh_set_reach_time(\u0026tbl-\u003eparms);\n+\ttbl-\u003elast_flush = now;\n+\ttbl-\u003elast_rand\t= now + tbl-\u003eparms.reachable_time * 20;\n \n \tspin_lock_init(\u0026tbl-\u003elock);\n \tmutex_init(\u0026tbl-\u003ephash_lock);\n+\tskb_queue_head_init_class(\u0026tbl-\u003eproxy_queue,\n+\t\t\t\t \u0026neigh_table_proxy_queue_class);\n+\ttimer_setup(\u0026tbl-\u003eproxy_timer, neigh_proxy_process, 0);\n \n+\tINIT_LIST_HEAD(\u0026tbl-\u003egc_list);\n \tINIT_DEFERRABLE_WORK(\u0026tbl-\u003egc_work, neigh_periodic_work);\n \tqueue_delayed_work(system_power_efficient_wq, \u0026tbl-\u003egc_work,\n-\t\t\ttbl-\u003eparms.reachable_time);\n+\t\t\t tbl-\u003eparms.reachable_time);\n+\n+\tINIT_LIST_HEAD(\u0026tbl-\u003emanaged_list);\n \tINIT_DEFERRABLE_WORK(\u0026tbl-\u003emanaged_work, neigh_managed_work);\n \tqueue_delayed_work(system_power_efficient_wq, \u0026tbl-\u003emanaged_work, 0);\n \n-\ttimer_setup(\u0026tbl-\u003eproxy_timer, neigh_proxy_process, 0);\n-\tskb_queue_head_init_class(\u0026tbl-\u003eproxy_queue,\n-\t\t\t\u0026neigh_table_proxy_queue_class);\n-\n-\ttbl-\u003elast_flush = now;\n-\ttbl-\u003elast_rand\t= now + tbl-\u003eparms.reachable_time * 20;\n+\treturn 0;\n \n-\trcu_assign_pointer(neigh_tables[index], tbl);\n+#ifdef CONFIG_PROC_FS\n+err_proc:\n+\tfree_percpu(tbl-\u003estats);\n+#endif\n+err_stats:\n+\tkfree(tbl-\u003ephash_buckets);\n+err_phash:\n+\tneigh_hash_free_rcu(\u0026rcu_dereference_protected(tbl-\u003enht, 1)-\u003ercu);\n+err_hash:\n+\treturn -ENOMEM;\n }\n \n-/*\n- * Only called from ndisc_cleanup(), which means this is dead code\n- * because we no longer can unload IPv6 module.\n- */\n-int neigh_table_clear(int index, struct neigh_table *tbl)\n+static void neigh_table_clear(struct net *net, struct neigh_table *tbl)\n {\n-\tRCU_INIT_POINTER(neigh_tables[index], NULL);\n-\tsynchronize_rcu();\n+\tstruct neigh_hash_table *nht;\n \n-\t/* It is not clean... Fix it to unload IPv6 module safely */\n \tcancel_delayed_work_sync(\u0026tbl-\u003emanaged_work);\n \tcancel_delayed_work_sync(\u0026tbl-\u003egc_work);\n-\ttimer_delete_sync(\u0026tbl-\u003eproxy_timer);\n-\tpneigh_queue_purge(\u0026tbl-\u003eproxy_queue, NULL, tbl-\u003efamily);\n+\ttimer_shutdown_sync(\u0026tbl-\u003eproxy_timer);\n+\n \tneigh_ifdown(tbl, NULL);\n-\tif (atomic_read(\u0026tbl-\u003eentries))\n-\t\tpr_crit(\"neighbour leakage\\n\");\n+\tDEBUG_NET_WARN_ON_ONCE(atomic_read(\u0026tbl-\u003eentries));\n \n-\tcall_rcu(\u0026rcu_dereference_protected(tbl-\u003enht, 1)-\u003ercu,\n-\t\t neigh_hash_free_rcu);\n-\ttbl-\u003enht = NULL;\n+\tremove_proc_entry(tbl-\u003eid, net-\u003eproc_net_stat);\n+\n+\tfree_percpu(tbl-\u003estats);\n+\ttbl-\u003estats = NULL;\n \n \tkfree(tbl-\u003ephash_buckets);\n \ttbl-\u003ephash_buckets = NULL;\n \n-\tremove_proc_entry(tbl-\u003eid, init_net.proc_net_stat);\n+\tnht = rcu_dereference_protected(tbl-\u003enht, 1);\n+\ttbl-\u003enht = NULL;\n+\tneigh_hash_free_rcu(\u0026nht-\u003ercu);\n+}\n \n-\tfree_percpu(tbl-\u003estats);\n-\ttbl-\u003estats = NULL;\n+int neigh_table_register(struct net *net, struct neigh_table *tbl, int index)\n+{\n+\tint err;\n+\n+\ttbl = kmemdup(tbl, sizeof(*tbl), GFP_KERNEL);\n+\tif (!tbl) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err;\n+\t}\n+\n+\terr = neigh_table_init(net, tbl);\n+\tif (err)\n+\t\tgoto free_table;\n+\n+\tnet-\u003eneigh_tables[index] = tbl;\n \n \treturn 0;\n+\n+free_table:\n+\tkfree(tbl);\n+err:\n+\treturn err;\n+}\n+\n+void neigh_table_unregister(struct net *net, int index)\n+{\n+\tstruct neigh_table *tbl = net-\u003eneigh_tables[index];\n+\n+\tnet-\u003eneigh_tables[index] = NULL;\n+\tneigh_table_clear(net, tbl);\n+\tkfree(tbl);\n }\n \n-static struct neigh_table *neigh_find_table(int family)\n+static struct neigh_table *neigh_find_table(struct net *net, int family)\n {\n \tstruct neigh_table *tbl = NULL;\n \n \tswitch (family) {\n \tcase AF_INET:\n-\t\ttbl = rcu_dereference_rtnl(neigh_tables[NEIGH_ARP_TABLE]);\n+\t\ttbl = net-\u003eneigh_tables[NEIGH_ARP_TABLE];\n \t\tbreak;\n \tcase AF_INET6:\n-\t\ttbl = rcu_dereference_rtnl(neigh_tables[NEIGH_ND_TABLE]);\n+\t\ttbl = net-\u003eneigh_tables[NEIGH_ND_TABLE];\n \t\tbreak;\n \t}\n \n@@ -1972,7 +1989,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t}\n \t}\n \n-\ttbl = neigh_find_table(ndm-\u003endm_family);\n+\ttbl = neigh_find_table(net, ndm-\u003endm_family);\n \tif (tbl == NULL)\n \t\treturn -EAFNOSUPPORT;\n \n@@ -1982,7 +1999,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t}\n \n \tif (ndm-\u003endm_flags \u0026 NTF_PROXY) {\n-\t\terr = pneigh_delete(tbl, net, nla_data(dst_attr), dev);\n+\t\terr = pneigh_delete(tbl, nla_data(dst_attr), dev);\n \t\tgoto out;\n \t}\n \n@@ -2058,7 +2075,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t}\n \t}\n \n-\ttbl = neigh_find_table(ndm-\u003endm_family);\n+\ttbl = neigh_find_table(net, ndm-\u003endm_family);\n \tif (tbl == NULL)\n \t\treturn -EAFNOSUPPORT;\n \n@@ -2078,7 +2095,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t\tgoto out;\n \t\t}\n \n-\t\terr = pneigh_create(tbl, net, dst, dev, ndm_flags, protocol,\n+\t\terr = pneigh_create(tbl, dst, dev, ndm_flags, protocol,\n \t\t\t\t !!(ndm-\u003endm_state \u0026 NUD_PERMANENT));\n \t\tgoto out;\n \t}\n@@ -2400,10 +2417,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\n \n \tndtmsg = nlmsg_data(nlh);\n \n-\trcu_read_lock();\n-\n \tfor (tidx = 0; tidx \u003c NEIGH_NR_TABLES; tidx++) {\n-\t\ttbl = rcu_dereference(neigh_tables[tidx]);\n+\t\ttbl = net-\u003eneigh_tables[tidx];\n \t\tif (!tbl)\n \t\t\tcontinue;\n \n@@ -2417,7 +2432,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t}\n \n \tif (!found) {\n-\t\trcu_read_unlock();\n \t\terr = -ENOENT;\n \t\tgoto errout;\n \t}\n@@ -2442,8 +2456,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\tif (tbp[NDTPA_IFINDEX])\n \t\t\tifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);\n \n-\t\tp = lookup_neigh_parms(tbl, net, ifindex);\n-\t\tif (p == NULL) {\n+\t\tp = lookup_neigh_parms(tbl, ifindex);\n+\t\tif (!p) {\n \t\t\terr = -ENOENT;\n \t\t\tgoto errout_tbl_lock;\n \t\t}\n@@ -2524,12 +2538,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\n \t\t}\n \t}\n \n-\terr = -ENOENT;\n-\tif ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] ||\n-\t tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) \u0026\u0026\n-\t !net_eq(net, \u0026init_net))\n-\t\tgoto errout_tbl_lock;\n-\n \tif (tb[NDTA_THRESH1])\n \t\tWRITE_ONCE(tbl-\u003egc_thresh1, nla_get_u32(tb[NDTA_THRESH1]));\n \n@@ -2546,7 +2554,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\n \n errout_tbl_lock:\n \tspin_unlock_bh(\u0026tbl-\u003elock);\n-\trcu_read_unlock();\n errout:\n \treturn err;\n }\n@@ -2598,7 +2605,7 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\n \tfor (tidx = 0; tidx \u003c NEIGH_NR_TABLES; tidx++) {\n \t\tstruct neigh_parms *p;\n \n-\t\ttbl = rcu_dereference(neigh_tables[tidx]);\n+\t\ttbl = net-\u003eneigh_tables[tidx];\n \t\tif (!tbl)\n \t\t\tcontinue;\n \n@@ -2613,9 +2620,6 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\n \t\tnidx = 0;\n \t\tp = list_next_entry(\u0026tbl-\u003eparms, list);\n \t\tlist_for_each_entry_from_rcu(p, \u0026tbl-\u003eparms_list, list) {\n-\t\t\tif (!net_eq(neigh_parms_net(p), net))\n-\t\t\t\tcontinue;\n-\n \t\t\tif (nidx \u003c neigh_skip)\n \t\t\t\tgoto next;\n \n@@ -2793,12 +2797,11 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,\n \t\t\t struct netlink_callback *cb,\n \t\t\t struct neigh_dump_filter *filter)\n {\n-\tstruct net *net = sock_net(skb-\u003esk);\n-\tstruct neighbour *n;\n-\tint err = 0, h, s_h = cb-\u003eargs[1];\n \tint idx, s_idx = idx = cb-\u003eargs[2];\n-\tstruct neigh_hash_table *nht;\n+\tint err = 0, h, s_h = cb-\u003eargs[1];\n \tunsigned int flags = NLM_F_MULTI;\n+\tstruct neigh_hash_table *nht;\n+\tstruct neighbour *n;\n \n \tif (filter-\u003edev_idx || filter-\u003emaster_idx)\n \t\tflags |= NLM_F_DUMP_FILTERED;\n@@ -2810,7 +2813,7 @@ static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,\n \t\t\ts_idx = 0;\n \t\tidx = 0;\n \t\tneigh_for_each_in_bucket_rcu(n, \u0026nht-\u003ehash_heads[h]) {\n-\t\t\tif (idx \u003c s_idx || !net_eq(dev_net(n-\u003edev), net))\n+\t\t\tif (idx \u003c s_idx)\n \t\t\t\tgoto next;\n \t\t\tif (neigh_ifindex_filtered(n-\u003edev, filter-\u003edev_idx) ||\n \t\t\t neigh_master_filtered(n-\u003edev, filter-\u003emaster_idx))\n@@ -2834,11 +2837,10 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,\n \t\t\t struct netlink_callback *cb,\n \t\t\t struct neigh_dump_filter *filter)\n {\n-\tstruct pneigh_entry *n;\n-\tstruct net *net = sock_net(skb-\u003esk);\n-\tint err = 0, h, s_h = cb-\u003eargs[3];\n \tint idx, s_idx = idx = cb-\u003eargs[4];\n+\tint err = 0, h, s_h = cb-\u003eargs[3];\n \tunsigned int flags = NLM_F_MULTI;\n+\tstruct pneigh_entry *n;\n \n \tif (filter-\u003edev_idx || filter-\u003emaster_idx)\n \t\tflags |= NLM_F_DUMP_FILTERED;\n@@ -2849,7 +2851,7 @@ static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,\n \t\tfor (n = rcu_dereference(tbl-\u003ephash_buckets[h]), idx = 0;\n \t\t n;\n \t\t n = rcu_dereference(n-\u003enext)) {\n-\t\t\tif (idx \u003c s_idx || pneigh_net(n) != net)\n+\t\t\tif (idx \u003c s_idx)\n \t\t\t\tgoto next;\n \t\t\tif (neigh_ifindex_filtered(n-\u003edev, filter-\u003edev_idx) ||\n \t\t\t neigh_master_filtered(n-\u003edev, filter-\u003emaster_idx))\n@@ -2935,6 +2937,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\n {\n \tconst struct nlmsghdr *nlh = cb-\u003enlh;\n \tstruct neigh_dump_filter filter = {};\n+\tstruct net *net = sock_net(skb-\u003esk);\n \tstruct neigh_table *tbl;\n \tint t, family, s_t;\n \tint proxy = 0;\n@@ -2958,8 +2961,7 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\n \n \trcu_read_lock();\n \tfor (t = 0; t \u003c NEIGH_NR_TABLES; t++) {\n-\t\ttbl = rcu_dereference(neigh_tables[t]);\n-\n+\t\ttbl = net-\u003eneigh_tables[t];\n \t\tif (!tbl)\n \t\t\tcontinue;\n \t\tif (t \u003c s_t || (family \u0026\u0026 tbl-\u003efamily != family))\n@@ -3081,7 +3083,7 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,\n \n \trcu_read_lock();\n \n-\ttbl = neigh_find_table(ndm-\u003endm_family);\n+\ttbl = neigh_find_table(net, ndm-\u003endm_family);\n \tif (!tbl) {\n \t\tNL_SET_ERR_MSG(extack, \"Unsupported family in header for neighbor get request\");\n \t\terr = -EAFNOSUPPORT;\n@@ -3108,7 +3110,7 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,\n \tif (ndm-\u003endm_flags \u0026 NTF_PROXY) {\n \t\tstruct pneigh_entry *pn;\n \n-\t\tpn = pneigh_lookup(tbl, net, dst, dev);\n+\t\tpn = pneigh_lookup(tbl, dst, dev);\n \t\tif (!pn) {\n \t\t\tNL_SET_ERR_MSG(extack, \"Proxy neighbour entry not found\");\n \t\t\terr = -ENOENT;\n@@ -3161,37 +3163,6 @@ void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void\n }\n EXPORT_SYMBOL(neigh_for_each);\n \n-/* The tbl-\u003elock must be held as a writer and BH disabled. */\n-void __neigh_for_each_release(struct neigh_table *tbl,\n-\t\t\t int (*cb)(struct neighbour *))\n-{\n-\tstruct neigh_hash_table *nht;\n-\tint chain;\n-\n-\tnht = rcu_dereference_protected(tbl-\u003enht,\n-\t\t\t\t\tlockdep_is_held(\u0026tbl-\u003elock));\n-\tfor (chain = 0; chain \u003c (1 \u003c\u003c nht-\u003ehash_shift); chain++) {\n-\t\tstruct hlist_node *tmp;\n-\t\tstruct neighbour *n;\n-\n-\t\tneigh_for_each_in_bucket_safe(n, tmp, \u0026nht-\u003ehash_heads[chain]) {\n-\t\t\tint release;\n-\n-\t\t\twrite_lock(\u0026n-\u003elock);\n-\t\t\trelease = cb(n);\n-\t\t\tif (release) {\n-\t\t\t\thlist_del_rcu(\u0026n-\u003ehash);\n-\t\t\t\thlist_del_rcu(\u0026n-\u003edev_list);\n-\t\t\t\tneigh_mark_dead(n);\n-\t\t\t}\n-\t\t\twrite_unlock(\u0026n-\u003elock);\n-\t\t\tif (release)\n-\t\t\t\tneigh_cleanup_and_release(n);\n-\t\t}\n-\t}\n-}\n-EXPORT_SYMBOL(__neigh_for_each_release);\n-\n int neigh_xmit(int index, struct net_device *dev,\n \t const void *addr, struct sk_buff *skb)\n {\n@@ -3200,9 +3171,11 @@ int neigh_xmit(int index, struct net_device *dev,\n \tif (likely(index \u003c NEIGH_NR_TABLES)) {\n \t\tstruct neigh_table *tbl;\n \t\tstruct neighbour *neigh;\n+\t\tstruct net *net;\n \n \t\trcu_read_lock();\n-\t\ttbl = rcu_dereference(neigh_tables[index]);\n+\t\tnet = dev_net_rcu(dev);\n+\t\ttbl = net-\u003eneigh_tables[index];\n \t\tif (!tbl) {\n \t\t\trcu_read_unlock();\n \t\t\tgoto out_kfree_skb;\n@@ -3245,10 +3218,6 @@ static struct neighbour *neigh_get_valid(struct seq_file *seq,\n \t\t\t\t\t loff_t *pos)\n {\n \tstruct neigh_seq_state *state = seq-\u003eprivate;\n-\tstruct net *net = seq_file_net(seq);\n-\n-\tif (!net_eq(dev_net(n-\u003edev), net))\n-\t\treturn NULL;\n \n \tif (state-\u003eneigh_sub_iter) {\n \t\tloff_t fakep = 0;\n@@ -3337,7 +3306,6 @@ static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)\n static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)\n {\n \tstruct neigh_seq_state *state = seq-\u003eprivate;\n-\tstruct net *net = seq_file_net(seq);\n \tstruct neigh_table *tbl = state-\u003etbl;\n \tstruct pneigh_entry *pn = NULL;\n \tint bucket;\n@@ -3345,9 +3313,6 @@ static struct pneigh_entry *pneigh_get_first(struct seq_file *seq)\n \tstate-\u003eflags |= NEIGH_SEQ_IS_PNEIGH;\n \tfor (bucket = 0; bucket \u003c= PNEIGH_HASHMASK; bucket++) {\n \t\tpn = rcu_dereference(tbl-\u003ephash_buckets[bucket]);\n-\n-\t\twhile (pn \u0026\u0026 !net_eq(pneigh_net(pn), net))\n-\t\t\tpn = rcu_dereference(pn-\u003enext);\n \t\tif (pn)\n \t\t\tbreak;\n \t}\n@@ -3361,21 +3326,15 @@ static struct pneigh_entry *pneigh_get_next(struct seq_file *seq,\n \t\t\t\t\t loff_t *pos)\n {\n \tstruct neigh_seq_state *state = seq-\u003eprivate;\n-\tstruct net *net = seq_file_net(seq);\n \tstruct neigh_table *tbl = state-\u003etbl;\n \n-\tdo {\n-\t\tpn = rcu_dereference(pn-\u003enext);\n-\t} while (pn \u0026\u0026 !net_eq(pneigh_net(pn), net));\n+\tpn = rcu_dereference(pn-\u003enext);\n \n \twhile (!pn) {\n \t\tif (++state-\u003ebucket \u003e PNEIGH_HASHMASK)\n \t\t\tbreak;\n \n \t\tpn = rcu_dereference(tbl-\u003ephash_buckets[state-\u003ebucket]);\n-\n-\t\twhile (pn \u0026\u0026 !net_eq(pneigh_net(pn), net))\n-\t\t\tpn = rcu_dereference(pn-\u003enext);\n \t\tif (pn)\n \t\t\tbreak;\n \t}\n@@ -3430,7 +3389,6 @@ void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl\n \n \treturn *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;\n }\n-EXPORT_SYMBOL(neigh_seq_start);\n \n void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)\n {\n@@ -3457,7 +3415,6 @@ void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)\n \t++(*pos);\n \treturn rc;\n }\n-EXPORT_SYMBOL(neigh_seq_next);\n \n void neigh_seq_stop(struct seq_file *seq, void *v)\n \t__releases(tbl-\u003elock)\n@@ -3469,7 +3426,6 @@ void neigh_seq_stop(struct seq_file *seq, void *v)\n \tspin_unlock_bh(\u0026tbl-\u003elock);\n \trcu_read_unlock();\n }\n-EXPORT_SYMBOL(neigh_seq_stop);\n \n /* statistics via seq_file */\n \n@@ -3593,7 +3549,6 @@ void neigh_app_ns(struct neighbour *n)\n {\n \tneigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0);\n }\n-EXPORT_SYMBOL(neigh_app_ns);\n \n #ifdef CONFIG_SYSCTL\n static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);\n@@ -3689,7 +3644,6 @@ int neigh_proc_dointvec(const struct ctl_table *ctl, int write, void *buffer,\n \tneigh_proc_update(ctl, write);\n \treturn ret;\n }\n-EXPORT_SYMBOL(neigh_proc_dointvec);\n \n int neigh_proc_dointvec_jiffies(const struct ctl_table *ctl, int write, void *buffer,\n \t\t\t\tsize_t *lenp, loff_t *ppos)\n@@ -3699,7 +3653,6 @@ int neigh_proc_dointvec_jiffies(const struct ctl_table *ctl, int write, void *bu\n \tneigh_proc_update(ctl, write);\n \treturn ret;\n }\n-EXPORT_SYMBOL(neigh_proc_dointvec_jiffies);\n \n static int neigh_proc_dointvec_userhz_jiffies(const struct ctl_table *ctl, int write,\n \t\t\t\t\t void *buffer, size_t *lenp,\n@@ -3719,7 +3672,6 @@ int neigh_proc_dointvec_ms_jiffies(const struct ctl_table *ctl, int write,\n \tneigh_proc_update(ctl, write);\n \treturn ret;\n }\n-EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);\n \n static int neigh_proc_dointvec_unres_qlen(const struct ctl_table *ctl, int write,\n \t\t\t\t\t void *buffer, size_t *lenp,\n@@ -3926,7 +3878,6 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,\n err:\n \treturn -ENOBUFS;\n }\n-EXPORT_SYMBOL(neigh_sysctl_register);\n \n void neigh_sysctl_unregister(struct neigh_parms *p)\n {\n@@ -3937,7 +3888,6 @@ void neigh_sysctl_unregister(struct neigh_parms *p)\n \t\tkfree(t);\n \t}\n }\n-EXPORT_SYMBOL(neigh_sysctl_unregister);\n \n #endif\t/* CONFIG_SYSCTL */\n \ndiff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c\nindex 4df76ff50699e..4f511476b992c 100644\n--- a/net/ieee802154/6lowpan/tx.c\n+++ b/net/ieee802154/6lowpan/tx.c\n@@ -58,8 +58,9 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,\n \t\tinfo-\u003edaddr.mode = IEEE802154_ADDR_SHORT;\n \t} else {\n \t\t__le16 short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);\n+\t\tstruct neigh_table *tbl = nd_table(dev_net(ldev));\n \n-\t\tn = neigh_lookup(\u0026nd_tbl, \u0026hdr-\u003edaddr, ldev);\n+\t\tn = neigh_lookup(tbl, \u0026hdr-\u003edaddr, ldev);\n \t\tif (n) {\n \t\t\tllneigh = lowpan_802154_neigh(neighbour_priv(n));\n \t\t\tread_lock_bh(\u0026n-\u003elock);\ndiff --git a/net/ipv4/arp.c b/net/ipv4/arp.c\nindex d409f606aec0e..90bc53fb80905 100644\n--- a/net/ipv4/arp.c\n+++ b/net/ipv4/arp.c\n@@ -149,7 +149,7 @@ static const struct neigh_ops arp_direct_ops = {\n \t.connected_output =\tneigh_direct_output,\n };\n \n-struct neigh_table arp_tbl = {\n+static struct neigh_table arp_tbl = {\n \t.family\t\t= AF_INET,\n \t.key_len\t= 4,\n \t.protocol\t= cpu_to_be16(ETH_P_IP),\n@@ -182,7 +182,6 @@ struct neigh_table arp_tbl = {\n \t.gc_thresh2\t= 512,\n \t.gc_thresh3\t= 1024,\n };\n-EXPORT_SYMBOL(arp_tbl);\n \n int arp_mc_map(__be32 addr, u8 *haddr, struct net_device *dev, int dir)\n {\n@@ -222,14 +221,18 @@ static bool arp_key_eq(const struct neighbour *neigh, const void *pkey)\n \n static int arp_constructor(struct neighbour *neigh)\n {\n-\t__be32 addr;\n \tstruct net_device *dev = neigh-\u003edev;\n-\tstruct in_device *in_dev;\n-\tstruct neigh_parms *parms;\n+\tstruct net *net = dev_net(dev);\n \tu32 inaddr_any = INADDR_ANY;\n+\tstruct neigh_parms *parms;\n+\tstruct in_device *in_dev;\n+\tstruct neigh_table *tbl;\n+\t__be32 addr;\n+\n+\ttbl = arp_table(net);\n \n \tif (dev-\u003eflags \u0026 (IFF_LOOPBACK | IFF_POINTOPOINT))\n-\t\tmemcpy(neigh-\u003eprimary_key, \u0026inaddr_any, arp_tbl.key_len);\n+\t\tmemcpy(neigh-\u003eprimary_key, \u0026inaddr_any, tbl-\u003ekey_len);\n \n \taddr = *(__be32 *)neigh-\u003eprimary_key;\n \trcu_read_lock();\n@@ -239,7 +242,7 @@ static int arp_constructor(struct neighbour *neigh)\n \t\treturn -EINVAL;\n \t}\n \n-\tneigh-\u003etype = inet_addr_type_dev_table(dev_net(dev), dev, addr);\n+\tneigh-\u003etype = inet_addr_type_dev_table(net, dev, addr);\n \n \tparms = in_dev-\u003earp_parms;\n \t__neigh_parms_put(neigh-\u003eparms);\n@@ -701,24 +704,24 @@ static bool arp_is_garp(struct net *net, struct net_device *dev,\n \n static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n {\n+\tstruct neigh_table *tbl = arp_table(net);\n+\tstruct dst_entry *reply_dst = NULL;\n \tstruct net_device *dev = skb-\u003edev;\n-\tstruct in_device *in_dev = __in_dev_get_rcu(dev);\n-\tstruct arphdr *arp;\n+\tunsigned char *sha, *tha = NULL;\n+\tstruct in_device *in_dev;\n+\tu16 dev_type = dev-\u003etype;\n \tunsigned char *arp_ptr;\n+\tbool is_garp = false;\n+\tstruct neighbour *n;\n+\tstruct arphdr *arp;\n \tstruct rtable *rt;\n-\tunsigned char *sha;\n-\tunsigned char *tha = NULL;\n \t__be32 sip, tip;\n-\tu16 dev_type = dev-\u003etype;\n \tint addr_type;\n-\tstruct neighbour *n;\n-\tstruct dst_entry *reply_dst = NULL;\n-\tbool is_garp = false;\n \n \t/* arp_rcv below verifies the ARP header and verifies the device\n \t * is ARP'able.\n \t */\n-\n+\tin_dev = __in_dev_get_rcu(dev);\n \tif (!in_dev)\n \t\tgoto out_free_skb;\n \n@@ -850,7 +853,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n \t\t\tif (!dont_send \u0026\u0026 IN_DEV_ARPFILTER(in_dev))\n \t\t\t\tdont_send = arp_filter(sip, tip, dev);\n \t\t\tif (!dont_send) {\n-\t\t\t\tn = neigh_event_ns(\u0026arp_tbl, sha, \u0026sip, dev);\n+\t\t\t\tn = neigh_event_ns(tbl, sha, \u0026sip, dev);\n \t\t\t\tif (n) {\n \t\t\t\t\tarp_send_dst(ARPOP_REPLY, ETH_P_ARP,\n \t\t\t\t\t\t sip, dev, tip, sha,\n@@ -865,8 +868,8 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n \t\t\t (arp_fwd_proxy(in_dev, dev, rt) ||\n \t\t\t arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||\n \t\t\t (rt-\u003edst.dev != dev \u0026\u0026\n-\t\t\t pneigh_lookup(\u0026arp_tbl, net, \u0026tip, dev)))) {\n-\t\t\t\tn = neigh_event_ns(\u0026arp_tbl, sha, \u0026sip, dev);\n+\t\t\t pneigh_lookup(tbl, \u0026tip, dev)))) {\n+\t\t\t\tn = neigh_event_ns(tbl, sha, \u0026sip, dev);\n \t\t\t\tif (n)\n \t\t\t\t\tneigh_release(n);\n \n@@ -878,7 +881,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n \t\t\t\t\t\t dev-\u003edev_addr, sha,\n \t\t\t\t\t\t reply_dst);\n \t\t\t\t} else {\n-\t\t\t\t\tpneigh_enqueue(\u0026arp_tbl,\n+\t\t\t\t\tpneigh_enqueue(tbl,\n \t\t\t\t\t\t in_dev-\u003earp_parms, skb);\n \t\t\t\t\tgoto out_free_dst;\n \t\t\t\t}\n@@ -889,7 +892,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n \n \t/* Update our ARP tables */\n \n-\tn = __neigh_lookup(\u0026arp_tbl, \u0026sip, dev, 0);\n+\tn = __neigh_lookup(tbl, \u0026sip, dev, 0);\n \n \taddr_type = -1;\n \tif (n || arp_accept(in_dev, sip)) {\n@@ -910,7 +913,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)\n \t\t\t/* postpone calculation to as late as possible */\n \t\t\tinet_addr_type_dev_table(net, dev, sip) ==\n \t\t\t\tRTN_UNICAST)))))\n-\t\t\tn = __neigh_lookup(\u0026arp_tbl, \u0026sip, dev, 1);\n+\t\t\tn = __neigh_lookup(tbl, \u0026sip, dev, 1);\n \t}\n \n \tif (n) {\n@@ -1077,9 +1080,10 @@ static int arp_req_set_proxy(struct net *net, struct net_device *dev, int on)\n }\n \n static int arp_req_set_public(struct net *net, struct arpreq *r,\n-\t\tstruct net_device *dev)\n+\t\t\t struct net_device *dev)\n {\n \t__be32 mask = ((struct sockaddr_in *)\u0026r-\u003earp_netmask)-\u003esin_addr.s_addr;\n+\tstruct neigh_table *tbl = arp_table(net);\n \n \tif (!dev \u0026\u0026 (r-\u003earp_flags \u0026 ATF_COM)) {\n \t\tdev = dev_getbyhwaddr(net, r-\u003earp_ha.sa_family,\n@@ -1090,7 +1094,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,\n \tif (mask) {\n \t\t__be32 ip = ((struct sockaddr_in *)\u0026r-\u003earp_pa)-\u003esin_addr.s_addr;\n \n-\t\treturn pneigh_create(\u0026arp_tbl, net, \u0026ip, dev, 0, 0, false);\n+\t\treturn pneigh_create(tbl, \u0026ip, dev, 0, 0, false);\n \t}\n \n \treturn arp_req_set_proxy(net, dev, 1);\n@@ -1098,6 +1102,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,\n \n static int arp_req_set(struct net *net, struct arpreq *r)\n {\n+\tstruct neigh_table *tbl = arp_table(net);\n \tstruct neighbour *neigh;\n \tstruct net_device *dev;\n \t__be32 ip;\n@@ -1133,7 +1138,7 @@ static int arp_req_set(struct net *net, struct arpreq *r)\n \n \tip = ((struct sockaddr_in *)\u0026r-\u003earp_pa)-\u003esin_addr.s_addr;\n \n-\tneigh = __neigh_lookup_errno(\u0026arp_tbl, \u0026ip, dev);\n+\tneigh = __neigh_lookup_errno(tbl, \u0026ip, dev);\n \terr = PTR_ERR(neigh);\n \tif (!IS_ERR(neigh)) {\n \t\tunsigned int state = NUD_STALE;\n@@ -1169,6 +1174,7 @@ static unsigned int arp_state_to_flags(struct neighbour *neigh)\n static int arp_req_get(struct net *net, struct arpreq *r)\n {\n \t__be32 ip = ((struct sockaddr_in *) \u0026r-\u003earp_pa)-\u003esin_addr.s_addr;\n+\tstruct neigh_table *tbl = arp_table(net);\n \tstruct neighbour *neigh;\n \tstruct net_device *dev;\n \n@@ -1179,7 +1185,7 @@ static int arp_req_get(struct net *net, struct arpreq *r)\n \tif (IS_ERR(dev))\n \t\treturn PTR_ERR(dev);\n \n-\tneigh = neigh_lookup(\u0026arp_tbl, \u0026ip, dev);\n+\tneigh = neigh_lookup(tbl, \u0026ip, dev);\n \tif (!neigh)\n \t\treturn -ENXIO;\n \n@@ -1204,10 +1210,11 @@ static int arp_req_get(struct net *net, struct arpreq *r)\n \n int arp_invalidate(struct net_device *dev, __be32 ip, bool force)\n {\n-\tstruct neighbour *neigh = neigh_lookup(\u0026arp_tbl, \u0026ip, dev);\n+\tstruct neigh_table *tbl = arp_table(dev_net(dev));\n+\tstruct neighbour *neigh;\n \tint err = -ENXIO;\n-\tstruct neigh_table *tbl = \u0026arp_tbl;\n \n+\tneigh = neigh_lookup(tbl, \u0026ip, dev);\n \tif (neigh) {\n \t\tif ((READ_ONCE(neigh-\u003enud_state) \u0026 NUD_VALID) \u0026\u0026 !force) {\n \t\t\tneigh_release(neigh);\n@@ -1228,14 +1235,15 @@ int arp_invalidate(struct net_device *dev, __be32 ip, bool force)\n }\n \n static int arp_req_delete_public(struct net *net, struct arpreq *r,\n-\t\tstruct net_device *dev)\n+\t\t\t\t struct net_device *dev)\n {\n \t__be32 mask = ((struct sockaddr_in *)\u0026r-\u003earp_netmask)-\u003esin_addr.s_addr;\n+\tstruct neigh_table *tbl = arp_table(net);\n \n \tif (mask) {\n \t\t__be32 ip = ((struct sockaddr_in *)\u0026r-\u003earp_pa)-\u003esin_addr.s_addr;\n \n-\t\treturn pneigh_delete(\u0026arp_tbl, net, \u0026ip, dev);\n+\t\treturn pneigh_delete(tbl, \u0026ip, dev);\n \t}\n \n \treturn arp_req_set_proxy(net, dev, 0);\n@@ -1325,18 +1333,22 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,\n {\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \tstruct netdev_notifier_change_info *change_info;\n+\tstruct net *net = dev_net(dev);\n \tstruct in_device *in_dev;\n+\tstruct neigh_table *tbl;\n \tbool evict_nocarrier;\n \n+\ttbl = arp_table(net);\n+\n \tswitch (event) {\n \tcase NETDEV_CHANGEADDR:\n-\t\tneigh_changeaddr(\u0026arp_tbl, dev);\n-\t\trt_cache_flush(dev_net(dev));\n+\t\tneigh_changeaddr(tbl, dev);\n+\t\trt_cache_flush(net);\n \t\tbreak;\n \tcase NETDEV_CHANGE:\n \t\tchange_info = ptr;\n \t\tif (change_info-\u003eflags_changed \u0026 IFF_NOARP)\n-\t\t\tneigh_changeaddr(\u0026arp_tbl, dev);\n+\t\t\tneigh_changeaddr(tbl, dev);\n \n \t\tin_dev = __in_dev_get_rtnl(dev);\n \t\tif (!in_dev)\n@@ -1345,7 +1357,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,\n \t\t\tevict_nocarrier = IN_DEV_ARP_EVICT_NOCARRIER(in_dev);\n \n \t\tif (evict_nocarrier \u0026\u0026 !netif_carrier_ok(dev))\n-\t\t\tneigh_carrier_down(\u0026arp_tbl, dev);\n+\t\t\tneigh_carrier_down(tbl, dev);\n \t\tbreak;\n \tdefault:\n \t\tbreak;\n@@ -1364,7 +1376,9 @@ static struct notifier_block arp_netdev_notifier = {\n */\n void arp_ifdown(struct net_device *dev)\n {\n-\tneigh_ifdown(\u0026arp_tbl, dev);\n+\tstruct neigh_table *tbl = arp_table(dev_net(dev));\n+\n+\tneigh_ifdown(tbl, dev);\n }\n \n \n@@ -1479,10 +1493,12 @@ static int arp_seq_show(struct seq_file *seq, void *v)\n \n static void *arp_seq_start(struct seq_file *seq, loff_t *pos)\n {\n+\tstruct neigh_table *tbl = arp_table(seq_file_net(seq));\n+\n \t/* Don't want to confuse \"arp -a\" w/ magic entries,\n \t * so we tell the generic iterator to skip NUD_NOARP.\n \t */\n-\treturn neigh_seq_start(seq, pos, \u0026arp_tbl, NEIGH_SEQ_SKIP_NOARP);\n+\treturn neigh_seq_start(seq, pos, tbl, NEIGH_SEQ_SKIP_NOARP);\n }\n \n static const struct seq_operations arp_seq_ops = {\n@@ -1495,15 +1511,47 @@ static const struct seq_operations arp_seq_ops = {\n \n static int __net_init arp_net_init(struct net *net)\n {\n+\tint err;\n+\n+\terr = neigh_table_register(net, \u0026arp_tbl, NEIGH_ARP_TABLE);\n+\tif (err)\n+\t\tgoto err;\n+\n+#ifdef CONFIG_PROC_FS\n+#ifdef CONFIG_SYSCTL\n+\terr = neigh_sysctl_register(NULL, \u0026arp_table(net)-\u003eparms, NULL);\n+\tif (err)\n+\t\tgoto err_sysctl;\n+#endif\n+\n \tif (!proc_create_net(\"arp\", 0444, net-\u003eproc_net, \u0026arp_seq_ops,\n-\t\t\tsizeof(struct neigh_seq_state)))\n-\t\treturn -ENOMEM;\n+\t\t\t sizeof(struct neigh_seq_state))) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err_proc_create;\n+\t}\n+#endif\n+\n \treturn 0;\n+\n+#ifdef CONFIG_PROC_FS\n+err_proc_create:\n+#ifdef CONFIG_SYSCTL\n+\tneigh_sysctl_unregister(\u0026arp_table(net)-\u003eparms);\n+err_sysctl:\n+#endif\n+\tneigh_table_unregister(net, NEIGH_ARP_TABLE);\n+#endif\n+err:\n+\treturn err;\n }\n \n static void __net_exit arp_net_exit(struct net *net)\n {\n \tremove_proc_entry(\"arp\", net-\u003eproc_net);\n+#ifdef CONFIG_SYSCTL\n+\tneigh_sysctl_unregister(\u0026arp_table(net)-\u003eparms);\n+#endif\n+\tneigh_table_unregister(net, NEIGH_ARP_TABLE);\n }\n \n static struct pernet_operations arp_net_ops = {\n@@ -1513,12 +1561,9 @@ static struct pernet_operations arp_net_ops = {\n \n void __init arp_init(void)\n {\n-\tneigh_table_init(NEIGH_ARP_TABLE, \u0026arp_tbl);\n+\tif (register_pernet_subsys(\u0026arp_net_ops))\n+\t\tpanic(\"Cannot allocate arp table\\n\");\n \n \tdev_add_pack(\u0026arp_packet_type);\n-\tregister_pernet_subsys(\u0026arp_net_ops);\n-#ifdef CONFIG_SYSCTL\n-\tneigh_sysctl_register(NULL, \u0026arp_tbl.parms, NULL);\n-#endif\n \tregister_netdevice_notifier(\u0026arp_netdev_notifier);\n }\ndiff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c\nindex 47ded0f607d4b..05de597ce086f 100644\n--- a/net/ipv4/devinet.c\n+++ b/net/ipv4/devinet.c\n@@ -265,7 +265,9 @@ EXPORT_SYMBOL(in_dev_finish_destroy);\n \n static struct in_device *inetdev_init(struct net_device *dev)\n {\n+\tstruct net *net = dev_net(dev);\n \tstruct in_device *in_dev;\n+\tstruct neigh_table *tbl;\n \tint err = -ENOMEM;\n \n \tASSERT_RTNL();\n@@ -273,11 +275,12 @@ static struct in_device *inetdev_init(struct net_device *dev)\n \tin_dev = kzalloc_obj(*in_dev);\n \tif (!in_dev)\n \t\tgoto out;\n-\tmemcpy(\u0026in_dev-\u003ecnf, dev_net(dev)-\u003eipv4.devconf_dflt,\n-\t\t\tsizeof(in_dev-\u003ecnf));\n+\n+\ttbl = arp_table(net);\n+\tmemcpy(\u0026in_dev-\u003ecnf, net-\u003eipv4.devconf_dflt, sizeof(in_dev-\u003ecnf));\n \tin_dev-\u003ecnf.sysctl = NULL;\n \tin_dev-\u003edev = dev;\n-\tin_dev-\u003earp_parms = neigh_parms_alloc(dev, \u0026arp_tbl);\n+\tin_dev-\u003earp_parms = neigh_parms_alloc(dev, tbl);\n \tif (!in_dev-\u003earp_parms)\n \t\tgoto out_kfree;\n \tif (IPV4_DEVCONF(in_dev-\u003ecnf, FORWARDING))\n@@ -291,7 +294,7 @@ static struct in_device *inetdev_init(struct net_device *dev)\n \t\terr = devinet_sysctl_register(in_dev);\n \t\tif (err) {\n \t\t\tin_dev-\u003edead = 1;\n-\t\t\tneigh_parms_release(\u0026arp_tbl, in_dev-\u003earp_parms);\n+\t\t\tneigh_parms_release(tbl, in_dev-\u003earp_parms);\n \t\t\tin_dev_put(in_dev);\n \t\t\tin_dev = NULL;\n \t\t\tgoto out;\n@@ -313,13 +316,12 @@ static struct in_device *inetdev_init(struct net_device *dev)\n \n static void inetdev_destroy(struct in_device *in_dev)\n {\n-\tstruct net_device *dev;\n+\tstruct net_device *dev = in_dev-\u003edev;\n+\tstruct net *net = dev_net(dev);\n \tstruct in_ifaddr *ifa;\n \n \tASSERT_RTNL();\n \n-\tdev = in_dev-\u003edev;\n-\n \tin_dev-\u003edead = 1;\n \n \tRCU_INIT_POINTER(dev-\u003eip_ptr, NULL);\n@@ -332,7 +334,7 @@ static void inetdev_destroy(struct in_device *in_dev)\n \t}\n \n \tdevinet_sysctl_unregister(in_dev);\n-\tneigh_parms_release(\u0026arp_tbl, in_dev-\u003earp_parms);\n+\tneigh_parms_release(arp_table(net), in_dev-\u003earp_parms);\n \tarp_ifdown(dev);\n \n \tin_dev_put(in_dev);\ndiff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c\nindex 78f84ae3ee120..cda150309dc6b 100644\n--- a/net/ipv4/fib_semantics.c\n+++ b/net/ipv4/fib_semantics.c\n@@ -610,13 +610,14 @@ static int fib_detect_death(struct fib_info *fi, int order,\n \t\t\t int dflt)\n {\n \tconst struct fib_nh_common *nhc = fib_info_nhc(fi, 0);\n-\tstruct neighbour *n;\n+\tstruct net *net = fi-\u003efib_net;\n \tint state = NUD_NONE;\n+\tstruct neighbour *n;\n \n \tif (likely(nhc-\u003enhc_gw_family == AF_INET))\n-\t\tn = neigh_lookup(\u0026arp_tbl, \u0026nhc-\u003enhc_gw.ipv4, nhc-\u003enhc_dev);\n+\t\tn = neigh_lookup(arp_table(net), \u0026nhc-\u003enhc_gw.ipv4, nhc-\u003enhc_dev);\n \telse if (IS_ENABLED(CONFIG_IPV6) \u0026\u0026 nhc-\u003enhc_gw_family == AF_INET6)\n-\t\tn = neigh_lookup(\u0026nd_tbl, \u0026nhc-\u003enhc_gw.ipv6, nhc-\u003enhc_dev);\n+\t\tn = neigh_lookup(nd_table(net), \u0026nhc-\u003enhc_gw.ipv6, nhc-\u003enhc_dev);\n \telse\n \t\tn = NULL;\n \ndiff --git a/net/ipv4/route.c b/net/ipv4/route.c\nindex fd688e1f879f5..b8b6b5d991294 100644\n--- a/net/ipv4/route.c\n+++ b/net/ipv4/route.c\n@@ -788,7 +788,7 @@ static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flow\n \n \tn = __ipv4_neigh_lookup(rt-\u003edst.dev, (__force u32)new_gw);\n \tif (!n)\n-\t\tn = neigh_create(\u0026arp_tbl, \u0026new_gw, rt-\u003edst.dev);\n+\t\tn = neigh_create(arp_table(net), \u0026new_gw, rt-\u003edst.dev);\n \tif (!IS_ERR(n)) {\n \t\tif (!(READ_ONCE(n-\u003enud_state) \u0026 NUD_VALID)) {\n \t\t\tneigh_event_send(n, NULL);\ndiff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c\nindex f6fa2715b450b..f3be7a8f996dd 100644\n--- a/net/ipv6/addrconf.c\n+++ b/net/ipv6/addrconf.c\n@@ -376,6 +376,8 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)\n \n static struct inet6_dev *ipv6_add_dev(struct net_device *dev)\n {\n+\tstruct net *net = dev_net(dev);\n+\tstruct neigh_table *tbl;\n \tstruct inet6_dev *ndev;\n \tint err = -ENOMEM;\n \n@@ -393,14 +395,15 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)\n \tndev-\u003edev = dev;\n \tINIT_LIST_HEAD(\u0026ndev-\u003eaddr_list);\n \ttimer_setup(\u0026ndev-\u003ers_timer, addrconf_rs_timer, 0);\n-\tmemcpy(\u0026ndev-\u003ecnf, dev_net(dev)-\u003eipv6.devconf_dflt, sizeof(ndev-\u003ecnf));\n+\tmemcpy(\u0026ndev-\u003ecnf, net-\u003eipv6.devconf_dflt, sizeof(ndev-\u003ecnf));\n \n \tif (ndev-\u003ecnf.stable_secret.initialized)\n \t\tndev-\u003ecnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;\n \n+\ttbl = nd_table(net);\n \tndev-\u003ecnf.mtu6 = dev-\u003emtu;\n \tndev-\u003era_mtu = 0;\n-\tndev-\u003end_parms = neigh_parms_alloc(dev, \u0026nd_tbl);\n+\tndev-\u003end_parms = neigh_parms_alloc(dev, tbl);\n \tif (!ndev-\u003end_parms) {\n \t\tkfree(ndev);\n \t\treturn ERR_PTR(err);\n@@ -413,7 +416,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)\n \tif (snmp6_alloc_dev(ndev) \u003c 0) {\n \t\tnetdev_dbg(dev, \"%s: cannot allocate memory for statistics\\n\",\n \t\t\t __func__);\n-\t\tneigh_parms_release(\u0026nd_tbl, ndev-\u003end_parms);\n+\t\tneigh_parms_release(tbl, ndev-\u003end_parms);\n \t\tnetdev_put(dev, \u0026ndev-\u003edev_tracker);\n \t\tkfree(ndev);\n \t\treturn ERR_PTR(err);\n@@ -481,7 +484,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)\n \treturn ndev;\n \n err_release:\n-\tneigh_parms_release(\u0026nd_tbl, ndev-\u003end_parms);\n+\tneigh_parms_release(tbl, ndev-\u003end_parms);\n \tndev-\u003edead = 1;\n \tin6_dev_finish_destroy(ndev);\n \treturn ERR_PTR(err);\n@@ -4038,9 +4041,11 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)\n \n \t/* Last: Shot the device (if unregistered) */\n \tif (unregister) {\n+\t\tstruct neigh_table *tbl = nd_table(net);\n+\n \t\taddrconf_sysctl_unregister(idev);\n-\t\tneigh_parms_release(\u0026nd_tbl, idev-\u003end_parms);\n-\t\tneigh_ifdown(\u0026nd_tbl, dev);\n+\t\tneigh_parms_release(tbl, idev-\u003end_parms);\n+\t\tneigh_ifdown(tbl, dev);\n \t\tin6_dev_put(idev);\n \t}\n \treturn 0;\ndiff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c\nindex 2c44e5ed61716..25fc57e52b5fa 100644\n--- a/net/ipv6/ip6_output.c\n+++ b/net/ipv6/ip6_output.c\n@@ -125,7 +125,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *\n \n \tif (IS_ERR_OR_NULL(neigh)) {\n \t\tif (unlikely(!neigh))\n-\t\t\tneigh = __neigh_create(\u0026nd_tbl, nexthop, dev, false);\n+\t\t\tneigh = __neigh_create(nd_table(net), nexthop, dev, false);\n \t\tif (IS_ERR(neigh)) {\n \t\t\tIP6_INC_STATS(net, idev, IPSTATS_MIB_OUTNOROUTES);\n \t\t\tkfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_CREATEFAIL);\n@@ -581,7 +581,7 @@ int ip6_forward(struct sk_buff *skb)\n \n \t/* XXX: idev-\u003ecnf.proxy_ndp? */\n \tif (READ_ONCE(net-\u003eipv6.devconf_all-\u003eproxy_ndp) \u0026\u0026\n-\t pneigh_lookup(\u0026nd_tbl, net, \u0026hdr-\u003edaddr, skb-\u003edev)) {\n+\t pneigh_lookup(nd_table(net), \u0026hdr-\u003edaddr, skb-\u003edev)) {\n \t\tint proxied = ip6_forward_proxy_check(skb);\n \n \t\thdr = ipv6_hdr(skb);\ndiff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c\nindex fe36b3f512850..3e16cb581f424 100644\n--- a/net/ipv6/ndisc.c\n+++ b/net/ipv6/ndisc.c\n@@ -766,10 +766,11 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)\n static int pndisc_is_router(const void *pkey,\n \t\t\t struct net_device *dev)\n {\n+\tstruct net *net = dev_net(dev);\n \tstruct pneigh_entry *n;\n \tint ret = -1;\n \n-\tn = pneigh_lookup(\u0026nd_tbl, dev_net(dev), pkey, dev);\n+\tn = pneigh_lookup(nd_table(net), pkey, dev);\n \tif (n)\n \t\tret = !!(READ_ONCE(n-\u003eflags) \u0026 NTF_ROUTER);\n \n@@ -787,19 +788,21 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh,\n \n static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\n {\n+\tu32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +\n+\t\t\t\t offsetof(struct nd_msg, opt));\n \tstruct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);\n \tconst struct in6_addr *saddr = \u0026ipv6_hdr(skb)-\u003esaddr;\n \tconst struct in6_addr *daddr = \u0026ipv6_hdr(skb)-\u003edaddr;\n-\tu8 *lladdr = NULL;\n-\tu32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +\n-\t\t\t\t offsetof(struct nd_msg, opt));\n-\tstruct ndisc_options ndopts;\n \tstruct net_device *dev = skb-\u003edev;\n-\tstruct inet6_ifaddr *ifp;\n+\tstruct net *net = dev_net(dev);\n+\tint dad = ipv6_addr_any(saddr);\n \tstruct inet6_dev *idev = NULL;\n+\tstruct ndisc_options ndopts;\n+\tstruct inet6_ifaddr *ifp;\n+\tstruct neigh_table *tbl;\n \tstruct neighbour *neigh;\n-\tint dad = ipv6_addr_any(saddr);\n \tint is_router = -1;\n+\tu8 *lladdr = NULL;\n \tSKB_DR(reason);\n \tu64 nonce = 0;\n \tbool inc;\n@@ -844,9 +847,10 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\n \tif (ndopts.nd_opts_nonce \u0026\u0026 ndopts.nd_opts_nonce-\u003end_opt_len == 1)\n \t\tmemcpy(\u0026nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);\n \n-\tinc = ipv6_addr_is_multicast(daddr);\n+\ttbl = nd_table(net);\n \n-\tifp = ipv6_get_ifaddr(dev_net(dev), \u0026msg-\u003etarget, dev, 1);\n+\tinc = ipv6_addr_is_multicast(daddr);\n+\tifp = ipv6_get_ifaddr(net, \u0026msg-\u003etarget, dev, 1);\n \tif (ifp) {\n have_ifp:\n \t\tif (ifp-\u003eflags \u0026 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {\n@@ -879,8 +883,6 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\n \n \t\tidev = ifp-\u003eidev;\n \t} else {\n-\t\tstruct net *net = dev_net(dev);\n-\n \t\t/* perhaps an address on the master device */\n \t\tif (netif_is_l3_slave(dev)) {\n \t\t\tstruct net_device *mdev;\n@@ -917,7 +919,7 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\n \t\t\t\t */\n \t\t\t\tstruct sk_buff *n = skb_clone(skb, GFP_ATOMIC);\n \t\t\t\tif (n)\n-\t\t\t\t\tpneigh_enqueue(\u0026nd_tbl, idev-\u003end_parms, n);\n+\t\t\t\t\tpneigh_enqueue(tbl, idev-\u003end_parms, n);\n \t\t\t\tgoto out;\n \t\t\t}\n \t\t} else {\n@@ -936,15 +938,15 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\n \t}\n \n \tif (inc)\n-\t\tNEIGH_CACHE_STAT_INC(\u0026nd_tbl, rcv_probes_mcast);\n+\t\tNEIGH_CACHE_STAT_INC(tbl, rcv_probes_mcast);\n \telse\n-\t\tNEIGH_CACHE_STAT_INC(\u0026nd_tbl, rcv_probes_ucast);\n+\t\tNEIGH_CACHE_STAT_INC(tbl, rcv_probes_ucast);\n \n \t/*\n \t *\tupdate / create cache entry\n \t *\tfor the source address\n \t */\n-\tneigh = __neigh_lookup(\u0026nd_tbl, saddr, dev,\n+\tneigh = __neigh_lookup(tbl, saddr, dev,\n \t\t\t !inc || lladdr || !dev-\u003eaddr_len);\n \tif (neigh)\n \t\tndisc_update(dev, neigh, lladdr, NUD_STALE,\n@@ -986,17 +988,19 @@ static int accept_untracked_na(struct inet6_dev *idev, struct in6_addr *saddr)\n \n static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n {\n-\tstruct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);\n-\tstruct in6_addr *saddr = \u0026ipv6_hdr(skb)-\u003esaddr;\n-\tconst struct in6_addr *daddr = \u0026ipv6_hdr(skb)-\u003edaddr;\n-\tu8 *lladdr = NULL;\n \tu32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +\n \t\t\t\t offsetof(struct nd_msg, opt));\n-\tstruct ndisc_options ndopts;\n+\tstruct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);\n+\tconst struct in6_addr *daddr = \u0026ipv6_hdr(skb)-\u003edaddr;\n+\tstruct in6_addr *saddr = \u0026ipv6_hdr(skb)-\u003esaddr;\n \tstruct net_device *dev = skb-\u003edev;\n-\tstruct inet6_dev *idev = __in6_dev_get(dev);\n+\tstruct net *net = dev_net(dev);\n+\tstruct ndisc_options ndopts;\n \tstruct inet6_ifaddr *ifp;\n+\tstruct neigh_table *tbl;\n \tstruct neighbour *neigh;\n+\tstruct inet6_dev *idev;\n+\tu8 *lladdr = NULL;\n \tSKB_DR(reason);\n \tu8 new_state;\n \n@@ -1019,6 +1023,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n \t * and thus should not be accepted.\n \t * drop_unsolicited_na takes precedence over accept_untracked_na\n \t */\n+\tidev = __in6_dev_get(dev);\n \tif (!msg-\u003eicmph.icmp6_solicited \u0026\u0026 idev \u0026\u0026\n \t READ_ONCE(idev-\u003ecnf.drop_unsolicited_na))\n \t\treturn reason;\n@@ -1033,7 +1038,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n \t\t\treturn reason;\n \t\t}\n \t}\n-\tifp = ipv6_get_ifaddr(dev_net(dev), \u0026msg-\u003etarget, dev, 1);\n+\tifp = ipv6_get_ifaddr(net, \u0026msg-\u003etarget, dev, 1);\n \tif (ifp) {\n \t\tif (skb-\u003epkt_type != PACKET_LOOPBACK\n \t\t \u0026\u0026 (ifp-\u003eflags \u0026 IFA_F_TENTATIVE)) {\n@@ -1057,7 +1062,8 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n \t\treturn reason;\n \t}\n \n-\tneigh = neigh_lookup(\u0026nd_tbl, \u0026msg-\u003etarget, dev);\n+\ttbl = nd_table(net);\n+\tneigh = neigh_lookup(tbl, \u0026msg-\u003etarget, dev);\n \n \t/* RFC 9131 updates original Neighbour Discovery RFC 4861.\n \t * NAs with Target LL Address option without a corresponding\n@@ -1077,14 +1083,13 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n \tnew_state = msg-\u003eicmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE;\n \tif (!neigh \u0026\u0026 lladdr \u0026\u0026 idev \u0026\u0026 READ_ONCE(idev-\u003ecnf.forwarding)) {\n \t\tif (accept_untracked_na(idev, saddr)) {\n-\t\t\tneigh = neigh_create(\u0026nd_tbl, \u0026msg-\u003etarget, dev);\n+\t\t\tneigh = neigh_create(tbl, \u0026msg-\u003etarget, dev);\n \t\t\tnew_state = NUD_STALE;\n \t\t}\n \t}\n \n \tif (neigh \u0026\u0026 !IS_ERR(neigh)) {\n \t\tu8 old_flags = neigh-\u003eflags;\n-\t\tstruct net *net = dev_net(dev);\n \n \t\tif (READ_ONCE(neigh-\u003enud_state) \u0026 NUD_FAILED)\n \t\t\tgoto out;\n@@ -1097,7 +1102,7 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n \t\tif (lladdr \u0026\u0026 !memcmp(lladdr, dev-\u003edev_addr, dev-\u003eaddr_len) \u0026\u0026\n \t\t READ_ONCE(net-\u003eipv6.devconf_all-\u003eforwarding) \u0026\u0026\n \t\t READ_ONCE(net-\u003eipv6.devconf_all-\u003eproxy_ndp) \u0026\u0026\n-\t\t pneigh_lookup(\u0026nd_tbl, net, \u0026msg-\u003etarget, dev)) {\n+\t\t pneigh_lookup(tbl, \u0026msg-\u003etarget, dev)) {\n \t\t\t/* XXX: idev-\u003ecnf.proxy_ndp */\n \t\t\tgoto out;\n \t\t}\n@@ -1126,18 +1131,20 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)\n static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)\n {\n \tstruct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);\n+\tconst struct in6_addr *saddr = \u0026ipv6_hdr(skb)-\u003esaddr;\n \tunsigned long ndoptlen = skb-\u003elen - sizeof(*rs_msg);\n+\tstruct net_device *dev = skb-\u003edev;\n+\tstruct ndisc_options ndopts;\n+\tstruct neigh_table *tbl;\n \tstruct neighbour *neigh;\n \tstruct inet6_dev *idev;\n-\tconst struct in6_addr *saddr = \u0026ipv6_hdr(skb)-\u003esaddr;\n-\tstruct ndisc_options ndopts;\n \tu8 *lladdr = NULL;\n \tSKB_DR(reason);\n \n \tif (skb-\u003elen \u003c sizeof(*rs_msg))\n \t\treturn SKB_DROP_REASON_PKT_TOO_SMALL;\n \n-\tidev = __in6_dev_get(skb-\u003edev);\n+\tidev = __in6_dev_get(dev);\n \tif (!idev) {\n \t\tnet_err_ratelimited(\"RS: can't find in6 device\\n\");\n \t\treturn reason;\n@@ -1155,19 +1162,19 @@ static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)\n \t\tgoto out;\n \n \t/* Parse ND options */\n-\tif (!ndisc_parse_options(skb-\u003edev, rs_msg-\u003eopt, ndoptlen, \u0026ndopts))\n+\tif (!ndisc_parse_options(dev, rs_msg-\u003eopt, ndoptlen, \u0026ndopts))\n \t\treturn SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;\n \n \tif (ndopts.nd_opts_src_lladdr) {\n-\t\tlladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,\n-\t\t\t\t\t skb-\u003edev);\n+\t\tlladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);\n \t\tif (!lladdr)\n \t\t\tgoto out;\n \t}\n \n-\tneigh = __neigh_lookup(\u0026nd_tbl, saddr, skb-\u003edev, 1);\n+\ttbl = nd_table(dev_net(dev));\n+\tneigh = __neigh_lookup(tbl, saddr, dev, 1);\n \tif (neigh) {\n-\t\tndisc_update(skb-\u003edev, neigh, lladdr, NUD_STALE,\n+\t\tndisc_update(dev, neigh, lladdr, NUD_STALE,\n \t\t\t NEIGH_UPDATE_F_WEAK_OVERRIDE|\n \t\t\t NEIGH_UPDATE_F_OVERRIDE|\n \t\t\t NEIGH_UPDATE_F_OVERRIDE_ISROUTER,\n@@ -1231,6 +1238,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)\n static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)\n {\n \tstruct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);\n+\tstruct net *net = dev_net(skb-\u003edev);\n \tbool send_ifinfo_notify = false;\n \tstruct neighbour *neigh = NULL;\n \tstruct ndisc_options ndopts;\n@@ -1240,7 +1248,6 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)\n \tu32 defrtr_usr_metric;\n \tunsigned int pref = 0;\n \t__u32 old_if_flags;\n-\tstruct net *net;\n \tSKB_DR(reason);\n \tint lifetime;\n \tint optlen;\n@@ -1329,7 +1336,6 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)\n \t/* Do not accept RA with source-addr found on local machine unless\n \t * accept_ra_from_local is set to true.\n \t */\n-\tnet = dev_net(in6_dev-\u003edev);\n \tif (!READ_ONCE(in6_dev-\u003ecnf.accept_ra_from_local) \u0026\u0026\n \t ipv6_chk_addr(net, \u0026ipv6_hdr(skb)-\u003esaddr, in6_dev-\u003edev, 0)) {\n \t\tnet_dbg_ratelimited(\"RA from local address detected on dev: %s: default router ignored\\n\",\n@@ -1465,7 +1471,7 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)\n \t */\n \n \tif (!neigh)\n-\t\tneigh = __neigh_lookup(\u0026nd_tbl, \u0026ipv6_hdr(skb)-\u003esaddr,\n+\t\tneigh = __neigh_lookup(nd_table(net), \u0026ipv6_hdr(skb)-\u003esaddr,\n \t\t\t\t skb-\u003edev, 1);\n \tif (neigh) {\n \t\tu8 *lladdr = NULL;\n@@ -1858,12 +1864,15 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,\n \tstruct net_device *dev = netdev_notifier_info_to_dev(ptr);\n \tstruct netdev_notifier_change_info *change_info;\n \tstruct net *net = dev_net(dev);\n+\tstruct neigh_table *tbl;\n \tstruct inet6_dev *idev;\n \tbool evict_nocarrier;\n \n+\ttbl = nd_table(net);\n+\n \tswitch (event) {\n \tcase NETDEV_CHANGEADDR:\n-\t\tneigh_changeaddr(\u0026nd_tbl, dev);\n+\t\tneigh_changeaddr(tbl, dev);\n \t\tfib6_run_gc(0, net, false);\n \t\tfallthrough;\n \tcase NETDEV_UP:\n@@ -1887,12 +1896,12 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,\n \n \t\tchange_info = ptr;\n \t\tif (change_info-\u003eflags_changed \u0026 IFF_NOARP)\n-\t\t\tneigh_changeaddr(\u0026nd_tbl, dev);\n+\t\t\tneigh_changeaddr(tbl, dev);\n \t\tif (evict_nocarrier \u0026\u0026 !netif_carrier_ok(dev))\n-\t\t\tneigh_carrier_down(\u0026nd_tbl, dev);\n+\t\t\tneigh_carrier_down(tbl, dev);\n \t\tbreak;\n \tcase NETDEV_DOWN:\n-\t\tneigh_ifdown(\u0026nd_tbl, dev);\n+\t\tneigh_ifdown(tbl, dev);\n \t\tfib6_run_gc(0, net, false);\n \t\tbreak;\n \tcase NETDEV_NOTIFY_PEERS:\n@@ -1971,12 +1980,23 @@ static int __net_init ndisc_net_init(struct net *net)\n \tstruct sock *sk;\n \tint err;\n \n+\terr = neigh_table_register(net, \u0026nd_tbl, NEIGH_ND_TABLE);\n+\tif (err)\n+\t\tgoto err;\n+\n+#ifdef CONFIG_SYSCTL\n+\terr = neigh_sysctl_register(NULL, \u0026nd_table(net)-\u003eparms,\n+\t\t\t\t ndisc_ifinfo_sysctl_change);\n+\tif (err)\n+\t\tgoto err_sysctl;\n+#endif\n+\n \terr = inet_ctl_sock_create(\u0026sk, PF_INET6,\n \t\t\t\t SOCK_RAW, IPPROTO_ICMPV6, net);\n \tif (err \u003c 0) {\n \t\tnet_err_ratelimited(\"NDISC: Failed to initialize the control socket (err %d)\\n\",\n \t\t\t\t err);\n-\t\treturn err;\n+\t\tgoto err_sock_create;\n \t}\n \n \tnet-\u003eipv6.ndisc_sk = sk;\n@@ -1987,11 +2007,24 @@ static int __net_init ndisc_net_init(struct net *net)\n \tinet6_clear_bit(MC6_LOOP, sk);\n \n \treturn 0;\n+\n+err_sock_create:\n+#ifdef CONFIG_SYSCTL\n+\tneigh_sysctl_unregister(\u0026nd_table(net)-\u003eparms);\n+err_sysctl:\n+#endif\n+\tneigh_table_unregister(net, NEIGH_ND_TABLE);\n+err:\n+\treturn err;\n }\n \n static void __net_exit ndisc_net_exit(struct net *net)\n {\n \tinet_ctl_sock_destroy(net-\u003eipv6.ndisc_sk);\n+#ifdef CONFIG_SYSCTL\n+\tneigh_sysctl_unregister(\u0026nd_table(net)-\u003eparms);\n+#endif\n+\tneigh_table_unregister(net, NEIGH_ND_TABLE);\n }\n \n static struct pernet_operations ndisc_net_ops = {\n@@ -2001,30 +2034,7 @@ static struct pernet_operations ndisc_net_ops = {\n \n int __init ndisc_init(void)\n {\n-\tint err;\n-\n-\terr = register_pernet_subsys(\u0026ndisc_net_ops);\n-\tif (err)\n-\t\treturn err;\n-\t/*\n-\t * Initialize the neighbour table\n-\t */\n-\tneigh_table_init(NEIGH_ND_TABLE, \u0026nd_tbl);\n-\n-#ifdef CONFIG_SYSCTL\n-\terr = neigh_sysctl_register(NULL, \u0026nd_tbl.parms,\n-\t\t\t\t ndisc_ifinfo_sysctl_change);\n-\tif (err)\n-\t\tgoto out_unregister_pernet;\n-out:\n-#endif\n-\treturn err;\n-\n-#ifdef CONFIG_SYSCTL\n-out_unregister_pernet:\n-\tunregister_pernet_subsys(\u0026ndisc_net_ops);\n-\tgoto out;\n-#endif\n+\treturn register_pernet_subsys(\u0026ndisc_net_ops);\n }\n \n int __init ndisc_late_init(void)\n@@ -2039,9 +2049,5 @@ void ndisc_late_cleanup(void)\n \n void ndisc_cleanup(void)\n {\n-#ifdef CONFIG_SYSCTL\n-\tneigh_sysctl_unregister(\u0026nd_tbl.parms);\n-#endif\n-\tneigh_table_clear(NEIGH_ND_TABLE, \u0026nd_tbl);\n \tunregister_pernet_subsys(\u0026ndisc_net_ops);\n }\ndiff --git a/net/ipv6/route.c b/net/ipv6/route.c\nindex 5968ce5ad1508..cea1cdc2b8e8e 100644\n--- a/net/ipv6/route.c\n+++ b/net/ipv6/route.c\n@@ -217,7 +217,7 @@ struct neighbour *ip6_neigh_lookup(const struct in6_addr *gw,\n \tif (n)\n \t\treturn n;\n \n-\tn = neigh_create(\u0026nd_tbl, daddr, dev);\n+\tn = neigh_create(nd_table(dev_net(dev)), daddr, dev);\n \treturn IS_ERR(n) ? NULL : n;\n }\n \n@@ -4229,6 +4229,7 @@ static int ip6_route_del(struct fib6_config *cfg,\n static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb)\n {\n \tstruct netevent_redirect netevent;\n+\tstruct net_device *dev = skb-\u003edev;\n \tstruct rt6_info *rt, *nrt = NULL;\n \tstruct fib6_result res = {};\n \tstruct ndisc_options ndopts;\n@@ -4262,7 +4263,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu\n \t\treturn;\n \t}\n \n-\tin6_dev = __in6_dev_get(skb-\u003edev);\n+\tin6_dev = __in6_dev_get(dev);\n \tif (!in6_dev)\n \t\treturn;\n \tif (READ_ONCE(in6_dev-\u003ecnf.forwarding) ||\n@@ -4274,15 +4275,14 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu\n \t *\tfirst-hop router for the specified ICMP Destination Address.\n \t */\n \n-\tif (!ndisc_parse_options(skb-\u003edev, msg-\u003eopt, optlen, \u0026ndopts)) {\n+\tif (!ndisc_parse_options(dev, msg-\u003eopt, optlen, \u0026ndopts)) {\n \t\tnet_dbg_ratelimited(\"rt6_redirect: invalid ND options\\n\");\n \t\treturn;\n \t}\n \n \tlladdr = NULL;\n \tif (ndopts.nd_opts_tgt_lladdr) {\n-\t\tlladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,\n-\t\t\t\t\t skb-\u003edev);\n+\t\tlladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);\n \t\tif (!lladdr) {\n \t\t\tnet_dbg_ratelimited(\"rt6_redirect: invalid link-layer address length\\n\");\n \t\t\treturn;\n@@ -4301,7 +4301,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu\n \t */\n \tdst_confirm_neigh(\u0026rt-\u003edst, \u0026ipv6_hdr(skb)-\u003esaddr);\n \n-\tneigh = __neigh_lookup(\u0026nd_tbl, \u0026msg-\u003etarget, skb-\u003edev, 1);\n+\tneigh = __neigh_lookup(nd_table(dev_net(dev)), \u0026msg-\u003etarget, dev, 1);\n \tif (!neigh)\n \t\treturn;\n \n@@ -4309,7 +4309,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu\n \t *\tWe have finally decided to accept it.\n \t */\n \n-\tndisc_update(skb-\u003edev, neigh, lladdr, NUD_STALE,\n+\tndisc_update(dev, neigh, lladdr, NUD_STALE,\n \t\t NEIGH_UPDATE_F_WEAK_OVERRIDE|\n \t\t NEIGH_UPDATE_F_OVERRIDE|\n \t\t (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER|\n@@ -5030,9 +5030,11 @@ void rt6_sync_down_dev(struct net_device *dev, unsigned long event)\n \n void rt6_disable_ip(struct net_device *dev, unsigned long event)\n {\n+\tstruct net *net = dev_net(dev);\n+\n \trt6_sync_down_dev(dev, event);\n \trt6_uncached_list_flush_dev(dev);\n-\tneigh_ifdown(\u0026nd_tbl, dev);\n+\tneigh_ifdown(nd_table(net), dev);\n }\n \n struct rt6_mtu_change_arg {\ndiff --git a/tools/testing/selftests/net/test_neigh.sh b/tools/testing/selftests/net/test_neigh.sh\nindex 7c594bf6ead0d..286da09a2010f 100755\n--- a/tools/testing/selftests/net/test_neigh.sh\n+++ b/tools/testing/selftests/net/test_neigh.sh\n@@ -241,18 +241,18 @@ extern_valid_common()\n \tlocal orig_thresh3\n \n \trun_cmd \"ip -n $ns1 neigh flush dev veth0\"\n-\torig_thresh1=$(ip -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"thresh1\"]')\n-\torig_thresh2=$(ip -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh2\")) | .[\"thresh2\"]')\n-\torig_thresh3=$(ip -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh3\")) | .[\"thresh3\"]')\n-\trun_cmd \"ip ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8\"\n+\torig_thresh1=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"thresh1\"]')\n+\torig_thresh2=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh2\")) | .[\"thresh2\"]')\n+\torig_thresh3=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh3\")) | .[\"thresh3\"]')\n+\trun_cmd \"ip -n $ns1 ntable change name $tbl_name thresh3 10 thresh2 9 thresh1 8\"\n \trun_cmd \"ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid\"\n \trun_cmd \"ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0\"\n \trun_cmd \"sleep 5\"\n-\tforced_gc_runs_t0=$(ip -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"forced_gc_runs\")) | .[\"forced_gc_runs\"]')\n+\tforced_gc_runs_t0=$(ip -n \"$ns1\" -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"forced_gc_runs\")) | .[\"forced_gc_runs\"]')\n \tfor i in {1..20}; do\n \t\trun_cmd \"ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0\"\n \tdone\n-\tforced_gc_runs_t1=$(ip -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"forced_gc_runs\")) | .[\"forced_gc_runs\"]')\n+\tforced_gc_runs_t1=$(ip -n \"$ns1\" -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"forced_gc_runs\")) | .[\"forced_gc_runs\"]')\n \tif [[ $forced_gc_runs_t1 -eq $forced_gc_runs_t0 ]]; then\n \t\tcheck_err 1 \"Forced garbage collection did not run\"\n \tfi\n@@ -263,7 +263,7 @@ extern_valid_common()\n \n \tlog_test \"$af_str \\\"extern_valid\\\" flag: Forced garbage collection\"\n \n-\trun_cmd \"ip ntable change name $tbl_name thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1\"\n+\trun_cmd \"ip -n $ns1 ntable change name $tbl_name thresh3 $orig_thresh3 thresh2 $orig_thresh2 thresh1 $orig_thresh1\"\n \n \tRET=0\n \n@@ -285,9 +285,9 @@ extern_valid_common()\n \tlocal orig_gc_stale\n \n \trun_cmd \"ip -n $ns1 neigh flush dev veth0\"\n-\torig_thresh1=$(ip -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"thresh1\"]')\n-\torig_base_reachable=$(ip -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"base_reachable\"]')\n-\trun_cmd \"ip ntable change name $tbl_name thresh1 10 base_reachable 10000\"\n+\torig_thresh1=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"thresh1\"]')\n+\torig_base_reachable=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" | jq '.[] | select(has(\"thresh1\")) | .[\"base_reachable\"]')\n+\trun_cmd \"ip -n $ns1 ntable change name $tbl_name thresh1 10 base_reachable 10000\"\n \torig_gc_stale=$(ip -n \"$ns1\" -j ntable show name \"$tbl_name\" dev veth0 | jq '.[][\"gc_stale\"]')\n \trun_cmd \"ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale 1000\"\n \trun_cmd \"ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid\"\n@@ -297,9 +297,9 @@ extern_valid_common()\n \tfor i in {1..20}; do\n \t\trun_cmd \"ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0\"\n \tdone\n-\tperiodic_gc_runs_t0=$(ip -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"periodic_gc_runs\")) | .[\"periodic_gc_runs\"]')\n+\tperiodic_gc_runs_t0=$(ip -n \"$ns1\" -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"periodic_gc_runs\")) | .[\"periodic_gc_runs\"]')\n \trun_cmd \"sleep 10\"\n-\tperiodic_gc_runs_t1=$(ip -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"periodic_gc_runs\")) | .[\"periodic_gc_runs\"]')\n+\tperiodic_gc_runs_t1=$(ip -n \"$ns1\" -j -s ntable show name \"$tbl_name\" | jq '.[] | select(has(\"periodic_gc_runs\")) | .[\"periodic_gc_runs\"]')\n \t[[ $periodic_gc_runs_t1 -ne $periodic_gc_runs_t0 ]]\n \tcheck_err $? \"Periodic garbage collection did not run\"\n \trun_cmd \"ip -n $ns1 neigh get $ip_addr dev veth0 | grep \\\"extern_valid\\\"\"\n@@ -310,7 +310,7 @@ extern_valid_common()\n \tlog_test \"$af_str \\\"extern_valid\\\" flag: Periodic garbage collection\"\n \n \trun_cmd \"ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale $orig_gc_stale\"\n-\trun_cmd \"ip ntable change name $tbl_name thresh1 $orig_thresh1 base_reachable $orig_base_reachable\"\n+\trun_cmd \"ip -n $ns1 ntable change name $tbl_name thresh1 $orig_thresh1 base_reachable $orig_base_reachable\"\n }\n \n extern_valid_ipv4()\n"
} |
| 2/1 | 2026/08/07 23:53 | agent | patch-evaluator |
7mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"NET_NS",
"IPV6",
"INET"
],
"FocusSymbols": [
"neigh_table_register",
"neigh_table_unregister",
"arp_rcv",
"ndisc_rcv",
"pneigh_create",
"pneigh_delete",
"pneigh_lookup",
"neigh_xmit"
],
"Reasoning": "The patch changes the IPv4 ARP and IPv6 Neighbor Discovery tables from being global to being per-network namespace. This is a significant functional change to the core networking stack, affecting how neighbor tables are initialized, accessed, and destroyed. It impacts network namespace creation/destruction, ARP/ND packet processing, and netlink neighbor management. Fuzzing should focus on namespace operations, ARP/ND packet reception, and proxy neighbor management.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit a5688afda8d14b0a24271682d891e388f5701c90
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 7 23:53:29 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 16a015b672063..895a3e3f7310b 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1378,7 +1378,8 @@ struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
return neigh;
}
-static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv,
+ int gc_interval)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
@@ -1397,7 +1398,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
goto out_unlock;
/* neigh is obsolete if it was idle for two GC periods */
- dt = 2 * arp_tbl.gc_interval;
+ dt = 2 * gc_interval;
neigh_obsolete = jiffies - dt;
for (i = 0; i < htbl->size; i++) {
@@ -1433,11 +1434,16 @@ static void ipoib_reap_neigh(struct work_struct *work)
{
struct ipoib_dev_priv *priv =
container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
+ struct net_device *dev = priv->dev;
+ struct net *net = dev_net(dev);
+ struct neigh_table *tbl;
+ int gc_interval;
- __ipoib_reap_neigh(priv);
+ tbl = arp_table(net);
+ gc_interval = tbl->gc_interval;
+ __ipoib_reap_neigh(priv, gc_interval);
- queue_delayed_work(priv->wq, &priv->neigh_reap_task,
- arp_tbl.gc_interval);
+ queue_delayed_work(priv->wq, &priv->neigh_reap_task, gc_interval);
}
@@ -1590,8 +1596,11 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
- struct ipoib_neigh_hash *htbl;
struct ipoib_neigh __rcu **buckets;
+ struct net_device *dev = priv->dev;
+ struct net *net = dev_net(dev);
+ struct ipoib_neigh_hash *htbl;
+ struct neigh_table *tbl;
u32 size;
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
@@ -1599,7 +1608,9 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl = kzalloc_obj(*htbl);
if (!htbl)
return -ENOMEM;
- size = roundup_pow_of_two(arp_tbl.gc_thresh3);
+
+ tbl = arp_table(net);
+ size = roundup_pow_of_two(tbl->gc_thresh3);
buckets = kvzalloc_objs(*buckets, size);
if (!buckets) {
kfree(htbl);
@@ -1614,7 +1625,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
/* start garbage collection */
queue_delayed_work(priv->wq, &priv->neigh_reap_task,
- arp_tbl.gc_interval);
+ tbl->gc_interval);
return 0;
}
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
index 0c4f462baa6ef..ba45b61b09bb0 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 648f4521c0964..6a8d8aac7d16f 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 8b827201935eb..67c12ca19d59d 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 db260e3d1412f..37a8ddee3ea1e 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 3d6fdbab05e01..f4a435885ba43 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 ae63d549b5429..1cd8347c274d4 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 ca30702f88780..d650d33e3709c 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 84a55f2b48ffe..0fcd65fc0af7f 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 15d19a8a17101..ead8b447b89c4 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 b84235e93ffe9..793a562fc1f7c 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 c2ad3a358d201..152c4639ecc6f 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 a0557a3a70260..50ec04cc0213f 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 fd5505153d0b6..345d1883d993e 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;
@@ -2047,7 +2048,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;
@@ -2100,6 +2101,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct neigh_table *tbl;
struct neighbour *n;
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@@ -2113,8 +2115,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,
@@ -2139,8 +2143,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 e8747e0713c79..e932def63d62f 100644
--- a/include/net/arp.h
+++ b/include/net/arp.h
@@ -7,8 +7,10 @@
#include <linux/hash.h>
#include <net/neighbour.h>
-
-extern struct neigh_table arp_tbl;
+static inline struct neigh_table *arp_table(struct net *net)
+{
+ return net->neigh_tables[NEIGH_ARP_TABLE];
+}
static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 *hash_rnd)
{
@@ -21,10 +23,12 @@ static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32
#ifdef CONFIG_INET
static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
{
+ struct neigh_table *tbl = arp_table(dev_net(dev));
+
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
key = INADDR_ANY;
- return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev);
+ return ___neigh_lookup_noref(tbl, neigh_key_eq32, arp_hashfn, &key, dev);
}
#else
static inline
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 3da1a6f8d3f92..5b7a36cbda169 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 8860cc2175fc1..954c13b3a2b4f 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -179,7 +179,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 {
@@ -339,8 +338,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 +388,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 501af1999fe83..f96a390ae5364 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -39,6 +39,7 @@
#include <net/netns/mctp.h>
#include <net/netns/vsock.h>
#include <net/net_trackers.h>
+#include <net/neighbour_tables.h>
#include <linux/ns_common.h>
#include <linux/idr.h>
#include <linux/skbuff.h>
@@ -47,6 +48,7 @@
struct user_namespace;
struct proc_dir_entry;
+struct neigh_table;
struct net_device;
struct sock;
struct ctl_table_header;
@@ -103,6 +105,8 @@ struct net {
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
+ struct neigh_table *neigh_tables[NEIGH_NR_TABLES];
+
#ifdef CONFIG_SYSCTL
struct ctl_table_set sysctls;
#endif
diff --git a/include/net/route.h b/include/net/route.h
index f90106f383c56..38a23f28ee14a 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -403,8 +403,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 23eb6931a2b4a..3b4cd709e70ea 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -192,7 +192,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;
@@ -469,7 +469,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;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb642..e22b0fe4b9521 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -131,10 +131,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 +149,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 +165,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 +178,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 +334,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 +345,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);
@@ -412,6 +394,9 @@ static void neigh_flush_one(struct neighbour *n)
write_unlock(&n->lock);
+ if (!check_net(neigh_parms_net(n->parms)))
+ timer_shutdown_sync(&n->timer);
+
neigh_cleanup_and_release(n);
}
@@ -471,8 +456,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;
@@ -752,8 +736,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 +749,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 +758,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 +769,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 +780,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 +812,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 +827,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);
@@ -1523,10 +1503,23 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
write_unlock_bh(&neigh->lock);
- if (((new ^ old) & NUD_PERMANENT) || gc_update)
- neigh_update_gc_list(neigh);
- if (managed_update)
- neigh_update_managed_list(neigh);
+ gc_update |= !!((new ^ old) & NUD_PERMANENT);
+ if (gc_update || managed_update) {
+ spin_lock_bh(&neigh->tbl->lock);
+
+ if (!neigh->dead) {
+ write_lock(&neigh->lock);
+
+ if (gc_update)
+ neigh_update_gc_list(neigh);
+ if (managed_update)
+ neigh_update_managed_list(neigh);
+
+ write_unlock(&neigh->lock);
+ }
+
+ spin_unlock_bh(&neigh->tbl->lock);
+ }
if (notify)
call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
@@ -1540,7 +1533,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 +1550,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 +1562,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 +1614,6 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL);
goto out;
}
-EXPORT_SYMBOL(neigh_resolve_output);
/* As fast as possible without hh cache */
@@ -1741,16 +1730,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 +1777,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 +1799,143 @@ 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);
+ RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
+ if (!tbl->nht)
+ goto err_hash;
- 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;
+ phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
+ tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
+ if (!tbl->phash_buckets)
+ goto err_phash;
+
+ tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
+ tbl->key_len, NEIGH_PRIV_ALIGN);
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
- panic("cannot create neighbour cache statistics");
+ goto err_stats;
#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");
+ 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
- RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
-
- phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
- tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
-
- if (!tbl->nht || !tbl->phash_buckets)
- panic("cannot allocate neighbour cache hashes");
-
- 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->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;
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);
+ 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);
- timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
- skb_queue_head_init_class(&tbl->proxy_queue,
- &neigh_table_proxy_queue_class);
-
- tbl->last_flush = now;
- tbl->last_rand = now + tbl->parms.reachable_time * 20;
+ return 0;
- rcu_assign_pointer(neigh_tables[index], tbl);
+#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_clear(struct net *net, struct neigh_table *tbl)
{
- RCU_INIT_POINTER(neigh_tables[index], NULL);
- synchronize_rcu();
+ struct neigh_hash_table *nht;
- /* 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);
+ timer_shutdown_sync(&tbl->proxy_timer);
+
neigh_ifdown(tbl, NULL);
- if (atomic_read(&tbl->entries))
- pr_crit("neighbour leakage\n");
+ DEBUG_NET_WARN_ON_ONCE(atomic_read(&tbl->entries));
- call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
- neigh_hash_free_rcu);
- tbl->nht = NULL;
+ remove_proc_entry(tbl->id, net->proc_net_stat);
+
+ free_percpu(tbl->stats);
+ tbl->stats = NULL;
kfree(tbl->phash_buckets);
tbl->phash_buckets = NULL;
- remove_proc_entry(tbl->id, init_net.proc_net_stat);
+ nht = rcu_dereference_protected(tbl->nht, 1);
+ tbl->nht = NULL;
+ neigh_hash_free_rcu(&nht->rcu);
+}
- free_percpu(tbl->stats);
- tbl->stats = NULL;
+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;
+ }
+
+ 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);
+ kfree(tbl);
}
-static struct neigh_table *neigh_find_table(int family)
+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 +1989,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 +1999,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 +2075,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 +2095,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;
}
@@ -2400,10 +2417,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 +2432,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (!found) {
- rcu_read_unlock();
err = -ENOENT;
goto errout;
}
@@ -2442,8 +2456,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 +2538,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 +2554,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 +2605,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 +2620,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 +2797,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 +2813,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 +2837,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 +2851,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 +2937,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 +2961,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 +3083,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 +3110,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 +3163,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 +3171,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 +3218,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 +3306,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 +3313,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 +3326,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 +3389,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 +3415,6 @@ void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
++(*pos);
return rc;
}
-EXPORT_SYMBOL(neigh_seq_next);
void neigh_seq_stop(struct seq_file *seq, void *v)
__releases(tbl->lock)
@@ -3469,7 +3426,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 */
@@ -3593,7 +3549,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 +3644,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 +3653,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 +3672,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 +3878,6 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
err:
return -ENOBUFS;
}
-EXPORT_SYMBOL(neigh_sysctl_register);
void neigh_sysctl_unregister(struct neigh_parms *p)
{
@@ -3937,7 +3888,6 @@ void neigh_sysctl_unregister(struct neigh_parms *p)
kfree(t);
}
}
-EXPORT_SYMBOL(neigh_sysctl_unregister);
#endif /* CONFIG_SYSCTL */
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index 4df76ff50699e..4f511476b992c 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 d409f606aec0e..90bc53fb80905 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 neigh_table *tbl = arp_table(net);
+ struct dst_entry *reply_dst = NULL;
struct net_device *dev = skb->dev;
- struct in_device *in_dev = __in_dev_get_rcu(dev);
- struct arphdr *arp;
+ 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;
- unsigned char *sha;
- unsigned char *tha = NULL;
__be32 sip, tip;
- u16 dev_type = dev->type;
int addr_type;
- struct neighbour *n;
- struct dst_entry *reply_dst = NULL;
- bool is_garp = false;
/* 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 47ded0f607d4b..05de597ce086f 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 @@ static struct in_device *inetdev_init(struct net_device *dev)
static void inetdev_destroy(struct in_device *in_dev)
{
- struct net_device *dev;
+ struct net_device *dev = in_dev->dev;
+ struct net *net = dev_net(dev);
struct in_ifaddr *ifa;
ASSERT_RTNL();
- dev = in_dev->dev;
-
in_dev->dead = 1;
RCU_INIT_POINTER(dev->ip_ptr, NULL);
@@ -332,7 +334,7 @@ static void inetdev_destroy(struct in_device *in_dev)
}
devinet_sysctl_unregister(in_dev);
- neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+ neigh_parms_release(arp_table(net), in_dev->arp_parms);
arp_ifdown(dev);
in_dev_put(in_dev);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 78f84ae3ee120..cda150309dc6b 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 fd688e1f879f5..b8b6b5d991294 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -788,7 +788,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 f6fa2715b450b..f3be7a8f996dd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -376,6 +376,8 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{
+ struct net *net = dev_net(dev);
+ struct neigh_table *tbl;
struct inet6_dev *ndev;
int err = -ENOMEM;
@@ -393,14 +395,15 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
ndev->dev = dev;
INIT_LIST_HEAD(&ndev->addr_list);
timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0);
- memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
+ memcpy(&ndev->cnf, net->ipv6.devconf_dflt, sizeof(ndev->cnf));
if (ndev->cnf.stable_secret.initialized)
ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ tbl = nd_table(net);
ndev->cnf.mtu6 = dev->mtu;
ndev->ra_mtu = 0;
- ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+ ndev->nd_parms = neigh_parms_alloc(dev, tbl);
if (!ndev->nd_parms) {
kfree(ndev);
return ERR_PTR(err);
@@ -413,7 +416,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
if (snmp6_alloc_dev(ndev) < 0) {
netdev_dbg(dev, "%s: cannot allocate memory for statistics\n",
__func__);
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ neigh_parms_release(tbl, ndev->nd_parms);
netdev_put(dev, &ndev->dev_tracker);
kfree(ndev);
return ERR_PTR(err);
@@ -481,7 +484,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
return ndev;
err_release:
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ neigh_parms_release(tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
return ERR_PTR(err);
@@ -4038,9 +4041,11 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
/* Last: Shot the device (if unregistered) */
if (unregister) {
+ struct neigh_table *tbl = nd_table(net);
+
addrconf_sysctl_unregister(idev);
- neigh_parms_release(&nd_tbl, idev->nd_parms);
- neigh_ifdown(&nd_tbl, dev);
+ neigh_parms_release(tbl, idev->nd_parms);
+ neigh_ifdown(tbl, dev);
in6_dev_put(idev);
}
return 0;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 2c44e5ed61716..25fc57e52b5fa 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -125,7 +125,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);
@@ -581,7 +581,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 fe36b3f512850..3e16cb581f424 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -766,10 +766,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);
@@ -787,19 +788,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 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 dad = ipv6_addr_any(saddr);
int is_router = -1;
+ u8 *lladdr = NULL;
SKB_DR(reason);
u64 nonce = 0;
bool inc;
@@ -844,9 +847,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)) {
@@ -879,8 +883,6 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
idev = ifp->idev;
} else {
- struct net *net = dev_net(dev);
-
/* perhaps an address on the master device */
if (netif_is_l3_slave(dev)) {
struct net_device *mdev;
@@ -917,7 +919,7 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
*/
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
if (n)
- pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
+ pneigh_enqueue(tbl, idev->nd_parms, n);
goto out;
}
} else {
@@ -936,15 +938,15 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
}
if (inc)
- NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
+ NEIGH_CACHE_STAT_INC(tbl, rcv_probes_mcast);
else
- NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
+ NEIGH_CACHE_STAT_INC(tbl, rcv_probes_ucast);
/*
* update / create cache entry
* for the source address
*/
- neigh = __neigh_lookup(&nd_tbl, saddr, dev,
+ neigh = __neigh_lookup(tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
if (neigh)
ndisc_update(dev, neigh, lladdr, NUD_STALE,
@@ -986,17 +988,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;
@@ -1019,6 +1023,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;
@@ -1033,7 +1038,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)) {
@@ -1057,7 +1062,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
@@ -1077,14 +1083,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;
@@ -1097,7 +1102,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;
}
@@ -1126,18 +1131,20 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
{
struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
+ const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
+ struct net_device *dev = skb->dev;
+ struct ndisc_options ndopts;
+ struct neigh_table *tbl;
struct neighbour *neigh;
struct inet6_dev *idev;
- const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
- struct ndisc_options ndopts;
u8 *lladdr = NULL;
SKB_DR(reason);
if (skb->len < sizeof(*rs_msg))
return SKB_DROP_REASON_PKT_TOO_SMALL;
- idev = __in6_dev_get(skb->dev);
+ idev = __in6_dev_get(dev);
if (!idev) {
net_err_ratelimited("RS: can't find in6 device\n");
return reason;
@@ -1155,19 +1162,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,
@@ -1231,6 +1238,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
+ struct net *net = dev_net(skb->dev);
bool send_ifinfo_notify = false;
struct neighbour *neigh = NULL;
struct ndisc_options ndopts;
@@ -1240,7 +1248,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;
@@ -1329,7 +1336,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",
@@ -1465,7 +1471,7 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
*/
if (!neigh)
- neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
+ neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
skb->dev, 1);
if (neigh) {
u8 *lladdr = NULL;
@@ -1858,12 +1864,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:
@@ -1887,12 +1896,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:
@@ -1971,12 +1980,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;
@@ -1987,11 +2007,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 = {
@@ -2001,30 +2034,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)
@@ -2039,9 +2049,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 5968ce5ad1508..cea1cdc2b8e8e 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;
}
@@ -4229,6 +4229,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;
@@ -4262,7 +4263,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) ||
@@ -4274,15 +4275,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;
@@ -4301,7 +4301,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;
@@ -4309,7 +4309,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|
@@ -5030,9 +5030,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 7c594bf6ead0d..286da09a2010f 100755
--- a/tools/testing/selftests/net/test_neigh.sh
+++ b/tools/testing/selftests/net/test_neigh.sh
@@ -241,18 +241,18 @@ 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 +263,7 @@ 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
@@ -285,9 +285,9 @@ extern_valid_common()
local orig_gc_stale
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_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 10000"
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"
@@ -297,9 +297,9 @@ extern_valid_common()
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"]')
+ periodic_gc_runs_t0=$(ip -n "$ns1" -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_t1=$(ip -n "$ns1" -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
[[ $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 +310,7 @@ 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()
|
| 3/2 | 2026/08/07 23:53 | llm | patch-evaluator |
2mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41795 output: 23 thoughts: 678 Thoughts: **Analyzing a Core Networking Change: Moving ARP/ND Tables to Network Namespaces** Okay, so we've got a patch here that's making a fundamental shift in how the Linux kernel handles ARP and IPv6 Neighbor Discovery tables. Instead of these being global variables, `arp_tbl` and `nd_tbl` are now per-network namespace. This is a pretty significant architectural change, impacting core networking functionality, specifically around ARP and IPv6 neighbor discovery. This has obvious implications for namespaces, routing, discovery, and any drivers that interact with those tables. The first thing I need to do is examine the diff to get a handle on the extent of the modifications. A quick scan reveals the functions being touched include: `neigh_table_init`, `neigh_table_clear`, `neigh_table_register`, `neigh_table_unregister`, inline uses of `arp_table` and `nd_table`, `arp_process`, `ndisc_recv_ns`, `ndisc_recv_na`, `ndisc_recv_rs`, `ndisc_router_discovery`, and the core neighbor management routines like `neigh_add`, `neigh_delete`, `neigh_dump_info`, `neigh_get`, along with some proxy neighbor functions `pneigh_create`, `pneigh_delete`, `pneigh_lookup`, and even things like `neigh_xmit`, `neigh_ifdown`, `neigh_changeaddr`, and `neigh_carrier_down`. This definitely warrants some serious fuzzing. Given the nature of the change, my focus will be on the netlink interfaces related to these neighbor tables, specifically, ARP and ND processing, and also, of course, namespace creation and destruction. The potential for subtle race conditions and unexpected interactions with the new per-namespace structure is high. Let me pinpoint the key functions I'll need to scrutinize. The patch looks to be touching things like `neigh_table_register`, `neigh_table_unregister`, `arp_process`, `ndisc_recv_ns`, `ndisc_recv_na`, `ndisc_recv_rs`, `ndisc_router_discovery`, and the standard `neigh_add`, `neigh_delete`, and `neigh_get` functions, along with a few others like `neightbl_set`, `neightbl_dump_info` and `neigh_dump_info`. Okay, time to double-check that these functions are actually reachable and aren't simply static or inlined. I know the basics, but it's important to be thorough. Alright, `neigh_table_register` and `neigh_table_unregister` are exported, which is good. `arp_process` is static within `net/ipv4/arp.c`, but it's the main ARP processing function. Hmm, it being static means it can't be called directly from outside the source file. But, `arp_rcv` *does* call `arp_process`, so that's the entry point. Similarly, `ndisc_rcv` is the entry point for IPv6 neighbor discovery, and that calls into `ndisc_recv_ns`, `ndisc_recv_na`, `ndisc_recv_rs`, and `ndisc_router_discovery`. `neigh_add`, `neigh_delete`, and `neigh_get` are static within `net/core/neighbour.c`, but they are registered as netlink callbacks, so that makes them accessible. I should probably just take a peek at `net/core/neighbour.c` while I'm at it to be sure. |
| 4/2 | 2026/08/07 23:56 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_add\\("
}Results: {
"Output": "net/core/neighbour.c=1962=static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\n--\nnet/core/neighbour.c-2026-\nnet/core/neighbour.c:2027:static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-2028-\t\t struct netlink_ext_ack *extack)\n"
} |
| 5/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43391 output: 22 thoughts: 98 Thoughts: **Analyzing the `neigh_add` Function and Netlink Callbacks** Okay, so the prompt emphasizes preferring non-static, non-inlined API entrypoints over static helper functions. The reasoning is that static helpers, when inlined, don't have distinct symbol addresses. But, `neigh_add` is giving me a bit of a pause. I know it's a netlink callback, and that means its address *is* taken, right? That pretty much guarantees it won't be inlined. The compiler can't optimize it away because its address is exposed for registration and use in the netlink framework. So, the prompt's preference doesn't apply here, even though `neigh_add` is declared as static. I need to confirm that my understanding of this netlink callback behavior is correct and go back to where it is registered. `rtnetlink_rcv_msg` seems like a likely candidate. I'll check the registration point of `neigh_add` to solidify my assessment. |
| 6/2 | 2026/08/07 23:56 | tool | grepper |
0mError:no matches Args: {
"Expression": "rtnl_register.*neigh_add"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43741 output: 17 thoughts: 13 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Investigating the `neigh_add` Registration** Okay, so I need to quickly verify the registration process for `neigh_add`. Specifically, I want to confirm how it's integrated and ensure there aren't any potential conflicts or unexpected behaviors. I need to understand exactly *how* it's registered within the larger context. This means digging into the registration logic, the associated data structures, and any relevant hooks or callbacks. I'm looking for the canonical method, any potential for misuse, and ensuring it functions exactly as I expect. It’s about ensuring there aren't any subtle gotchas lurking within the registration process. |
| 8/2 | 2026/08/07 23:56 | tool | grepper |
0mArgs:{
"Expression": "neigh_add"
}Results: {
"Output": "drivers/infiniband/ulp/ipoib/ipoib_main.c=971=static void neigh_refresh_path(struct ipoib_neigh *neigh, u8 *daddr,\n--\ndrivers/infiniband/ulp/ipoib/ipoib_main.c-988-\ndrivers/infiniband/ulp/ipoib/ipoib_main.c:989:static struct ipoib_neigh *neigh_add_path(struct sk_buff *skb, u8 *daddr,\ndrivers/infiniband/ulp/ipoib/ipoib_main.c-990-\t\t\t\t\t struct net_device *dev)\n--\ndrivers/infiniband/ulp/ipoib/ipoib_main.c=1137=static netdev_tx_t ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)\n--\ndrivers/infiniband/ulp/ipoib/ipoib_main.c-1179-\t\tif (unlikely(!neigh)) {\ndrivers/infiniband/ulp/ipoib/ipoib_main.c:1180:\t\t\tneigh = neigh_add_path(skb, phdr-\u003ehwaddr, dev);\ndrivers/infiniband/ulp/ipoib/ipoib_main.c-1181-\t\t\tif (likely(!neigh))\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c=1202=ofdpa_neigh_tbl_find(const struct ofdpa *ofdpa, __be32 ip_addr)\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1213-\ndrivers/net/ethernet/rocker/rocker_ofdpa.c:1214:static void ofdpa_neigh_add(struct ofdpa *ofdpa,\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1215-\t\t\t struct ofdpa_neigh_tbl_entry *entry)\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c=1242=static int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port,\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1275-\t\tentry-\u003ettl_check = true;\ndrivers/net/ethernet/rocker/rocker_ofdpa.c:1276:\t\tofdpa_neigh_add(ofdpa, entry);\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1277-\t} else if (removing) {\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c=1359=static int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port,\n--\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1386-\t\tentry-\u003edev = ofdpa_port-\u003edev;\ndrivers/net/ethernet/rocker/rocker_ofdpa.c:1387:\t\tofdpa_neigh_add(ofdpa, entry);\ndrivers/net/ethernet/rocker/rocker_ofdpa.c-1388-\t\t*index = entry-\u003eindex;\n--\nnet/batman-adv/bat_iv_ogm.c=175=batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,\nnet/batman-adv/bat_iv_ogm.c:176:\t\t\tconst u8 *neigh_addr,\nnet/batman-adv/bat_iv_ogm.c-177-\t\t\tstruct batadv_orig_node *orig_node)\n--\nnet/batman-adv/bat_iv_ogm.c-181-\tneigh_node = batadv_neigh_node_get_or_create(orig_node,\nnet/batman-adv/bat_iv_ogm.c:182:\t\t\t\t\t\t hard_iface, neigh_addr);\nnet/batman-adv/bat_iv_ogm.c-183-\treturn neigh_node;\n--\nnet/batman-adv/bat_iv_ogm.c=990=batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/bat_iv_ogm.c-1004-\tu8 sum_orig, sum_neigh;\nnet/batman-adv/bat_iv_ogm.c:1005:\tu8 *neigh_addr;\nnet/batman-adv/bat_iv_ogm.c-1006-\tu8 tq_avg;\n--\nnet/batman-adv/bat_iv_ogm.c-1014-\t\t\t\t \u0026orig_node-\u003eneigh_list, list) {\nnet/batman-adv/bat_iv_ogm.c:1015:\t\tneigh_addr = tmp_neigh_node-\u003eaddr;\nnet/batman-adv/bat_iv_ogm.c:1016:\t\tif (batadv_compare_eth(neigh_addr, ethhdr-\u003eh_source) \u0026\u0026\nnet/batman-adv/bat_iv_ogm.c-1017-\t\t tmp_neigh_node-\u003eif_incoming == if_incoming \u0026\u0026\n--\nnet/batman-adv/bat_iv_ogm.c=1271=batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,\n--\nnet/batman-adv/bat_iv_ogm.c-1286-\tu32 seqno = ntohl(batadv_ogm_packet-\u003eseqno);\nnet/batman-adv/bat_iv_ogm.c:1287:\tu8 *neigh_addr;\nnet/batman-adv/bat_iv_ogm.c-1288-\tu8 packet_count;\n--\nnet/batman-adv/bat_iv_ogm.c-1319-\nnet/batman-adv/bat_iv_ogm.c:1320:\t\tneigh_addr = neigh_node-\u003eaddr;\nnet/batman-adv/bat_iv_ogm.c-1321-\t\tis_dup = batadv_test_bit(neigh_ifinfo-\u003ebat_iv.real_bits,\n--\nnet/batman-adv/bat_iv_ogm.c-1324-\nnet/batman-adv/bat_iv_ogm.c:1325:\t\tif (batadv_compare_eth(neigh_addr, ethhdr-\u003eh_source) \u0026\u0026\nnet/batman-adv/bat_iv_ogm.c-1326-\t\t neigh_node-\u003eif_incoming == if_incoming) {\n--\nnet/batman-adv/bat_v_elp.c=475=void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *primary_iface)\n--\nnet/batman-adv/bat_v_elp.c-489- * @bat_priv: the bat priv with all the mesh interface information\nnet/batman-adv/bat_v_elp.c:490: * @neigh_addr: the neighbour interface address\nnet/batman-adv/bat_v_elp.c-491- * @if_incoming: the interface the packet was received through\n--\nnet/batman-adv/bat_v_elp.c=497=static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,\nnet/batman-adv/bat_v_elp.c:498:\t\t\t\t u8 *neigh_addr,\nnet/batman-adv/bat_v_elp.c-499-\t\t\t\t struct batadv_hard_iface *if_incoming,\n--\nnet/batman-adv/bat_v_elp.c-513-\tneigh = batadv_neigh_node_get_or_create(orig_neigh,\nnet/batman-adv/bat_v_elp.c:514:\t\t\t\t\t\tif_incoming, neigh_addr);\nnet/batman-adv/bat_v_elp.c-515-\tif (!neigh)\n--\nnet/batman-adv/bat_v_elp.c-517-\nnet/batman-adv/bat_v_elp.c:518:\thardif_neigh = batadv_hardif_neigh_get(if_incoming, neigh_addr);\nnet/batman-adv/bat_v_elp.c-519-\tif (!hardif_neigh)\n--\nnet/batman-adv/originator.c=529=batadv_neigh_node_get(const struct batadv_orig_node *orig_node,\n--\nnet/batman-adv/originator.c-556- * @hard_iface: the interface this neighbour is connected to\nnet/batman-adv/originator.c:557: * @neigh_addr: the interface address of the neighbour to retrieve\nnet/batman-adv/originator.c-558- * @orig_node: originator object representing the neighbour\n--\nnet/batman-adv/originator.c=563=batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.c:564:\t\t\t const u8 *neigh_addr,\nnet/batman-adv/originator.c-565-\t\t\t struct batadv_orig_node *orig_node)\n--\nnet/batman-adv/originator.c-572-\t/* check if neighbor hasn't been added in the meantime */\nnet/batman-adv/originator.c:573:\thardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);\nnet/batman-adv/originator.c-574-\tif (hardif_neigh)\n--\nnet/batman-adv/originator.c-582-\tINIT_HLIST_NODE(\u0026hardif_neigh-\u003elist);\nnet/batman-adv/originator.c:583:\tether_addr_copy(hardif_neigh-\u003eaddr, neigh_addr);\nnet/batman-adv/originator.c-584-\tether_addr_copy(hardif_neigh-\u003eorig, orig_node-\u003eorig);\n--\nnet/batman-adv/originator.c-603- * @hard_iface: the interface this neighbour is connected to\nnet/batman-adv/originator.c:604: * @neigh_addr: the interface address of the neighbour to retrieve\nnet/batman-adv/originator.c-605- * @orig_node: originator object representing the neighbour\n--\nnet/batman-adv/originator.c=610=batadv_hardif_neigh_get_or_create(struct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.c:611:\t\t\t\t const u8 *neigh_addr,\nnet/batman-adv/originator.c-612-\t\t\t\t struct batadv_orig_node *orig_node)\n--\nnet/batman-adv/originator.c-616-\t/* first check without locking to avoid the overhead */\nnet/batman-adv/originator.c:617:\thardif_neigh = batadv_hardif_neigh_get(hard_iface, neigh_addr);\nnet/batman-adv/originator.c-618-\tif (hardif_neigh)\n--\nnet/batman-adv/originator.c-620-\nnet/batman-adv/originator.c:621:\treturn batadv_hardif_neigh_create(hard_iface, neigh_addr, orig_node);\nnet/batman-adv/originator.c-622-}\n--\nnet/batman-adv/originator.c-626- * @hard_iface: the interface where this neighbour is connected to\nnet/batman-adv/originator.c:627: * @neigh_addr: the address of the neighbour\nnet/batman-adv/originator.c-628- *\n--\nnet/batman-adv/originator.c=634=batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.c:635:\t\t\tconst u8 *neigh_addr)\nnet/batman-adv/originator.c-636-{\n--\nnet/batman-adv/originator.c-641-\t\t\t\t \u0026hard_iface-\u003eneigh_list, list) {\nnet/batman-adv/originator.c:642:\t\tif (!batadv_compare_eth(tmp_hardif_neigh-\u003eaddr, neigh_addr))\nnet/batman-adv/originator.c-643-\t\t\tcontinue;\n--\nnet/batman-adv/originator.c-659- * @hard_iface: the interface where the neighbour is connected to\nnet/batman-adv/originator.c:660: * @neigh_addr: the mac address of the neighbour interface\nnet/batman-adv/originator.c-661- *\n--\nnet/batman-adv/originator.c=667=batadv_neigh_node_create(struct batadv_orig_node *orig_node,\nnet/batman-adv/originator.c-668-\t\t\t struct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.c:669:\t\t\t const u8 *neigh_addr)\nnet/batman-adv/originator.c-670-{\n--\nnet/batman-adv/originator.c-675-\nnet/batman-adv/originator.c:676:\tneigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);\nnet/batman-adv/originator.c-677-\tif (neigh_node)\n--\nnet/batman-adv/originator.c-680-\thardif_neigh = batadv_hardif_neigh_get_or_create(hard_iface,\nnet/batman-adv/originator.c:681:\t\t\t\t\t\t\t neigh_addr, orig_node);\nnet/batman-adv/originator.c-682-\tif (!hardif_neigh)\n--\nnet/batman-adv/originator.c-693-\tkref_get(\u0026hard_iface-\u003erefcount);\nnet/batman-adv/originator.c:694:\tether_addr_copy(neigh_node-\u003eaddr, neigh_addr);\nnet/batman-adv/originator.c-695-\tneigh_node-\u003eif_incoming = hard_iface;\n--\nnet/batman-adv/originator.c-713-\t\t \"Creating new neighbor %pM for orig_node %pM on interface %s\\n\",\nnet/batman-adv/originator.c:714:\t\t neigh_addr, orig_node-\u003eorig, hard_iface-\u003enet_dev-\u003ename);\nnet/batman-adv/originator.c-715-\n--\nnet/batman-adv/originator.c-726- * @hard_iface: the interface where the neighbour is connected to\nnet/batman-adv/originator.c:727: * @neigh_addr: the mac address of the neighbour interface\nnet/batman-adv/originator.c-728- *\n--\nnet/batman-adv/originator.c=732=batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node,\nnet/batman-adv/originator.c-733-\t\t\t\tstruct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.c:734:\t\t\t\tconst u8 *neigh_addr)\nnet/batman-adv/originator.c-735-{\n--\nnet/batman-adv/originator.c-738-\t/* first check without locking to avoid the overhead */\nnet/batman-adv/originator.c:739:\tneigh_node = batadv_neigh_node_get(orig_node, hard_iface, neigh_addr);\nnet/batman-adv/originator.c-740-\tif (neigh_node)\n--\nnet/batman-adv/originator.c-742-\nnet/batman-adv/originator.c:743:\treturn batadv_neigh_node_create(orig_node, hard_iface, neigh_addr);\nnet/batman-adv/originator.c-744-}\n--\nnet/batman-adv/originator.h=28=batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.h:29:\t\t\tconst u8 *neigh_addr);\nnet/batman-adv/originator.h-30-void batadv_hardif_neigh_release(struct kref *ref);\n--\nnet/batman-adv/originator.h=32=batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node,\nnet/batman-adv/originator.h-33-\t\t\t\tstruct batadv_hard_iface *hard_iface,\nnet/batman-adv/originator.h:34:\t\t\t\tconst u8 *neigh_addr);\nnet/batman-adv/originator.h-35-void batadv_neigh_node_release(struct kref *ref);\n--\nnet/batman-adv/send.c=836=static bool batadv_send_no_broadcast(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/send.c-842-\tu8 *orig_neigh;\nnet/batman-adv/send.c:843:\tu8 *neigh_addr;\nnet/batman-adv/send.c-844-\tchar *type;\n--\nnet/batman-adv/send.c-847-\tif (!own_packet) {\nnet/batman-adv/send.c:848:\t\tneigh_addr = eth_hdr(skb)-\u003eh_source;\nnet/batman-adv/send.c-849-\t\tneigh_node = batadv_hardif_neigh_get(if_out,\nnet/batman-adv/send.c:850:\t\t\t\t\t\t neigh_addr);\nnet/batman-adv/send.c-851-\t}\n--\nnet/core/neighbour.c-9- *\tFixes:\nnet/core/neighbour.c:10: *\tVitaly E. Lavrov\treleasing NULL neighbor in neigh_add.\nnet/core/neighbour.c-11- *\tHarald Welte\t\tAdd neighbour cache statistics like rtstat\n--\nnet/core/neighbour.c=238=static int neigh_forced_gc(struct neigh_table *tbl)\n--\nnet/core/neighbour.c-283-\nnet/core/neighbour.c:284:static void neigh_add_timer(struct neighbour *n, unsigned long when)\nnet/core/neighbour.c-285-{\n--\nnet/core/neighbour.c=1182=int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,\n--\nnet/core/neighbour.c-1213-\t\t\t}\nnet/core/neighbour.c:1214:\t\t\tneigh_add_timer(neigh, next);\nnet/core/neighbour.c-1215-\t\t} else {\n--\nnet/core/neighbour.c-1227-\t\tneigh-\u003eupdated = jiffies;\nnet/core/neighbour.c:1228:\t\tneigh_add_timer(neigh, jiffies +\nnet/core/neighbour.c-1229-\t\t\t\tNEIGH_VAR(neigh-\u003eparms, DELAY_PROBE_TIME));\n--\nnet/core/neighbour.c=1351=static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,\n--\nnet/core/neighbour.c-1465-\t\tif (new \u0026 NUD_IN_TIMER)\nnet/core/neighbour.c:1466:\t\t\tneigh_add_timer(neigh, (jiffies +\nnet/core/neighbour.c-1467-\t\t\t\t\t\t((new \u0026 NUD_REACHABLE) ?\n--\nnet/core/neighbour.c=1540=void __neigh_set_probe_once(struct neighbour *neigh)\n--\nnet/core/neighbour.c-1548-\tatomic_set(\u0026neigh-\u003eprobes, neigh_max_probes(neigh));\nnet/core/neighbour.c:1549:\tneigh_add_timer(neigh,\nnet/core/neighbour.c-1550-\t\t\tjiffies + max(NEIGH_VAR(neigh-\u003eparms, RETRANS_TIME),\n--\nnet/core/neighbour.c=1962=static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\n--\nnet/core/neighbour.c-2026-\nnet/core/neighbour.c:2027:static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-2028-\t\t struct netlink_ext_ack *extack)\n--\nnet/core/neighbour.c=3894=static const struct rtnl_msg_handler neigh_rtnl_msg_handlers[] __initconst = {\nnet/core/neighbour.c:3895:\t{.msgtype = RTM_NEWNEIGH, .doit = neigh_add},\nnet/core/neighbour.c-3896-\t{.msgtype = RTM_DELNEIGH, .doit = neigh_delete},\n--\nnet/mctp/neigh.c-23-\nnet/mctp/neigh.c:24:static int mctp_neigh_add(struct mctp_dev *mdev, mctp_eid_t eid,\nnet/mctp/neigh.c-25-\t\t\t enum mctp_neigh_source source,\n--\nnet/mctp/neigh.c=115=static int mctp_rtm_newneigh(struct sk_buff *skb, struct nlmsghdr *nlh,\n--\nnet/mctp/neigh.c-168-\nnet/mctp/neigh.c:169:\treturn mctp_neigh_add(mdev, eid, MCTP_NEIGH_STATIC,\nnet/mctp/neigh.c-170-\t\t\tlladdr_len, lladdr);\n--\nnet/mctp/test/route-test.c=1354=static void mctp_test_route_gw_output(struct kunit *test)\n--\nnet/mctp/test/route-test.c-1378-\nnet/mctp/test/route-test.c:1379:\t/* simple mctp_neigh_add for the gateway (not dest!) endpoint */\nnet/mctp/test/route-test.c-1380-\tINIT_LIST_HEAD(\u0026neigh.list);\n--\ntools/testing/selftests/net/test_vxlan_nh.sh=168=proxy_common()\n--\ntools/testing/selftests/net/test_vxlan_nh.sh-173-\tlocal remote_addr=$1; shift\ntools/testing/selftests/net/test_vxlan_nh.sh:174:\tlocal neigh_addr=$1; shift\ntools/testing/selftests/net/test_vxlan_nh.sh-175-\tlocal ping_cmd=$1; shift\n--\ntools/testing/selftests/net/test_vxlan_nh.sh-194-\ntools/testing/selftests/net/test_vxlan_nh.sh:195:\trun_cmd \"ip -n $ns1 neigh add $neigh_addr lladdr 00:11:22:33:44:55 nud perm dev vx0\"\ntools/testing/selftests/net/test_vxlan_nh.sh-196-\n--\ntools/testing/selftests/net/tun.c=728=FIXTURE_SETUP(tun_vnet_udptnl)\n--\ntools/testing/selftests/net/tun.c-748-\tASSERT_EQ(ret, 0);\ntools/testing/selftests/net/tun.c:749:\tret = ip_neigh_add(self-\u003eifname, family, dip, dmac);\ntools/testing/selftests/net/tun.c-750-\tASSERT_EQ(ret, 0);\n--\ntools/testing/selftests/net/tun.c-763-\tASSERT_EQ(ret, 0);\ntools/testing/selftests/net/tun.c:764:\tret = ip_neigh_add(param_dev_geneve_name, family, dip, dmac);\ntools/testing/selftests/net/tun.c-765-\tASSERT_EQ(ret, 0);\n--\ntools/testing/selftests/net/tuntap_helpers.h=75=static inline void fill_neigh_req_header(struct ndmsg *ndm, int family,\n--\ntools/testing/selftests/net/tuntap_helpers.h-84-\ntools/testing/selftests/net/tuntap_helpers.h:85:static inline int ip_neigh_add(const char *dev, int family, void *addr,\ntools/testing/selftests/net/tuntap_helpers.h-86-\t\t\t unsigned char *lladdr)\n"
} |
| 9/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 50340 output: 24 thoughts: 489 Thoughts: **Analyzing Neighbor Discovery in the Linux Kernel**
Okay, so I'm looking at how neighbor discovery is handled, specifically focusing on potential entry points for fuzzing. It seems `neigh_add` is registered as a Netlink handler via `neigh_rtnl_msg_handlers` in `net/core/neighbour.c`. Interesting. Specifically:
```c
static const struct rtnl_msg_handler neigh_rtnl_msg_handlers[] __initconst = {
{.msgtype = RTM_NEWNEIGH, .doit = neigh_add},
{.msgtype = RTM_DELNEIGH, .doit = neigh_delete},
{.msgtype = RTM_GETNEIGH, .doit = neigh_get, .dumpit = neigh_dump_info},
};
```
This means `neigh_add`, `neigh_delete`, `neigh_get`, and `neigh_dump_info` are all Netlink handlers. I was initially going to include them in `FocusSymbols`. The prompt also said "Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses)". Although `neigh_add` is static, it appears syzkaller can still fuzz it because the function is not inlined, and it's better to provide the netlink interface.
Then I paused to think about whether the `rtnl_register` would be better. But based on the documentation of syzkaller, I can just list these handler functions directly.
Okay, so the best approach would be to list the functions that are modified and are entry points.
I'll list these functions:
* `neigh_table_register`
* `neigh_table_unregister`
* `arp_process` (static, but called from `arp_rcv`)
* `ndisc_recv_ns` (static, called from `ndisc_rcv`)
* `ndisc_recv_na` (static, called from `ndisc_rcv`)
* `ndisc_recv_rs` (static, called from `ndisc_rcv`)
* `ndisc_router_discovery` (static, called from `ndisc_rcv`)
* `arp_rcv`
* `ndisc_rcv`
Now, let's take a closer look at `arp_rcv` and `ndisc_rcv` to confirm these are good candidates.
|
| 10/2 | 2026/08/07 23:56 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int arp_rcv\\("
}Results: {
"Output": "net/ipv4/arp.c=961=static int arp_is_multicast(const void *pkey)\n--\nnet/ipv4/arp.c-969-\nnet/ipv4/arp.c:970:static int arp_rcv(struct sk_buff *skb, struct net_device *dev,\nnet/ipv4/arp.c-971-\t\t struct packet_type *pt, struct net_device *orig_dev)\n"
} |
| 11/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 51526 output: 25 thoughts: 97 Thoughts: **Analyzing Packet Handling in the Kernel**
Okay, so I'm looking at this packet handling logic, and it seems like `arp_rcv` is statically defined as well. I'm seeing it's registered as a packet handler, which is pretty standard. Specifically, I'm noting that:
```c
static struct packet_type arp_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_ARP),
.func = arp_rcv,
};
```
This confirms my understanding; `arp_rcv` itself is a valid function to be called as a packet handler. That function is handled as I expect. So, I don't see any immediate issues there. Moving on, I need to check `ndisc_rcv` to continue this analysis.
|
| 12/2 | 2026/08/07 23:56 | tool | grepper |
0mError:no matches Args: {
"Expression": "^(static )?int ndisc_rcv\\("
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 51846 output: 20 thoughts: 16 Thoughts: Here's a summary of my thought process: **Diving into `ndisc_recv_ns` Calls** Alright, let's get down to brass tacks. I need to understand how the `ndisc_recv_ns` function is being utilized. My instinct is to trace its calls to get a complete picture. Understanding its invocation context is crucial for a lot of network analysis, and I want to identify any potential dependencies or areas of concern. I'll need to locate all instances where `ndisc_recv_ns` is referenced and analyze the surrounding code to determine its purpose within each call. I need to grasp its operational scope. This will likely involve some digging through the codebase, but it's a necessary step to comprehensively analyze its behavior. |
| 14/2 | 2026/08/07 23:56 | tool | grepper |
0mArgs:{
"Expression": "ndisc_recv_ns"
}Results: {
"Output": "net/ipv6/ndisc.c=780=void ndisc_update(const struct net_device *dev, struct neighbour *neigh,\n--\nnet/ipv6/ndisc.c-788-\nnet/ipv6/ndisc.c:789:static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)\nnet/ipv6/ndisc.c-790-{\n--\nnet/ipv6/ndisc.c=1785=static void pndisc_redo(struct sk_buff *skb)\nnet/ipv6/ndisc.c-1786-{\nnet/ipv6/ndisc.c:1787:\tenum skb_drop_reason reason = ndisc_recv_ns(skb);\nnet/ipv6/ndisc.c-1788-\n--\nnet/ipv6/ndisc.c=1811=enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)\n--\nnet/ipv6/ndisc.c-1838-\t\tmemset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));\nnet/ipv6/ndisc.c:1839:\t\treason = ndisc_recv_ns(skb);\nnet/ipv6/ndisc.c-1840-\t\tbreak;\n"
} |
| 15/2 | 2026/08/07 23:56 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52386 output: 28 thoughts: 52 Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective: **Diving into the Network Discovery Code** Alright, so I'm looking at `ndisc_rcv`, and it's immediately apparent that it's *not* a static function, or at least it isn't declared that way. That's a good first observation. It seems that its signature is `enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)`. Now I need to take a closer look at this function. I need to understand its role in the network discovery process, and what kind of SKB it processes. I’m thinking I will investigate what that enum represents and consider how this function connects into the broader packet processing flow. Essentially, let's trace this function and understand its implementation. |
| 16/2 | 2026/08/07 23:56 | tool | grepper |
0mArgs:{
"Expression": "^enum skb_drop_reason ndisc_rcv\\("
}Results: {
"Output": "include/net/ndisc.h=418=void ndisc_cleanup(void);\ninclude/net/ndisc.h-419-\ninclude/net/ndisc.h:420:enum skb_drop_reason ndisc_rcv(struct sk_buff *skb);\ninclude/net/ndisc.h-421-\n--\nnet/ipv6/ndisc.c=1797=static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)\n--\nnet/ipv6/ndisc.c-1810-\nnet/ipv6/ndisc.c:1811:enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)\nnet/ipv6/ndisc.c-1812-{\n"
} |
| 17/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 52854 output: 27 thoughts: 34 Thoughts: Okay, here's my interpretation of those thoughts, presented as a first-person expert summary: **Analyzing Network Device Discovery** Right, so `ndisc_rcv` is indeed a global function. Now, that's crucial because it immediately tells me something about its scope and potential impact. Global functions are, well, *globally* accessible which implies that it's likely involved in a core process. Understanding how it interacts with the rest of the network stack is key. Given this context, my next logical step is to examine the associated registration and unregistration mechanisms. I need to dive into `neigh_table_register` and `neigh_table_unregister`. I'm going to determine what exactly gets registered or unregistered. It is vital to discover what resources these functions manage, how the network neighbors are handled, and how this relates to the overall function. This gives me a much better grasp on the function's lifecycle and dependencies. This information will prove critical to troubleshooting any problems or understanding how to integrate modifications with the existing device discovery infrastructure. |
| 18/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(int|void) neigh_table_(un)?register\\("
}Results: {
"Output": "include/net/neighbour.h=330=static inline void neigh_confirm(struct neighbour *n)\n--\ninclude/net/neighbour.h-340-\ninclude/net/neighbour.h:341:int neigh_table_register(struct net *net, struct neigh_table *tbl, int index);\ninclude/net/neighbour.h:342:void neigh_table_unregister(struct net *net, int index);\ninclude/net/neighbour.h-343-struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,\n--\nnet/core/neighbour.c=1872=static void neigh_table_clear(struct net *net, struct neigh_table *tbl)\n--\nnet/core/neighbour.c-1895-\nnet/core/neighbour.c:1896:int neigh_table_register(struct net *net, struct neigh_table *tbl, int index)\nnet/core/neighbour.c-1897-{\n--\nnet/core/neighbour.c-1919-\nnet/core/neighbour.c:1920:void neigh_table_unregister(struct net *net, int index)\nnet/core/neighbour.c-1921-{\n"
} |
| 19/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 53443 output: 25 thoughts: 28 Thoughts: Okay, here's my summary, tailored for an expert audience, as if I'm processing these thoughts myself: **Investigating Global Scope** Right, the initial impression is correct: these are indeed global variables we're dealing with. My immediate next step is to drill down into how they're being managed and how they're being used. Specifically, I need to take a look at `neightbl_set` and `neightbl_dump_info`. Those two functions are likely going to be critical to understanding the lifecycle and potential impact of these global structures. I expect `neightbl_set` is involved in initialization or modification, and `neightbl_dump_info` probably serves some debugging, reporting, or diagnostic purpose related to the state of this global data. I need to understand what's happening within those functions, and what their calling patterns are, to get a clear picture of the global variable behavior. |
| 20/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neightbl_set\\("
}Results: {
"Output": "net/core/neighbour.c=2379=static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {\n--\nnet/core/neighbour.c-2397-\nnet/core/neighbour.c:2398:static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-2399-\t\t\tstruct netlink_ext_ack *extack)\n"
} |
| 21/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 53827 output: 23 thoughts: 29 Thoughts: Here's a summary of my thought process, as requested: **Deconstructing a Netlink Handler** Okay, so I'm looking at this code and immediately keying in on a couple of crucial points. First, `neightbl_set`. I see it's a static variable, which, in the context of the kernel, usually means it's a global within a specific module or function, and its scope is limited to that area. But the fact that it's a *set* also tells me this likely has something to do with networking, specifically something to do with the neighbor table. The key is that this is defined as a netlink handler. Therefore, the obvious next step is to drill into `neigh_add`. Let's confirm my assumptions regarding this function. I'm going to look at the details within `neigh_add` now to understand what actions trigger, and how `neightbl_set` is utilized. I anticipate the logic here involves adding a new entry to the neighbor table, which would involve setting up the associated data structures and managing the various states, potentially including interaction with this `neightbl_set`. |
| 22/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_add\\("
}Results: {
"Output": "net/core/neighbour.c=1962=static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\n--\nnet/core/neighbour.c-2026-\nnet/core/neighbour.c:2027:static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-2028-\t\t struct netlink_ext_ack *extack)\n"
} |
| 23/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54251 output: 23 thoughts: 9 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Analyzing the `neigh_delete` Function** Alright, time to dig into `neigh_delete`. I need to refresh my mental model of its purpose and how it's implemented. Specifically, I'm thinking about its interactions with the core data structures – the neighborhood lists, the potential edge index modifications, and any possible cache invalidations. Is this routine responsible for physically removing the node, or is it more of a "mark as deleted" kind of operation? Knowing that will significantly impact my understanding of downstream dependencies and potential concurrency issues. I also want to quickly run through all the possible return types to make sure I'm not missing any error or corner case. What are the specific memory management implications here? Are there any opportunities for optimization, such as deferred deletion or batched updates? And crucially, I need to look for any potential for subtle bugs, like dangling pointers or race conditions if multithreading is involved. I'm going to pay extra attention to any conditional statements, nested loops, or recursion inside this function, as those are often the hotspots for unexpected behavior. Let's see if this function is written in a way that minimizes memory copying. Are we taking care to avoid any unneeded work or operations? Let's take a look. |
| 24/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_delete\\("
}Results: {
"Output": "net/core/neighbour.c=1945=const struct nla_policy nda_policy[NDA_MAX+1] = {\n--\nnet/core/neighbour.c-1961-\nnet/core/neighbour.c:1962:static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-1963-\t\t\tstruct netlink_ext_ack *extack)\n"
} |
| 25/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54692 output: 23 thoughts: 9 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Internal Audit - Examining `neigh_get`** Alright, time to dig in. Let's see... the task is pointing me towards `neigh_get`. My immediate thought is to check that function. I need to refresh my memory on the specifics of this function, its role within the broader system, and potentially its interaction with other modules or processes. I need to verify its current state, recent revisions, and if any known issues exist, or potentially could exist, within the current code-base. Let's go through my mental checklist: input parameters, potential error conditions, return values, any side effects... everything. Let's make sure nothing is immediately obvious, I am sure to catch any issues early on. Now, where to begin? |
| 26/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_get\\("
}Results: {
"Output": "net/core/neighbour.c=3050=static inline size_t pneigh_nlmsg_size(void)\n--\nnet/core/neighbour.c-3057-\nnet/core/neighbour.c:3058:static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,\nnet/core/neighbour.c-3059-\t\t struct netlink_ext_ack *extack)\n"
} |
| 27/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 55029 output: 25 thoughts: 11 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary: **Analyzing the Neighborhood Dump Information** Alright, let's dive into this. My initial thought is, "Okay, let's zero in on `neigh_dump_info`." This is the key piece, the core dataset, or the crucial function, depending on the context, right? I need to understand what's contained within this variable, or the output produced. What level of detail does it provide? What format is the information in? Is it structured, unstructured, or something in between? Is it a pointer to a data structure, a direct value, or a function that returns the information on demand? This is the starting point for any deeper analysis; without knowing the nature of `neigh_dump_info`, any further discussion would be premature. I'm assuming that it is core to whatever task is at hand, and worth the focused first step. |
| 28/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_dump_info\\("
}Results: {
"Output": "net/core/neighbour.c=2875=static int neigh_valid_dump_req(const struct nlmsghdr *nlh,\n--\nnet/core/neighbour.c-2935-\nnet/core/neighbour.c:2936:static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\nnet/core/neighbour.c-2937-{\n"
} |
| 29/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 55385 output: 27 thoughts: 13 Thoughts: Okay, let's dive into this. Here's my thought process on what we're looking at, summarized in the first person, assuming I'm already pretty familiar with the subject matter: **Checking `neightbl_dump_info`** Alright, time to get started. My immediate thought is to check `neightbl_dump_info`. I know this is a critical piece of the puzzle, likely a central data structure or function related to network neighbor information. It probably holds key details about adjacent nodes or devices. I'm hoping it provides a complete and easily parsable view into the current state. This will be the starting point for whatever analysis or debugging I'm about to do. I’m expecting a structured output, maybe a table, dictionary, or some form of list that organizes the neighbor information logically. My experience tells me that issues with neighbor discovery or routing often surface here. So, let’s see what's inside. Hopefully, it is well documented! |
| 30/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neightbl_dump_info\\("
}Results: {
"Output": "net/core/neighbour.c=2561=static int neightbl_valid_dump_info(const struct nlmsghdr *nlh,\n--\nnet/core/neighbour.c-2584-\nnet/core/neighbour.c:2585:static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)\nnet/core/neighbour.c-2586-{\n"
} |
| 31/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 55751 output: 24 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating `pneigh_create`** Right, let's dive into this. I've got to start with `pneigh_create`. I need to mentally walk through what's happening there. I'm thinking about the implications, the potential gotchas... I already know the general function, but let's see what stands out immediately. What's the context, and are there any potential conflicts I can foresee? Is the function doing more or less than it should? |
| 32/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int pneigh_create\\("
}Results: {
"Output": "include/net/neighbour.h=391=struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\ninclude/net/neighbour.h-392-\t\t\t\t const void *key, struct net_device *dev);\ninclude/net/neighbour.h:393:int pneigh_create(struct neigh_table *tbl, const void *key,\ninclude/net/neighbour.h-394-\t\t struct net_device *dev, u32 flags, u8 protocol,\n--\nnet/core/neighbour.c=738=struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\n--\nnet/core/neighbour.c-760-\nnet/core/neighbour.c:761:int pneigh_create(struct neigh_table *tbl,\nnet/core/neighbour.c-762-\t\t const void *pkey, struct net_device *dev,\n"
} |
| 33/2 | 2026/08/07 23:57 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 56147 output: 24 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Debugging the `pneigh_delete` Function** Alright, time to dive in. My mind immediately goes to `pneigh_delete`. We need to start there. This feels like the critical piece, the linchpin of whatever issue we're facing. Let's see what's happening within that function. I have a hunch, based on the previous evidence, that the problems lie around how this is handled. Time to scrutinize. Let's go. |
| 34/2 | 2026/08/07 23:57 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int pneigh_delete\\("
}Results: {
"Output": "include/net/neighbour.h=393=int pneigh_create(struct neigh_table *tbl, const void *key,\n--\ninclude/net/neighbour.h-395-\t\t bool permanent);\ninclude/net/neighbour.h:396:int pneigh_delete(struct neigh_table *tbl, const void *key,\ninclude/net/neighbour.h-397-\t\t struct net_device *dev);\n--\nnet/core/neighbour.c=807=static void pneigh_destroy(struct rcu_head *rcu)\n--\nnet/core/neighbour.c-814-\nnet/core/neighbour.c:815:int pneigh_delete(struct neigh_table *tbl, const void *pkey,\nnet/core/neighbour.c-816-\t\t struct net_device *dev)\n"
} |
| 35/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 56521 output: 29 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating `pneigh_lookup`** Alright, time to dig in. The first thought that comes to mind is to check on `pneigh_lookup`. I need to understand what's going on with this function, its role in the current problem, and whether it's behaving as expected. Let's see what's what. I'm going to quickly refresh my memory on the specifics of how `pneigh_lookup` is implemented. I should also re-evaluate any recent changes in code related to neighbor discovery. |
| 36/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?struct pneigh_entry \\*pneigh_lookup\\("
}Results: {
"Output": "include/net/neighbour.h=389=void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,\ninclude/net/neighbour.h-390-\t\t struct sk_buff *skb);\ninclude/net/neighbour.h:391:struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\ninclude/net/neighbour.h-392-\t\t\t\t const void *key, struct net_device *dev);\n--\nnet/core/neighbour.c=728=static u32 pneigh_hash(const void *pkey, unsigned int key_len)\n--\nnet/core/neighbour.c-737-\nnet/core/neighbour.c:738:struct pneigh_entry *pneigh_lookup(struct neigh_table *tbl,\nnet/core/neighbour.c-739-\t\t\t\t const void *pkey, struct net_device *dev)\n"
} |
| 37/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 56934 output: 24 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating `neigh_xmit`** Alright, time to dig into `neigh_xmit`. I need to refresh my mental model of how this is supposed to work. Where does it fit in the larger picture? What dependencies does it have? I need to quickly run through the relevant sections of the architecture to remember the context. Let's see how it interfaces with the neighbor discovery process and the actual packet transmission paths. Got to make sure I don't miss any dependencies. I'm especially keen to look for any potential bottlenecks or race conditions here. This section is often a trouble spot. Let's start with a quick code review. I'll focus on the data structures it uses, paying attention to shared memory and concurrency issues. Once I have a good handle on its basic operation, I'll identify any potential areas for optimization or debugging. Time to get started. |
| 38/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_xmit\\("
}Results: {
"Output": "include/net/neighbour.h=400=void neigh_for_each(struct neigh_table *tbl,\ninclude/net/neighbour.h-401-\t\t void (*cb)(struct neighbour *, void *), void *cookie);\ninclude/net/neighbour.h:402:int neigh_xmit(int fam, struct net_device *, const void *, struct sk_buff *);\ninclude/net/neighbour.h-403-\n--\nnet/core/neighbour.c=3164=EXPORT_SYMBOL(neigh_for_each);\nnet/core/neighbour.c-3165-\nnet/core/neighbour.c:3166:int neigh_xmit(int index, struct net_device *dev,\nnet/core/neighbour.c-3167-\t const void *addr, struct sk_buff *skb)\n"
} |
| 39/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 57393 output: 24 thoughts: 10 Thoughts: Okay, here's my summarized thought process, presented as if I were thinking it: **Checking `neigh_ifdown`** Right, let's dive in. I need to make sure I'm not overlooking anything crucial. My primary focus right now is that `neigh_ifdown` function or process. I need to examine its purpose and implementation within the current context. It's important to understand how it behaves, its side effects, and any potential interactions it might have with other network components. Specifically, I need to know: what triggers it, what it does precisely when invoked, and how those actions are implemented to confirm it works as expected. The details here are critical for debugging or extending functionality. The potential impact of it not functioning correctly could be problematic and need to be ruled out. Let's see what's involved! |
| 40/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_ifdown\\("
}Results: {
"Output": "include/net/neighbour.h=360=void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h:361:int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h-362-int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev);\n--\nnet/core/neighbour.c=465=int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)\n--\nnet/core/neighbour.c-470-\nnet/core/neighbour.c:471:int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)\nnet/core/neighbour.c-472-{\n"
} |
| 41/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 57812 output: 24 thoughts: 10 Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience: **Examining `neigh_changeaddr`** Alright, let's dive right in and check out the `neigh_changeaddr` function. Given the context, I'm assuming it deals with address changes in the neighbor table – probably related to ARP, NDP, or some other layer-2 address resolution mechanism. My initial thought process is to understand exactly how it handles these updates. Is it a straightforward replacement, or does it involve any more sophisticated logic, like invalidating old entries, notifying other relevant subsystems, or potentially dealing with address conflicts? I'll be looking for how it interacts with the kernel's network stack, specifically concerning the neighbor cache and any associated timers. Understanding the locking mechanisms employed will be crucial; I need to quickly assess if there are potential race conditions or performance bottlenecks related to concurrent access to the neighbor table during address changes. I'll pay close attention to the error handling as well; what scenarios does it anticipate, and how robust is the approach to handling failures? Specifically, I'm thinking about how it deals with situations like the target address being unavailable, or an unexpected change in link state. Finally, I'll be interested in how the function interacts with any related policy frameworks – does it adhere to configured limits, or does it have the ability to override some default behaviours? |
| 42/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?void neigh_changeaddr\\("
}Results: {
"Output": "include/net/neighbour.h=359=bool neigh_remove_one(struct neighbour *ndel);\ninclude/net/neighbour.h:360:void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h-361-int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);\n--\nnet/core/neighbour.c=422=static void neigh_flush_table(struct neigh_table *tbl)\n--\nnet/core/neighbour.c-438-\nnet/core/neighbour.c:439:void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)\nnet/core/neighbour.c-440-{\n"
} |
| 43/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 58330 output: 25 thoughts: 11 Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert: **Investigating a Potential Network Issue: `neigh_carrier_down`** Right, let's dive into this. That `neigh_carrier_down` alert has caught my eye. It's a key indicator, and I need to assess its implications. This means digging into the network configuration, probable cause, and overall effect on the system as a whole. Time to grab my tools and delve deeper. |
| 44/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_carrier_down\\("
}Results: {
"Output": "include/net/neighbour.h=361=int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h:362:int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h-363-int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb);\n--\nnet/core/neighbour.c=446=static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,\n--\nnet/core/neighbour.c-464-\nnet/core/neighbour.c:465:int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)\nnet/core/neighbour.c-466-{\n"
} |
| 45/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 58689 output: 23 thoughts: 9 Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person: **Reviewing `neigh_update`** Alright, time to dig into `neigh_update`. My immediate focus is to quickly assess its role and impact. *What specific function is this intended to perform?* I'm looking for its core logic - the algorithm, the data structures, the critical dependencies. Is it a core computational routine, or is it more of a pre/post-processing step? *Where does it sit within the larger picture of the simulation or analysis?* Are there any obvious areas for optimization, or potential bottlenecks given my experience? I'll be searching for any edge cases it might not be properly handling. Are boundary conditions appropriately taken into consideration? I want to make sure I'm following every conditional statement and loop to ensure it's doing exactly what it should. And, ultimately, does it return the correct and expected value? If the update is designed to make use of any external information, I'll need to keep an eye on how that's being communicated. |
| 46/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_update\\("
}Results: {
"Output": "include/net/neighbour.h=354=int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,\ninclude/net/neighbour.h-355-\t\t const bool immediate_ok);\ninclude/net/neighbour.h:356:int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, u32 flags,\ninclude/net/neighbour.h-357-\t\t u32 nlmsg_pid);\n--\nnet/core/neighbour.c=1351=static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,\n--\nnet/core/neighbour.c-1530-\nnet/core/neighbour.c:1531:int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,\nnet/core/neighbour.c-1532-\t\t u32 flags, u32 nlmsg_pid)\n"
} |
| 47/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 59197 output: 27 thoughts: 11 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in my domain of expertise: **Checking `neigh_event_ns`** Right, let's take a quick look at `neigh_event_ns`. I need to refresh my mental model here. Is this the right place to be looking for that particular signal or timestamp? Am I expecting a particular value range or pattern? Knowing the context of this data structure, I'm probably trying to correlate something – likely timing-related, given the `ns` suffix - with events or actions happening in the neighborhood. I have a hunch it's related to some kind of network or resource contention. I need to quickly ascertain: does this variable hold what I anticipate, or do I need to look elsewhere, maybe at the associated log files or other related monitoring metrics? I have to ensure this is exactly what I think it is, or this debug will go sideways fast. |
| 48/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?struct neighbour \\*neigh_event_ns\\("
}Results: {
"Output": "include/net/neighbour.h=365=int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb);\ninclude/net/neighbour.h:366:struct neighbour *neigh_event_ns(struct neigh_table *tbl,\ninclude/net/neighbour.h-367-\t\t\t\t\t\tu8 *lladdr, void *saddr,\n--\nnet/core/neighbour.c=1540=void __neigh_set_probe_once(struct neighbour *neigh)\n--\nnet/core/neighbour.c-1553-\nnet/core/neighbour.c:1554:struct neighbour *neigh_event_ns(struct neigh_table *tbl,\nnet/core/neighbour.c-1555-\t\t\t\t u8 *lladdr, void *saddr,\n"
} |
| 49/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 59634 output: 25 thoughts: 11 Thoughts: Okay, here's my attempt at summarizing that thought process, tailored for an expert audience: **Debugging `neigh_resolve_output`** Alright, time to get my hands dirty. My primary focus right now needs to be on `neigh_resolve_output`. This function, I suspect, is the key to untangling whatever's going sideways. I need to understand its behavior, its dependencies, and what potential failure points might be lurking within it. I'll need to meticulously examine its code, tracing the flow of data in and out. I'll be particularly interested in identifying any points where it might be making incorrect assumptions about the network configuration, the state of adjacent nodes, or the expected data formats. I should also consider how this function interacts with any locking mechanisms; if there's a concurrency issue, `neigh_resolve_output` would be a likely culprit. I'm thinking I'll start with the inputs it expects and the outputs it generates, working back from any failures I'm seeing to understand how the expected state diverges from the actual state. Time to get down to the nitty-gritty. |
| 50/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_resolve_output\\("
}Results: {
"Output": "include/net/neighbour.h=362=int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev);\ninclude/net/neighbour.h:363:int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb);\ninclude/net/neighbour.h-364-int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb);\n--\nnet/core/neighbour.c=1567=static void neigh_hh_init(struct neighbour *n)\n--\nnet/core/neighbour.c-1585-\nnet/core/neighbour.c:1586:int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)\nnet/core/neighbour.c-1587-{\n"
} |
| 51/2 | 2026/08/07 23:58 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 60110 output: 24 thoughts: 10 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in my domain of expertise: **Checking `pneigh_enqueue`** Alright, time to dig into `pneigh_enqueue`. Given the context of what I'm working on – likely something to do with packet neighbor discovery or management in a network stack – this is a critical function. I need to understand its behavior, especially its interaction with the neighbor cache and the overall queuing strategy. I'm thinking about: * **The arguments:** What data structures are passed into `pneigh_enqueue`? Are they pointers to packet buffers, neighbor entries, or something else? Understanding the argument types tells me what information this function operates on. * **The internal logic:** I want to trace the execution flow. Does it perform any checks before queuing? What criteria determine whether a packet is enqueued, dropped, or forwarded immediately? Is there any locking involved? I need to look for potential race conditions or performance bottlenecks. * **The queuing mechanism:** What kind of queue is used? Is it a simple FIFO, a priority queue, or something more sophisticated? What are the implications of the queue's properties in terms of packet ordering, latency, and congestion management? * **Interaction with other functions:** How does `pneigh_enqueue` interact with other functions in the neighbor subsystem, such as `pneigh_lookup`, `pneigh_resolve`, or interrupt handlers? I want to see if this function is only adding items to a queue, or if other parts of the system are going to affect its usage, like setting time outs etc. * **Error handling:** What happens if the queue is full, or if there's an issue with memory allocation? Are packets dropped silently, or are there error codes? The error handling is important to analyze, so I know how the overall system behaves. This is where my mind goes as I start to investigate `pneigh_enqueue`. I'm mentally breaking down the function into its core components to fully grasp its behavior and potential impact on the overall system performance. |
| 52/2 | 2026/08/07 23:58 | tool | grepper |
0mArgs:{
"Expression": "^(static )?void pneigh_enqueue\\("
}Results: {
"Output": "include/net/neighbour.h=382=static inline void neigh_set_reach_time(struct neigh_parms *p)\n--\ninclude/net/neighbour.h-388-\ninclude/net/neighbour.h:389:void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,\ninclude/net/neighbour.h-390-\t\t struct sk_buff *skb);\n--\nnet/core/neighbour.c=1697=static unsigned long neigh_proxy_delay(struct neigh_parms *p)\n--\nnet/core/neighbour.c-1707-\nnet/core/neighbour.c:1708:void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,\nnet/core/neighbour.c-1709-\t\t struct sk_buff *skb)\n"
} |
| 53/2 | 2026/08/07 23:59 | llm | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 60807 output: 31 thoughts: 12 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as if they were my own: **Investigating `neigh_parms_alloc`** Right, let's dive into this. I'm going to start by taking a close look at `neigh_parms_alloc`. It’s where everything begins, right? I want to refresh my understanding of its initialization process. I know the basics, but I need to make sure I haven’t missed anything subtle. I'm thinking about memory allocation specifics – what are the typical sizes it's using? Are there any potential issues with fragmentation based on the current configuration or the expected workload? And, of course, I'm checking for any obvious signs of errors like uninitialized data or improper memory management. I’m thinking about possible edge cases, and I'll see if I can run through some scenarios in my head, imagining different network conditions. Time to see what's what. |
| 54/2 | 2026/08/08 00:00 | tool | grepper |
0mArgs:{
"Expression": "^(static )?struct neigh_parms \\*neigh_parms_alloc\\("
}Results: {
"Output": "include/net/neighbour.h=366=struct neighbour *neigh_event_ns(struct neigh_table *tbl,\n--\ninclude/net/neighbour.h-369-\ninclude/net/neighbour.h:370:struct neigh_parms *neigh_parms_alloc(struct net_device *dev,\ninclude/net/neighbour.h-371-\t\t\t\t struct neigh_table *tbl);\n--\nnet/core/neighbour.c=1734=static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,\n--\nnet/core/neighbour.c-1747-\nnet/core/neighbour.c:1748:struct neigh_parms *neigh_parms_alloc(struct net_device *dev,\nnet/core/neighbour.c-1749-\t\t\t\t struct neigh_table *tbl)\n"
} |
| 55/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 61279 output: 26 thoughts: 12 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary: **Checking `neigh_parms_release`** Alright, time to dive in. My first thought is to zero in on `neigh_parms_release`. I need to verify its current state. Is this variable set? What value does it hold? Is it the one I expect, given the current stage of the process? This check is crucial because a misconfiguration here can cascade into serious downstream effects. I need to quickly confirm its correctness, ideally with a method that provides full visibility and minimal cognitive overhead. I'll need to correlate what I find with my mental model of the system. I also need to be prepared for the event that I have to alter it; I will need a strategy for changing its value safely. |
| 56/2 | 2026/08/08 00:00 | tool | grepper |
0mArgs:{
"Expression": "^(static )?void neigh_parms_release\\("
}Results: {
"Output": "include/net/neighbour.h=370=struct neigh_parms *neigh_parms_alloc(struct net_device *dev,\ninclude/net/neighbour.h-371-\t\t\t\t struct neigh_table *tbl);\ninclude/net/neighbour.h:372:void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms);\ninclude/net/neighbour.h-373-\n--\nnet/core/neighbour.c=1781=static void neigh_rcu_free_parms(struct rcu_head *head)\n--\nnet/core/neighbour.c-1788-\nnet/core/neighbour.c:1789:void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)\nnet/core/neighbour.c-1790-{\n"
} |
| 57/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 61716 output: 26 thoughts: 25 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing Neighborhood Sequence Functions** Okay, let's dive in. My immediate concern is ensuring the correct behavior of the neighborhood sequence logic. I need to verify how these functions interact and behave at different stages. Specifically, I'm thinking about the flow and edge cases within `neigh_seq_start`, `neigh_seq_next`, and `neigh_seq_stop`. Does `neigh_seq_start` initialize the sequence properly, particularly edge cases? Then, `neigh_seq_next`, is it correctly generating the subsequent neighbors within the data structure? Are there any potential issues with iterating past the expected bounds? Lastly, is `neigh_seq_stop` doing its cleanup, freeing up any resources, or handling any finalization correctly? I'll probably want to mentally trace a few scenarios with specific examples. I want to make certain that the sequence behavior is consistent and robust under various conditions. I will be looking out for any memory leaks or incorrect object releases. |
| 58/2 | 2026/08/08 00:00 | tool | grepper |
0mArgs:{
"Expression": "^(static )?void \\*neigh_seq_start\\("
}Results: {
"Output": "include/net/neighbour.h=404=struct neigh_seq_state {\n--\ninclude/net/neighbour.h-415-};\ninclude/net/neighbour.h:416:void *neigh_seq_start(struct seq_file *, loff_t *, struct neigh_table *,\ninclude/net/neighbour.h-417-\t\t unsigned int);\n--\nnet/core/neighbour.c=3363=static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)\n--\nnet/core/neighbour.c-3375-\nnet/core/neighbour.c:3376:void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)\nnet/core/neighbour.c-3377-\t__acquires(tbl-\u003elock)\n"
} |
| 59/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 62219 output: 26 thoughts: 21 Thoughts: Here's my thought process, as if I were considering this code: **Checking Sysctl Registration** Okay, let's dive into this. My initial instinct is to scrutinize how these neighbor-related sysctls are registered and unregistered. Specifically, I need to understand the mechanism behind `neigh_sysctl_register` and `neigh_sysctl_unregister`. Are we using the standard sysctl framework? What knobs are we exposing to the user? What dependencies exist between registration and module loading/unloading? Are there any potential race conditions I should be looking out for, especially during system startup or shutdown? Are we properly handling errors during registration and unregistration, perhaps logging appropriately or falling back to defaults? I need to verify that resources are cleaned up correctly in all scenarios and that the sysctl values are appropriately protected against concurrent access. |
| 60/2 | 2026/08/08 00:00 | tool | grepper |
0mArgs:{
"Expression": "^(static )?int neigh_sysctl_register\\("
}Results: {
"Output": "include/net/neighbour.h=426=int neigh_proc_dointvec_ms_jiffies(const struct ctl_table *ctl, int write,\n--\ninclude/net/neighbour.h-428-\ninclude/net/neighbour.h:429:int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,\ninclude/net/neighbour.h-430-\t\t\t proc_handler *proc_handler);\n--\nnet/core/neighbour.c=3740=static struct neigh_sysctl_table {\n--\nnet/core/neighbour.c-3795-\nnet/core/neighbour.c:3796:int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,\nnet/core/neighbour.c-3797-\t\t\t proc_handler *handler)\n"
} |
| 61/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 62667 output: 19 thoughts: 944 Thoughts: **Analyzing the Neighbor Table Relocation Patch** Okay, so this patch is all about moving the `arp_tbl` and `nd_tbl` structures into `net->neigh_tables`. This is going to fundamentally change how neighbor tables are initialized, accessed, and managed within the network stack, so it warrants a thorough review. First, I need to identify the key functions that are directly affected. Looking at the changes, the primary ones are: `neigh_table_register`, `neigh_table_unregister`, `neigh_table_init`, `neigh_table_clear`, `arp_net_init`, `arp_net_exit`, `ndisc_net_init`, `ndisc_net_exit`, the packet handlers `arp_rcv` and `ndisc_rcv`, and the netlink handlers `neigh_add`, `neigh_delete`, `neigh_get`, `neightbl_set`, `neightbl_dump_info`, `neigh_dump_info`, and then `pneigh_create`, `pneigh_delete`, `pneigh_lookup`, and `neigh_xmit`. I'll want to add those functions to the `FocusSymbols` list. `arp_rcv` and `ndisc_rcv` are good entry points to start with, being the packet handlers. Then `neigh_table_register` and `neigh_table_unregister` which should be called during namespace creation/destruction. Along those lines, I'll need to remember `arp_net_init` and `ndisc_net_init` since they are called during namespace creation as well. The netlink handlers are likely handling the bulk of the configuration changes, so I'll keep an eye on those. Now, I need to narrow down the list to what's most relevant to the core logic affected by the move. Let's see... Taking into account both modified functions and those that directly touch the tables, I've got to focus on: `neigh_table_register`, `neigh_table_unregister`, `arp_rcv` (and the `arp_process` it calls), `ndisc_rcv` (including `ndisc_recv_ns`, `ndisc_recv_na`, `ndisc_recv_rs`, and `ndisc_router_discovery`), netlink handlers `neigh_add`, `neigh_delete`, `neigh_get`, `neigh_dump_info`, `neightbl_set`, and `neightbl_dump_info`, and also `pneigh_lookup`, `pneigh_create`, `pneigh_delete`. I also need to keep `neigh_xmit`, `neigh_ifdown`, `neigh_changeaddr`, `neigh_carrier_down`, `neigh_seq_start`, `neigh_sysctl_register`, and `neigh_sysctl_unregister` in mind. Okay, let's condense that down to the absolute core: `neigh_table_register`, `neigh_table_unregister`, `arp_rcv`, `ndisc_rcv`, `pneigh_create`, `pneigh_delete`, `pneigh_lookup`, and `neigh_xmit`. Finally, for configuration, `CONFIG_NET` is a given, `CONFIG_INET` and `CONFIG_IPV6` for the respective address families, and crucially, `CONFIG_NET_NS` for supporting network namespaces since this patch is all about per-namespace neighbor tables. I'll make sure those are properly set up when I test this. |
| 62/2 | 2026/08/08 00:00 | tool | grepper |
0mError:no matches Args: {
"Expression": "depends on NET_NS"
}Results: {
"error": "no matches"
} |
| 63/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 64397 output: 18 thoughts: 18 Thoughts: Okay, here's my summarized thought process, keeping in mind my expertise: **Checking `CONFIG_NET_NS` in `net/Kconfig`** Alright, let's dive into this. The context implies we're dealing with network namespaces, and likely configuring them within a kernel build. My initial thought is to confirm the `CONFIG_NET_NS` option. I need to quickly establish if it's even enabled or available as a build option. Jumping into `net/Kconfig` is the logical first step, as that's where core network-related configuration is usually housed. This Kconfig file will reveal whether the feature is enabled (directly or indirectly through dependencies), what its default setting is, and any relevant help text. The goal is to quickly ascertain the state of the base network namespace configuration option. From there, I'll be in a better position to understand the impact of any related settings or troubleshoot any build or runtime issues related to network namespaces. I'll be looking for things like dependencies, potential conflicts with other features, and perhaps even some hints about the intended use cases for this particular namespace configuration. This is basic due diligence, but essential before diving into more complex debugging or configuration. |
| 64/2 | 2026/08/08 00:00 | tool | grepper |
0mArgs:{
"Expression": "config NET_NS"
}Results: {
"Output": "init/Kconfig=1451=config PID_NS\n--\ninit/Kconfig-1458-\ninit/Kconfig:1459:config NET_NS\ninit/Kconfig-1460-\tbool \"Network namespace\"\n--\nnet/Kconfig.debug=3=config NET_DEV_REFCNT_TRACKER\n--\nnet/Kconfig.debug-11-\nnet/Kconfig.debug:12:config NET_NS_REFCNT_TRACKER\nnet/Kconfig.debug-13-\tbool \"Enable networking namespace refcount tracking\"\n--\nnet/nsh/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nnet/nsh/Kconfig:2:menuconfig NET_NSH\nnet/nsh/Kconfig-3-\ttristate \"Network Service Header (NSH) protocol\"\n"
} |
| 65/2 | 2026/08/08 00:00 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 64903 output: 190 thoughts: 456 Thoughts: **My Analysis of Network Namespace Neighbor Table Fuzzing** Okay, so I'm looking at this configuration and patch, and here's what's going through my head. First, I see that we're dealing with network namespaces, confirmed by the `NET_NS` config, and we need `IPV6` for `ndisc_rcv`, which makes sense. Let's get this down to brass tacks: * **WorthFuzzing**: *True* – Definitely worth the effort. * **FocusSymbols**: My target functions are the ones directly involved in neighbor table management, registration, and transmission. This includes `neigh_table_register`, `neigh_table_unregister`, `arp_rcv`, `ndisc_rcv`, `pneigh_create`, `pneigh_delete`, `pneigh_lookup`, and `neigh_xmit`. * **EnableConfigs**: We have `NET_NS`, `IPV6`, and `INET` enabled. That checks out. Now, I'm considering whether we should add `neigh_add` and other netlink handlers. They are static, registered through `neigh_rtnl_msg_handlers`, and I'm on the fence about whether to include them. Syzkaller can still fuzz them if we provide their names, and I considered providing the functions that are exported or global, but since `neigh_add` isn't inlined, it makes the fuzzing approach viable. I'll stick to the global ones for now; Syzkaller can handle the static ones just fine. Then I saw the extent of the patch's reach. It touches a significant number of drivers and subsystems that are related to or interact with neighbor tables. This includes Mellanox, Netronome, Rocker, SFC, VXLAN, VRF, bridge and 6lowpan. This means the core change is this: per-namespace neighbor tables. I'll use `set-results` to record all of this. |
| 66/2 | 2026/08/08 00:01 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"NET_NS",
"IPV6",
"INET"
],
"FocusSymbols": [
"neigh_table_register",
"neigh_table_unregister",
"arp_rcv",
"ndisc_rcv",
"pneigh_create",
"pneigh_delete",
"pneigh_lookup",
"neigh_xmit"
],
"Reasoning": "The patch changes the IPv4 ARP and IPv6 Neighbor Discovery tables from being global to being per-network namespace. This is a significant functional change to the core networking stack, affecting how neighbor tables are initialized, accessed, and destroyed. It impacts network namespace creation/destruction, ARP/ND packet processing, and netlink neighbor management. Fuzzing should focus on namespace operations, ARP/ND packet reception, and proxy neighbor management.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"NET_NS",
"IPV6",
"INET"
],
"FocusSymbols": [
"neigh_table_register",
"neigh_table_unregister",
"arp_rcv",
"ndisc_rcv",
"pneigh_create",
"pneigh_delete",
"pneigh_lookup",
"neigh_xmit"
],
"Reasoning": "The patch changes the IPv4 ARP and IPv6 Neighbor Discovery tables from being global to being per-network namespace. This is a significant functional change to the core networking stack, affecting how neighbor tables are initialized, accessed, and destroyed. It impacts network namespace creation/destruction, ARP/ND packet processing, and netlink neighbor management. Fuzzing should focus on namespace operations, ARP/ND packet reception, and proxy neighbor management.",
"WorthFuzzing": true
} |
| 67/1 | 2026/08/08 00:01 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 68/2 | 2026/08/08 00:01 | agent | kmsan-evaluator |
0mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch series refactors the IPv4 ARP and IPv6 NDISC neighbour tables (`arp_tbl` and `nd_tbl`) to be per-network namespace instead of global. It modifies various network drivers and core networking code to retrieve the neighbour table from the network namespace (`arp_table(net)` and `nd_table(net)`) rather than using the global variables.\n\nKey changes:\n1. `struct neigh_table` instances are now dynamically allocated per netns via `neigh_table_register()`, which uses `kmemdup()` to duplicate a statically allocated template (`arp_tbl` or `nd_tbl`). Since the templates are static globals, their memory (including padding) is zero-initialized.\n2. The `net` field is removed from `struct pneigh_entry` because the neighbour tables are now per-netns, making the field redundant.\n3. Various lookups, creations, and garbage collection logic are updated to use the per-netns tables.\n\nKMSAN vs KASAN applicability:\n- KMSAN detects uses of uninitialized memory and info-leaks. The new dynamic allocations for the neighbour tables use `kmemdup()` from zero-initialized static templates, `kzalloc()` for hash buckets, and `alloc_percpu()` for stats. No uninitialized memory is introduced.\n- The structures are not copied directly to user space; their fields are serialized into netlink messages via `nla_put_*` functions, which do not leak padding.\n- Any potential bugs introduced by this refactoring (e.g., use-after-free during netns teardown, NULL pointer dereferences, or out-of-bounds accesses) would be reliably caught by standard KASAN and other bug detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit a5688afda8d14b0a24271682d891e388f5701c90
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 7 23:53:29 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 16a015b672063..895a3e3f7310b 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1378,7 +1378,8 @@ struct ipoib_neigh *ipoib_neigh_get(struct net_device *dev, u8 *daddr)
return neigh;
}
-static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
+static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv,
+ int gc_interval)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
@@ -1397,7 +1398,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
goto out_unlock;
/* neigh is obsolete if it was idle for two GC periods */
- dt = 2 * arp_tbl.gc_interval;
+ dt = 2 * gc_interval;
neigh_obsolete = jiffies - dt;
for (i = 0; i < htbl->size; i++) {
@@ -1433,11 +1434,16 @@ static void ipoib_reap_neigh(struct work_struct *work)
{
struct ipoib_dev_priv *priv =
container_of(work, struct ipoib_dev_priv, neigh_reap_task.work);
+ struct net_device *dev = priv->dev;
+ struct net *net = dev_net(dev);
+ struct neigh_table *tbl;
+ int gc_interval;
- __ipoib_reap_neigh(priv);
+ tbl = arp_table(net);
+ gc_interval = tbl->gc_interval;
+ __ipoib_reap_neigh(priv, gc_interval);
- queue_delayed_work(priv->wq, &priv->neigh_reap_task,
- arp_tbl.gc_interval);
+ queue_delayed_work(priv->wq, &priv->neigh_reap_task, gc_interval);
}
@@ -1590,8 +1596,11 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
{
struct ipoib_neigh_table *ntbl = &priv->ntbl;
- struct ipoib_neigh_hash *htbl;
struct ipoib_neigh __rcu **buckets;
+ struct net_device *dev = priv->dev;
+ struct net *net = dev_net(dev);
+ struct ipoib_neigh_hash *htbl;
+ struct neigh_table *tbl;
u32 size;
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
@@ -1599,7 +1608,9 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl = kzalloc_obj(*htbl);
if (!htbl)
return -ENOMEM;
- size = roundup_pow_of_two(arp_tbl.gc_thresh3);
+
+ tbl = arp_table(net);
+ size = roundup_pow_of_two(tbl->gc_thresh3);
buckets = kvzalloc_objs(*buckets, size);
if (!buckets) {
kfree(htbl);
@@ -1614,7 +1625,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
/* start garbage collection */
queue_delayed_work(priv->wq, &priv->neigh_reap_task,
- arp_tbl.gc_interval);
+ tbl->gc_interval);
return 0;
}
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c
index 0c4f462baa6ef..ba45b61b09bb0 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 648f4521c0964..6a8d8aac7d16f 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 8b827201935eb..67c12ca19d59d 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 db260e3d1412f..37a8ddee3ea1e 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 3d6fdbab05e01..f4a435885ba43 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 ae63d549b5429..1cd8347c274d4 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 ca30702f88780..d650d33e3709c 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 84a55f2b48ffe..0fcd65fc0af7f 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 15d19a8a17101..ead8b447b89c4 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 b84235e93ffe9..793a562fc1f7c 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 c2ad3a358d201..152c4639ecc6f 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 a0557a3a70260..50ec04cc0213f 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 fd5505153d0b6..345d1883d993e 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;
@@ -2047,7 +2048,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;
@@ -2100,6 +2101,7 @@ static int neigh_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
{
struct vxlan_dev *vxlan = netdev_priv(dev);
+ struct neigh_table *tbl;
struct neighbour *n;
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest))
@@ -2113,8 +2115,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,
@@ -2139,8 +2143,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 e8747e0713c79..e932def63d62f 100644
--- a/include/net/arp.h
+++ b/include/net/arp.h
@@ -7,8 +7,10 @@
#include <linux/hash.h>
#include <net/neighbour.h>
-
-extern struct neigh_table arp_tbl;
+static inline struct neigh_table *arp_table(struct net *net)
+{
+ return net->neigh_tables[NEIGH_ARP_TABLE];
+}
static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32 *hash_rnd)
{
@@ -21,10 +23,12 @@ static inline u32 arp_hashfn(const void *pkey, const struct net_device *dev, u32
#ifdef CONFIG_INET
static inline struct neighbour *__ipv4_neigh_lookup_noref(struct net_device *dev, u32 key)
{
+ struct neigh_table *tbl = arp_table(dev_net(dev));
+
if (dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))
key = INADDR_ANY;
- return ___neigh_lookup_noref(&arp_tbl, neigh_key_eq32, arp_hashfn, &key, dev);
+ return ___neigh_lookup_noref(tbl, neigh_key_eq32, arp_hashfn, &key, dev);
}
#else
static inline
diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 3da1a6f8d3f92..5b7a36cbda169 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 8860cc2175fc1..954c13b3a2b4f 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -179,7 +179,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 {
@@ -339,8 +338,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 +388,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 501af1999fe83..f96a390ae5364 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -39,6 +39,7 @@
#include <net/netns/mctp.h>
#include <net/netns/vsock.h>
#include <net/net_trackers.h>
+#include <net/neighbour_tables.h>
#include <linux/ns_common.h>
#include <linux/idr.h>
#include <linux/skbuff.h>
@@ -47,6 +48,7 @@
struct user_namespace;
struct proc_dir_entry;
+struct neigh_table;
struct net_device;
struct sock;
struct ctl_table_header;
@@ -103,6 +105,8 @@ struct net {
struct proc_dir_entry *proc_net;
struct proc_dir_entry *proc_net_stat;
+ struct neigh_table *neigh_tables[NEIGH_NR_TABLES];
+
#ifdef CONFIG_SYSCTL
struct ctl_table_set sysctls;
#endif
diff --git a/include/net/route.h b/include/net/route.h
index f90106f383c56..38a23f28ee14a 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -403,8 +403,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 23eb6931a2b4a..3b4cd709e70ea 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -192,7 +192,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;
@@ -469,7 +469,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;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb642..e22b0fe4b9521 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -131,10 +131,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 +149,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 +165,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 +178,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 +334,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 +345,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);
@@ -412,6 +394,9 @@ static void neigh_flush_one(struct neighbour *n)
write_unlock(&n->lock);
+ if (!check_net(neigh_parms_net(n->parms)))
+ timer_shutdown_sync(&n->timer);
+
neigh_cleanup_and_release(n);
}
@@ -471,8 +456,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;
@@ -752,8 +736,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 +749,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 +758,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 +769,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 +780,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 +812,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 +827,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);
@@ -1523,10 +1503,23 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
write_unlock_bh(&neigh->lock);
- if (((new ^ old) & NUD_PERMANENT) || gc_update)
- neigh_update_gc_list(neigh);
- if (managed_update)
- neigh_update_managed_list(neigh);
+ gc_update |= !!((new ^ old) & NUD_PERMANENT);
+ if (gc_update || managed_update) {
+ spin_lock_bh(&neigh->tbl->lock);
+
+ if (!neigh->dead) {
+ write_lock(&neigh->lock);
+
+ if (gc_update)
+ neigh_update_gc_list(neigh);
+ if (managed_update)
+ neigh_update_managed_list(neigh);
+
+ write_unlock(&neigh->lock);
+ }
+
+ spin_unlock_bh(&neigh->tbl->lock);
+ }
if (notify)
call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
@@ -1540,7 +1533,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 +1550,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 +1562,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 +1614,6 @@ int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_HH_FILLFAIL);
goto out;
}
-EXPORT_SYMBOL(neigh_resolve_output);
/* As fast as possible without hh cache */
@@ -1741,16 +1730,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 +1777,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 +1799,143 @@ 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);
+ RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
+ if (!tbl->nht)
+ goto err_hash;
- 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;
+ phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
+ tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
+ if (!tbl->phash_buckets)
+ goto err_phash;
+
+ tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
+ tbl->key_len, NEIGH_PRIV_ALIGN);
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
- panic("cannot create neighbour cache statistics");
+ goto err_stats;
#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");
+ 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
- RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
-
- phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
- tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
-
- if (!tbl->nht || !tbl->phash_buckets)
- panic("cannot allocate neighbour cache hashes");
-
- 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->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;
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);
+ 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);
- timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
- skb_queue_head_init_class(&tbl->proxy_queue,
- &neigh_table_proxy_queue_class);
-
- tbl->last_flush = now;
- tbl->last_rand = now + tbl->parms.reachable_time * 20;
+ return 0;
- rcu_assign_pointer(neigh_tables[index], tbl);
+#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_clear(struct net *net, struct neigh_table *tbl)
{
- RCU_INIT_POINTER(neigh_tables[index], NULL);
- synchronize_rcu();
+ struct neigh_hash_table *nht;
- /* 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);
+ timer_shutdown_sync(&tbl->proxy_timer);
+
neigh_ifdown(tbl, NULL);
- if (atomic_read(&tbl->entries))
- pr_crit("neighbour leakage\n");
+ DEBUG_NET_WARN_ON_ONCE(atomic_read(&tbl->entries));
- call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
- neigh_hash_free_rcu);
- tbl->nht = NULL;
+ remove_proc_entry(tbl->id, net->proc_net_stat);
+
+ free_percpu(tbl->stats);
+ tbl->stats = NULL;
kfree(tbl->phash_buckets);
tbl->phash_buckets = NULL;
- remove_proc_entry(tbl->id, init_net.proc_net_stat);
+ nht = rcu_dereference_protected(tbl->nht, 1);
+ tbl->nht = NULL;
+ neigh_hash_free_rcu(&nht->rcu);
+}
- free_percpu(tbl->stats);
- tbl->stats = NULL;
+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;
+ }
+
+ 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);
+ kfree(tbl);
}
-static struct neigh_table *neigh_find_table(int family)
+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 +1989,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 +1999,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 +2075,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 +2095,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;
}
@@ -2400,10 +2417,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 +2432,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (!found) {
- rcu_read_unlock();
err = -ENOENT;
goto errout;
}
@@ -2442,8 +2456,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 +2538,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 +2554,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 +2605,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 +2620,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 +2797,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 +2813,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 +2837,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 +2851,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 +2937,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 +2961,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 +3083,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 +3110,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 +3163,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 +3171,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 +3218,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 +3306,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 +3313,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 +3326,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 +3389,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 +3415,6 @@ void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
++(*pos);
return rc;
}
-EXPORT_SYMBOL(neigh_seq_next);
void neigh_seq_stop(struct seq_file *seq, void *v)
__releases(tbl->lock)
@@ -3469,7 +3426,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 */
@@ -3593,7 +3549,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 +3644,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 +3653,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 +3672,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 +3878,6 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
err:
return -ENOBUFS;
}
-EXPORT_SYMBOL(neigh_sysctl_register);
void neigh_sysctl_unregister(struct neigh_parms *p)
{
@@ -3937,7 +3888,6 @@ void neigh_sysctl_unregister(struct neigh_parms *p)
kfree(t);
}
}
-EXPORT_SYMBOL(neigh_sysctl_unregister);
#endif /* CONFIG_SYSCTL */
diff --git a/net/ieee802154/6lowpan/tx.c b/net/ieee802154/6lowpan/tx.c
index 4df76ff50699e..4f511476b992c 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 d409f606aec0e..90bc53fb80905 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 neigh_table *tbl = arp_table(net);
+ struct dst_entry *reply_dst = NULL;
struct net_device *dev = skb->dev;
- struct in_device *in_dev = __in_dev_get_rcu(dev);
- struct arphdr *arp;
+ 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;
- unsigned char *sha;
- unsigned char *tha = NULL;
__be32 sip, tip;
- u16 dev_type = dev->type;
int addr_type;
- struct neighbour *n;
- struct dst_entry *reply_dst = NULL;
- bool is_garp = false;
/* 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 47ded0f607d4b..05de597ce086f 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 @@ static struct in_device *inetdev_init(struct net_device *dev)
static void inetdev_destroy(struct in_device *in_dev)
{
- struct net_device *dev;
+ struct net_device *dev = in_dev->dev;
+ struct net *net = dev_net(dev);
struct in_ifaddr *ifa;
ASSERT_RTNL();
- dev = in_dev->dev;
-
in_dev->dead = 1;
RCU_INIT_POINTER(dev->ip_ptr, NULL);
@@ -332,7 +334,7 @@ static void inetdev_destroy(struct in_device *in_dev)
}
devinet_sysctl_unregister(in_dev);
- neigh_parms_release(&arp_tbl, in_dev->arp_parms);
+ neigh_parms_release(arp_table(net), in_dev->arp_parms);
arp_ifdown(dev);
in_dev_put(in_dev);
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 78f84ae3ee120..cda150309dc6b 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 fd688e1f879f5..b8b6b5d991294 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -788,7 +788,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 f6fa2715b450b..f3be7a8f996dd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -376,6 +376,8 @@ static int snmp6_alloc_dev(struct inet6_dev *idev)
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{
+ struct net *net = dev_net(dev);
+ struct neigh_table *tbl;
struct inet6_dev *ndev;
int err = -ENOMEM;
@@ -393,14 +395,15 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
ndev->dev = dev;
INIT_LIST_HEAD(&ndev->addr_list);
timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0);
- memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
+ memcpy(&ndev->cnf, net->ipv6.devconf_dflt, sizeof(ndev->cnf));
if (ndev->cnf.stable_secret.initialized)
ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ tbl = nd_table(net);
ndev->cnf.mtu6 = dev->mtu;
ndev->ra_mtu = 0;
- ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
+ ndev->nd_parms = neigh_parms_alloc(dev, tbl);
if (!ndev->nd_parms) {
kfree(ndev);
return ERR_PTR(err);
@@ -413,7 +416,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
if (snmp6_alloc_dev(ndev) < 0) {
netdev_dbg(dev, "%s: cannot allocate memory for statistics\n",
__func__);
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ neigh_parms_release(tbl, ndev->nd_parms);
netdev_put(dev, &ndev->dev_tracker);
kfree(ndev);
return ERR_PTR(err);
@@ -481,7 +484,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
return ndev;
err_release:
- neigh_parms_release(&nd_tbl, ndev->nd_parms);
+ neigh_parms_release(tbl, ndev->nd_parms);
ndev->dead = 1;
in6_dev_finish_destroy(ndev);
return ERR_PTR(err);
@@ -4038,9 +4041,11 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
/* Last: Shot the device (if unregistered) */
if (unregister) {
+ struct neigh_table *tbl = nd_table(net);
+
addrconf_sysctl_unregister(idev);
- neigh_parms_release(&nd_tbl, idev->nd_parms);
- neigh_ifdown(&nd_tbl, dev);
+ neigh_parms_release(tbl, idev->nd_parms);
+ neigh_ifdown(tbl, dev);
in6_dev_put(idev);
}
return 0;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 2c44e5ed61716..25fc57e52b5fa 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -125,7 +125,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);
@@ -581,7 +581,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 fe36b3f512850..3e16cb581f424 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -766,10 +766,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);
@@ -787,19 +788,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 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 dad = ipv6_addr_any(saddr);
int is_router = -1;
+ u8 *lladdr = NULL;
SKB_DR(reason);
u64 nonce = 0;
bool inc;
@@ -844,9 +847,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)) {
@@ -879,8 +883,6 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
idev = ifp->idev;
} else {
- struct net *net = dev_net(dev);
-
/* perhaps an address on the master device */
if (netif_is_l3_slave(dev)) {
struct net_device *mdev;
@@ -917,7 +919,7 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
*/
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
if (n)
- pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
+ pneigh_enqueue(tbl, idev->nd_parms, n);
goto out;
}
} else {
@@ -936,15 +938,15 @@ static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
}
if (inc)
- NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
+ NEIGH_CACHE_STAT_INC(tbl, rcv_probes_mcast);
else
- NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
+ NEIGH_CACHE_STAT_INC(tbl, rcv_probes_ucast);
/*
* update / create cache entry
* for the source address
*/
- neigh = __neigh_lookup(&nd_tbl, saddr, dev,
+ neigh = __neigh_lookup(tbl, saddr, dev,
!inc || lladdr || !dev->addr_len);
if (neigh)
ndisc_update(dev, neigh, lladdr, NUD_STALE,
@@ -986,17 +988,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;
@@ -1019,6 +1023,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;
@@ -1033,7 +1038,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)) {
@@ -1057,7 +1062,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
@@ -1077,14 +1083,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;
@@ -1097,7 +1102,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;
}
@@ -1126,18 +1131,20 @@ static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
{
struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
+ const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
+ struct net_device *dev = skb->dev;
+ struct ndisc_options ndopts;
+ struct neigh_table *tbl;
struct neighbour *neigh;
struct inet6_dev *idev;
- const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
- struct ndisc_options ndopts;
u8 *lladdr = NULL;
SKB_DR(reason);
if (skb->len < sizeof(*rs_msg))
return SKB_DROP_REASON_PKT_TOO_SMALL;
- idev = __in6_dev_get(skb->dev);
+ idev = __in6_dev_get(dev);
if (!idev) {
net_err_ratelimited("RS: can't find in6 device\n");
return reason;
@@ -1155,19 +1162,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,
@@ -1231,6 +1238,7 @@ static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
+ struct net *net = dev_net(skb->dev);
bool send_ifinfo_notify = false;
struct neighbour *neigh = NULL;
struct ndisc_options ndopts;
@@ -1240,7 +1248,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;
@@ -1329,7 +1336,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",
@@ -1465,7 +1471,7 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
*/
if (!neigh)
- neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
+ neigh = __neigh_lookup(nd_table(net), &ipv6_hdr(skb)->saddr,
skb->dev, 1);
if (neigh) {
u8 *lladdr = NULL;
@@ -1858,12 +1864,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:
@@ -1887,12 +1896,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:
@@ -1971,12 +1980,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;
@@ -1987,11 +2007,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 = {
@@ -2001,30 +2034,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)
@@ -2039,9 +2049,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 5968ce5ad1508..cea1cdc2b8e8e 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;
}
@@ -4229,6 +4229,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;
@@ -4262,7 +4263,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) ||
@@ -4274,15 +4275,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;
@@ -4301,7 +4301,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;
@@ -4309,7 +4309,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|
@@ -5030,9 +5030,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 7c594bf6ead0d..286da09a2010f 100755
--- a/tools/testing/selftests/net/test_neigh.sh
+++ b/tools/testing/selftests/net/test_neigh.sh
@@ -241,18 +241,18 @@ 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 +263,7 @@ 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
@@ -285,9 +285,9 @@ extern_valid_common()
local orig_gc_stale
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_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 10000"
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"
@@ -297,9 +297,9 @@ extern_valid_common()
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"]')
+ periodic_gc_runs_t0=$(ip -n "$ns1" -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_t1=$(ip -n "$ns1" -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
[[ $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 +310,7 @@ 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()
|
| 69/3 | 2026/08/08 00:01 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 41792 output: 410 thoughts: 1750 Thoughts: **Analysis of Neighbour Table Per-Netns Patch Series** Okay, let's break this down. My initial assessment is that this patch series is refactoring the `arp_tbl` and `nd_tbl` structures, making them per-network namespace rather than global. This is a significant change, so we need to be thorough. My primary concern, as always, is whether this introduces any risks related to uninitialized memory being exposed to user space or used in control flow, which KMSAN would be particularly good at catching. The first thing that jumps out is the use of `kmemdup` in `neigh_table_register`. The code duplicates a `neigh_table` from a global template (`arp_tbl` or `nd_tbl`). My immediate thought: "Is this safe?" I then remind myself that these globals are *statically* allocated. Static variables in C are zero-initialized, which means any padding in those structures will also be zeroed out. So, `kmemdup` is copying zeroed memory and we are safe on this aspect. Further, `neigh_table_init` is then called, which allocates memory and initializes more fields, preventing any chance of uninitialized memory. Next, I need to consider how this data gets exposed to user space. It's done via netlink (`neigh_dump_info`, `neigh_get`, etc.) and procfs. We need to check those functions. But, since the core tables are now per-netns, it would be a very bad idea for the code to expose internal kernel structures. I then remind myself that the functions like `neightbl_fill_info` use `nla_put_u32` and other similar functions. These functions only write explicitly initialized fields to the netlink message, which eliminates the possibility of uninitialized padding being sent. Excellent. I then check any allocations made for the table itself, to see if there is any chance of uninitialized memory at this step. `kmemdup`, `kzalloc` for `phash_buckets` and `alloc_percpu` for `stats` look like they are zeroed. I need to confirm there are no changes to `struct neighbour` or `struct pneigh_entry`. The `net` field has been correctly removed from `struct pneigh_entry` because the per-netns tables make this redundant. This is a good sign; it's a simplification, not a risk. I need to check how the now modified structures get used, for example, `ndisc_recv_ns`, `ndisc_recv_na`, and `arp_process`. These functions just use `arp_table(net)` instead of using the global variables. Given all of this, what risks are there? The main classes of bugs that could arise are use-after-free, null pointer dereferences, memory leaks and out-of-bounds accesses. KASAN is the tool for those. KMSAN only detects uninitialized memory reads or leaks. There is no direct transfer of `struct neigh_table` to userspace. Instead, individual fields are put into netlink messages via appropriate functions. So, again, no uninitialized data leaks here. The tables themselves are not directly exposed. Let's do a double check. If `kmemdup` *did* copy uninitialized memory, it would be dangerous. However, the source is always either `&arp_tbl` or `&nd_tbl`, which are static globals. Thus, any padding is zeroed. And finally, the initialization of the table uses the static `arp_tbl`. Okay, so the summary: This patch series properly refactors the IPv4 ARP and IPv6 NDISC neighbour tables to be per-network namespace. It utilizes `kmemdup()` from zero-initialized static templates, `kzalloc()` for hash buckets, and `alloc_percpu()` for stats. The struct changes only result in fields being put into netlink, using `nla_put_*` functions. Therefore, there's no way to expose uninitialized memory. There's no risk of uninitialized memory leaks or uninitialized memory being used in control flow. KMSAN is therefore not warranted for this patch series; KASAN will catch the more likely introduced bugs. |
| 70/3 | 2026/08/08 00:01 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series refactors the IPv4 ARP and IPv6 NDISC neighbour tables (`arp_tbl` and `nd_tbl`) to be per-network namespace instead of global. It modifies various network drivers and core networking code to retrieve the neighbour table from the network namespace (`arp_table(net)` and `nd_table(net)`) rather than using the global variables.\n\nKey changes:\n1. `struct neigh_table` instances are now dynamically allocated per netns via `neigh_table_register()`, which uses `kmemdup()` to duplicate a statically allocated template (`arp_tbl` or `nd_tbl`). Since the templates are static globals, their memory (including padding) is zero-initialized.\n2. The `net` field is removed from `struct pneigh_entry` because the neighbour tables are now per-netns, making the field redundant.\n3. Various lookups, creations, and garbage collection logic are updated to use the per-netns tables.\n\nKMSAN vs KASAN applicability:\n- KMSAN detects uses of uninitialized memory and info-leaks. The new dynamic allocations for the neighbour tables use `kmemdup()` from zero-initialized static templates, `kzalloc()` for hash buckets, and `alloc_percpu()` for stats. No uninitialized memory is introduced.\n- The structures are not copied directly to user space; their fields are serialized into netlink messages via `nla_put_*` functions, which do not leak padding.\n- Any potential bugs introduced by this refactoring (e.g., use-after-free during netns teardown, NULL pointer dereferences, or out-of-bounds accesses) would be reliably caught by standard KASAN and other bug detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series refactors the IPv4 ARP and IPv6 NDISC neighbour tables (`arp_tbl` and `nd_tbl`) to be per-network namespace instead of global. It modifies various network drivers and core networking code to retrieve the neighbour table from the network namespace (`arp_table(net)` and `nd_table(net)`) rather than using the global variables.\n\nKey changes:\n1. `struct neigh_table` instances are now dynamically allocated per netns via `neigh_table_register()`, which uses `kmemdup()` to duplicate a statically allocated template (`arp_tbl` or `nd_tbl`). Since the templates are static globals, their memory (including padding) is zero-initialized.\n2. The `net` field is removed from `struct pneigh_entry` because the neighbour tables are now per-netns, making the field redundant.\n3. Various lookups, creations, and garbage collection logic are updated to use the per-netns tables.\n\nKMSAN vs KASAN applicability:\n- KMSAN detects uses of uninitialized memory and info-leaks. The new dynamic allocations for the neighbour tables use `kmemdup()` from zero-initialized static templates, `kzalloc()` for hash buckets, and `alloc_percpu()` for stats. No uninitialized memory is introduced.\n- The structures are not copied directly to user space; their fields are serialized into netlink messages via `nla_put_*` functions, which do not leak padding.\n- Any potential bugs introduced by this refactoring (e.g., use-after-free during netns teardown, NULL pointer dereferences, or out-of-bounds accesses) would be reliably caught by standard KASAN and other bug detectors.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|