diff options
-rw-r--r-- | Documentation/networking/ip-sysctl.txt | 9 | ||||
-rw-r--r-- | include/linux/ipv6.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/ipv6.h | 1 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 162 |
4 files changed, 12 insertions, 161 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index b183e2b606c8..e0ac25210f7c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt | |||
@@ -1568,15 +1568,6 @@ temp_prefered_lft - INTEGER | |||
1568 | Preferred lifetime (in seconds) for temporary addresses. | 1568 | Preferred lifetime (in seconds) for temporary addresses. |
1569 | Default: 86400 (1 day) | 1569 | Default: 86400 (1 day) |
1570 | 1570 | ||
1571 | keep_addr_on_down - INTEGER | ||
1572 | Keep all IPv6 addresses on an interface down event. If set static | ||
1573 | global addresses with no expiration time are not flushed. | ||
1574 | >0 : enabled | ||
1575 | 0 : system default | ||
1576 | <0 : disabled | ||
1577 | |||
1578 | Default: 0 (addresses are removed) | ||
1579 | |||
1580 | max_desync_factor - INTEGER | 1571 | max_desync_factor - INTEGER |
1581 | Maximum value for DESYNC_FACTOR, which is a random value | 1572 | Maximum value for DESYNC_FACTOR, which is a random value |
1582 | that ensures that clients don't synchronize with each | 1573 | that ensures that clients don't synchronize with each |
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 7edc14fb66b6..4b2267e1b7c3 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h | |||
@@ -62,7 +62,6 @@ struct ipv6_devconf { | |||
62 | struct in6_addr secret; | 62 | struct in6_addr secret; |
63 | } stable_secret; | 63 | } stable_secret; |
64 | __s32 use_oif_addrs_only; | 64 | __s32 use_oif_addrs_only; |
65 | __s32 keep_addr_on_down; | ||
66 | void *sysctl; | 65 | void *sysctl; |
67 | }; | 66 | }; |
68 | 67 | ||
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h index 395876060f50..ec117b65d5a5 100644 --- a/include/uapi/linux/ipv6.h +++ b/include/uapi/linux/ipv6.h | |||
@@ -176,7 +176,6 @@ enum { | |||
176 | DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, | 176 | DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN, |
177 | DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, | 177 | DEVCONF_DROP_UNICAST_IN_L2_MULTICAST, |
178 | DEVCONF_DROP_UNSOLICITED_NA, | 178 | DEVCONF_DROP_UNSOLICITED_NA, |
179 | DEVCONF_KEEP_ADDR_ON_DOWN, | ||
180 | DEVCONF_MAX | 179 | DEVCONF_MAX |
181 | }; | 180 | }; |
182 | 181 | ||
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 23cec53b568a..d77ba395d593 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -216,7 +216,6 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { | |||
216 | }, | 216 | }, |
217 | .use_oif_addrs_only = 0, | 217 | .use_oif_addrs_only = 0, |
218 | .ignore_routes_with_linkdown = 0, | 218 | .ignore_routes_with_linkdown = 0, |
219 | .keep_addr_on_down = 0, | ||
220 | }; | 219 | }; |
221 | 220 | ||
222 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | 221 | static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { |
@@ -261,7 +260,6 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { | |||
261 | }, | 260 | }, |
262 | .use_oif_addrs_only = 0, | 261 | .use_oif_addrs_only = 0, |
263 | .ignore_routes_with_linkdown = 0, | 262 | .ignore_routes_with_linkdown = 0, |
264 | .keep_addr_on_down = 0, | ||
265 | }; | 263 | }; |
266 | 264 | ||
267 | /* Check if a valid qdisc is available */ | 265 | /* Check if a valid qdisc is available */ |
@@ -3176,81 +3174,6 @@ static void addrconf_gre_config(struct net_device *dev) | |||
3176 | } | 3174 | } |
3177 | #endif | 3175 | #endif |
3178 | 3176 | ||
3179 | #if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) | ||
3180 | /* If the host route is cached on the addr struct make sure it is associated | ||
3181 | * with the proper table. e.g., enslavement can change and if so the cached | ||
3182 | * host route needs to move to the new table. | ||
3183 | */ | ||
3184 | static void l3mdev_check_host_rt(struct inet6_dev *idev, | ||
3185 | struct inet6_ifaddr *ifp) | ||
3186 | { | ||
3187 | if (ifp->rt) { | ||
3188 | u32 tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; | ||
3189 | |||
3190 | if (tb_id != ifp->rt->rt6i_table->tb6_id) { | ||
3191 | ip6_del_rt(ifp->rt); | ||
3192 | ifp->rt = NULL; | ||
3193 | } | ||
3194 | } | ||
3195 | } | ||
3196 | #else | ||
3197 | static void l3mdev_check_host_rt(struct inet6_dev *idev, | ||
3198 | struct inet6_ifaddr *ifp) | ||
3199 | { | ||
3200 | } | ||
3201 | #endif | ||
3202 | |||
3203 | static int fixup_permanent_addr(struct inet6_dev *idev, | ||
3204 | struct inet6_ifaddr *ifp) | ||
3205 | { | ||
3206 | l3mdev_check_host_rt(idev, ifp); | ||
3207 | |||
3208 | if (!ifp->rt) { | ||
3209 | struct rt6_info *rt; | ||
3210 | |||
3211 | rt = addrconf_dst_alloc(idev, &ifp->addr, false); | ||
3212 | if (unlikely(IS_ERR(rt))) | ||
3213 | return PTR_ERR(rt); | ||
3214 | |||
3215 | ifp->rt = rt; | ||
3216 | } | ||
3217 | |||
3218 | if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { | ||
3219 | addrconf_prefix_route(&ifp->addr, ifp->prefix_len, | ||
3220 | idev->dev, 0, 0); | ||
3221 | } | ||
3222 | |||
3223 | addrconf_dad_start(ifp); | ||
3224 | |||
3225 | return 0; | ||
3226 | } | ||
3227 | |||
3228 | static void addrconf_permanent_addr(struct net_device *dev) | ||
3229 | { | ||
3230 | struct inet6_ifaddr *ifp, *tmp; | ||
3231 | struct inet6_dev *idev; | ||
3232 | |||
3233 | idev = __in6_dev_get(dev); | ||
3234 | if (!idev) | ||
3235 | return; | ||
3236 | |||
3237 | write_lock_bh(&idev->lock); | ||
3238 | |||
3239 | list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) { | ||
3240 | if ((ifp->flags & IFA_F_PERMANENT) && | ||
3241 | fixup_permanent_addr(idev, ifp) < 0) { | ||
3242 | write_unlock_bh(&idev->lock); | ||
3243 | ipv6_del_addr(ifp); | ||
3244 | write_lock_bh(&idev->lock); | ||
3245 | |||
3246 | net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", | ||
3247 | idev->dev->name, &ifp->addr); | ||
3248 | } | ||
3249 | } | ||
3250 | |||
3251 | write_unlock_bh(&idev->lock); | ||
3252 | } | ||
3253 | |||
3254 | static int addrconf_notify(struct notifier_block *this, unsigned long event, | 3177 | static int addrconf_notify(struct notifier_block *this, unsigned long event, |
3255 | void *ptr) | 3178 | void *ptr) |
3256 | { | 3179 | { |
@@ -3337,9 +3260,6 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, | |||
3337 | run_pending = 1; | 3260 | run_pending = 1; |
3338 | } | 3261 | } |
3339 | 3262 | ||
3340 | /* restore routes for permanent addresses */ | ||
3341 | addrconf_permanent_addr(dev); | ||
3342 | |||
3343 | switch (dev->type) { | 3263 | switch (dev->type) { |
3344 | #if IS_ENABLED(CONFIG_IPV6_SIT) | 3264 | #if IS_ENABLED(CONFIG_IPV6_SIT) |
3345 | case ARPHRD_SIT: | 3265 | case ARPHRD_SIT: |
@@ -3448,20 +3368,11 @@ static void addrconf_type_change(struct net_device *dev, unsigned long event) | |||
3448 | ipv6_mc_unmap(idev); | 3368 | ipv6_mc_unmap(idev); |
3449 | } | 3369 | } |
3450 | 3370 | ||
3451 | static bool addr_is_local(const struct in6_addr *addr) | ||
3452 | { | ||
3453 | return ipv6_addr_type(addr) & | ||
3454 | (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK); | ||
3455 | } | ||
3456 | |||
3457 | static int addrconf_ifdown(struct net_device *dev, int how) | 3371 | static int addrconf_ifdown(struct net_device *dev, int how) |
3458 | { | 3372 | { |
3459 | struct net *net = dev_net(dev); | 3373 | struct net *net = dev_net(dev); |
3460 | struct inet6_dev *idev; | 3374 | struct inet6_dev *idev; |
3461 | struct inet6_ifaddr *ifa, *tmp; | 3375 | struct inet6_ifaddr *ifa; |
3462 | struct list_head del_list; | ||
3463 | int _keep_addr; | ||
3464 | bool keep_addr; | ||
3465 | int state, i; | 3376 | int state, i; |
3466 | 3377 | ||
3467 | ASSERT_RTNL(); | 3378 | ASSERT_RTNL(); |
@@ -3488,16 +3399,6 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3488 | 3399 | ||
3489 | } | 3400 | } |
3490 | 3401 | ||
3491 | /* aggregate the system setting and interface setting */ | ||
3492 | _keep_addr = net->ipv6.devconf_all->keep_addr_on_down; | ||
3493 | if (!_keep_addr) | ||
3494 | _keep_addr = idev->cnf.keep_addr_on_down; | ||
3495 | |||
3496 | /* combine the user config with event to determine if permanent | ||
3497 | * addresses are to be removed from address hash table | ||
3498 | */ | ||
3499 | keep_addr = !(how || _keep_addr <= 0); | ||
3500 | |||
3501 | /* Step 2: clear hash table */ | 3402 | /* Step 2: clear hash table */ |
3502 | for (i = 0; i < IN6_ADDR_HSIZE; i++) { | 3403 | for (i = 0; i < IN6_ADDR_HSIZE; i++) { |
3503 | struct hlist_head *h = &inet6_addr_lst[i]; | 3404 | struct hlist_head *h = &inet6_addr_lst[i]; |
@@ -3506,16 +3407,9 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3506 | restart: | 3407 | restart: |
3507 | hlist_for_each_entry_rcu(ifa, h, addr_lst) { | 3408 | hlist_for_each_entry_rcu(ifa, h, addr_lst) { |
3508 | if (ifa->idev == idev) { | 3409 | if (ifa->idev == idev) { |
3410 | hlist_del_init_rcu(&ifa->addr_lst); | ||
3509 | addrconf_del_dad_work(ifa); | 3411 | addrconf_del_dad_work(ifa); |
3510 | /* combined flag + permanent flag decide if | 3412 | goto restart; |
3511 | * address is retained on a down event | ||
3512 | */ | ||
3513 | if (!keep_addr || | ||
3514 | !(ifa->flags & IFA_F_PERMANENT) || | ||
3515 | addr_is_local(&ifa->addr)) { | ||
3516 | hlist_del_init_rcu(&ifa->addr_lst); | ||
3517 | goto restart; | ||
3518 | } | ||
3519 | } | 3413 | } |
3520 | } | 3414 | } |
3521 | spin_unlock_bh(&addrconf_hash_lock); | 3415 | spin_unlock_bh(&addrconf_hash_lock); |
@@ -3549,54 +3443,31 @@ restart: | |||
3549 | write_lock_bh(&idev->lock); | 3443 | write_lock_bh(&idev->lock); |
3550 | } | 3444 | } |
3551 | 3445 | ||
3552 | /* re-combine the user config with event to determine if permanent | 3446 | while (!list_empty(&idev->addr_list)) { |
3553 | * addresses are to be removed from the interface list | 3447 | ifa = list_first_entry(&idev->addr_list, |
3554 | */ | 3448 | struct inet6_ifaddr, if_list); |
3555 | keep_addr = (!how && _keep_addr > 0); | ||
3556 | |||
3557 | INIT_LIST_HEAD(&del_list); | ||
3558 | list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) { | ||
3559 | addrconf_del_dad_work(ifa); | 3449 | addrconf_del_dad_work(ifa); |
3560 | 3450 | ||
3561 | write_unlock_bh(&idev->lock); | 3451 | list_del(&ifa->if_list); |
3562 | spin_lock_bh(&ifa->lock); | ||
3563 | |||
3564 | if (keep_addr && (ifa->flags & IFA_F_PERMANENT) && | ||
3565 | !addr_is_local(&ifa->addr)) { | ||
3566 | /* set state to skip the notifier below */ | ||
3567 | state = INET6_IFADDR_STATE_DEAD; | ||
3568 | ifa->state = 0; | ||
3569 | if (!(ifa->flags & IFA_F_NODAD)) | ||
3570 | ifa->flags |= IFA_F_TENTATIVE; | ||
3571 | } else { | ||
3572 | state = ifa->state; | ||
3573 | ifa->state = INET6_IFADDR_STATE_DEAD; | ||
3574 | 3452 | ||
3575 | list_del(&ifa->if_list); | 3453 | write_unlock_bh(&idev->lock); |
3576 | list_add(&ifa->if_list, &del_list); | ||
3577 | } | ||
3578 | 3454 | ||
3455 | spin_lock_bh(&ifa->lock); | ||
3456 | state = ifa->state; | ||
3457 | ifa->state = INET6_IFADDR_STATE_DEAD; | ||
3579 | spin_unlock_bh(&ifa->lock); | 3458 | spin_unlock_bh(&ifa->lock); |
3580 | 3459 | ||
3581 | if (state != INET6_IFADDR_STATE_DEAD) { | 3460 | if (state != INET6_IFADDR_STATE_DEAD) { |
3582 | __ipv6_ifa_notify(RTM_DELADDR, ifa); | 3461 | __ipv6_ifa_notify(RTM_DELADDR, ifa); |
3583 | inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); | 3462 | inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); |
3584 | } | 3463 | } |
3464 | in6_ifa_put(ifa); | ||
3585 | 3465 | ||
3586 | write_lock_bh(&idev->lock); | 3466 | write_lock_bh(&idev->lock); |
3587 | } | 3467 | } |
3588 | 3468 | ||
3589 | write_unlock_bh(&idev->lock); | 3469 | write_unlock_bh(&idev->lock); |
3590 | 3470 | ||
3591 | /* now clean up addresses to be removed */ | ||
3592 | while (!list_empty(&del_list)) { | ||
3593 | ifa = list_first_entry(&del_list, | ||
3594 | struct inet6_ifaddr, if_list); | ||
3595 | list_del(&ifa->if_list); | ||
3596 | |||
3597 | in6_ifa_put(ifa); | ||
3598 | } | ||
3599 | |||
3600 | /* Step 5: Discard anycast and multicast list */ | 3471 | /* Step 5: Discard anycast and multicast list */ |
3601 | if (how) { | 3472 | if (how) { |
3602 | ipv6_ac_destroy_dev(idev); | 3473 | ipv6_ac_destroy_dev(idev); |
@@ -4861,7 +4732,6 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, | |||
4861 | array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only; | 4732 | array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only; |
4862 | array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; | 4733 | array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; |
4863 | array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; | 4734 | array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; |
4864 | array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down; | ||
4865 | } | 4735 | } |
4866 | 4736 | ||
4867 | static inline size_t inet6_ifla6_size(void) | 4737 | static inline size_t inet6_ifla6_size(void) |
@@ -5950,14 +5820,6 @@ static struct addrconf_sysctl_table | |||
5950 | .proc_handler = proc_dointvec, | 5820 | .proc_handler = proc_dointvec, |
5951 | }, | 5821 | }, |
5952 | { | 5822 | { |
5953 | .procname = "keep_addr_on_down", | ||
5954 | .data = &ipv6_devconf.keep_addr_on_down, | ||
5955 | .maxlen = sizeof(int), | ||
5956 | .mode = 0644, | ||
5957 | .proc_handler = proc_dointvec, | ||
5958 | |||
5959 | }, | ||
5960 | { | ||
5961 | /* sentinel */ | 5823 | /* sentinel */ |
5962 | } | 5824 | } |
5963 | }, | 5825 | }, |