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 | ||