java-topology/docs/tickets/linux-0003-neigh-parms-ifindex-linear-scan.md
russell@unturf.com 9934133dcf whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
2026-03-27 15:23:43 -04:00

3.5 KiB
Raw Blame History

linux-0003: 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