]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
net: ndisc: introduce ndisc_evict_nocarrier sysctl parameter
authorJames Prestwood <prestwoj@gmail.com>
Mon, 1 Nov 2021 17:36:29 +0000 (10:36 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 2 Nov 2021 02:57:14 +0000 (19:57 -0700)
In most situations the neighbor discovery cache should be cleared on a
NOCARRIER event which is currently done unconditionally. But for wireless
roams the neighbor discovery cache can and should remain intact since
the underlying network has not changed.

This patch introduces a sysctl option ndisc_evict_nocarrier which can
be disabled by a wireless supplicant during a roam. This allows packets
to be sent after a roam immediately without having to wait for
neighbor discovery.

A user reported roughly a 1 second delay after a roam before packets
could be sent out (note, on IPv4). This delay was due to the ARP
cache being cleared. During testing of this same scenario using IPv6
no delay was noticed, but regardless there is no reason to clear
the ndisc cache for wireless roams.

Signed-off-by: James Prestwood <prestwoj@gmail.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/networking/ip-sysctl.rst
include/linux/ipv6.h
include/uapi/linux/ipv6.h
net/ipv6/addrconf.c
net/ipv6/ndisc.c

index 18fde4ed7a5e2b204a678379ed2f1caf56cbd7ae..c61cc0219f4c1c31a34a81b2f3da00f622b4cd0c 100644 (file)
@@ -2350,6 +2350,15 @@ ndisc_tclass - INTEGER
 
        * 0 - (default)
 
+ndisc_evict_nocarrier - BOOLEAN
+       Clears the neighbor discovery table on NOCARRIER events. This option is
+       important for wireless devices where the neighbor discovery cache should
+       not be cleared when roaming between access points on the same network.
+       In most cases this should remain as the default (1).
+
+       - 1 - (default): Clear neighbor discover cache on NOCARRIER events.
+       - 0 - Do not clear neighbor discovery cache on NOCARRIER events.
+
 mldv1_unsolicited_report_interval - INTEGER
        The interval in milliseconds in which the next unsolicited
        MLDv1 report retransmit will take place.
index c383630d3f0658908eac65c030daf97b0a0d0c7c..20c1f968da7c1223a9721deb88f94175bc718389 100644 (file)
@@ -79,6 +79,7 @@ struct ipv6_devconf {
        __u32           ioam6_id;
        __u32           ioam6_id_wide;
        __u8            ioam6_enabled;
+       __u8            ndisc_evict_nocarrier;
 
        struct ctl_table_header *sysctl_header;
 };
index b243a53fa985b3937fea1c02d1c1ed13a415dd80..d4178dace0bfbb4b4004553542b3e47886f812ea 100644 (file)
@@ -193,6 +193,7 @@ enum {
        DEVCONF_IOAM6_ENABLED,
        DEVCONF_IOAM6_ID,
        DEVCONF_IOAM6_ID_WIDE,
+       DEVCONF_NDISC_EVICT_NOCARRIER,
        DEVCONF_MAX
 };
 
index 9e1463a2acaedf9f00797a6ac42059782437524d..3445f8017430f145496bad78afd6378bf5cb1c02 100644 (file)
@@ -241,6 +241,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .ioam6_enabled          = 0,
        .ioam6_id               = IOAM6_DEFAULT_IF_ID,
        .ioam6_id_wide          = IOAM6_DEFAULT_IF_ID_WIDE,
+       .ndisc_evict_nocarrier  = 1,
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -300,6 +301,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .ioam6_enabled          = 0,
        .ioam6_id               = IOAM6_DEFAULT_IF_ID,
        .ioam6_id_wide          = IOAM6_DEFAULT_IF_ID_WIDE,
+       .ndisc_evict_nocarrier  = 1,
 };
 
 /* Check if link is ready: is it up and is a valid qdisc available */
@@ -5545,6 +5547,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
        array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
        array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
+       array[DEVCONF_NDISC_EVICT_NOCARRIER] = cnf->ndisc_evict_nocarrier;
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -6986,6 +6989,15 @@ static const struct ctl_table addrconf_sysctl[] = {
                .mode           = 0644,
                .proc_handler   = proc_douintvec,
        },
+       {
+               .procname       = "ndisc_evict_nocarrier",
+               .data           = &ipv6_devconf.ndisc_evict_nocarrier,
+               .maxlen         = sizeof(u8),
+               .mode           = 0644,
+               .proc_handler   = proc_dou8vec_minmax,
+               .extra1         = (void *)SYSCTL_ZERO,
+               .extra2         = (void *)SYSCTL_ONE,
+       },
        {
                /* sentinel */
        }
index 184190b9ea25bc417592355cf2d7d9a0e21a6b5d..f03b597e4121b5ce1c47418ec73c1a81babd2730 100644 (file)
@@ -1794,6 +1794,7 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
        struct netdev_notifier_change_info *change_info;
        struct net *net = dev_net(dev);
        struct inet6_dev *idev;
+       bool evict_nocarrier;
 
        switch (event) {
        case NETDEV_CHANGEADDR:
@@ -1810,10 +1811,19 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
                in6_dev_put(idev);
                break;
        case NETDEV_CHANGE:
+               idev = in6_dev_get(dev);
+               if (!idev)
+                       evict_nocarrier = true;
+               else {
+                       evict_nocarrier = idev->cnf.ndisc_evict_nocarrier &&
+                                         net->ipv6.devconf_all->ndisc_evict_nocarrier;
+                       in6_dev_put(idev);
+               }
+
                change_info = ptr;
                if (change_info->flags_changed & IFF_NOARP)
                        neigh_changeaddr(&nd_tbl, dev);
-               if (!netif_carrier_ok(dev))
+               if (evict_nocarrier && !netif_carrier_ok(dev))
                        neigh_carrier_down(&nd_tbl, dev);
                break;
        case NETDEV_DOWN: