aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2016-04-26 11:47:41 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-26 11:47:41 -0400
commit6a923934c33c750a595868af6bef5f1a1fa90054 (patch)
tree337dae9d15323478ce4b85ebf5a9f113b74718cc
parentfc96256c906362e845d848d0f6a6354450059e81 (diff)
Revert "ipv6: Revert optional address flusing on ifdown."
This reverts commit 841645b5f2dfceac69b78fcd0c9050868d41ea61. Ok, this puts the feature back. I've decided to apply David A.'s bug fix and run with that rather than make everyone wait another whole release for this feature. Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/networking/ip-sysctl.txt9
-rw-r--r--include/linux/ipv6.h1
-rw-r--r--include/uapi/linux/ipv6.h1
-rw-r--r--net/ipv6/addrconf.c162
4 files changed, 161 insertions, 12 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index e0ac25210f7c..b183e2b606c8 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1568,6 +1568,15 @@ 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
1571keep_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
1571max_desync_factor - INTEGER 1580max_desync_factor - INTEGER
1572 Maximum value for DESYNC_FACTOR, which is a random value 1581 Maximum value for DESYNC_FACTOR, which is a random value
1573 that ensures that clients don't synchronize with each 1582 that ensures that clients don't synchronize with each
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 4b2267e1b7c3..7edc14fb66b6 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -62,6 +62,7 @@ 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;
65 void *sysctl; 66 void *sysctl;
66}; 67};
67 68
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index ec117b65d5a5..395876060f50 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -176,6 +176,7 @@ 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,
179 DEVCONF_MAX 180 DEVCONF_MAX
180}; 181};
181 182
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d77ba395d593..23cec53b568a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -216,6 +216,7 @@ 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,
219}; 220};
220 221
221static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { 222static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -260,6 +261,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
260 }, 261 },
261 .use_oif_addrs_only = 0, 262 .use_oif_addrs_only = 0,
262 .ignore_routes_with_linkdown = 0, 263 .ignore_routes_with_linkdown = 0,
264 .keep_addr_on_down = 0,
263}; 265};
264 266
265/* Check if a valid qdisc is available */ 267/* Check if a valid qdisc is available */
@@ -3174,6 +3176,81 @@ static void addrconf_gre_config(struct net_device *dev)
3174} 3176}
3175#endif 3177#endif
3176 3178
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 */
3184static 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
3197static void l3mdev_check_host_rt(struct inet6_dev *idev,
3198 struct inet6_ifaddr *ifp)
3199{
3200}
3201#endif
3202
3203static 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
3228static 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
3177static int addrconf_notify(struct notifier_block *this, unsigned long event, 3254static int addrconf_notify(struct notifier_block *this, unsigned long event,
3178 void *ptr) 3255 void *ptr)
3179{ 3256{
@@ -3260,6 +3337,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
3260 run_pending = 1; 3337 run_pending = 1;
3261 } 3338 }
3262 3339
3340 /* restore routes for permanent addresses */
3341 addrconf_permanent_addr(dev);
3342
3263 switch (dev->type) { 3343 switch (dev->type) {
3264#if IS_ENABLED(CONFIG_IPV6_SIT) 3344#if IS_ENABLED(CONFIG_IPV6_SIT)
3265 case ARPHRD_SIT: 3345 case ARPHRD_SIT:
@@ -3368,11 +3448,20 @@ static void addrconf_type_change(struct net_device *dev, unsigned long event)
3368 ipv6_mc_unmap(idev); 3448 ipv6_mc_unmap(idev);
3369} 3449}
3370 3450
3451static 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
3371static int addrconf_ifdown(struct net_device *dev, int how) 3457static int addrconf_ifdown(struct net_device *dev, int how)
3372{ 3458{
3373 struct net *net = dev_net(dev); 3459 struct net *net = dev_net(dev);
3374 struct inet6_dev *idev; 3460 struct inet6_dev *idev;
3375 struct inet6_ifaddr *ifa; 3461 struct inet6_ifaddr *ifa, *tmp;
3462 struct list_head del_list;
3463 int _keep_addr;
3464 bool keep_addr;
3376 int state, i; 3465 int state, i;
3377 3466
3378 ASSERT_RTNL(); 3467 ASSERT_RTNL();
@@ -3399,6 +3488,16 @@ static int addrconf_ifdown(struct net_device *dev, int how)
3399 3488
3400 } 3489 }
3401 3490
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
3402 /* Step 2: clear hash table */ 3501 /* Step 2: clear hash table */
3403 for (i = 0; i < IN6_ADDR_HSIZE; i++) { 3502 for (i = 0; i < IN6_ADDR_HSIZE; i++) {
3404 struct hlist_head *h = &inet6_addr_lst[i]; 3503 struct hlist_head *h = &inet6_addr_lst[i];
@@ -3407,9 +3506,16 @@ static int addrconf_ifdown(struct net_device *dev, int how)
3407restart: 3506restart:
3408 hlist_for_each_entry_rcu(ifa, h, addr_lst) { 3507 hlist_for_each_entry_rcu(ifa, h, addr_lst) {
3409 if (ifa->idev == idev) { 3508 if (ifa->idev == idev) {
3410 hlist_del_init_rcu(&ifa->addr_lst);
3411 addrconf_del_dad_work(ifa); 3509 addrconf_del_dad_work(ifa);
3412 goto restart; 3510 /* combined flag + permanent flag decide if
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 }
3413 } 3519 }
3414 } 3520 }
3415 spin_unlock_bh(&addrconf_hash_lock); 3521 spin_unlock_bh(&addrconf_hash_lock);
@@ -3443,31 +3549,54 @@ restart:
3443 write_lock_bh(&idev->lock); 3549 write_lock_bh(&idev->lock);
3444 } 3550 }
3445 3551
3446 while (!list_empty(&idev->addr_list)) { 3552 /* re-combine the user config with event to determine if permanent
3447 ifa = list_first_entry(&idev->addr_list, 3553 * addresses are to be removed from the interface list
3448 struct inet6_ifaddr, if_list); 3554 */
3449 addrconf_del_dad_work(ifa); 3555 keep_addr = (!how && _keep_addr > 0);
3450 3556
3451 list_del(&ifa->if_list); 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);
3452 3560
3453 write_unlock_bh(&idev->lock); 3561 write_unlock_bh(&idev->lock);
3454
3455 spin_lock_bh(&ifa->lock); 3562 spin_lock_bh(&ifa->lock);
3456 state = ifa->state; 3563
3457 ifa->state = INET6_IFADDR_STATE_DEAD; 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
3575 list_del(&ifa->if_list);
3576 list_add(&ifa->if_list, &del_list);
3577 }
3578
3458 spin_unlock_bh(&ifa->lock); 3579 spin_unlock_bh(&ifa->lock);
3459 3580
3460 if (state != INET6_IFADDR_STATE_DEAD) { 3581 if (state != INET6_IFADDR_STATE_DEAD) {
3461 __ipv6_ifa_notify(RTM_DELADDR, ifa); 3582 __ipv6_ifa_notify(RTM_DELADDR, ifa);
3462 inet6addr_notifier_call_chain(NETDEV_DOWN, ifa); 3583 inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);
3463 } 3584 }
3464 in6_ifa_put(ifa);
3465 3585
3466 write_lock_bh(&idev->lock); 3586 write_lock_bh(&idev->lock);
3467 } 3587 }
3468 3588
3469 write_unlock_bh(&idev->lock); 3589 write_unlock_bh(&idev->lock);
3470 3590
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
3471 /* Step 5: Discard anycast and multicast list */ 3600 /* Step 5: Discard anycast and multicast list */
3472 if (how) { 3601 if (how) {
3473 ipv6_ac_destroy_dev(idev); 3602 ipv6_ac_destroy_dev(idev);
@@ -4732,6 +4861,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
4732 array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only; 4861 array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only;
4733 array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast; 4862 array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast;
4734 array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na; 4863 array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
4864 array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
4735} 4865}
4736 4866
4737static inline size_t inet6_ifla6_size(void) 4867static inline size_t inet6_ifla6_size(void)
@@ -5820,6 +5950,14 @@ static struct addrconf_sysctl_table
5820 .proc_handler = proc_dointvec, 5950 .proc_handler = proc_dointvec,
5821 }, 5951 },
5822 { 5952 {
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 {
5823 /* sentinel */ 5961 /* sentinel */
5824 } 5962 }
5825 }, 5963 },