@@ -925,6 +925,15 @@ accept_source_route - INTEGER
Default: 0
+address_flush - BOOLEAN
+ Flush all addresses when link goes down
+ Default: TRUE
+
+ When network device is set to admin down:
+ TRUE - Delete all addresses
+ FALSE - Only addresses that are temporary or linklocal and not permanent
+ are deleted. Other addresses restart DAD (if configured).
+
autoconf - BOOLEAN
Autoconfigure addresses using Prefix Information in Router
Advertisements.
@@ -166,6 +166,7 @@ struct ipv6_devconf {
#endif
__s32 disable_ipv6;
__s32 accept_dad;
+ __s32 address_flush;
void *sysctl;
};
#endif
@@ -200,6 +201,7 @@ enum {
DEVCONF_MC_FORWARDING,
DEVCONF_DISABLE_IPV6,
DEVCONF_ACCEPT_DAD,
+ DEVCONF_ADDRESS_FLUSH,
DEVCONF_MAX
};
@@ -186,6 +186,7 @@ static struct ipv6_devconf ipv6_devconf
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
+ .address_flush = 1,
};
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -220,6 +221,7 @@ static struct ipv6_devconf ipv6_devconf_
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
+ .address_flush = 1,
};
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -2678,18 +2680,37 @@ static int addrconf_ifdown(struct net_de
write_lock_bh(&idev->lock);
}
#endif
- while ((ifa = idev->addr_list) != NULL) {
- idev->addr_list = ifa->if_next;
- ifa->if_next = NULL;
- ifa->dead = 1;
- addrconf_del_timer(ifa);
- write_unlock_bh(&idev->lock);
+ /* clear regular address list on removal */
+ bifa = &idev->addr_list;
+ while ((ifa = *bifa) != NULL) {
+ if (how || idev->cnf.address_flush ||
+ ((ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL)
+ && !(ifa->flags & IFA_F_PERMANENT))) {
+ *bifa = ifa->if_next;
+ ifa->if_next = NULL;
+ ifa->dead = 1;
+ addrconf_del_timer(ifa);
+ write_unlock_bh(&idev->lock);
+
+ __ipv6_ifa_notify(RTM_DELADDR, ifa);
+ atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
+ in6_ifa_put(ifa);
- __ipv6_ifa_notify(RTM_DELADDR, ifa);
- atomic_notifier_call_chain(&inet6addr_chain, NETDEV_DOWN, ifa);
- in6_ifa_put(ifa);
+ write_lock_bh(&idev->lock);
+ continue;
+ }
- write_lock_bh(&idev->lock);
+ bifa = &ifa->if_next;
+ if (ifa->flags & IFA_F_NODAD)
+ continue;
+
+ /* Retain address but force DAD */
+ ifa->flags |= IFA_F_TENTATIVE;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ if (idev->cnf.optimistic_dad &&
+ !dev_net(idev->dev)->ipv6.devconf_all->forwarding)
+ ifa->flags |= IFA_F_OPTIMISTIC;
+#endif
}
write_unlock_bh(&idev->lock);
@@ -3697,6 +3718,7 @@ static inline void ipv6_store_devconf(st
#endif
array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
+ array[DEVCONF_ADDRESS_FLUSH] = cnf->address_flush;
}
static inline size_t inet6_if_nlmsg_size(void)
@@ -4271,6 +4293,14 @@ static struct addrconf_sysctl_table
.proc_handler = proc_dointvec,
},
{
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "address_flush",
+ .data = &ipv6_devconf.address_flush,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
.ctl_name = 0, /* sentinel */
}
},