diff options
Diffstat (limited to 'net/sctp/input.c')
| -rw-r--r-- | net/sctp/input.c | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/net/sctp/input.c b/net/sctp/input.c index 4aa6fc60357c..cb78b50868ee 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c | |||
| @@ -257,20 +257,26 @@ int sctp_rcv(struct sk_buff *skb) | |||
| 257 | */ | 257 | */ |
| 258 | sctp_bh_lock_sock(sk); | 258 | sctp_bh_lock_sock(sk); |
| 259 | 259 | ||
| 260 | /* It is possible that the association could have moved to a different | ||
| 261 | * socket if it is peeled off. If so, update the sk. | ||
| 262 | */ | ||
| 263 | if (sk != rcvr->sk) { | ||
| 264 | sctp_bh_lock_sock(rcvr->sk); | ||
| 265 | sctp_bh_unlock_sock(sk); | ||
| 266 | sk = rcvr->sk; | ||
| 267 | } | ||
| 268 | |||
| 260 | if (sock_owned_by_user(sk)) | 269 | if (sock_owned_by_user(sk)) |
| 261 | sk_add_backlog(sk, skb); | 270 | sk_add_backlog(sk, skb); |
| 262 | else | 271 | else |
| 263 | sctp_backlog_rcv(sk, skb); | 272 | sctp_backlog_rcv(sk, skb); |
| 264 | 273 | ||
| 265 | /* Release the sock and any reference counts we took in the | 274 | /* Release the sock and the sock ref we took in the lookup calls. |
| 266 | * lookup calls. | 275 | * The asoc/ep ref will be released in sctp_backlog_rcv. |
| 267 | */ | 276 | */ |
| 268 | sctp_bh_unlock_sock(sk); | 277 | sctp_bh_unlock_sock(sk); |
| 269 | if (asoc) | ||
| 270 | sctp_association_put(asoc); | ||
| 271 | else | ||
| 272 | sctp_endpoint_put(ep); | ||
| 273 | sock_put(sk); | 278 | sock_put(sk); |
| 279 | |||
| 274 | return ret; | 280 | return ret; |
| 275 | 281 | ||
| 276 | discard_it: | 282 | discard_it: |
| @@ -296,12 +302,50 @@ discard_release: | |||
| 296 | int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) | 302 | int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) |
| 297 | { | 303 | { |
| 298 | struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; | 304 | struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; |
| 299 | struct sctp_inq *inqueue = &chunk->rcvr->inqueue; | 305 | struct sctp_inq *inqueue = NULL; |
| 300 | 306 | struct sctp_ep_common *rcvr = NULL; | |
| 301 | sctp_inq_push(inqueue, chunk); | 307 | |
| 308 | rcvr = chunk->rcvr; | ||
| 309 | |||
| 310 | BUG_TRAP(rcvr->sk == sk); | ||
| 311 | |||
| 312 | if (rcvr->dead) { | ||
| 313 | sctp_chunk_free(chunk); | ||
| 314 | } else { | ||
| 315 | inqueue = &chunk->rcvr->inqueue; | ||
| 316 | sctp_inq_push(inqueue, chunk); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* Release the asoc/ep ref we took in the lookup calls in sctp_rcv. */ | ||
| 320 | if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) | ||
| 321 | sctp_association_put(sctp_assoc(rcvr)); | ||
| 322 | else | ||
| 323 | sctp_endpoint_put(sctp_ep(rcvr)); | ||
| 324 | |||
| 302 | return 0; | 325 | return 0; |
| 303 | } | 326 | } |
| 304 | 327 | ||
| 328 | void sctp_backlog_migrate(struct sctp_association *assoc, | ||
| 329 | struct sock *oldsk, struct sock *newsk) | ||
| 330 | { | ||
| 331 | struct sk_buff *skb; | ||
| 332 | struct sctp_chunk *chunk; | ||
| 333 | |||
| 334 | skb = oldsk->sk_backlog.head; | ||
| 335 | oldsk->sk_backlog.head = oldsk->sk_backlog.tail = NULL; | ||
| 336 | while (skb != NULL) { | ||
| 337 | struct sk_buff *next = skb->next; | ||
| 338 | |||
| 339 | chunk = SCTP_INPUT_CB(skb)->chunk; | ||
| 340 | skb->next = NULL; | ||
| 341 | if (&assoc->base == chunk->rcvr) | ||
| 342 | sk_add_backlog(newsk, skb); | ||
| 343 | else | ||
| 344 | sk_add_backlog(oldsk, skb); | ||
| 345 | skb = next; | ||
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 305 | /* Handle icmp frag needed error. */ | 349 | /* Handle icmp frag needed error. */ |
| 306 | void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, | 350 | void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, |
| 307 | struct sctp_transport *t, __u32 pmtu) | 351 | struct sctp_transport *t, __u32 pmtu) |
| @@ -544,10 +588,16 @@ int sctp_rcv_ootb(struct sk_buff *skb) | |||
| 544 | sctp_errhdr_t *err; | 588 | sctp_errhdr_t *err; |
| 545 | 589 | ||
| 546 | ch = (sctp_chunkhdr_t *) skb->data; | 590 | ch = (sctp_chunkhdr_t *) skb->data; |
| 547 | ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); | ||
| 548 | 591 | ||
| 549 | /* Scan through all the chunks in the packet. */ | 592 | /* Scan through all the chunks in the packet. */ |
| 550 | while (ch_end > (__u8 *)ch && ch_end < skb->tail) { | 593 | do { |
| 594 | /* Break out if chunk length is less then minimal. */ | ||
| 595 | if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t)) | ||
| 596 | break; | ||
| 597 | |||
| 598 | ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); | ||
| 599 | if (ch_end > skb->tail) | ||
| 600 | break; | ||
| 551 | 601 | ||
| 552 | /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the | 602 | /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the |
| 553 | * receiver MUST silently discard the OOTB packet and take no | 603 | * receiver MUST silently discard the OOTB packet and take no |
| @@ -578,8 +628,7 @@ int sctp_rcv_ootb(struct sk_buff *skb) | |||
| 578 | } | 628 | } |
| 579 | 629 | ||
| 580 | ch = (sctp_chunkhdr_t *) ch_end; | 630 | ch = (sctp_chunkhdr_t *) ch_end; |
| 581 | ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); | 631 | } while (ch_end < skb->tail); |
| 582 | } | ||
| 583 | 632 | ||
| 584 | return 0; | 633 | return 0; |
| 585 | 634 | ||
