},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
- .keep_addr_on_down = 0,
};
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
},
.use_oif_addrs_only = 0,
.ignore_routes_with_linkdown = 0,
- .keep_addr_on_down = 0,
};
/* Check if a valid qdisc is available */
}
#endif
-#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV)
-/* If the host route is cached on the addr struct make sure it is associated
- * with the proper table. e.g., enslavement can change and if so the cached
- * host route needs to move to the new table.
- */
-static void l3mdev_check_host_rt(struct inet6_dev *idev,
- struct inet6_ifaddr *ifp)
-{
- if (ifp->rt) {
- u32 tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL;
-
- if (tb_id != ifp->rt->rt6i_table->tb6_id) {
- ip6_del_rt(ifp->rt);
- ifp->rt = NULL;
- }
- }
-}
-#else
-static void l3mdev_check_host_rt(struct inet6_dev *idev,
- struct inet6_ifaddr *ifp)
-{
-}
-#endif
-
-static int fixup_permanent_addr(struct inet6_dev *idev,
- struct inet6_ifaddr *ifp)
-{
- l3mdev_check_host_rt(idev, ifp);
-
- if (!ifp->rt) {
- struct rt6_info *rt;
-
- rt = addrconf_dst_alloc(idev, &ifp->addr, false);
- if (unlikely(IS_ERR(rt)))
- return PTR_ERR(rt);
-
- ifp->rt = rt;
- }
-
- if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
- addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
- idev->dev, 0, 0);
- }
-
- addrconf_dad_start(ifp);
-
- return 0;
-}
-
-static void addrconf_permanent_addr(struct net_device *dev)
-{
- struct inet6_ifaddr *ifp, *tmp;
- struct inet6_dev *idev;
-
- idev = __in6_dev_get(dev);
- if (!idev)
- return;
-
- write_lock_bh(&idev->lock);
-
- list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) {
- if ((ifp->flags & IFA_F_PERMANENT) &&
- fixup_permanent_addr(idev, ifp) < 0) {
- write_unlock_bh(&idev->lock);
- ipv6_del_addr(ifp);
- write_lock_bh(&idev->lock);
-
- net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n",
- idev->dev->name, &ifp->addr);
- }
- }
-
- write_unlock_bh(&idev->lock);
-}
-
static int addrconf_notify(struct notifier_block *this, unsigned long event,
void *ptr)
{
run_pending = 1;
}
- /* restore routes for permanent addresses */
- addrconf_permanent_addr(dev);
-
switch (dev->type) {
#if IS_ENABLED(CONFIG_IPV6_SIT)
case ARPHRD_SIT:
ipv6_mc_unmap(idev);
}
-static bool addr_is_local(const struct in6_addr *addr)
-{
- return ipv6_addr_type(addr) &
- (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
-}
-
static int addrconf_ifdown(struct net_device *dev, int how)
{
struct net *net = dev_net(dev);
struct inet6_dev *idev;
- struct inet6_ifaddr *ifa, *tmp;
- struct list_head del_list;
- int _keep_addr;
- bool keep_addr;
+ struct inet6_ifaddr *ifa;
int state, i;
ASSERT_RTNL();
}
- /* aggregate the system setting and interface setting */
- _keep_addr = net->ipv6.devconf_all->keep_addr_on_down;
- if (!_keep_addr)
- _keep_addr = idev->cnf.keep_addr_on_down;
-
- /* combine the user config with event to determine if permanent
- * addresses are to be removed from address hash table
- */
- keep_addr = !(how || _keep_addr <= 0);
-
/* Step 2: clear hash table */
for (i = 0; i < IN6_ADDR_HSIZE; i++) {
struct hlist_head *h = &inet6_addr_lst[i];
restart:
hlist_for_each_entry_rcu(ifa, h, addr_lst) {
if (ifa->idev == idev) {
+ hlist_del_init_rcu(&ifa->addr_lst);
addrconf_del_dad_work(ifa);
- /* combined flag + permanent flag decide if
- * address is retained on a down event
- */
- if (!keep_addr ||
- !(ifa->flags & IFA_F_PERMANENT) ||
- addr_is_local(&ifa->addr)) {
- hlist_del_init_rcu(&ifa->addr_lst);
- goto restart;
- }
+ goto restart;
}
}
spin_unlock_bh(&addrconf_hash_lock);
write_lock_bh(&idev->lock);
}
- /* re-combine the user config with event to determine if permanent
- * addresses are to be removed from the interface list
- */
- keep_addr = (!how && _keep_addr > 0);
-
- INIT_LIST_HEAD(&del_list);
- list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) {
+ while (!list_empty(&idev->addr_list)) {
+ ifa = list_first_entry(&idev->addr_list,
+ struct inet6_ifaddr, if_list);
addrconf_del_dad_work(ifa);
- write_unlock_bh(&idev->lock);
- spin_lock_bh(&ifa->lock);
-
- if (keep_addr && (ifa->flags & IFA_F_PERMANENT) &&
- !addr_is_local(&ifa->addr)) {
- /* set state to skip the notifier below */
- state = INET6_IFADDR_STATE_DEAD;
- ifa->state = 0;
- if (!(ifa->flags & IFA_F_NODAD))
- ifa->flags |= IFA_F_TENTATIVE;
- } else {
- state = ifa->state;
- ifa->state = INET6_IFADDR_STATE_DEAD;
+ list_del(&ifa->if_list);
- list_del(&ifa->if_list);
- list_add(&ifa->if_list, &del_list);
- }
+ write_unlock_bh(&idev->lock);
+ spin_lock_bh(&ifa->lock);
+ state = ifa->state;
+ ifa->state = INET6_IFADDR_STATE_DEAD;
spin_unlock_bh(&ifa->lock);
if (state != INET6_IFADDR_STATE_DEAD) {
__ipv6_ifa_notify(RTM_DELADDR, ifa);
inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);
}
+ in6_ifa_put(ifa);
write_lock_bh(&idev->lock);
}
write_unlock_bh(&idev->lock);
- /* now clean up addresses to be removed */
- while (!list_empty(&del_list)) {
- ifa = list_first_entry(&del_list,
- struct inet6_ifaddr, if_list);
- list_del(&ifa->if_list);
-
- in6_ifa_put(ifa);
- }
-
/* Step 5: Discard anycast and multicast list */
if (how) {
ipv6_ac_destroy_dev(idev);
array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only;
array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast;
array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
- array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
}
static inline size_t inet6_ifla6_size(void)
.proc_handler = proc_dointvec,
},
{
- .procname = "keep_addr_on_down",
- .data = &ipv6_devconf.keep_addr_on_down,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec,
-
- },
- {
/* sentinel */
}
},