diff options
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r-- | net/sctp/ipv6.c | 46 |
1 files changed, 29 insertions, 17 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index f8aa23dda1c1..670fd2740b89 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c | |||
@@ -77,13 +77,18 @@ | |||
77 | 77 | ||
78 | #include <asm/uaccess.h> | 78 | #include <asm/uaccess.h> |
79 | 79 | ||
80 | /* Event handler for inet6 address addition/deletion events. */ | 80 | /* Event handler for inet6 address addition/deletion events. |
81 | * The sctp_local_addr_list needs to be protocted by a spin lock since | ||
82 | * multiple notifiers (say IPv4 and IPv6) may be running at the same | ||
83 | * time and thus corrupt the list. | ||
84 | * The reader side is protected with RCU. | ||
85 | */ | ||
81 | static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, | 86 | static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, |
82 | void *ptr) | 87 | void *ptr) |
83 | { | 88 | { |
84 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; | 89 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; |
85 | struct sctp_sockaddr_entry *addr; | 90 | struct sctp_sockaddr_entry *addr = NULL; |
86 | struct list_head *pos, *temp; | 91 | struct sctp_sockaddr_entry *temp; |
87 | 92 | ||
88 | switch (ev) { | 93 | switch (ev) { |
89 | case NETDEV_UP: | 94 | case NETDEV_UP: |
@@ -94,19 +99,26 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, | |||
94 | memcpy(&addr->a.v6.sin6_addr, &ifa->addr, | 99 | memcpy(&addr->a.v6.sin6_addr, &ifa->addr, |
95 | sizeof(struct in6_addr)); | 100 | sizeof(struct in6_addr)); |
96 | addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; | 101 | addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; |
97 | list_add_tail(&addr->list, &sctp_local_addr_list); | 102 | addr->valid = 1; |
103 | spin_lock_bh(&sctp_local_addr_lock); | ||
104 | list_add_tail_rcu(&addr->list, &sctp_local_addr_list); | ||
105 | spin_unlock_bh(&sctp_local_addr_lock); | ||
98 | } | 106 | } |
99 | break; | 107 | break; |
100 | case NETDEV_DOWN: | 108 | case NETDEV_DOWN: |
101 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { | 109 | spin_lock_bh(&sctp_local_addr_lock); |
102 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 110 | list_for_each_entry_safe(addr, temp, |
103 | if (ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { | 111 | &sctp_local_addr_list, list) { |
104 | list_del(pos); | 112 | if (ipv6_addr_equal(&addr->a.v6.sin6_addr, |
105 | kfree(addr); | 113 | &ifa->addr)) { |
114 | addr->valid = 0; | ||
115 | list_del_rcu(&addr->list); | ||
106 | break; | 116 | break; |
107 | } | 117 | } |
108 | } | 118 | } |
109 | 119 | spin_unlock_bh(&sctp_local_addr_lock); | |
120 | if (addr && !addr->valid) | ||
121 | call_rcu(&addr->rcu, sctp_local_addr_free); | ||
110 | break; | 122 | break; |
111 | } | 123 | } |
112 | 124 | ||
@@ -290,9 +302,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
290 | union sctp_addr *saddr) | 302 | union sctp_addr *saddr) |
291 | { | 303 | { |
292 | struct sctp_bind_addr *bp; | 304 | struct sctp_bind_addr *bp; |
293 | rwlock_t *addr_lock; | ||
294 | struct sctp_sockaddr_entry *laddr; | 305 | struct sctp_sockaddr_entry *laddr; |
295 | struct list_head *pos; | ||
296 | sctp_scope_t scope; | 306 | sctp_scope_t scope; |
297 | union sctp_addr *baddr = NULL; | 307 | union sctp_addr *baddr = NULL; |
298 | __u8 matchlen = 0; | 308 | __u8 matchlen = 0; |
@@ -312,14 +322,14 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
312 | scope = sctp_scope(daddr); | 322 | scope = sctp_scope(daddr); |
313 | 323 | ||
314 | bp = &asoc->base.bind_addr; | 324 | bp = &asoc->base.bind_addr; |
315 | addr_lock = &asoc->base.addr_lock; | ||
316 | 325 | ||
317 | /* Go through the bind address list and find the best source address | 326 | /* Go through the bind address list and find the best source address |
318 | * that matches the scope of the destination address. | 327 | * that matches the scope of the destination address. |
319 | */ | 328 | */ |
320 | sctp_read_lock(addr_lock); | 329 | rcu_read_lock(); |
321 | list_for_each(pos, &bp->address_list) { | 330 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
322 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 331 | if (!laddr->valid) |
332 | continue; | ||
323 | if ((laddr->use_as_src) && | 333 | if ((laddr->use_as_src) && |
324 | (laddr->a.sa.sa_family == AF_INET6) && | 334 | (laddr->a.sa.sa_family == AF_INET6) && |
325 | (scope <= sctp_scope(&laddr->a))) { | 335 | (scope <= sctp_scope(&laddr->a))) { |
@@ -341,7 +351,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
341 | __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); | 351 | __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); |
342 | } | 352 | } |
343 | 353 | ||
344 | sctp_read_unlock(addr_lock); | 354 | rcu_read_unlock(); |
345 | } | 355 | } |
346 | 356 | ||
347 | /* Make a copy of all potential local addresses. */ | 357 | /* Make a copy of all potential local addresses. */ |
@@ -367,7 +377,9 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist, | |||
367 | addr->a.v6.sin6_port = 0; | 377 | addr->a.v6.sin6_port = 0; |
368 | addr->a.v6.sin6_addr = ifp->addr; | 378 | addr->a.v6.sin6_addr = ifp->addr; |
369 | addr->a.v6.sin6_scope_id = dev->ifindex; | 379 | addr->a.v6.sin6_scope_id = dev->ifindex; |
380 | addr->valid = 1; | ||
370 | INIT_LIST_HEAD(&addr->list); | 381 | INIT_LIST_HEAD(&addr->list); |
382 | INIT_RCU_HEAD(&addr->rcu); | ||
371 | list_add_tail(&addr->list, addrlist); | 383 | list_add_tail(&addr->list, addrlist); |
372 | } | 384 | } |
373 | } | 385 | } |