Skip to content

Commit 18ac597

Browse files
jprestwokuba-moo
authored andcommitted
net: ndisc: introduce ndisc_evict_nocarrier sysctl parameter
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 <[email protected]> Reviewed-by: David Ahern <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent fcdb44d commit 18ac597

File tree

5 files changed

+34
-1
lines changed

5 files changed

+34
-1
lines changed

Documentation/networking/ip-sysctl.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,15 @@ ndisc_tclass - INTEGER
23502350

23512351
* 0 - (default)
23522352

2353+
ndisc_evict_nocarrier - BOOLEAN
2354+
Clears the neighbor discovery table on NOCARRIER events. This option is
2355+
important for wireless devices where the neighbor discovery cache should
2356+
not be cleared when roaming between access points on the same network.
2357+
In most cases this should remain as the default (1).
2358+
2359+
- 1 - (default): Clear neighbor discover cache on NOCARRIER events.
2360+
- 0 - Do not clear neighbor discovery cache on NOCARRIER events.
2361+
23532362
mldv1_unsolicited_report_interval - INTEGER
23542363
The interval in milliseconds in which the next unsolicited
23552364
MLDv1 report retransmit will take place.

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct ipv6_devconf {
7979
__u32 ioam6_id;
8080
__u32 ioam6_id_wide;
8181
__u8 ioam6_enabled;
82+
__u8 ndisc_evict_nocarrier;
8283

8384
struct ctl_table_header *sysctl_header;
8485
};

include/uapi/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ enum {
193193
DEVCONF_IOAM6_ENABLED,
194194
DEVCONF_IOAM6_ID,
195195
DEVCONF_IOAM6_ID_WIDE,
196+
DEVCONF_NDISC_EVICT_NOCARRIER,
196197
DEVCONF_MAX
197198
};
198199

net/ipv6/addrconf.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
241241
.ioam6_enabled = 0,
242242
.ioam6_id = IOAM6_DEFAULT_IF_ID,
243243
.ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
244+
.ndisc_evict_nocarrier = 1,
244245
};
245246

246247
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -300,6 +301,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
300301
.ioam6_enabled = 0,
301302
.ioam6_id = IOAM6_DEFAULT_IF_ID,
302303
.ioam6_id_wide = IOAM6_DEFAULT_IF_ID_WIDE,
304+
.ndisc_evict_nocarrier = 1,
303305
};
304306

305307
/* 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,
55455547
array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
55465548
array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
55475549
array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
5550+
array[DEVCONF_NDISC_EVICT_NOCARRIER] = cnf->ndisc_evict_nocarrier;
55485551
}
55495552

55505553
static inline size_t inet6_ifla6_size(void)
@@ -6986,6 +6989,15 @@ static const struct ctl_table addrconf_sysctl[] = {
69866989
.mode = 0644,
69876990
.proc_handler = proc_douintvec,
69886991
},
6992+
{
6993+
.procname = "ndisc_evict_nocarrier",
6994+
.data = &ipv6_devconf.ndisc_evict_nocarrier,
6995+
.maxlen = sizeof(u8),
6996+
.mode = 0644,
6997+
.proc_handler = proc_dou8vec_minmax,
6998+
.extra1 = (void *)SYSCTL_ZERO,
6999+
.extra2 = (void *)SYSCTL_ONE,
7000+
},
69897001
{
69907002
/* sentinel */
69917003
}

net/ipv6/ndisc.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,7 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
17941794
struct netdev_notifier_change_info *change_info;
17951795
struct net *net = dev_net(dev);
17961796
struct inet6_dev *idev;
1797+
bool evict_nocarrier;
17971798

17981799
switch (event) {
17991800
case NETDEV_CHANGEADDR:
@@ -1810,10 +1811,19 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
18101811
in6_dev_put(idev);
18111812
break;
18121813
case NETDEV_CHANGE:
1814+
idev = in6_dev_get(dev);
1815+
if (!idev)
1816+
evict_nocarrier = true;
1817+
else {
1818+
evict_nocarrier = idev->cnf.ndisc_evict_nocarrier &&
1819+
net->ipv6.devconf_all->ndisc_evict_nocarrier;
1820+
in6_dev_put(idev);
1821+
}
1822+
18131823
change_info = ptr;
18141824
if (change_info->flags_changed & IFF_NOARP)
18151825
neigh_changeaddr(&nd_tbl, dev);
1816-
if (!netif_carrier_ok(dev))
1826+
if (evict_nocarrier && !netif_carrier_ok(dev))
18171827
neigh_carrier_down(&nd_tbl, dev);
18181828
break;
18191829
case NETDEV_DOWN:

0 commit comments

Comments
 (0)