java-topology/docs/tickets/linux-0004-neigh-parms-ifindex-linear-scan.md
russell@unturf.com 998e2b7b0f linux: fix ticket doc numbering; add missing docs for 0001/0005/0006/0007/0008
- Renamed linux-0001-audit-filter-inodes → linux-0002 (matches patch file reality)
- Renamed linux-0002-dev-alloc-name → linux-0003
- Renamed linux-0003-neigh-parms → linux-0004
- Added linux-0001-headerdep-hash.md (scripts/headerdep.pl detect_cycles CWE-407)
- Added linux-0005-component-find-quadratic.md (drivers/base/component.c)
- Added linux-0006-btf-module-scan-hash.md (kernel/bpf/btf.c bpf_find_btf_id)
- Added linux-0007-pktgen-thread-dev-xarray.md (net/core/pktgen.c)
- Added linux-0008-taskstats-listener-hashset.md (kernel/taskstats.c)
2026-04-04 11:41:36 -04:00

3.5 KiB
Raw Blame History

linux-0004: lookup_neigh_parms — O(P) linear scan by ifindex on every netlink neigh table op

File: net/core/neighbour.c Function: lookup_neigh_parms() (line ~1752) Severity: MEDIUM — triggered on every ip neigh operation and ARP/NDP table config CWE: CWE-407 (Inefficient Algorithmic Complexity)

Code

static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
                                                     struct net *net,
                                                     int ifindex)
{
    struct neigh_parms *p;

    list_for_each_entry(p, &tbl->parms_list, list) {   // O(P) linear scan
        if ((p->dev && p->dev->ifindex == ifindex &&
             net_eq(neigh_parms_net(p), net)) ||
            (!p->dev && !ifindex && net_eq(net, &init_net)))
            return p;
    }
    return NULL;
}

tbl->parms_list holds one neigh_parms per network device that has joined the neighbour table. In environments with many network devices (bridges, VLANs, VxLAN tunnels, bond members), this list grows to O(D) entries.

Complexity

Variable Meaning
P Length of tbl->parms_list — one entry per netdev registered with this neigh_table

lookup_neigh_parms is called from neigh_table_set_key() (netlink path) whenever ip neigh change, ip neigh add, or ip ntable change is issued. On a host with D=300 network interfaces (common in VxLAN fabrics), each such command walks 300 parms entries.

When Triggered

ip ntable change name arp dev eth0        # calls lookup_neigh_parms O(P)
ip neigh change 192.168.1.1 dev eth0 ...  # calls neigh_lookup + parms lookup

Automation scripts that reconfigure neighbour parameters across many interfaces (e.g., setting base_reachable_time for all VTEP devices) issue O(D) netlink commands, each doing an O(D) scan → O(D²) total.

Root Cause

parms_list is a flat linked list ordered by insertion. Lookup by ifindex is O(P) because there is no secondary index.

The natural key for neigh_parms is (net, ifindex). An xarray keyed by ifindex within each net provides O(1) lookup with no extra memory per entry.

Fix

Replace the parms_list linear search with an xarray stored in struct neigh_table:

/* In struct neigh_table (include/net/neighbour.h): */
struct xarray        parms_xa;   /* keyed by ifindex, value = neigh_parms * */
struct list_head     parms_list; /* keep for iteration (GC, sysctl dumps) */
/* neigh_parms_alloc: */
xa_store(&tbl->parms_xa, p->dev ? p->dev->ifindex : 0, p, GFP_KERNEL);

/* lookup_neigh_parms replacement: */
static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
                                                     struct net *net,
                                                     int ifindex)
{
    struct neigh_parms *p = xa_load(&tbl->parms_xa, ifindex);
    if (p && net_eq(neigh_parms_net(p), net))
        return p;
    return NULL;
}

/* neigh_parms_release: */
xa_erase(&tbl->parms_xa, p->dev ? p->dev->ifindex : 0);

The parms_list is retained for the GC timer path (neigh_periodic_work) which iterates all parms to call neigh_set_reach_time.

Impact

  • O(D) per netlink command; O(D²) for configuration scripts covering all devices.
  • On a VxLAN gateway with 500 VTEPs: 500 parms entries × 500 commands = 250,000 list-node comparisons per configuration pass.
  • With xarray: 500 commands × O(1) = 500 xa_load calls.

Patch

See defects/linux/patch/linux-0004-neigh-parms-xarray-lookup.patch