diff options
Diffstat (limited to 'net/sctp/protocol.c')
-rw-r--r-- | net/sctp/protocol.c | 79 |
1 files changed, 49 insertions, 30 deletions
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e98579b788b8..3d036cdfae41 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c | |||
@@ -153,6 +153,9 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist, | |||
153 | addr->a.v4.sin_family = AF_INET; | 153 | addr->a.v4.sin_family = AF_INET; |
154 | addr->a.v4.sin_port = 0; | 154 | addr->a.v4.sin_port = 0; |
155 | addr->a.v4.sin_addr.s_addr = ifa->ifa_local; | 155 | addr->a.v4.sin_addr.s_addr = ifa->ifa_local; |
156 | addr->valid = 1; | ||
157 | INIT_LIST_HEAD(&addr->list); | ||
158 | INIT_RCU_HEAD(&addr->rcu); | ||
156 | list_add_tail(&addr->list, addrlist); | 159 | list_add_tail(&addr->list, addrlist); |
157 | } | 160 | } |
158 | } | 161 | } |
@@ -192,16 +195,24 @@ static void sctp_free_local_addr_list(void) | |||
192 | } | 195 | } |
193 | } | 196 | } |
194 | 197 | ||
198 | void sctp_local_addr_free(struct rcu_head *head) | ||
199 | { | ||
200 | struct sctp_sockaddr_entry *e = container_of(head, | ||
201 | struct sctp_sockaddr_entry, rcu); | ||
202 | kfree(e); | ||
203 | } | ||
204 | |||
195 | /* Copy the local addresses which are valid for 'scope' into 'bp'. */ | 205 | /* Copy the local addresses which are valid for 'scope' into 'bp'. */ |
196 | int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | 206 | int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, |
197 | gfp_t gfp, int copy_flags) | 207 | gfp_t gfp, int copy_flags) |
198 | { | 208 | { |
199 | struct sctp_sockaddr_entry *addr; | 209 | struct sctp_sockaddr_entry *addr; |
200 | int error = 0; | 210 | int error = 0; |
201 | struct list_head *pos, *temp; | ||
202 | 211 | ||
203 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { | 212 | rcu_read_lock(); |
204 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 213 | list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) { |
214 | if (!addr->valid) | ||
215 | continue; | ||
205 | if (sctp_in_scope(&addr->a, scope)) { | 216 | if (sctp_in_scope(&addr->a, scope)) { |
206 | /* Now that the address is in scope, check to see if | 217 | /* Now that the address is in scope, check to see if |
207 | * the address type is really supported by the local | 218 | * the address type is really supported by the local |
@@ -213,7 +224,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | |||
213 | (copy_flags & SCTP_ADDR6_ALLOWED) && | 224 | (copy_flags & SCTP_ADDR6_ALLOWED) && |
214 | (copy_flags & SCTP_ADDR6_PEERSUPP)))) { | 225 | (copy_flags & SCTP_ADDR6_PEERSUPP)))) { |
215 | error = sctp_add_bind_addr(bp, &addr->a, 1, | 226 | error = sctp_add_bind_addr(bp, &addr->a, 1, |
216 | GFP_ATOMIC); | 227 | GFP_ATOMIC); |
217 | if (error) | 228 | if (error) |
218 | goto end_copy; | 229 | goto end_copy; |
219 | } | 230 | } |
@@ -221,6 +232,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | |||
221 | } | 232 | } |
222 | 233 | ||
223 | end_copy: | 234 | end_copy: |
235 | rcu_read_unlock(); | ||
224 | return error; | 236 | return error; |
225 | } | 237 | } |
226 | 238 | ||
@@ -416,9 +428,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
416 | struct rtable *rt; | 428 | struct rtable *rt; |
417 | struct flowi fl; | 429 | struct flowi fl; |
418 | struct sctp_bind_addr *bp; | 430 | struct sctp_bind_addr *bp; |
419 | rwlock_t *addr_lock; | ||
420 | struct sctp_sockaddr_entry *laddr; | 431 | struct sctp_sockaddr_entry *laddr; |
421 | struct list_head *pos; | ||
422 | struct dst_entry *dst = NULL; | 432 | struct dst_entry *dst = NULL; |
423 | union sctp_addr dst_saddr; | 433 | union sctp_addr dst_saddr; |
424 | 434 | ||
@@ -447,23 +457,20 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
447 | goto out; | 457 | goto out; |
448 | 458 | ||
449 | bp = &asoc->base.bind_addr; | 459 | bp = &asoc->base.bind_addr; |
450 | addr_lock = &asoc->base.addr_lock; | ||
451 | 460 | ||
452 | if (dst) { | 461 | if (dst) { |
453 | /* Walk through the bind address list and look for a bind | 462 | /* Walk through the bind address list and look for a bind |
454 | * address that matches the source address of the returned dst. | 463 | * address that matches the source address of the returned dst. |
455 | */ | 464 | */ |
456 | sctp_read_lock(addr_lock); | 465 | rcu_read_lock(); |
457 | list_for_each(pos, &bp->address_list) { | 466 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
458 | laddr = list_entry(pos, struct sctp_sockaddr_entry, | 467 | if (!laddr->valid || !laddr->use_as_src) |
459 | list); | ||
460 | if (!laddr->use_as_src) | ||
461 | continue; | 468 | continue; |
462 | sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); | 469 | sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); |
463 | if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) | 470 | if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) |
464 | goto out_unlock; | 471 | goto out_unlock; |
465 | } | 472 | } |
466 | sctp_read_unlock(addr_lock); | 473 | rcu_read_unlock(); |
467 | 474 | ||
468 | /* None of the bound addresses match the source address of the | 475 | /* None of the bound addresses match the source address of the |
469 | * dst. So release it. | 476 | * dst. So release it. |
@@ -475,10 +482,10 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
475 | /* Walk through the bind address list and try to get a dst that | 482 | /* Walk through the bind address list and try to get a dst that |
476 | * matches a bind address as the source address. | 483 | * matches a bind address as the source address. |
477 | */ | 484 | */ |
478 | sctp_read_lock(addr_lock); | 485 | rcu_read_lock(); |
479 | list_for_each(pos, &bp->address_list) { | 486 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
480 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 487 | if (!laddr->valid) |
481 | 488 | continue; | |
482 | if ((laddr->use_as_src) && | 489 | if ((laddr->use_as_src) && |
483 | (AF_INET == laddr->a.sa.sa_family)) { | 490 | (AF_INET == laddr->a.sa.sa_family)) { |
484 | fl.fl4_src = laddr->a.v4.sin_addr.s_addr; | 491 | fl.fl4_src = laddr->a.v4.sin_addr.s_addr; |
@@ -490,7 +497,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
490 | } | 497 | } |
491 | 498 | ||
492 | out_unlock: | 499 | out_unlock: |
493 | sctp_read_unlock(addr_lock); | 500 | rcu_read_unlock(); |
494 | out: | 501 | out: |
495 | if (dst) | 502 | if (dst) |
496 | SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n", | 503 | SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n", |
@@ -600,13 +607,18 @@ static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr) | |||
600 | seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); | 607 | seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); |
601 | } | 608 | } |
602 | 609 | ||
603 | /* Event handler for inet address addition/deletion events. */ | 610 | /* Event handler for inet address addition/deletion events. |
611 | * The sctp_local_addr_list needs to be protocted by a spin lock since | ||
612 | * multiple notifiers (say IPv4 and IPv6) may be running at the same | ||
613 | * time and thus corrupt the list. | ||
614 | * The reader side is protected with RCU. | ||
615 | */ | ||
604 | static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, | 616 | static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, |
605 | void *ptr) | 617 | void *ptr) |
606 | { | 618 | { |
607 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; | 619 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
608 | struct sctp_sockaddr_entry *addr; | 620 | struct sctp_sockaddr_entry *addr = NULL; |
609 | struct list_head *pos, *temp; | 621 | struct sctp_sockaddr_entry *temp; |
610 | 622 | ||
611 | switch (ev) { | 623 | switch (ev) { |
612 | case NETDEV_UP: | 624 | case NETDEV_UP: |
@@ -615,19 +627,25 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, | |||
615 | addr->a.v4.sin_family = AF_INET; | 627 | addr->a.v4.sin_family = AF_INET; |
616 | addr->a.v4.sin_port = 0; | 628 | addr->a.v4.sin_port = 0; |
617 | addr->a.v4.sin_addr.s_addr = ifa->ifa_local; | 629 | addr->a.v4.sin_addr.s_addr = ifa->ifa_local; |
618 | list_add_tail(&addr->list, &sctp_local_addr_list); | 630 | addr->valid = 1; |
631 | spin_lock_bh(&sctp_local_addr_lock); | ||
632 | list_add_tail_rcu(&addr->list, &sctp_local_addr_list); | ||
633 | spin_unlock_bh(&sctp_local_addr_lock); | ||
619 | } | 634 | } |
620 | break; | 635 | break; |
621 | case NETDEV_DOWN: | 636 | case NETDEV_DOWN: |
622 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { | 637 | spin_lock_bh(&sctp_local_addr_lock); |
623 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 638 | list_for_each_entry_safe(addr, temp, |
639 | &sctp_local_addr_list, list) { | ||
624 | if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { | 640 | if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { |
625 | list_del(pos); | 641 | addr->valid = 0; |
626 | kfree(addr); | 642 | list_del_rcu(&addr->list); |
627 | break; | 643 | break; |
628 | } | 644 | } |
629 | } | 645 | } |
630 | 646 | spin_unlock_bh(&sctp_local_addr_lock); | |
647 | if (addr && !addr->valid) | ||
648 | call_rcu(&addr->rcu, sctp_local_addr_free); | ||
631 | break; | 649 | break; |
632 | } | 650 | } |
633 | 651 | ||
@@ -1160,6 +1178,7 @@ SCTP_STATIC __init int sctp_init(void) | |||
1160 | 1178 | ||
1161 | /* Initialize the local address list. */ | 1179 | /* Initialize the local address list. */ |
1162 | INIT_LIST_HEAD(&sctp_local_addr_list); | 1180 | INIT_LIST_HEAD(&sctp_local_addr_list); |
1181 | spin_lock_init(&sctp_local_addr_lock); | ||
1163 | sctp_get_local_addr_list(); | 1182 | sctp_get_local_addr_list(); |
1164 | 1183 | ||
1165 | /* Register notifier for inet address additions/deletions. */ | 1184 | /* Register notifier for inet address additions/deletions. */ |
@@ -1227,6 +1246,9 @@ SCTP_STATIC __exit void sctp_exit(void) | |||
1227 | sctp_v6_del_protocol(); | 1246 | sctp_v6_del_protocol(); |
1228 | inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); | 1247 | inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); |
1229 | 1248 | ||
1249 | /* Unregister notifier for inet address additions/deletions. */ | ||
1250 | unregister_inetaddr_notifier(&sctp_inetaddr_notifier); | ||
1251 | |||
1230 | /* Free the local address list. */ | 1252 | /* Free the local address list. */ |
1231 | sctp_free_local_addr_list(); | 1253 | sctp_free_local_addr_list(); |
1232 | 1254 | ||
@@ -1240,9 +1262,6 @@ SCTP_STATIC __exit void sctp_exit(void) | |||
1240 | inet_unregister_protosw(&sctp_stream_protosw); | 1262 | inet_unregister_protosw(&sctp_stream_protosw); |
1241 | inet_unregister_protosw(&sctp_seqpacket_protosw); | 1263 | inet_unregister_protosw(&sctp_seqpacket_protosw); |
1242 | 1264 | ||
1243 | /* Unregister notifier for inet address additions/deletions. */ | ||
1244 | unregister_inetaddr_notifier(&sctp_inetaddr_notifier); | ||
1245 | |||
1246 | sctp_sysctl_unregister(); | 1265 | sctp_sysctl_unregister(); |
1247 | list_del(&sctp_ipv4_specific.list); | 1266 | list_del(&sctp_ipv4_specific.list); |
1248 | 1267 | ||