Skip to content

Commit f1705ec

Browse files
David Aherndavem330
David Ahern
authored andcommitted
net: ipv6: Make address flushing on ifdown optional
Currently, all ipv6 addresses are flushed when the interface is configured down, including global, static addresses: $ ip -6 addr show dev eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000 inet6 2100:1::2/120 scope global valid_lft forever preferred_lft forever inet6 fe80::e0:f9ff:fe79:34bd/64 scope link valid_lft forever preferred_lft forever $ ip link set dev eth1 down $ ip -6 addr show dev eth1 << nothing; all addresses have been flushed>> Add a new sysctl to make this behavior optional. The new setting defaults to flush all addresses to maintain backwards compatibility. When the set global addresses with no expire times are not flushed on an admin down. The sysctl is per-interface or system-wide for all interfaces $ sysctl -w net.ipv6.conf.eth1.keep_addr_on_down=1 or $ sysctl -w net.ipv6.conf.all.keep_addr_on_down=1 Will keep addresses on eth1 on an admin down. $ ip -6 addr show dev eth1 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000 inet6 2100:1::2/120 scope global valid_lft forever preferred_lft forever inet6 fe80::e0:f9ff:fe79:34bd/64 scope link valid_lft forever preferred_lft forever $ ip link set dev eth1 down $ ip -6 addr show dev eth1 3: eth1: <BROADCAST,MULTICAST> mtu 1500 state DOWN qlen 1000 inet6 2100:1::2/120 scope global tentative valid_lft forever preferred_lft forever inet6 fe80::e0:f9ff:fe79:34bd/64 scope link tentative valid_lft forever preferred_lft forever Signed-off-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 619b174 commit f1705ec

File tree

4 files changed

+132
-15
lines changed

4 files changed

+132
-15
lines changed

Documentation/networking/ip-sysctl.txt

+9
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,15 @@ temp_prefered_lft - INTEGER
15631563
Preferred lifetime (in seconds) for temporary addresses.
15641564
Default: 86400 (1 day)
15651565

1566+
keep_addr_on_down - INTEGER
1567+
Keep all IPv6 addresses on an interface down event. If set static
1568+
global addresses with no expiration time are not flushed.
1569+
>0 : enabled
1570+
0 : system default
1571+
<0 : disabled
1572+
1573+
Default: 0 (addresses are removed)
1574+
15661575
max_desync_factor - INTEGER
15671576
Maximum value for DESYNC_FACTOR, which is a random value
15681577
that ensures that clients don't synchronize with each

include/linux/ipv6.h

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct ipv6_devconf {
6262
struct in6_addr secret;
6363
} stable_secret;
6464
__s32 use_oif_addrs_only;
65+
__s32 keep_addr_on_down;
6566
void *sysctl;
6667
};
6768

include/uapi/linux/ipv6.h

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ enum {
176176
DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
177177
DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
178178
DEVCONF_DROP_UNSOLICITED_NA,
179+
DEVCONF_KEEP_ADDR_ON_DOWN,
179180
DEVCONF_MAX
180181
};
181182

net/ipv6/addrconf.c

+121-15
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
216216
},
217217
.use_oif_addrs_only = 0,
218218
.ignore_routes_with_linkdown = 0,
219+
.keep_addr_on_down = 0,
219220
};
220221

221222
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -260,6 +261,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
260261
},
261262
.use_oif_addrs_only = 0,
262263
.ignore_routes_with_linkdown = 0,
264+
.keep_addr_on_down = 0,
263265
};
264266

265267
/* Check if a valid qdisc is available */
@@ -3168,6 +3170,55 @@ static void addrconf_gre_config(struct net_device *dev)
31683170
}
31693171
#endif
31703172

3173+
static int fixup_permanent_addr(struct inet6_dev *idev,
3174+
struct inet6_ifaddr *ifp)
3175+
{
3176+
if (!ifp->rt) {
3177+
struct rt6_info *rt;
3178+
3179+
rt = addrconf_dst_alloc(idev, &ifp->addr, false);
3180+
if (unlikely(IS_ERR(rt)))
3181+
return PTR_ERR(rt);
3182+
3183+
ifp->rt = rt;
3184+
}
3185+
3186+
if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
3187+
addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
3188+
idev->dev, 0, 0);
3189+
}
3190+
3191+
addrconf_dad_start(ifp);
3192+
3193+
return 0;
3194+
}
3195+
3196+
static void addrconf_permanent_addr(struct net_device *dev)
3197+
{
3198+
struct inet6_ifaddr *ifp, *tmp;
3199+
struct inet6_dev *idev;
3200+
3201+
idev = __in6_dev_get(dev);
3202+
if (!idev)
3203+
return;
3204+
3205+
write_lock_bh(&idev->lock);
3206+
3207+
list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) {
3208+
if ((ifp->flags & IFA_F_PERMANENT) &&
3209+
fixup_permanent_addr(idev, ifp) < 0) {
3210+
write_unlock_bh(&idev->lock);
3211+
ipv6_del_addr(ifp);
3212+
write_lock_bh(&idev->lock);
3213+
3214+
net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n",
3215+
idev->dev->name, &ifp->addr);
3216+
}
3217+
}
3218+
3219+
write_unlock_bh(&idev->lock);
3220+
}
3221+
31713222
static int addrconf_notify(struct notifier_block *this, unsigned long event,
31723223
void *ptr)
31733224
{
@@ -3253,6 +3304,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
32533304
run_pending = 1;
32543305
}
32553306

3307+
/* restore routes for permanent addresses */
3308+
addrconf_permanent_addr(dev);
3309+
32563310
switch (dev->type) {
32573311
#if IS_ENABLED(CONFIG_IPV6_SIT)
32583312
case ARPHRD_SIT:
@@ -3356,7 +3410,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
33563410
{
33573411
struct net *net = dev_net(dev);
33583412
struct inet6_dev *idev;
3359-
struct inet6_ifaddr *ifa;
3413+
struct inet6_ifaddr *ifa, *tmp;
3414+
struct list_head del_list;
3415+
int _keep_addr;
3416+
bool keep_addr;
33603417
int state, i;
33613418

33623419
ASSERT_RTNL();
@@ -3383,6 +3440,16 @@ static int addrconf_ifdown(struct net_device *dev, int how)
33833440

33843441
}
33853442

3443+
/* aggregate the system setting and interface setting */
3444+
_keep_addr = net->ipv6.devconf_all->keep_addr_on_down;
3445+
if (!_keep_addr)
3446+
_keep_addr = idev->cnf.keep_addr_on_down;
3447+
3448+
/* combine the user config with event to determine if permanent
3449+
* addresses are to be removed from address hash table
3450+
*/
3451+
keep_addr = !(how || _keep_addr <= 0);
3452+
33863453
/* Step 2: clear hash table */
33873454
for (i = 0; i < IN6_ADDR_HSIZE; i++) {
33883455
struct hlist_head *h = &inet6_addr_lst[i];
@@ -3391,9 +3458,15 @@ static int addrconf_ifdown(struct net_device *dev, int how)
33913458
restart:
33923459
hlist_for_each_entry_rcu(ifa, h, addr_lst) {
33933460
if (ifa->idev == idev) {
3394-
hlist_del_init_rcu(&ifa->addr_lst);
33953461
addrconf_del_dad_work(ifa);
3396-
goto restart;
3462+
/* combined flag + permanent flag decide if
3463+
* address is retained on a down event
3464+
*/
3465+
if (!keep_addr ||
3466+
!(ifa->flags & IFA_F_PERMANENT)) {
3467+
hlist_del_init_rcu(&ifa->addr_lst);
3468+
goto restart;
3469+
}
33973470
}
33983471
}
33993472
spin_unlock_bh(&addrconf_hash_lock);
@@ -3427,31 +3500,53 @@ static int addrconf_ifdown(struct net_device *dev, int how)
34273500
write_lock_bh(&idev->lock);
34283501
}
34293502

3430-
while (!list_empty(&idev->addr_list)) {
3431-
ifa = list_first_entry(&idev->addr_list,
3432-
struct inet6_ifaddr, if_list);
3433-
addrconf_del_dad_work(ifa);
3503+
/* re-combine the user config with event to determine if permanent
3504+
* addresses are to be removed from the interface list
3505+
*/
3506+
keep_addr = (!how && _keep_addr > 0);
34343507

3435-
list_del(&ifa->if_list);
3508+
INIT_LIST_HEAD(&del_list);
3509+
list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) {
3510+
addrconf_del_dad_work(ifa);
34363511

34373512
write_unlock_bh(&idev->lock);
3438-
34393513
spin_lock_bh(&ifa->lock);
3440-
state = ifa->state;
3441-
ifa->state = INET6_IFADDR_STATE_DEAD;
3514+
3515+
if (keep_addr && (ifa->flags & IFA_F_PERMANENT)) {
3516+
/* set state to skip the notifier below */
3517+
state = INET6_IFADDR_STATE_DEAD;
3518+
ifa->state = 0;
3519+
if (!(ifa->flags & IFA_F_NODAD))
3520+
ifa->flags |= IFA_F_TENTATIVE;
3521+
} else {
3522+
state = ifa->state;
3523+
ifa->state = INET6_IFADDR_STATE_DEAD;
3524+
3525+
list_del(&ifa->if_list);
3526+
list_add(&ifa->if_list, &del_list);
3527+
}
3528+
34423529
spin_unlock_bh(&ifa->lock);
34433530

34443531
if (state != INET6_IFADDR_STATE_DEAD) {
34453532
__ipv6_ifa_notify(RTM_DELADDR, ifa);
34463533
inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);
34473534
}
3448-
in6_ifa_put(ifa);
34493535

34503536
write_lock_bh(&idev->lock);
34513537
}
34523538

34533539
write_unlock_bh(&idev->lock);
34543540

3541+
/* now clean up addresses to be removed */
3542+
while (!list_empty(&del_list)) {
3543+
ifa = list_first_entry(&del_list,
3544+
struct inet6_ifaddr, if_list);
3545+
list_del(&ifa->if_list);
3546+
3547+
in6_ifa_put(ifa);
3548+
}
3549+
34553550
/* Step 5: Discard anycast and multicast list */
34563551
if (how) {
34573552
ipv6_ac_destroy_dev(idev);
@@ -4716,6 +4811,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
47164811
array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only;
47174812
array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast;
47184813
array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
4814+
array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
47194815
}
47204816

47214817
static inline size_t inet6_ifla6_size(void)
@@ -5197,10 +5293,12 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
51975293
if (rt)
51985294
ip6_del_rt(rt);
51995295
}
5200-
dst_hold(&ifp->rt->dst);
5201-
5202-
ip6_del_rt(ifp->rt);
5296+
if (ifp->rt) {
5297+
dst_hold(&ifp->rt->dst);
52035298

5299+
ip6_del_rt(ifp->rt);
5300+
ifp->rt = NULL;
5301+
}
52045302
rt_genid_bump_ipv6(net);
52055303
break;
52065304
}
@@ -5803,6 +5901,14 @@ static struct addrconf_sysctl_table
58035901
.mode = 0644,
58045902
.proc_handler = proc_dointvec,
58055903
},
5904+
{
5905+
.procname = "keep_addr_on_down",
5906+
.data = &ipv6_devconf.keep_addr_on_down,
5907+
.maxlen = sizeof(int),
5908+
.mode = 0644,
5909+
.proc_handler = proc_dointvec,
5910+
5911+
},
58065912
{
58075913
/* sentinel */
58085914
}

0 commit comments

Comments
 (0)