diff options
| -rw-r--r-- | include/net/sctp/structs.h | 7 | ||||
| -rw-r--r-- | net/sctp/associola.c | 14 | ||||
| -rw-r--r-- | net/sctp/bind_addr.c | 68 | ||||
| -rw-r--r-- | net/sctp/endpointola.c | 27 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 12 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 25 | ||||
| -rw-r--r-- | net/sctp/sm_make_chunk.c | 18 | ||||
| -rw-r--r-- | net/sctp/socket.c | 98 |
8 files changed, 106 insertions, 163 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index a89e36197afb..c2fe2dcc9afc 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
| @@ -1155,7 +1155,9 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest, | |||
| 1155 | int flags); | 1155 | int flags); |
| 1156 | int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, | 1156 | int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, |
| 1157 | __u8 use_as_src, gfp_t gfp); | 1157 | __u8 use_as_src, gfp_t gfp); |
| 1158 | int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); | 1158 | int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *, |
| 1159 | void (*rcu_call)(struct rcu_head *, | ||
| 1160 | void (*func)(struct rcu_head *))); | ||
| 1159 | int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, | 1161 | int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, |
| 1160 | struct sctp_sock *); | 1162 | struct sctp_sock *); |
| 1161 | union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, | 1163 | union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, |
| @@ -1226,9 +1228,6 @@ struct sctp_ep_common { | |||
| 1226 | * bind_addr.address_list is our set of local IP addresses. | 1228 | * bind_addr.address_list is our set of local IP addresses. |
| 1227 | */ | 1229 | */ |
| 1228 | struct sctp_bind_addr bind_addr; | 1230 | struct sctp_bind_addr bind_addr; |
| 1229 | |||
| 1230 | /* Protection during address list comparisons. */ | ||
| 1231 | rwlock_t addr_lock; | ||
| 1232 | }; | 1231 | }; |
| 1233 | 1232 | ||
| 1234 | 1233 | ||
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 2ad1caf1ea42..9bad8ba0feda 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c | |||
| @@ -99,7 +99,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a | |||
| 99 | 99 | ||
| 100 | /* Initialize the bind addr area. */ | 100 | /* Initialize the bind addr area. */ |
| 101 | sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port); | 101 | sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port); |
| 102 | rwlock_init(&asoc->base.addr_lock); | ||
| 103 | 102 | ||
| 104 | asoc->state = SCTP_STATE_CLOSED; | 103 | asoc->state = SCTP_STATE_CLOSED; |
| 105 | 104 | ||
| @@ -937,8 +936,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, | |||
| 937 | { | 936 | { |
| 938 | struct sctp_transport *transport; | 937 | struct sctp_transport *transport; |
| 939 | 938 | ||
| 940 | sctp_read_lock(&asoc->base.addr_lock); | ||
| 941 | |||
| 942 | if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) && | 939 | if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) && |
| 943 | (htons(asoc->peer.port) == paddr->v4.sin_port)) { | 940 | (htons(asoc->peer.port) == paddr->v4.sin_port)) { |
| 944 | transport = sctp_assoc_lookup_paddr(asoc, paddr); | 941 | transport = sctp_assoc_lookup_paddr(asoc, paddr); |
| @@ -952,7 +949,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc, | |||
| 952 | transport = NULL; | 949 | transport = NULL; |
| 953 | 950 | ||
| 954 | out: | 951 | out: |
| 955 | sctp_read_unlock(&asoc->base.addr_lock); | ||
| 956 | return transport; | 952 | return transport; |
| 957 | } | 953 | } |
| 958 | 954 | ||
| @@ -1376,19 +1372,13 @@ int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc, | |||
| 1376 | int sctp_assoc_lookup_laddr(struct sctp_association *asoc, | 1372 | int sctp_assoc_lookup_laddr(struct sctp_association *asoc, |
| 1377 | const union sctp_addr *laddr) | 1373 | const union sctp_addr *laddr) |
| 1378 | { | 1374 | { |
| 1379 | int found; | 1375 | int found = 0; |
| 1380 | 1376 | ||
| 1381 | sctp_read_lock(&asoc->base.addr_lock); | ||
| 1382 | if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) && | 1377 | if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) && |
| 1383 | sctp_bind_addr_match(&asoc->base.bind_addr, laddr, | 1378 | sctp_bind_addr_match(&asoc->base.bind_addr, laddr, |
| 1384 | sctp_sk(asoc->base.sk))) { | 1379 | sctp_sk(asoc->base.sk))) |
| 1385 | found = 1; | 1380 | found = 1; |
| 1386 | goto out; | ||
| 1387 | } | ||
| 1388 | 1381 | ||
| 1389 | found = 0; | ||
| 1390 | out: | ||
| 1391 | sctp_read_unlock(&asoc->base.addr_lock); | ||
| 1392 | return found; | 1382 | return found; |
| 1393 | } | 1383 | } |
| 1394 | 1384 | ||
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 7fc369f9035d..d35cbf5aae33 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c | |||
| @@ -167,7 +167,11 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, | |||
| 167 | 167 | ||
| 168 | INIT_LIST_HEAD(&addr->list); | 168 | INIT_LIST_HEAD(&addr->list); |
| 169 | INIT_RCU_HEAD(&addr->rcu); | 169 | INIT_RCU_HEAD(&addr->rcu); |
| 170 | list_add_tail(&addr->list, &bp->address_list); | 170 | |
| 171 | /* We always hold a socket lock when calling this function, | ||
| 172 | * and that acts as a writer synchronizing lock. | ||
| 173 | */ | ||
| 174 | list_add_tail_rcu(&addr->list, &bp->address_list); | ||
| 171 | SCTP_DBG_OBJCNT_INC(addr); | 175 | SCTP_DBG_OBJCNT_INC(addr); |
| 172 | 176 | ||
| 173 | return 0; | 177 | return 0; |
| @@ -176,23 +180,35 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, | |||
| 176 | /* Delete an address from the bind address list in the SCTP_bind_addr | 180 | /* Delete an address from the bind address list in the SCTP_bind_addr |
| 177 | * structure. | 181 | * structure. |
| 178 | */ | 182 | */ |
| 179 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) | 183 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr, |
| 184 | void (*rcu_call)(struct rcu_head *head, | ||
| 185 | void (*func)(struct rcu_head *head))) | ||
| 180 | { | 186 | { |
| 181 | struct list_head *pos, *temp; | 187 | struct sctp_sockaddr_entry *addr, *temp; |
| 182 | struct sctp_sockaddr_entry *addr; | ||
| 183 | 188 | ||
| 184 | list_for_each_safe(pos, temp, &bp->address_list) { | 189 | /* We hold the socket lock when calling this function, |
| 185 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 190 | * and that acts as a writer synchronizing lock. |
| 191 | */ | ||
| 192 | list_for_each_entry_safe(addr, temp, &bp->address_list, list) { | ||
| 186 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { | 193 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { |
| 187 | /* Found the exact match. */ | 194 | /* Found the exact match. */ |
| 188 | list_del(pos); | 195 | addr->valid = 0; |
| 189 | kfree(addr); | 196 | list_del_rcu(&addr->list); |
| 190 | SCTP_DBG_OBJCNT_DEC(addr); | 197 | break; |
| 191 | |||
| 192 | return 0; | ||
| 193 | } | 198 | } |
| 194 | } | 199 | } |
| 195 | 200 | ||
| 201 | /* Call the rcu callback provided in the args. This function is | ||
| 202 | * called by both BH packet processing and user side socket option | ||
| 203 | * processing, but it works on different lists in those 2 contexts. | ||
| 204 | * Each context provides it's own callback, whether call_rcu_bh() | ||
| 205 | * or call_rcu(), to make sure that we wait for an appropriate time. | ||
| 206 | */ | ||
| 207 | if (addr && !addr->valid) { | ||
| 208 | rcu_call(&addr->rcu, sctp_local_addr_free); | ||
| 209 | SCTP_DBG_OBJCNT_DEC(addr); | ||
| 210 | } | ||
| 211 | |||
| 196 | return -EINVAL; | 212 | return -EINVAL; |
| 197 | } | 213 | } |
| 198 | 214 | ||
| @@ -302,15 +318,20 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp, | |||
| 302 | struct sctp_sock *opt) | 318 | struct sctp_sock *opt) |
| 303 | { | 319 | { |
| 304 | struct sctp_sockaddr_entry *laddr; | 320 | struct sctp_sockaddr_entry *laddr; |
| 305 | struct list_head *pos; | 321 | int match = 0; |
| 306 | 322 | ||
| 307 | list_for_each(pos, &bp->address_list) { | 323 | rcu_read_lock(); |
| 308 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 324 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 309 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | 325 | if (!laddr->valid) |
| 310 | return 1; | 326 | continue; |
| 327 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) { | ||
| 328 | match = 1; | ||
| 329 | break; | ||
| 330 | } | ||
| 311 | } | 331 | } |
| 332 | rcu_read_unlock(); | ||
| 312 | 333 | ||
| 313 | return 0; | 334 | return match; |
| 314 | } | 335 | } |
| 315 | 336 | ||
| 316 | /* Find the first address in the bind address list that is not present in | 337 | /* Find the first address in the bind address list that is not present in |
| @@ -325,18 +346,19 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, | |||
| 325 | union sctp_addr *addr; | 346 | union sctp_addr *addr; |
| 326 | void *addr_buf; | 347 | void *addr_buf; |
| 327 | struct sctp_af *af; | 348 | struct sctp_af *af; |
| 328 | struct list_head *pos; | ||
| 329 | int i; | 349 | int i; |
| 330 | 350 | ||
| 331 | list_for_each(pos, &bp->address_list) { | 351 | /* This is only called sctp_send_asconf_del_ip() and we hold |
| 332 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 352 | * the socket lock in that code patch, so that address list |
| 333 | 353 | * can't change. | |
| 354 | */ | ||
| 355 | list_for_each_entry(laddr, &bp->address_list, list) { | ||
| 334 | addr_buf = (union sctp_addr *)addrs; | 356 | addr_buf = (union sctp_addr *)addrs; |
| 335 | for (i = 0; i < addrcnt; i++) { | 357 | for (i = 0; i < addrcnt; i++) { |
| 336 | addr = (union sctp_addr *)addr_buf; | 358 | addr = (union sctp_addr *)addr_buf; |
| 337 | af = sctp_get_af_specific(addr->v4.sin_family); | 359 | af = sctp_get_af_specific(addr->v4.sin_family); |
| 338 | if (!af) | 360 | if (!af) |
| 339 | return NULL; | 361 | break; |
| 340 | 362 | ||
| 341 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) | 363 | if (opt->pf->cmp_addr(&laddr->a, addr, opt)) |
| 342 | break; | 364 | break; |
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 1404a9e2e78f..8f485a0d14bd 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c | |||
| @@ -92,7 +92,6 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, | |||
| 92 | 92 | ||
| 93 | /* Initialize the bind addr area */ | 93 | /* Initialize the bind addr area */ |
| 94 | sctp_bind_addr_init(&ep->base.bind_addr, 0); | 94 | sctp_bind_addr_init(&ep->base.bind_addr, 0); |
| 95 | rwlock_init(&ep->base.addr_lock); | ||
| 96 | 95 | ||
| 97 | /* Remember who we are attached to. */ | 96 | /* Remember who we are attached to. */ |
| 98 | ep->base.sk = sk; | 97 | ep->base.sk = sk; |
| @@ -225,21 +224,14 @@ void sctp_endpoint_put(struct sctp_endpoint *ep) | |||
| 225 | struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, | 224 | struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, |
| 226 | const union sctp_addr *laddr) | 225 | const union sctp_addr *laddr) |
| 227 | { | 226 | { |
| 228 | struct sctp_endpoint *retval; | 227 | struct sctp_endpoint *retval = NULL; |
| 229 | 228 | ||
| 230 | sctp_read_lock(&ep->base.addr_lock); | ||
| 231 | if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) { | 229 | if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) { |
| 232 | if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, | 230 | if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, |
| 233 | sctp_sk(ep->base.sk))) { | 231 | sctp_sk(ep->base.sk))) |
| 234 | retval = ep; | 232 | retval = ep; |
| 235 | goto out; | ||
| 236 | } | ||
| 237 | } | 233 | } |
| 238 | 234 | ||
| 239 | retval = NULL; | ||
| 240 | |||
| 241 | out: | ||
| 242 | sctp_read_unlock(&ep->base.addr_lock); | ||
| 243 | return retval; | 235 | return retval; |
| 244 | } | 236 | } |
| 245 | 237 | ||
| @@ -261,9 +253,7 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc( | |||
| 261 | list_for_each(pos, &ep->asocs) { | 253 | list_for_each(pos, &ep->asocs) { |
| 262 | asoc = list_entry(pos, struct sctp_association, asocs); | 254 | asoc = list_entry(pos, struct sctp_association, asocs); |
| 263 | if (rport == asoc->peer.port) { | 255 | if (rport == asoc->peer.port) { |
| 264 | sctp_read_lock(&asoc->base.addr_lock); | ||
| 265 | *transport = sctp_assoc_lookup_paddr(asoc, paddr); | 256 | *transport = sctp_assoc_lookup_paddr(asoc, paddr); |
| 266 | sctp_read_unlock(&asoc->base.addr_lock); | ||
| 267 | 257 | ||
| 268 | if (*transport) | 258 | if (*transport) |
| 269 | return asoc; | 259 | return asoc; |
| @@ -295,20 +285,17 @@ struct sctp_association *sctp_endpoint_lookup_assoc( | |||
| 295 | int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, | 285 | int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, |
| 296 | const union sctp_addr *paddr) | 286 | const union sctp_addr *paddr) |
| 297 | { | 287 | { |
| 298 | struct list_head *pos; | ||
| 299 | struct sctp_sockaddr_entry *addr; | 288 | struct sctp_sockaddr_entry *addr; |
| 300 | struct sctp_bind_addr *bp; | 289 | struct sctp_bind_addr *bp; |
| 301 | 290 | ||
| 302 | sctp_read_lock(&ep->base.addr_lock); | ||
| 303 | bp = &ep->base.bind_addr; | 291 | bp = &ep->base.bind_addr; |
| 304 | list_for_each(pos, &bp->address_list) { | 292 | /* This function is called with the socket lock held, |
| 305 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 293 | * so the address_list can not change. |
| 306 | if (sctp_has_association(&addr->a, paddr)) { | 294 | */ |
| 307 | sctp_read_unlock(&ep->base.addr_lock); | 295 | list_for_each_entry(addr, &bp->address_list, list) { |
| 296 | if (sctp_has_association(&addr->a, paddr)) | ||
| 308 | return 1; | 297 | return 1; |
| 309 | } | ||
| 310 | } | 298 | } |
| 311 | sctp_read_unlock(&ep->base.addr_lock); | ||
| 312 | 299 | ||
| 313 | return 0; | 300 | return 0; |
| 314 | } | 301 | } |
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index e12fa0a91da4..670fd2740b89 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c | |||
| @@ -302,9 +302,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
| 302 | union sctp_addr *saddr) | 302 | union sctp_addr *saddr) |
| 303 | { | 303 | { |
| 304 | struct sctp_bind_addr *bp; | 304 | struct sctp_bind_addr *bp; |
| 305 | rwlock_t *addr_lock; | ||
| 306 | struct sctp_sockaddr_entry *laddr; | 305 | struct sctp_sockaddr_entry *laddr; |
| 307 | struct list_head *pos; | ||
| 308 | sctp_scope_t scope; | 306 | sctp_scope_t scope; |
| 309 | union sctp_addr *baddr = NULL; | 307 | union sctp_addr *baddr = NULL; |
| 310 | __u8 matchlen = 0; | 308 | __u8 matchlen = 0; |
| @@ -324,14 +322,14 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
| 324 | scope = sctp_scope(daddr); | 322 | scope = sctp_scope(daddr); |
| 325 | 323 | ||
| 326 | bp = &asoc->base.bind_addr; | 324 | bp = &asoc->base.bind_addr; |
| 327 | addr_lock = &asoc->base.addr_lock; | ||
| 328 | 325 | ||
| 329 | /* 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 |
| 330 | * that matches the scope of the destination address. | 327 | * that matches the scope of the destination address. |
| 331 | */ | 328 | */ |
| 332 | sctp_read_lock(addr_lock); | 329 | rcu_read_lock(); |
| 333 | list_for_each(pos, &bp->address_list) { | 330 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 334 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 331 | if (!laddr->valid) |
| 332 | continue; | ||
| 335 | if ((laddr->use_as_src) && | 333 | if ((laddr->use_as_src) && |
| 336 | (laddr->a.sa.sa_family == AF_INET6) && | 334 | (laddr->a.sa.sa_family == AF_INET6) && |
| 337 | (scope <= sctp_scope(&laddr->a))) { | 335 | (scope <= sctp_scope(&laddr->a))) { |
| @@ -353,7 +351,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc, | |||
| 353 | __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); | 351 | __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); |
| 354 | } | 352 | } |
| 355 | 353 | ||
| 356 | sctp_read_unlock(addr_lock); | 354 | rcu_read_unlock(); |
| 357 | } | 355 | } |
| 358 | 356 | ||
| 359 | /* Make a copy of all potential local addresses. */ | 357 | /* Make a copy of all potential local addresses. */ |
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 7ee120e85913..3d036cdfae41 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c | |||
| @@ -224,7 +224,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | |||
| 224 | (copy_flags & SCTP_ADDR6_ALLOWED) && | 224 | (copy_flags & SCTP_ADDR6_ALLOWED) && |
| 225 | (copy_flags & SCTP_ADDR6_PEERSUPP)))) { | 225 | (copy_flags & SCTP_ADDR6_PEERSUPP)))) { |
| 226 | error = sctp_add_bind_addr(bp, &addr->a, 1, | 226 | error = sctp_add_bind_addr(bp, &addr->a, 1, |
| 227 | GFP_ATOMIC); | 227 | GFP_ATOMIC); |
| 228 | if (error) | 228 | if (error) |
| 229 | goto end_copy; | 229 | goto end_copy; |
| 230 | } | 230 | } |
| @@ -428,9 +428,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
| 428 | struct rtable *rt; | 428 | struct rtable *rt; |
| 429 | struct flowi fl; | 429 | struct flowi fl; |
| 430 | struct sctp_bind_addr *bp; | 430 | struct sctp_bind_addr *bp; |
| 431 | rwlock_t *addr_lock; | ||
| 432 | struct sctp_sockaddr_entry *laddr; | 431 | struct sctp_sockaddr_entry *laddr; |
| 433 | struct list_head *pos; | ||
| 434 | struct dst_entry *dst = NULL; | 432 | struct dst_entry *dst = NULL; |
| 435 | union sctp_addr dst_saddr; | 433 | union sctp_addr dst_saddr; |
| 436 | 434 | ||
| @@ -459,23 +457,20 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
| 459 | goto out; | 457 | goto out; |
| 460 | 458 | ||
| 461 | bp = &asoc->base.bind_addr; | 459 | bp = &asoc->base.bind_addr; |
| 462 | addr_lock = &asoc->base.addr_lock; | ||
| 463 | 460 | ||
| 464 | if (dst) { | 461 | if (dst) { |
| 465 | /* Walk through the bind address list and look for a bind | 462 | /* Walk through the bind address list and look for a bind |
| 466 | * address that matches the source address of the returned dst. | 463 | * address that matches the source address of the returned dst. |
| 467 | */ | 464 | */ |
| 468 | sctp_read_lock(addr_lock); | 465 | rcu_read_lock(); |
| 469 | list_for_each(pos, &bp->address_list) { | 466 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 470 | laddr = list_entry(pos, struct sctp_sockaddr_entry, | 467 | if (!laddr->valid || !laddr->use_as_src) |
| 471 | list); | ||
| 472 | if (!laddr->use_as_src) | ||
| 473 | continue; | 468 | continue; |
| 474 | sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); | 469 | sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); |
| 475 | if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) | 470 | if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) |
| 476 | goto out_unlock; | 471 | goto out_unlock; |
| 477 | } | 472 | } |
| 478 | sctp_read_unlock(addr_lock); | 473 | rcu_read_unlock(); |
| 479 | 474 | ||
| 480 | /* None of the bound addresses match the source address of the | 475 | /* None of the bound addresses match the source address of the |
| 481 | * dst. So release it. | 476 | * dst. So release it. |
| @@ -487,10 +482,10 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
| 487 | /* 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 |
| 488 | * matches a bind address as the source address. | 483 | * matches a bind address as the source address. |
| 489 | */ | 484 | */ |
| 490 | sctp_read_lock(addr_lock); | 485 | rcu_read_lock(); |
| 491 | list_for_each(pos, &bp->address_list) { | 486 | list_for_each_entry_rcu(laddr, &bp->address_list, list) { |
| 492 | laddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 487 | if (!laddr->valid) |
| 493 | 488 | continue; | |
| 494 | if ((laddr->use_as_src) && | 489 | if ((laddr->use_as_src) && |
| 495 | (AF_INET == laddr->a.sa.sa_family)) { | 490 | (AF_INET == laddr->a.sa.sa_family)) { |
| 496 | fl.fl4_src = laddr->a.v4.sin_addr.s_addr; | 491 | fl.fl4_src = laddr->a.v4.sin_addr.s_addr; |
| @@ -502,7 +497,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, | |||
| 502 | } | 497 | } |
| 503 | 498 | ||
| 504 | out_unlock: | 499 | out_unlock: |
| 505 | sctp_read_unlock(addr_lock); | 500 | rcu_read_unlock(); |
| 506 | out: | 501 | out: |
| 507 | if (dst) | 502 | if (dst) |
| 508 | 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", |
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 79856c924525..2e34220d94cd 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c | |||
| @@ -1531,7 +1531,7 @@ no_hmac: | |||
| 1531 | /* Also, add the destination address. */ | 1531 | /* Also, add the destination address. */ |
| 1532 | if (list_empty(&retval->base.bind_addr.address_list)) { | 1532 | if (list_empty(&retval->base.bind_addr.address_list)) { |
| 1533 | sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1, | 1533 | sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1, |
| 1534 | GFP_ATOMIC); | 1534 | GFP_ATOMIC); |
| 1535 | } | 1535 | } |
| 1536 | 1536 | ||
| 1537 | retval->next_tsn = retval->c.initial_tsn; | 1537 | retval->next_tsn = retval->c.initial_tsn; |
| @@ -2613,22 +2613,16 @@ static int sctp_asconf_param_success(struct sctp_association *asoc, | |||
| 2613 | 2613 | ||
| 2614 | switch (asconf_param->param_hdr.type) { | 2614 | switch (asconf_param->param_hdr.type) { |
| 2615 | case SCTP_PARAM_ADD_IP: | 2615 | case SCTP_PARAM_ADD_IP: |
| 2616 | sctp_local_bh_disable(); | 2616 | /* This is always done in BH context with a socket lock |
| 2617 | sctp_write_lock(&asoc->base.addr_lock); | 2617 | * held, so the list can not change. |
| 2618 | list_for_each(pos, &bp->address_list) { | 2618 | */ |
| 2619 | saddr = list_entry(pos, struct sctp_sockaddr_entry, list); | 2619 | list_for_each_entry(saddr, &bp->address_list, list) { |
| 2620 | if (sctp_cmp_addr_exact(&saddr->a, &addr)) | 2620 | if (sctp_cmp_addr_exact(&saddr->a, &addr)) |
| 2621 | saddr->use_as_src = 1; | 2621 | saddr->use_as_src = 1; |
| 2622 | } | 2622 | } |
| 2623 | sctp_write_unlock(&asoc->base.addr_lock); | ||
| 2624 | sctp_local_bh_enable(); | ||
| 2625 | break; | 2623 | break; |
| 2626 | case SCTP_PARAM_DEL_IP: | 2624 | case SCTP_PARAM_DEL_IP: |
| 2627 | sctp_local_bh_disable(); | 2625 | retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh); |
| 2628 | sctp_write_lock(&asoc->base.addr_lock); | ||
| 2629 | retval = sctp_del_bind_addr(bp, &addr); | ||
| 2630 | sctp_write_unlock(&asoc->base.addr_lock); | ||
| 2631 | sctp_local_bh_enable(); | ||
| 2632 | list_for_each(pos, &asoc->peer.transport_addr_list) { | 2626 | list_for_each(pos, &asoc->peer.transport_addr_list) { |
| 2633 | transport = list_entry(pos, struct sctp_transport, | 2627 | transport = list_entry(pos, struct sctp_transport, |
| 2634 | transports); | 2628 | transports); |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a3acf78d06ba..772fbfb4bfda 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
| @@ -367,14 +367,10 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) | |||
| 367 | if (!bp->port) | 367 | if (!bp->port) |
| 368 | bp->port = inet_sk(sk)->num; | 368 | bp->port = inet_sk(sk)->num; |
| 369 | 369 | ||
| 370 | /* Add the address to the bind address list. */ | 370 | /* Add the address to the bind address list. |
| 371 | sctp_local_bh_disable(); | 371 | * Use GFP_ATOMIC since BHs will be disabled. |
| 372 | sctp_write_lock(&ep->base.addr_lock); | 372 | */ |
| 373 | |||
| 374 | /* Use GFP_ATOMIC since BHs are disabled. */ | ||
| 375 | ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC); | 373 | ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC); |
| 376 | sctp_write_unlock(&ep->base.addr_lock); | ||
| 377 | sctp_local_bh_enable(); | ||
| 378 | 374 | ||
| 379 | /* Copy back into socket for getsockname() use. */ | 375 | /* Copy back into socket for getsockname() use. */ |
| 380 | if (!ret) { | 376 | if (!ret) { |
| @@ -544,15 +540,12 @@ static int sctp_send_asconf_add_ip(struct sock *sk, | |||
| 544 | if (i < addrcnt) | 540 | if (i < addrcnt) |
| 545 | continue; | 541 | continue; |
| 546 | 542 | ||
| 547 | /* Use the first address in bind addr list of association as | 543 | /* Use the first valid address in bind addr list of |
| 548 | * Address Parameter of ASCONF CHUNK. | 544 | * association as Address Parameter of ASCONF CHUNK. |
| 549 | */ | 545 | */ |
| 550 | sctp_read_lock(&asoc->base.addr_lock); | ||
| 551 | bp = &asoc->base.bind_addr; | 546 | bp = &asoc->base.bind_addr; |
| 552 | p = bp->address_list.next; | 547 | p = bp->address_list.next; |
| 553 | laddr = list_entry(p, struct sctp_sockaddr_entry, list); | 548 | laddr = list_entry(p, struct sctp_sockaddr_entry, list); |
| 554 | sctp_read_unlock(&asoc->base.addr_lock); | ||
| 555 | |||
| 556 | chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs, | 549 | chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs, |
| 557 | addrcnt, SCTP_PARAM_ADD_IP); | 550 | addrcnt, SCTP_PARAM_ADD_IP); |
| 558 | if (!chunk) { | 551 | if (!chunk) { |
| @@ -567,8 +560,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk, | |||
| 567 | /* Add the new addresses to the bind address list with | 560 | /* Add the new addresses to the bind address list with |
| 568 | * use_as_src set to 0. | 561 | * use_as_src set to 0. |
| 569 | */ | 562 | */ |
| 570 | sctp_local_bh_disable(); | ||
| 571 | sctp_write_lock(&asoc->base.addr_lock); | ||
| 572 | addr_buf = addrs; | 563 | addr_buf = addrs; |
| 573 | for (i = 0; i < addrcnt; i++) { | 564 | for (i = 0; i < addrcnt; i++) { |
| 574 | addr = (union sctp_addr *)addr_buf; | 565 | addr = (union sctp_addr *)addr_buf; |
| @@ -578,8 +569,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk, | |||
| 578 | GFP_ATOMIC); | 569 | GFP_ATOMIC); |
| 579 | addr_buf += af->sockaddr_len; | 570 | addr_buf += af->sockaddr_len; |
| 580 | } | 571 | } |
| 581 | sctp_write_unlock(&asoc->base.addr_lock); | ||
| 582 | sctp_local_bh_enable(); | ||
| 583 | } | 572 | } |
| 584 | 573 | ||
| 585 | out: | 574 | out: |
| @@ -651,13 +640,7 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt) | |||
| 651 | * socket routing and failover schemes. Refer to comments in | 640 | * socket routing and failover schemes. Refer to comments in |
| 652 | * sctp_do_bind(). -daisy | 641 | * sctp_do_bind(). -daisy |
| 653 | */ | 642 | */ |
| 654 | sctp_local_bh_disable(); | 643 | retval = sctp_del_bind_addr(bp, sa_addr, call_rcu); |
| 655 | sctp_write_lock(&ep->base.addr_lock); | ||
| 656 | |||
| 657 | retval = sctp_del_bind_addr(bp, sa_addr); | ||
| 658 | |||
| 659 | sctp_write_unlock(&ep->base.addr_lock); | ||
| 660 | sctp_local_bh_enable(); | ||
| 661 | 644 | ||
| 662 | addr_buf += af->sockaddr_len; | 645 | addr_buf += af->sockaddr_len; |
| 663 | err_bindx_rem: | 646 | err_bindx_rem: |
| @@ -748,14 +731,16 @@ static int sctp_send_asconf_del_ip(struct sock *sk, | |||
| 748 | * make sure that we do not delete all the addresses in the | 731 | * make sure that we do not delete all the addresses in the |
| 749 | * association. | 732 | * association. |
| 750 | */ | 733 | */ |
| 751 | sctp_read_lock(&asoc->base.addr_lock); | ||
| 752 | bp = &asoc->base.bind_addr; | 734 | bp = &asoc->base.bind_addr; |
| 753 | laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, | 735 | laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, |
| 754 | addrcnt, sp); | 736 | addrcnt, sp); |
| 755 | sctp_read_unlock(&asoc->base.addr_lock); | ||
| 756 | if (!laddr) | 737 | if (!laddr) |
| 757 | continue; | 738 | continue; |
| 758 | 739 | ||
| 740 | /* We do not need RCU protection throughout this loop | ||
| 741 | * because this is done under a socket lock from the | ||
| 742 | * setsockopt call. | ||
| 743 | */ | ||
| 759 | chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, | 744 | chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, |
| 760 | SCTP_PARAM_DEL_IP); | 745 | SCTP_PARAM_DEL_IP); |
| 761 | if (!chunk) { | 746 | if (!chunk) { |
| @@ -766,23 +751,16 @@ static int sctp_send_asconf_del_ip(struct sock *sk, | |||
| 766 | /* Reset use_as_src flag for the addresses in the bind address | 751 | /* Reset use_as_src flag for the addresses in the bind address |
| 767 | * list that are to be deleted. | 752 | * list that are to be deleted. |
| 768 | */ | 753 | */ |
| 769 | sctp_local_bh_disable(); | ||
| 770 | sctp_write_lock(&asoc->base.addr_lock); | ||
| 771 | addr_buf = addrs; | 754 | addr_buf = addrs; |
| 772 | for (i = 0; i < addrcnt; i++) { | 755 | for (i = 0; i < addrcnt; i++) { |
| 773 | laddr = (union sctp_addr *)addr_buf; | 756 | laddr = (union sctp_addr *)addr_buf; |
| 774 | af = sctp_get_af_specific(laddr->v4.sin_family); | 757 | af = sctp_get_af_specific(laddr->v4.sin_family); |
| 775 | list_for_each(pos1, &bp->address_list) { | 758 | list_for_each_entry(saddr, &bp->address_list, list) { |
| 776 | saddr = list_entry(pos1, | ||
| 777 | struct sctp_sockaddr_entry, | ||
| 778 | list); | ||
| 779 | if (sctp_cmp_addr_exact(&saddr->a, laddr)) | 759 | if (sctp_cmp_addr_exact(&saddr->a, laddr)) |
| 780 | saddr->use_as_src = 0; | 760 | saddr->use_as_src = 0; |
| 781 | } | 761 | } |
| 782 | addr_buf += af->sockaddr_len; | 762 | addr_buf += af->sockaddr_len; |
| 783 | } | 763 | } |
| 784 | sctp_write_unlock(&asoc->base.addr_lock); | ||
| 785 | sctp_local_bh_enable(); | ||
| 786 | 764 | ||
| 787 | /* Update the route and saddr entries for all the transports | 765 | /* Update the route and saddr entries for all the transports |
| 788 | * as some of the addresses in the bind address list are | 766 | * as some of the addresses in the bind address list are |
| @@ -4057,11 +4035,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 4057 | int __user *optlen) | 4035 | int __user *optlen) |
| 4058 | { | 4036 | { |
| 4059 | sctp_assoc_t id; | 4037 | sctp_assoc_t id; |
| 4060 | struct list_head *pos; | ||
| 4061 | struct sctp_bind_addr *bp; | 4038 | struct sctp_bind_addr *bp; |
| 4062 | struct sctp_association *asoc; | 4039 | struct sctp_association *asoc; |
| 4063 | struct sctp_sockaddr_entry *addr; | 4040 | struct sctp_sockaddr_entry *addr; |
| 4064 | rwlock_t *addr_lock; | ||
| 4065 | int cnt = 0; | 4041 | int cnt = 0; |
| 4066 | 4042 | ||
| 4067 | if (len < sizeof(sctp_assoc_t)) | 4043 | if (len < sizeof(sctp_assoc_t)) |
| @@ -4078,17 +4054,13 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 4078 | */ | 4054 | */ |
| 4079 | if (0 == id) { | 4055 | if (0 == id) { |
| 4080 | bp = &sctp_sk(sk)->ep->base.bind_addr; | 4056 | bp = &sctp_sk(sk)->ep->base.bind_addr; |
| 4081 | addr_lock = &sctp_sk(sk)->ep->base.addr_lock; | ||
| 4082 | } else { | 4057 | } else { |
| 4083 | asoc = sctp_id2assoc(sk, id); | 4058 | asoc = sctp_id2assoc(sk, id); |
| 4084 | if (!asoc) | 4059 | if (!asoc) |
| 4085 | return -EINVAL; | 4060 | return -EINVAL; |
| 4086 | bp = &asoc->base.bind_addr; | 4061 | bp = &asoc->base.bind_addr; |
| 4087 | addr_lock = &asoc->base.addr_lock; | ||
| 4088 | } | 4062 | } |
| 4089 | 4063 | ||
| 4090 | sctp_read_lock(addr_lock); | ||
| 4091 | |||
| 4092 | /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid | 4064 | /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid |
| 4093 | * addresses from the global local address list. | 4065 | * addresses from the global local address list. |
| 4094 | */ | 4066 | */ |
| @@ -4115,12 +4087,14 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 4115 | goto done; | 4087 | goto done; |
| 4116 | } | 4088 | } |
| 4117 | 4089 | ||
| 4118 | list_for_each(pos, &bp->address_list) { | 4090 | /* Protection on the bound address list is not needed, |
| 4091 | * since in the socket option context we hold the socket lock, | ||
| 4092 | * so there is no way that the bound address list can change. | ||
| 4093 | */ | ||
| 4094 | list_for_each_entry(addr, &bp->address_list, list) { | ||
| 4119 | cnt ++; | 4095 | cnt ++; |
| 4120 | } | 4096 | } |
| 4121 | |||
| 4122 | done: | 4097 | done: |
| 4123 | sctp_read_unlock(addr_lock); | ||
| 4124 | return cnt; | 4098 | return cnt; |
| 4125 | } | 4099 | } |
| 4126 | 4100 | ||
| @@ -4204,7 +4178,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4204 | { | 4178 | { |
| 4205 | struct sctp_bind_addr *bp; | 4179 | struct sctp_bind_addr *bp; |
| 4206 | struct sctp_association *asoc; | 4180 | struct sctp_association *asoc; |
| 4207 | struct list_head *pos; | ||
| 4208 | int cnt = 0; | 4181 | int cnt = 0; |
| 4209 | struct sctp_getaddrs_old getaddrs; | 4182 | struct sctp_getaddrs_old getaddrs; |
| 4210 | struct sctp_sockaddr_entry *addr; | 4183 | struct sctp_sockaddr_entry *addr; |
| @@ -4212,7 +4185,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4212 | union sctp_addr temp; | 4185 | union sctp_addr temp; |
| 4213 | struct sctp_sock *sp = sctp_sk(sk); | 4186 | struct sctp_sock *sp = sctp_sk(sk); |
| 4214 | int addrlen; | 4187 | int addrlen; |
| 4215 | rwlock_t *addr_lock; | ||
| 4216 | int err = 0; | 4188 | int err = 0; |
| 4217 | void *addrs; | 4189 | void *addrs; |
| 4218 | void *buf; | 4190 | void *buf; |
| @@ -4234,13 +4206,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4234 | */ | 4206 | */ |
| 4235 | if (0 == getaddrs.assoc_id) { | 4207 | if (0 == getaddrs.assoc_id) { |
| 4236 | bp = &sctp_sk(sk)->ep->base.bind_addr; | 4208 | bp = &sctp_sk(sk)->ep->base.bind_addr; |
| 4237 | addr_lock = &sctp_sk(sk)->ep->base.addr_lock; | ||
| 4238 | } else { | 4209 | } else { |
| 4239 | asoc = sctp_id2assoc(sk, getaddrs.assoc_id); | 4210 | asoc = sctp_id2assoc(sk, getaddrs.assoc_id); |
| 4240 | if (!asoc) | 4211 | if (!asoc) |
| 4241 | return -EINVAL; | 4212 | return -EINVAL; |
| 4242 | bp = &asoc->base.bind_addr; | 4213 | bp = &asoc->base.bind_addr; |
| 4243 | addr_lock = &asoc->base.addr_lock; | ||
| 4244 | } | 4214 | } |
| 4245 | 4215 | ||
| 4246 | to = getaddrs.addrs; | 4216 | to = getaddrs.addrs; |
| @@ -4254,8 +4224,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4254 | if (!addrs) | 4224 | if (!addrs) |
| 4255 | return -ENOMEM; | 4225 | return -ENOMEM; |
| 4256 | 4226 | ||
| 4257 | sctp_read_lock(addr_lock); | ||
| 4258 | |||
| 4259 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid | 4227 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid |
| 4260 | * addresses from the global local address list. | 4228 | * addresses from the global local address list. |
| 4261 | */ | 4229 | */ |
| @@ -4271,8 +4239,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4271 | } | 4239 | } |
| 4272 | 4240 | ||
| 4273 | buf = addrs; | 4241 | buf = addrs; |
| 4274 | list_for_each(pos, &bp->address_list) { | 4242 | /* Protection on the bound address list is not needed since |
| 4275 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 4243 | * in the socket option context we hold a socket lock and |
| 4244 | * thus the bound address list can't change. | ||
| 4245 | */ | ||
| 4246 | list_for_each_entry(addr, &bp->address_list, list) { | ||
| 4276 | memcpy(&temp, &addr->a, sizeof(temp)); | 4247 | memcpy(&temp, &addr->a, sizeof(temp)); |
| 4277 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); | 4248 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); |
| 4278 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4249 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
| @@ -4284,8 +4255,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
| 4284 | } | 4255 | } |
| 4285 | 4256 | ||
| 4286 | copy_getaddrs: | 4257 | copy_getaddrs: |
| 4287 | sctp_read_unlock(addr_lock); | ||
| 4288 | |||
| 4289 | /* copy the entire address list into the user provided space */ | 4258 | /* copy the entire address list into the user provided space */ |
| 4290 | if (copy_to_user(to, addrs, bytes_copied)) { | 4259 | if (copy_to_user(to, addrs, bytes_copied)) { |
| 4291 | err = -EFAULT; | 4260 | err = -EFAULT; |
| @@ -4307,7 +4276,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4307 | { | 4276 | { |
| 4308 | struct sctp_bind_addr *bp; | 4277 | struct sctp_bind_addr *bp; |
| 4309 | struct sctp_association *asoc; | 4278 | struct sctp_association *asoc; |
| 4310 | struct list_head *pos; | ||
| 4311 | int cnt = 0; | 4279 | int cnt = 0; |
| 4312 | struct sctp_getaddrs getaddrs; | 4280 | struct sctp_getaddrs getaddrs; |
| 4313 | struct sctp_sockaddr_entry *addr; | 4281 | struct sctp_sockaddr_entry *addr; |
| @@ -4315,7 +4283,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4315 | union sctp_addr temp; | 4283 | union sctp_addr temp; |
| 4316 | struct sctp_sock *sp = sctp_sk(sk); | 4284 | struct sctp_sock *sp = sctp_sk(sk); |
| 4317 | int addrlen; | 4285 | int addrlen; |
| 4318 | rwlock_t *addr_lock; | ||
| 4319 | int err = 0; | 4286 | int err = 0; |
| 4320 | size_t space_left; | 4287 | size_t space_left; |
| 4321 | int bytes_copied = 0; | 4288 | int bytes_copied = 0; |
| @@ -4336,13 +4303,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4336 | */ | 4303 | */ |
| 4337 | if (0 == getaddrs.assoc_id) { | 4304 | if (0 == getaddrs.assoc_id) { |
| 4338 | bp = &sctp_sk(sk)->ep->base.bind_addr; | 4305 | bp = &sctp_sk(sk)->ep->base.bind_addr; |
| 4339 | addr_lock = &sctp_sk(sk)->ep->base.addr_lock; | ||
| 4340 | } else { | 4306 | } else { |
| 4341 | asoc = sctp_id2assoc(sk, getaddrs.assoc_id); | 4307 | asoc = sctp_id2assoc(sk, getaddrs.assoc_id); |
| 4342 | if (!asoc) | 4308 | if (!asoc) |
| 4343 | return -EINVAL; | 4309 | return -EINVAL; |
| 4344 | bp = &asoc->base.bind_addr; | 4310 | bp = &asoc->base.bind_addr; |
| 4345 | addr_lock = &asoc->base.addr_lock; | ||
| 4346 | } | 4311 | } |
| 4347 | 4312 | ||
| 4348 | to = optval + offsetof(struct sctp_getaddrs,addrs); | 4313 | to = optval + offsetof(struct sctp_getaddrs,addrs); |
| @@ -4352,8 +4317,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4352 | if (!addrs) | 4317 | if (!addrs) |
| 4353 | return -ENOMEM; | 4318 | return -ENOMEM; |
| 4354 | 4319 | ||
| 4355 | sctp_read_lock(addr_lock); | ||
| 4356 | |||
| 4357 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid | 4320 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid |
| 4358 | * addresses from the global local address list. | 4321 | * addresses from the global local address list. |
| 4359 | */ | 4322 | */ |
| @@ -4365,21 +4328,24 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4365 | space_left, &bytes_copied); | 4328 | space_left, &bytes_copied); |
| 4366 | if (cnt < 0) { | 4329 | if (cnt < 0) { |
| 4367 | err = cnt; | 4330 | err = cnt; |
| 4368 | goto error_lock; | 4331 | goto out; |
| 4369 | } | 4332 | } |
| 4370 | goto copy_getaddrs; | 4333 | goto copy_getaddrs; |
| 4371 | } | 4334 | } |
| 4372 | } | 4335 | } |
| 4373 | 4336 | ||
| 4374 | buf = addrs; | 4337 | buf = addrs; |
| 4375 | list_for_each(pos, &bp->address_list) { | 4338 | /* Protection on the bound address list is not needed since |
| 4376 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 4339 | * in the socket option context we hold a socket lock and |
| 4340 | * thus the bound address list can't change. | ||
| 4341 | */ | ||
| 4342 | list_for_each_entry(addr, &bp->address_list, list) { | ||
| 4377 | memcpy(&temp, &addr->a, sizeof(temp)); | 4343 | memcpy(&temp, &addr->a, sizeof(temp)); |
| 4378 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); | 4344 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); |
| 4379 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4345 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
| 4380 | if (space_left < addrlen) { | 4346 | if (space_left < addrlen) { |
| 4381 | err = -ENOMEM; /*fixme: right error?*/ | 4347 | err = -ENOMEM; /*fixme: right error?*/ |
| 4382 | goto error_lock; | 4348 | goto out; |
| 4383 | } | 4349 | } |
| 4384 | memcpy(buf, &temp, addrlen); | 4350 | memcpy(buf, &temp, addrlen); |
| 4385 | buf += addrlen; | 4351 | buf += addrlen; |
| @@ -4389,8 +4355,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
| 4389 | } | 4355 | } |
| 4390 | 4356 | ||
| 4391 | copy_getaddrs: | 4357 | copy_getaddrs: |
| 4392 | sctp_read_unlock(addr_lock); | ||
| 4393 | |||
| 4394 | if (copy_to_user(to, addrs, bytes_copied)) { | 4358 | if (copy_to_user(to, addrs, bytes_copied)) { |
| 4395 | err = -EFAULT; | 4359 | err = -EFAULT; |
| 4396 | goto out; | 4360 | goto out; |
| @@ -4401,12 +4365,6 @@ copy_getaddrs: | |||
| 4401 | } | 4365 | } |
| 4402 | if (put_user(bytes_copied, optlen)) | 4366 | if (put_user(bytes_copied, optlen)) |
| 4403 | err = -EFAULT; | 4367 | err = -EFAULT; |
| 4404 | |||
| 4405 | goto out; | ||
| 4406 | |||
| 4407 | error_lock: | ||
| 4408 | sctp_read_unlock(addr_lock); | ||
| 4409 | |||
| 4410 | out: | 4368 | out: |
| 4411 | kfree(addrs); | 4369 | kfree(addrs); |
| 4412 | return err; | 4370 | return err; |
