aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Ahern <dsa@cumulusnetworks.com>2016-02-24 12:25:37 -0500
committerDavid S. Miller <davem@davemloft.net>2016-02-25 21:45:15 -0500
commitf1705ec197e705b79ea40fe7a2cc5acfa1d3bfac (patch)
tree1efb568eda46c1039a05fb07173b3c71592d7123
parent619b17452a388d6be69a7b28dc92310ea3f5aa65 (diff)
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>
-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.c136
4 files changed, 132 insertions, 15 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 24ce97f42d35..d5df40c75aa4 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1563,6 +1563,15 @@ temp_prefered_lft - INTEGER
1563 Preferred lifetime (in seconds) for temporary addresses. 1563 Preferred lifetime (in seconds) for temporary addresses.
1564 Default: 86400 (1 day) 1564 Default: 86400 (1 day)
1565 1565
1566keep_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
1566max_desync_factor - INTEGER 1575max_desync_factor - INTEGER
1567 Maximum value for DESYNC_FACTOR, which is a random value 1576 Maximum value for DESYNC_FACTOR, which is a random value
1568 that ensures that clients don't synchronize with each 1577 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 4751f8922362..a2d6f6c242af 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 */
@@ -3168,6 +3170,55 @@ static void addrconf_gre_config(struct net_device *dev)
3168} 3170}
3169#endif 3171#endif
3170 3172
3173static 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
3196static 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
3171static int addrconf_notify(struct notifier_block *this, unsigned long event, 3222static int addrconf_notify(struct notifier_block *this, unsigned long event,
3172 void *ptr) 3223 void *ptr)
3173{ 3224{
@@ -3253,6 +3304,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
3253 run_pending = 1; 3304 run_pending = 1;
3254 } 3305 }
3255 3306
3307 /* restore routes for permanent addresses */
3308 addrconf_permanent_addr(dev);
3309
3256 switch (dev->type) { 3310 switch (dev->type) {
3257#if IS_ENABLED(CONFIG_IPV6_SIT) 3311#if IS_ENABLED(CONFIG_IPV6_SIT)
3258 case ARPHRD_SIT: 3312 case ARPHRD_SIT:
@@ -3356,7 +3410,10 @@ static int addrconf_ifdown(struct net_device *dev, int how)
3356{ 3410{
3357 struct net *net = dev_net(dev); 3411 struct net *net = dev_net(dev);
3358 struct inet6_dev *idev; 3412 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;
3360 int state, i; 3417 int state, i;
3361 3418
3362 ASSERT_RTNL(); 3419 ASSERT_RTNL();
@@ -3383,6 +3440,16 @@ static int addrconf_ifdown(struct net_device *dev, int how)
3383 3440
3384 } 3441 }
3385 3442
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
3386 /* Step 2: clear hash table */ 3453 /* Step 2: clear hash table */
3387 for (i = 0; i < IN6_ADDR_HSIZE; i++) { 3454 for (i = 0; i < IN6_ADDR_HSIZE; i++) {
3388 struct hlist_head *h = &inet6_addr_lst[i]; 3455 struct hlist_head *h = &inet6_addr_lst[i];
@@ -3391,9 +3458,15 @@ static int addrconf_ifdown(struct net_device *dev, int how)
3391restart: 3458restart:
3392 hlist_for_each_entry_rcu(ifa, h, addr_lst) { 3459 hlist_for_each_entry_rcu(ifa, h, addr_lst) {
3393 if (ifa->idev == idev) { 3460 if (ifa->idev == idev) {
3394 hlist_del_init_rcu(&ifa->addr_lst);
3395 addrconf_del_dad_work(ifa); 3461 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 }
3397 } 3470 }
3398 } 3471 }
3399 spin_unlock_bh(&addrconf_hash_lock); 3472 spin_unlock_bh(&addrconf_hash_lock);
@@ -3427,31 +3500,53 @@ restart:
3427 write_lock_bh(&idev->lock); 3500 write_lock_bh(&idev->lock);
3428 } 3501 }
3429 3502
3430 while (!list_empty(&idev->addr_list)) { 3503 /* re-combine the user config with event to determine