diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-06-23 12:39:01 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-06-25 19:23:03 -0400 |
commit | b7b1bfce0bb68bd8f6e62a28295922785cc63781 (patch) | |
tree | 212102b875b453496d86f8c448892bd90eb58c92 /net/ipv6/addrconf.c | |
parent | 51151a16a60f0a886a0b1e4a0697001198af50c4 (diff) |
ipv6: split duplicate address detection and router solicitation timer
This patch splits the timers for duplicate address detection and router
solicitations apart. The router solicitations timer goes into inet6_dev
and the dad timer stays in inet6_ifaddr.
The reason behind this patch is to reduce the number of unneeded router
solicitations send out by the host if additional link-local addresses
are created. Currently we send out RS for every link-local address on
an interface.
If the RS timer fires we pick a source address with ipv6_get_lladdr. This
change could hurt people adding additional link-local addresses and
specifying these addresses in the radvd clients section because we
no longer guarantee that we use every ll address as source address in
router solicitations.
Cc: Flavio Leitner <fleitner@redhat.com>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: David Stevens <dlstevens@us.ibm.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Reviewed-by: Flavio Leitner <fbl@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 164 |
1 files changed, 91 insertions, 73 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 90788a1c6bbc..c06bc76280b2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -253,37 +253,32 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev) | |||
253 | return !qdisc_tx_is_noop(dev); | 253 | return !qdisc_tx_is_noop(dev); |
254 | } | 254 | } |
255 | 255 | ||
256 | static void addrconf_del_timer(struct inet6_ifaddr *ifp) | 256 | static void addrconf_del_rs_timer(struct inet6_dev *idev) |
257 | { | 257 | { |
258 | if (del_timer(&ifp->timer)) | 258 | if (del_timer(&idev->rs_timer)) |
259 | __in6_dev_put(idev); | ||
260 | } | ||
261 | |||
262 | static void addrconf_del_dad_timer(struct inet6_ifaddr *ifp) | ||
263 | { | ||
264 | if (del_timer(&ifp->dad_timer)) | ||
259 | __in6_ifa_put(ifp); | 265 | __in6_ifa_put(ifp); |
260 | } | 266 | } |
261 | 267 | ||
262 | enum addrconf_timer_t { | 268 | static void addrconf_mod_rs_timer(struct inet6_dev *idev, |
263 | AC_NONE, | 269 | unsigned long when) |
264 | AC_DAD, | 270 | { |
265 | AC_RS, | 271 | if (!timer_pending(&idev->rs_timer)) |
266 | }; | 272 | in6_dev_hold(idev); |
273 | mod_timer(&idev->rs_timer, jiffies + when); | ||
274 | } | ||
267 | 275 | ||
268 | static void addrconf_mod_timer(struct inet6_ifaddr *ifp, | 276 | static void addrconf_mod_dad_timer(struct inet6_ifaddr *ifp, |
269 | enum addrconf_timer_t what, | 277 | unsigned long when) |
270 | unsigned long when) | ||
271 | { | 278 | { |
272 | if (!del_timer(&ifp->timer)) | 279 | if (!timer_pending(&ifp->dad_timer)) |
273 | in6_ifa_hold(ifp); | 280 | in6_ifa_hold(ifp); |
274 | 281 | mod_timer(&ifp->dad_timer, jiffies + when); | |
275 | switch (what) { | ||
276 | case AC_DAD: | ||
277 | ifp->timer.function = addrconf_dad_timer; | ||
278 | break; | ||
279 | case AC_RS: | ||
280 | ifp->timer.function = addrconf_rs_timer; | ||
281 | break; | ||
282 | default: | ||
283 | break; | ||
284 | } | ||
285 | ifp->timer.expires = jiffies + when; | ||
286 | add_timer(&ifp->timer); | ||
287 | } | 282 | } |
288 | 283 | ||
289 | static int snmp6_alloc_dev(struct inet6_dev *idev) | 284 | static int snmp6_alloc_dev(struct inet6_dev *idev) |
@@ -326,6 +321,7 @@ void in6_dev_finish_destroy(struct inet6_dev *idev) | |||
326 | 321 | ||
327 | WARN_ON(!list_empty(&idev->addr_list)); | 322 | WARN_ON(!list_empty(&idev->addr_list)); |
328 | WARN_ON(idev->mc_list != NULL); | 323 | WARN_ON(idev->mc_list != NULL); |
324 | WARN_ON(timer_pending(&idev->rs_timer)); | ||
329 | 325 | ||
330 | #ifdef NET_REFCNT_DEBUG | 326 | #ifdef NET_REFCNT_DEBUG |
331 | pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL"); | 327 | pr_debug("%s: %s\n", __func__, dev ? dev->name : "NIL"); |
@@ -357,7 +353,8 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) | |||
357 | rwlock_init(&ndev->lock); | 353 | rwlock_init(&ndev->lock); |
358 | ndev->dev = dev; | 354 | ndev->dev = dev; |
359 | INIT_LIST_HEAD(&ndev->addr_list); | 355 | INIT_LIST_HEAD(&ndev->addr_list); |
360 | 356 | setup_timer(&ndev->rs_timer, addrconf_rs_timer, | |
357 | (unsigned long)ndev); | ||
361 | memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf)); | 358 | memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf)); |
362 | ndev->cnf.mtu6 = dev->mtu; | 359 | ndev->cnf.mtu6 = dev->mtu; |
363 | ndev->cnf.sysctl = NULL; | 360 | ndev->cnf.sysctl = NULL; |
@@ -776,7 +773,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) | |||
776 | 773 | ||
777 | in6_dev_put(ifp->idev); | 774 | in6_dev_put(ifp->idev); |
778 | 775 | ||
779 | if (del_timer(&ifp->timer)) | 776 | if (del_timer(&ifp->dad_timer)) |
780 | pr_notice("Timer is still running, when freeing ifa=%p\n", ifp); | 777 | pr_notice("Timer is still running, when freeing ifa=%p\n", ifp); |
781 | 778 | ||
782 | if (ifp->state != INET6_IFADDR_STATE_DEAD) { | 779 | if (ifp->state != INET6_IFADDR_STATE_DEAD) { |
@@ -869,9 +866,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, | |||
869 | 866 | ||
870 | spin_lock_init(&ifa->lock); | 867 | spin_lock_init(&ifa->lock); |
871 | spin_lock_init(&ifa->state_lock); | 868 | spin_lock_init(&ifa->state_lock); |
872 | init_timer(&ifa->timer); | 869 | setup_timer(&ifa->dad_timer, addrconf_dad_timer, |
870 | (unsigned long)ifa); | ||
873 | INIT_HLIST_NODE(&ifa->addr_lst); | 871 | INIT_HLIST_NODE(&ifa->addr_lst); |
874 | ifa->timer.data = (unsigned long) ifa; | ||
875 | ifa->scope = scope; | 872 | ifa->scope = scope; |
876 | ifa->prefix_len = pfxlen; | 873 | ifa->prefix_len = pfxlen; |
877 | ifa->flags = flags | IFA_F_TENTATIVE; | 874 | ifa->flags = flags | IFA_F_TENTATIVE; |
@@ -994,7 +991,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
994 | } | 991 | } |
995 | write_unlock_bh(&idev->lock); | 992 | write_unlock_bh(&idev->lock); |
996 | 993 | ||
997 | addrconf_del_timer(ifp); | 994 | addrconf_del_dad_timer(ifp); |
998 | 995 | ||
999 | ipv6_ifa_notify(RTM_DELADDR, ifp); | 996 | ipv6_ifa_notify(RTM_DELADDR, ifp); |
1000 | 997 | ||
@@ -1447,6 +1444,23 @@ try_nextdev: | |||
1447 | } | 1444 | } |
1448 | EXPORT_SYMBOL(ipv6_dev_get_saddr); | 1445 | EXPORT_SYMBOL(ipv6_dev_get_saddr); |
1449 | 1446 | ||
1447 | static int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, | ||
1448 | unsigned char banned_flags) | ||
1449 | { | ||
1450 | struct inet6_ifaddr *ifp; | ||
1451 | int err = -EADDRNOTAVAIL; | ||
1452 | |||
1453 | list_for_each_entry(ifp, &idev->addr_list, if_list) { | ||
1454 | if (ifp->scope == IFA_LINK && | ||
1455 | !(ifp->flags & banned_flags)) { | ||
1456 | *addr = ifp->addr; | ||
1457 | err = 0; | ||
1458 | break; | ||
1459 | } | ||
1460 | } | ||
1461 | return err; | ||
1462 | } | ||
1463 | |||
1450 | int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, | 1464 | int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, |
1451 | unsigned char banned_flags) | 1465 | unsigned char banned_flags) |
1452 | { | 1466 | { |
@@ -1456,17 +1470,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, | |||
1456 | rcu_read_lock(); | 1470 | rcu_read_lock(); |
1457 | idev = __in6_dev_get(dev); | 1471 | idev = __in6_dev_get(dev); |
1458 | if (idev) { | 1472 | if (idev) { |
1459 | struct inet6_ifaddr *ifp; | ||
1460 | |||
1461 | read_lock_bh(&idev->lock); | 1473 | read_lock_bh(&idev->lock); |
1462 | list_for_each_entry(ifp, &idev->addr_list, if_list) { | 1474 | err = __ipv6_get_lladdr(idev, addr, banned_flags); |
1463 | if (ifp->scope == IFA_LINK && | ||
1464 | !(ifp->flags & banned_flags)) { | ||
1465 | *addr = ifp->addr; | ||
1466 | err = 0; | ||
1467 | break; | ||
1468 | } | ||
1469 | } | ||
1470 | read_unlock_bh(&idev->lock); | 1475 | read_unlock_bh(&idev->lock); |
1471 | } | 1476 | } |
1472 | rcu_read_unlock(); | 1477 | rcu_read_unlock(); |
@@ -1580,7 +1585,7 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) | |||
1580 | { | 1585 | { |
1581 | if (ifp->flags&IFA_F_PERMANENT) { | 1586 | if (ifp->flags&IFA_F_PERMANENT) { |
1582 | spin_lock_bh(&ifp->lock); | 1587 | spin_lock_bh(&ifp->lock); |
1583 | addrconf_del_timer(ifp); | 1588 | addrconf_del_dad_timer(ifp); |
1584 | ifp->flags |= IFA_F_TENTATIVE; | 1589 | ifp->flags |= IFA_F_TENTATIVE; |
1585 | if (dad_failed) | 1590 | if (dad_failed) |
1586 | ifp->flags |= IFA_F_DADFAILED; | 1591 | ifp->flags |= IFA_F_DADFAILED; |
@@ -3036,7 +3041,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3036 | hlist_for_each_entry_rcu(ifa, h, addr_lst) { | 3041 | hlist_for_each_entry_rcu(ifa, h, addr_lst) { |
3037 | if (ifa->idev == idev) { | 3042 | if (ifa->idev == idev) { |
3038 | hlist_del_init_rcu(&ifa->addr_lst); | 3043 | hlist_del_init_rcu(&ifa->addr_lst); |
3039 | addrconf_del_timer(ifa); | 3044 | addrconf_del_dad_timer(ifa); |
3040 | goto restart; | 3045 | goto restart; |
3041 | } | 3046 | } |
3042 | } | 3047 | } |
@@ -3045,6 +3050,8 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3045 | 3050 | ||
3046 | write_lock_bh(&idev->lock); | 3051 | write_lock_bh(&idev->lock); |
3047 | 3052 | ||
3053 | addrconf_del_rs_timer(idev); | ||
3054 | |||
3048 | /* Step 2: clear flags for stateless addrconf */ | 3055 | /* Step 2: clear flags for stateless addrconf */ |
3049 | if (!how) | 3056 | if (!how) |
3050 | idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); | 3057 | idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); |
@@ -3074,7 +3081,7 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3074 | while (!list_empty(&idev->addr_list)) { | 3081 | while (!list_empty(&idev->addr_list)) { |
3075 | ifa = list_first_entry(&idev->addr_list, | 3082 | ifa = list_first_entry(&idev->addr_list, |
3076 | struct inet6_ifaddr, if_list); | 3083 | struct inet6_ifaddr, if_list); |
3077 | addrconf_del_timer(ifa); | 3084 | addrconf_del_dad_timer(ifa); |
3078 | 3085 | ||
3079 | list_del(&ifa->if_list); | 3086 | list_del(&ifa->if_list); |
3080 | 3087 | ||
@@ -3116,10 +3123,10 @@ static int addrconf_ifdown(struct net_device *dev, int how) | |||
3116 | 3123 | ||
3117 | static void addrconf_rs_timer(unsigned long data) | 3124 | static void addrconf_rs_timer(unsigned long data) |
3118 | { | 3125 | { |
3119 | struct inet6_ifaddr *ifp = (struct inet6_ifaddr *) data; | 3126 | struct inet6_dev *idev = (struct inet6_dev *)data; |
3120 | struct inet6_dev *idev = ifp->idev; | 3127 | struct in6_addr lladdr; |
3121 | 3128 | ||
3122 | read_lock(&idev->lock); | 3129 | write_lock(&idev->lock); |
3123 | if (idev->dead || !(idev->if_flags & IF_READY)) | 3130 | if (idev->dead || !(idev->if_flags & IF_READY)) |
3124 | goto out; | 3131 | goto out; |
3125 | 3132 | ||
@@ -3130,18 +3137,19 @@ static void addrconf_rs_timer(unsigned long data) | |||
3130 | if (idev->if_flags & IF_RA_RCVD) | 3137 | if (idev->if_flags & IF_RA_RCVD) |
3131 | goto out; | 3138 | goto out; |
3132 | 3139 | ||
3133 | spin_lock(&ifp->lock); | 3140 | if (idev->rs_probes++ < idev->cnf.rtr_solicits) { |
3134 | if (ifp->probes++ < idev->cnf.rtr_solicits) { | 3141 | if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE)) |
3135 | /* The wait after the last probe can be shorter */ | 3142 | ndisc_send_rs(idev->dev, &lladdr, |
3136 | addrconf_mod_timer(ifp, AC_RS, | 3143 | &in6addr_linklocal_allrouters); |
3137 | (ifp->probes == idev->cnf.rtr_solicits) ? | 3144 | else |
3138 | idev->cnf.rtr_solicit_delay : | 3145 | goto out; |
3139 | idev->cnf.rtr_solicit_interval); | ||
3140 | spin_unlock(&ifp->lock); | ||
3141 | 3146 | ||
3142 | ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); | 3147 | /* The wait after the last probe can be shorter */ |
3148 | addrconf_mod_rs_timer(idev, (idev->rs_probes == | ||
3149 | idev->cnf.rtr_solicits) ? | ||
3150 | idev->cnf.rtr_solicit_delay : | ||
3151 | idev->cnf.rtr_solicit_interval); | ||
3143 | } else { | 3152 | } else { |
3144 | spin_unlock(&ifp->lock); | ||
3145 | /* | 3153 | /* |
3146 | * Note: we do not support deprecated "all on-link" | 3154 | * Note: we do not support deprecated "all on-link" |
3147 | * assumption any longer. | 3155 | * assumption any longer. |
@@ -3150,8 +3158,8 @@ static void addrconf_rs_timer(unsigned long data) | |||
3150 | } | 3158 | } |
3151 | 3159 | ||
3152 | out: | 3160 | out: |
3153 | read_unlock(&idev->lock); | 3161 | write_unlock(&idev->lock); |
3154 | in6_ifa_put(ifp); | 3162 | in6_dev_put(idev); |
3155 | } | 3163 | } |
3156 | 3164 | ||
3157 | /* | 3165 | /* |
@@ -3167,8 +3175,8 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp) | |||
3167 | else | 3175 | else |
3168 | rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); | 3176 | rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1); |
3169 | 3177 | ||
3170 | ifp->probes = idev->cnf.dad_transmits; | 3178 | ifp->dad_probes = idev->cnf.dad_transmits; |
3171 | addrconf_mod_timer(ifp, AC_DAD, rand_num); | 3179 | addrconf_mod_dad_timer(ifp, rand_num); |
3172 | } | 3180 | } |
3173 | 3181 | ||
3174 | static void addrconf_dad_start(struct inet6_ifaddr *ifp) | 3182 | static void addrconf_dad_start(struct inet6_ifaddr *ifp) |
@@ -3229,40 +3237,40 @@ static void addrconf_dad_timer(unsigned long data) | |||
3229 | struct inet6_dev *idev = ifp->idev; | 3237 | struct inet6_dev *idev = ifp->idev; |
3230 | struct in6_addr mcaddr; | 3238 | struct in6_addr mcaddr; |
3231 | 3239 | ||
3232 | if (!ifp->probes && addrconf_dad_end(ifp)) | 3240 | if (!ifp->dad_probes && addrconf_dad_end(ifp)) |
3233 | goto out; | 3241 | goto out; |
3234 | 3242 | ||
3235 | read_lock(&idev->lock); | 3243 | write_lock(&idev->lock); |
3236 | if (idev->dead || !(idev->if_flags & IF_READY)) { | 3244 | if (idev->dead || !(idev->if_flags & IF_READY)) { |
3237 | read_unlock(&idev->lock); | 3245 | write_unlock(&idev->lock); |
3238 | goto out; | 3246 | goto out; |
3239 | } | 3247 | } |
3240 | 3248 | ||
3241 | spin_lock(&ifp->lock); | 3249 | spin_lock(&ifp->lock); |
3242 | if (ifp->state == INET6_IFADDR_STATE_DEAD) { | 3250 | if (ifp->state == INET6_IFADDR_STATE_DEAD) { |
3243 | spin_unlock(&ifp->lock); | 3251 | spin_unlock(&ifp->lock); |
3244 | read_unlock(&idev->lock); | 3252 | write_unlock(&idev->lock); |
3245 | goto out; | 3253 | goto out; |
3246 | } | 3254 | } |
3247 | 3255 | ||
3248 | if (ifp->probes == 0) { | 3256 | if (ifp->dad_probes == 0) { |
3249 | /* | 3257 | /* |
3250 | * DAD was successful | 3258 | * DAD was successful |
3251 | */ | 3259 | */ |
3252 | 3260 | ||
3253 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); | 3261 | ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED); |
3254 | spin_unlock(&ifp->lock); | 3262 | spin_unlock(&ifp->lock); |
3255 | read_unlock(&idev->lock); | 3263 | write_unlock(&idev->lock); |
3256 | 3264 | ||
3257 | addrconf_dad_completed(ifp); | 3265 | addrconf_dad_completed(ifp); |
3258 | 3266 | ||
3259 | goto out; | 3267 | goto out; |
3260 | } | 3268 | } |
3261 | 3269 | ||
3262 | ifp->probes--; | 3270 | ifp->dad_probes--; |
3263 | addrconf_mod_timer(ifp, AC_DAD, ifp->idev->nd_parms->retrans_time); | 3271 | addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time); |
3264 | spin_unlock(&ifp->lock); | 3272 | spin_unlock(&ifp->lock); |
3265 | read_unlock(&idev->lock); | 3273 | write_unlock(&idev->lock); |
3266 | 3274 | ||
3267 | /* send a neighbour solicitation for our addr */ | 3275 | /* send a neighbour solicitation for our addr */ |
3268 | addrconf_addr_solict_mult(&ifp->addr, &mcaddr); | 3276 | addrconf_addr_solict_mult(&ifp->addr, &mcaddr); |
@@ -3274,6 +3282,9 @@ out: | |||
3274 | static void addrconf_dad_completed(struct inet6_ifaddr *ifp) | 3282 | static void addrconf_dad_completed(struct inet6_ifaddr *ifp) |
3275 | { | 3283 | { |
3276 | struct net_device *dev = ifp->idev->dev; | 3284 | struct net_device *dev = ifp->idev->dev; |
3285 | struct in6_addr lladdr; | ||
3286 | |||
3287 | addrconf_del_dad_timer(ifp); | ||
3277 | 3288 | ||
3278 | /* | 3289 | /* |
3279 | * Configure the address for reception. Now it is valid. | 3290 | * Configure the address for reception. Now it is valid. |
@@ -3294,13 +3305,20 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) | |||
3294 | * [...] as part of DAD [...] there is no need | 3305 | * [...] as part of DAD [...] there is no need |
3295 | * to delay again before sending the first RS | 3306 | * to delay again before sending the first RS |
3296 | */ | 3307 | */ |
3297 | ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters); | 3308 | if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE)) |
3309 | ndisc_send_rs(dev, &lladdr, | ||
3310 | &in6addr_linklocal_allrouters); | ||
3311 | else | ||
3312 | return; | ||
3298 | 3313 | ||
3299 | spin_lock_bh(&ifp->lock); | 3314 | write_lock_bh(&ifp->idev->lock); |
3300 | ifp->probes = 1; | 3315 | spin_lock(&ifp->lock); |
3316 | ifp->idev->rs_probes = 1; | ||
3301 | ifp->idev->if_flags |= IF_RS_SENT; | 3317 | ifp->idev->if_flags |= IF_RS_SENT; |
3302 | addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval); | 3318 | addrconf_mod_rs_timer(ifp->idev, |
3303 | spin_unlock_bh(&ifp->lock); | 3319 | ifp->idev->cnf.rtr_solicit_interval); |
3320 | spin_unlock(&ifp->lock); | ||
3321 | write_unlock_bh(&ifp->idev->lock); | ||
3304 | } | 3322 | } |
3305 | } | 3323 | } |
3306 | 3324 | ||