162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* SCTP kernel implementation
362306a36Sopenharmony_ci * (C) Copyright Red Hat Inc. 2017
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file is part of the SCTP kernel implementation
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * These functions implement sctp stream message interleaving, mostly
862306a36Sopenharmony_ci * including I-DATA and I-FORWARD-TSN chunks process.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Please send any bug reports or fixes you make to the
1162306a36Sopenharmony_ci * email addresched(es):
1262306a36Sopenharmony_ci *    lksctp developers <linux-sctp@vger.kernel.org>
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Written or modified by:
1562306a36Sopenharmony_ci *    Xin Long <lucien.xin@gmail.com>
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <net/busy_poll.h>
1962306a36Sopenharmony_ci#include <net/sctp/sctp.h>
2062306a36Sopenharmony_ci#include <net/sctp/sm.h>
2162306a36Sopenharmony_ci#include <net/sctp/ulpevent.h>
2262306a36Sopenharmony_ci#include <linux/sctp.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct sctp_chunk *sctp_make_idatafrag_empty(
2562306a36Sopenharmony_ci					const struct sctp_association *asoc,
2662306a36Sopenharmony_ci					const struct sctp_sndrcvinfo *sinfo,
2762306a36Sopenharmony_ci					int len, __u8 flags, gfp_t gfp)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct sctp_chunk *retval;
3062306a36Sopenharmony_ci	struct sctp_idatahdr dp;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	memset(&dp, 0, sizeof(dp));
3362306a36Sopenharmony_ci	dp.stream = htons(sinfo->sinfo_stream);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	if (sinfo->sinfo_flags & SCTP_UNORDERED)
3662306a36Sopenharmony_ci		flags |= SCTP_DATA_UNORDERED;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	retval = sctp_make_idata(asoc, flags, sizeof(dp) + len, gfp);
3962306a36Sopenharmony_ci	if (!retval)
4062306a36Sopenharmony_ci		return NULL;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	retval->subh.idata_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
4362306a36Sopenharmony_ci	memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return retval;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void sctp_chunk_assign_mid(struct sctp_chunk *chunk)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct sctp_stream *stream;
5162306a36Sopenharmony_ci	struct sctp_chunk *lchunk;
5262306a36Sopenharmony_ci	__u32 cfsn = 0;
5362306a36Sopenharmony_ci	__u16 sid;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (chunk->has_mid)
5662306a36Sopenharmony_ci		return;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	sid = sctp_chunk_stream_no(chunk);
5962306a36Sopenharmony_ci	stream = &chunk->asoc->stream;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	list_for_each_entry(lchunk, &chunk->msg->chunks, frag_list) {
6262306a36Sopenharmony_ci		struct sctp_idatahdr *hdr;
6362306a36Sopenharmony_ci		__u32 mid;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		lchunk->has_mid = 1;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci		hdr = lchunk->subh.idata_hdr;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		if (lchunk->chunk_hdr->flags & SCTP_DATA_FIRST_FRAG)
7062306a36Sopenharmony_ci			hdr->ppid = lchunk->sinfo.sinfo_ppid;
7162306a36Sopenharmony_ci		else
7262306a36Sopenharmony_ci			hdr->fsn = htonl(cfsn++);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci		if (lchunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
7562306a36Sopenharmony_ci			mid = lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG ?
7662306a36Sopenharmony_ci				sctp_mid_uo_next(stream, out, sid) :
7762306a36Sopenharmony_ci				sctp_mid_uo_peek(stream, out, sid);
7862306a36Sopenharmony_ci		} else {
7962306a36Sopenharmony_ci			mid = lchunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG ?
8062306a36Sopenharmony_ci				sctp_mid_next(stream, out, sid) :
8162306a36Sopenharmony_ci				sctp_mid_peek(stream, out, sid);
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci		hdr->mid = htonl(mid);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic bool sctp_validate_data(struct sctp_chunk *chunk)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct sctp_stream *stream;
9062306a36Sopenharmony_ci	__u16 sid, ssn;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (chunk->chunk_hdr->type != SCTP_CID_DATA)
9362306a36Sopenharmony_ci		return false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
9662306a36Sopenharmony_ci		return true;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	stream = &chunk->asoc->stream;
9962306a36Sopenharmony_ci	sid = sctp_chunk_stream_no(chunk);
10062306a36Sopenharmony_ci	ssn = ntohs(chunk->subh.data_hdr->ssn);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return !SSN_lt(ssn, sctp_ssn_peek(stream, in, sid));
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic bool sctp_validate_idata(struct sctp_chunk *chunk)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct sctp_stream *stream;
10862306a36Sopenharmony_ci	__u32 mid;
10962306a36Sopenharmony_ci	__u16 sid;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (chunk->chunk_hdr->type != SCTP_CID_I_DATA)
11262306a36Sopenharmony_ci		return false;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
11562306a36Sopenharmony_ci		return true;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	stream = &chunk->asoc->stream;
11862306a36Sopenharmony_ci	sid = sctp_chunk_stream_no(chunk);
11962306a36Sopenharmony_ci	mid = ntohl(chunk->subh.idata_hdr->mid);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return !MID_lt(mid, sctp_mid_peek(stream, in, sid));
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void sctp_intl_store_reasm(struct sctp_ulpq *ulpq,
12562306a36Sopenharmony_ci				  struct sctp_ulpevent *event)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct sctp_ulpevent *cevent;
12862306a36Sopenharmony_ci	struct sk_buff *pos, *loc;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	pos = skb_peek_tail(&ulpq->reasm);
13162306a36Sopenharmony_ci	if (!pos) {
13262306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
13362306a36Sopenharmony_ci		return;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	cevent = sctp_skb2event(pos);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (event->stream == cevent->stream &&
13962306a36Sopenharmony_ci	    event->mid == cevent->mid &&
14062306a36Sopenharmony_ci	    (cevent->msg_flags & SCTP_DATA_FIRST_FRAG ||
14162306a36Sopenharmony_ci	     (!(event->msg_flags & SCTP_DATA_FIRST_FRAG) &&
14262306a36Sopenharmony_ci	      event->fsn > cevent->fsn))) {
14362306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
14462306a36Sopenharmony_ci		return;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if ((event->stream == cevent->stream &&
14862306a36Sopenharmony_ci	     MID_lt(cevent->mid, event->mid)) ||
14962306a36Sopenharmony_ci	    event->stream > cevent->stream) {
15062306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
15162306a36Sopenharmony_ci		return;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	loc = NULL;
15562306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm, pos) {
15662306a36Sopenharmony_ci		cevent = sctp_skb2event(pos);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		if (event->stream < cevent->stream ||
15962306a36Sopenharmony_ci		    (event->stream == cevent->stream &&
16062306a36Sopenharmony_ci		     MID_lt(event->mid, cevent->mid))) {
16162306a36Sopenharmony_ci			loc = pos;
16262306a36Sopenharmony_ci			break;
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		if (event->stream == cevent->stream &&
16562306a36Sopenharmony_ci		    event->mid == cevent->mid &&
16662306a36Sopenharmony_ci		    !(cevent->msg_flags & SCTP_DATA_FIRST_FRAG) &&
16762306a36Sopenharmony_ci		    (event->msg_flags & SCTP_DATA_FIRST_FRAG ||
16862306a36Sopenharmony_ci		     event->fsn < cevent->fsn)) {
16962306a36Sopenharmony_ci			loc = pos;
17062306a36Sopenharmony_ci			break;
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (!loc)
17562306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
17662306a36Sopenharmony_ci	else
17762306a36Sopenharmony_ci		__skb_queue_before(&ulpq->reasm, loc, sctp_event2skb(event));
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_partial(
18162306a36Sopenharmony_ci						struct sctp_ulpq *ulpq,
18262306a36Sopenharmony_ci						struct sctp_ulpevent *event)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct sk_buff *first_frag = NULL;
18562306a36Sopenharmony_ci	struct sk_buff *last_frag = NULL;
18662306a36Sopenharmony_ci	struct sctp_ulpevent *retval;
18762306a36Sopenharmony_ci	struct sctp_stream_in *sin;
18862306a36Sopenharmony_ci	struct sk_buff *pos;
18962306a36Sopenharmony_ci	__u32 next_fsn = 0;
19062306a36Sopenharmony_ci	int is_last = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm, pos) {
19562306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		if (cevent->stream < event->stream)
19862306a36Sopenharmony_ci			continue;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		if (cevent->stream > event->stream ||
20162306a36Sopenharmony_ci		    cevent->mid != sin->mid)
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
20562306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
20662306a36Sopenharmony_ci			goto out;
20762306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
20862306a36Sopenharmony_ci			if (!first_frag) {
20962306a36Sopenharmony_ci				if (cevent->fsn == sin->fsn) {
21062306a36Sopenharmony_ci					first_frag = pos;
21162306a36Sopenharmony_ci					last_frag = pos;
21262306a36Sopenharmony_ci					next_fsn = cevent->fsn + 1;
21362306a36Sopenharmony_ci				}
21462306a36Sopenharmony_ci			} else if (cevent->fsn == next_fsn) {
21562306a36Sopenharmony_ci				last_frag = pos;
21662306a36Sopenharmony_ci				next_fsn++;
21762306a36Sopenharmony_ci			} else {
21862306a36Sopenharmony_ci				goto out;
21962306a36Sopenharmony_ci			}
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
22262306a36Sopenharmony_ci			if (!first_frag) {
22362306a36Sopenharmony_ci				if (cevent->fsn == sin->fsn) {
22462306a36Sopenharmony_ci					first_frag = pos;
22562306a36Sopenharmony_ci					last_frag = pos;
22662306a36Sopenharmony_ci					next_fsn = 0;
22762306a36Sopenharmony_ci					is_last = 1;
22862306a36Sopenharmony_ci				}
22962306a36Sopenharmony_ci			} else if (cevent->fsn == next_fsn) {
23062306a36Sopenharmony_ci				last_frag = pos;
23162306a36Sopenharmony_ci				next_fsn = 0;
23262306a36Sopenharmony_ci				is_last = 1;
23362306a36Sopenharmony_ci			}
23462306a36Sopenharmony_ci			goto out;
23562306a36Sopenharmony_ci		default:
23662306a36Sopenharmony_ci			goto out;
23762306a36Sopenharmony_ci		}
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ciout:
24162306a36Sopenharmony_ci	if (!first_frag)
24262306a36Sopenharmony_ci		return NULL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(ulpq->asoc->base.net, &ulpq->reasm,
24562306a36Sopenharmony_ci					     first_frag, last_frag);
24662306a36Sopenharmony_ci	if (retval) {
24762306a36Sopenharmony_ci		sin->fsn = next_fsn;
24862306a36Sopenharmony_ci		if (is_last) {
24962306a36Sopenharmony_ci			retval->msg_flags |= MSG_EOR;
25062306a36Sopenharmony_ci			sin->pd_mode = 0;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return retval;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_reassembled(
25862306a36Sopenharmony_ci						struct sctp_ulpq *ulpq,
25962306a36Sopenharmony_ci						struct sctp_ulpevent *event)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct sctp_association *asoc = ulpq->asoc;
26262306a36Sopenharmony_ci	struct sk_buff *pos, *first_frag = NULL;
26362306a36Sopenharmony_ci	struct sctp_ulpevent *retval = NULL;
26462306a36Sopenharmony_ci	struct sk_buff *pd_first = NULL;
26562306a36Sopenharmony_ci	struct sk_buff *pd_last = NULL;
26662306a36Sopenharmony_ci	struct sctp_stream_in *sin;
26762306a36Sopenharmony_ci	__u32 next_fsn = 0;
26862306a36Sopenharmony_ci	__u32 pd_point = 0;
26962306a36Sopenharmony_ci	__u32 pd_len = 0;
27062306a36Sopenharmony_ci	__u32 mid = 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm, pos) {
27562306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		if (cevent->stream < event->stream)
27862306a36Sopenharmony_ci			continue;
27962306a36Sopenharmony_ci		if (cevent->stream > event->stream)
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (MID_lt(cevent->mid, event->mid))
28362306a36Sopenharmony_ci			continue;
28462306a36Sopenharmony_ci		if (MID_lt(event->mid, cevent->mid))
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
28862306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
28962306a36Sopenharmony_ci			if (cevent->mid == sin->mid) {
29062306a36Sopenharmony_ci				pd_first = pos;
29162306a36Sopenharmony_ci				pd_last = pos;
29262306a36Sopenharmony_ci				pd_len = pos->len;
29362306a36Sopenharmony_ci			}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci			first_frag = pos;
29662306a36Sopenharmony_ci			next_fsn = 0;
29762306a36Sopenharmony_ci			mid = cevent->mid;
29862306a36Sopenharmony_ci			break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
30162306a36Sopenharmony_ci			if (first_frag && cevent->mid == mid &&
30262306a36Sopenharmony_ci			    cevent->fsn == next_fsn) {
30362306a36Sopenharmony_ci				next_fsn++;
30462306a36Sopenharmony_ci				if (pd_first) {
30562306a36Sopenharmony_ci					pd_last = pos;
30662306a36Sopenharmony_ci					pd_len += pos->len;
30762306a36Sopenharmony_ci				}
30862306a36Sopenharmony_ci			} else {
30962306a36Sopenharmony_ci				first_frag = NULL;
31062306a36Sopenharmony_ci			}
31162306a36Sopenharmony_ci			break;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
31462306a36Sopenharmony_ci			if (first_frag && cevent->mid == mid &&
31562306a36Sopenharmony_ci			    cevent->fsn == next_fsn)
31662306a36Sopenharmony_ci				goto found;
31762306a36Sopenharmony_ci			else
31862306a36Sopenharmony_ci				first_frag = NULL;
31962306a36Sopenharmony_ci			break;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (!pd_first)
32462306a36Sopenharmony_ci		goto out;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	pd_point = sctp_sk(asoc->base.sk)->pd_point;
32762306a36Sopenharmony_ci	if (pd_point && pd_point <= pd_len) {
32862306a36Sopenharmony_ci		retval = sctp_make_reassembled_event(asoc->base.net,
32962306a36Sopenharmony_ci						     &ulpq->reasm,
33062306a36Sopenharmony_ci						     pd_first, pd_last);
33162306a36Sopenharmony_ci		if (retval) {
33262306a36Sopenharmony_ci			sin->fsn = next_fsn;
33362306a36Sopenharmony_ci			sin->pd_mode = 1;
33462306a36Sopenharmony_ci		}
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci	goto out;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cifound:
33962306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(asoc->base.net, &ulpq->reasm,
34062306a36Sopenharmony_ci					     first_frag, pos);
34162306a36Sopenharmony_ci	if (retval)
34262306a36Sopenharmony_ci		retval->msg_flags |= MSG_EOR;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciout:
34562306a36Sopenharmony_ci	return retval;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_reasm(struct sctp_ulpq *ulpq,
34962306a36Sopenharmony_ci					     struct sctp_ulpevent *event)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct sctp_ulpevent *retval = NULL;
35262306a36Sopenharmony_ci	struct sctp_stream_in *sin;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) {
35562306a36Sopenharmony_ci		event->msg_flags |= MSG_EOR;
35662306a36Sopenharmony_ci		return event;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	sctp_intl_store_reasm(ulpq, event);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
36262306a36Sopenharmony_ci	if (sin->pd_mode && event->mid == sin->mid &&
36362306a36Sopenharmony_ci	    event->fsn == sin->fsn)
36462306a36Sopenharmony_ci		retval = sctp_intl_retrieve_partial(ulpq, event);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (!retval)
36762306a36Sopenharmony_ci		retval = sctp_intl_retrieve_reassembled(ulpq, event);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return retval;
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void sctp_intl_store_ordered(struct sctp_ulpq *ulpq,
37362306a36Sopenharmony_ci				    struct sctp_ulpevent *event)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct sctp_ulpevent *cevent;
37662306a36Sopenharmony_ci	struct sk_buff *pos, *loc;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	pos = skb_peek_tail(&ulpq->lobby);
37962306a36Sopenharmony_ci	if (!pos) {
38062306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
38162306a36Sopenharmony_ci		return;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	cevent = (struct sctp_ulpevent *)pos->cb;
38562306a36Sopenharmony_ci	if (event->stream == cevent->stream &&
38662306a36Sopenharmony_ci	    MID_lt(cevent->mid, event->mid)) {
38762306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
38862306a36Sopenharmony_ci		return;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (event->stream > cevent->stream) {
39262306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	loc = NULL;
39762306a36Sopenharmony_ci	skb_queue_walk(&ulpq->lobby, pos) {
39862306a36Sopenharmony_ci		cevent = (struct sctp_ulpevent *)pos->cb;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		if (cevent->stream > event->stream) {
40162306a36Sopenharmony_ci			loc = pos;
40262306a36Sopenharmony_ci			break;
40362306a36Sopenharmony_ci		}
40462306a36Sopenharmony_ci		if (cevent->stream == event->stream &&
40562306a36Sopenharmony_ci		    MID_lt(event->mid, cevent->mid)) {
40662306a36Sopenharmony_ci			loc = pos;
40762306a36Sopenharmony_ci			break;
40862306a36Sopenharmony_ci		}
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!loc)
41262306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
41362306a36Sopenharmony_ci	else
41462306a36Sopenharmony_ci		__skb_queue_before(&ulpq->lobby, loc, sctp_event2skb(event));
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void sctp_intl_retrieve_ordered(struct sctp_ulpq *ulpq,
41862306a36Sopenharmony_ci				       struct sctp_ulpevent *event)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct sk_buff_head *event_list;
42162306a36Sopenharmony_ci	struct sctp_stream *stream;
42262306a36Sopenharmony_ci	struct sk_buff *pos, *tmp;
42362306a36Sopenharmony_ci	__u16 sid = event->stream;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	stream  = &ulpq->asoc->stream;
42662306a36Sopenharmony_ci	event_list = (struct sk_buff_head *)sctp_event2skb(event)->prev;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
42962306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = (struct sctp_ulpevent *)pos->cb;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		if (cevent->stream > sid)
43262306a36Sopenharmony_ci			break;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		if (cevent->stream < sid)
43562306a36Sopenharmony_ci			continue;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (cevent->mid != sctp_mid_peek(stream, in, sid))
43862306a36Sopenharmony_ci			break;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		sctp_mid_next(stream, in, sid);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		__skb_unlink(pos, &ulpq->lobby);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		__skb_queue_tail(event_list, pos);
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_order(struct sctp_ulpq *ulpq,
44962306a36Sopenharmony_ci					     struct sctp_ulpevent *event)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct sctp_stream *stream;
45262306a36Sopenharmony_ci	__u16 sid;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	stream  = &ulpq->asoc->stream;
45562306a36Sopenharmony_ci	sid = event->stream;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (event->mid != sctp_mid_peek(stream, in, sid)) {
45862306a36Sopenharmony_ci		sctp_intl_store_ordered(ulpq, event);
45962306a36Sopenharmony_ci		return NULL;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	sctp_mid_next(stream, in, sid);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	sctp_intl_retrieve_ordered(ulpq, event);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return event;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int sctp_enqueue_event(struct sctp_ulpq *ulpq,
47062306a36Sopenharmony_ci			      struct sk_buff_head *skb_list)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct sock *sk = ulpq->asoc->base.sk;
47362306a36Sopenharmony_ci	struct sctp_sock *sp = sctp_sk(sk);
47462306a36Sopenharmony_ci	struct sctp_ulpevent *event;
47562306a36Sopenharmony_ci	struct sk_buff *skb;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	skb = __skb_peek(skb_list);
47862306a36Sopenharmony_ci	event = sctp_skb2event(skb);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (sk->sk_shutdown & RCV_SHUTDOWN &&
48162306a36Sopenharmony_ci	    (sk->sk_shutdown & SEND_SHUTDOWN ||
48262306a36Sopenharmony_ci	     !sctp_ulpevent_is_notification(event)))
48362306a36Sopenharmony_ci		goto out_free;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (!sctp_ulpevent_is_notification(event)) {
48662306a36Sopenharmony_ci		sk_mark_napi_id(sk, skb);
48762306a36Sopenharmony_ci		sk_incoming_cpu_update(sk);
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (!sctp_ulpevent_is_enabled(event, ulpq->asoc->subscribe))
49162306a36Sopenharmony_ci		goto out_free;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	skb_queue_splice_tail_init(skb_list,
49462306a36Sopenharmony_ci				   &sk->sk_receive_queue);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (!sp->data_ready_signalled) {
49762306a36Sopenharmony_ci		sp->data_ready_signalled = 1;
49862306a36Sopenharmony_ci		sk->sk_data_ready(sk);
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return 1;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciout_free:
50462306a36Sopenharmony_ci	sctp_queue_purge_ulpevents(skb_list);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic void sctp_intl_store_reasm_uo(struct sctp_ulpq *ulpq,
51062306a36Sopenharmony_ci				     struct sctp_ulpevent *event)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct sctp_ulpevent *cevent;
51362306a36Sopenharmony_ci	struct sk_buff *pos;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	pos = skb_peek_tail(&ulpq->reasm_uo);
51662306a36Sopenharmony_ci	if (!pos) {
51762306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm_uo, sctp_event2skb(event));
51862306a36Sopenharmony_ci		return;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	cevent = sctp_skb2event(pos);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (event->stream == cevent->stream &&
52462306a36Sopenharmony_ci	    event->mid == cevent->mid &&
52562306a36Sopenharmony_ci	    (cevent->msg_flags & SCTP_DATA_FIRST_FRAG ||
52662306a36Sopenharmony_ci	     (!(event->msg_flags & SCTP_DATA_FIRST_FRAG) &&
52762306a36Sopenharmony_ci	      event->fsn > cevent->fsn))) {
52862306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm_uo, sctp_event2skb(event));
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if ((event->stream == cevent->stream &&
53362306a36Sopenharmony_ci	     MID_lt(cevent->mid, event->mid)) ||
53462306a36Sopenharmony_ci	    event->stream > cevent->stream) {
53562306a36Sopenharmony_ci		__skb_queue_tail(&ulpq->reasm_uo, sctp_event2skb(event));
53662306a36Sopenharmony_ci		return;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm_uo, pos) {
54062306a36Sopenharmony_ci		cevent = sctp_skb2event(pos);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		if (event->stream < cevent->stream ||
54362306a36Sopenharmony_ci		    (event->stream == cevent->stream &&
54462306a36Sopenharmony_ci		     MID_lt(event->mid, cevent->mid)))
54562306a36Sopenharmony_ci			break;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci		if (event->stream == cevent->stream &&
54862306a36Sopenharmony_ci		    event->mid == cevent->mid &&
54962306a36Sopenharmony_ci		    !(cevent->msg_flags & SCTP_DATA_FIRST_FRAG) &&
55062306a36Sopenharmony_ci		    (event->msg_flags & SCTP_DATA_FIRST_FRAG ||
55162306a36Sopenharmony_ci		     event->fsn < cevent->fsn))
55262306a36Sopenharmony_ci			break;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	__skb_queue_before(&ulpq->reasm_uo, pos, sctp_event2skb(event));
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_partial_uo(
55962306a36Sopenharmony_ci						struct sctp_ulpq *ulpq,
56062306a36Sopenharmony_ci						struct sctp_ulpevent *event)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct sk_buff *first_frag = NULL;
56362306a36Sopenharmony_ci	struct sk_buff *last_frag = NULL;
56462306a36Sopenharmony_ci	struct sctp_ulpevent *retval;
56562306a36Sopenharmony_ci	struct sctp_stream_in *sin;
56662306a36Sopenharmony_ci	struct sk_buff *pos;
56762306a36Sopenharmony_ci	__u32 next_fsn = 0;
56862306a36Sopenharmony_ci	int is_last = 0;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm_uo, pos) {
57362306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (cevent->stream < event->stream)
57662306a36Sopenharmony_ci			continue;
57762306a36Sopenharmony_ci		if (cevent->stream > event->stream)
57862306a36Sopenharmony_ci			break;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (MID_lt(cevent->mid, sin->mid_uo))
58162306a36Sopenharmony_ci			continue;
58262306a36Sopenharmony_ci		if (MID_lt(sin->mid_uo, cevent->mid))
58362306a36Sopenharmony_ci			break;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
58662306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
58762306a36Sopenharmony_ci			goto out;
58862306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
58962306a36Sopenharmony_ci			if (!first_frag) {
59062306a36Sopenharmony_ci				if (cevent->fsn == sin->fsn_uo) {
59162306a36Sopenharmony_ci					first_frag = pos;
59262306a36Sopenharmony_ci					last_frag = pos;
59362306a36Sopenharmony_ci					next_fsn = cevent->fsn + 1;
59462306a36Sopenharmony_ci				}
59562306a36Sopenharmony_ci			} else if (cevent->fsn == next_fsn) {
59662306a36Sopenharmony_ci				last_frag = pos;
59762306a36Sopenharmony_ci				next_fsn++;
59862306a36Sopenharmony_ci			} else {
59962306a36Sopenharmony_ci				goto out;
60062306a36Sopenharmony_ci			}
60162306a36Sopenharmony_ci			break;
60262306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
60362306a36Sopenharmony_ci			if (!first_frag) {
60462306a36Sopenharmony_ci				if (cevent->fsn == sin->fsn_uo) {
60562306a36Sopenharmony_ci					first_frag = pos;
60662306a36Sopenharmony_ci					last_frag = pos;
60762306a36Sopenharmony_ci					next_fsn = 0;
60862306a36Sopenharmony_ci					is_last = 1;
60962306a36Sopenharmony_ci				}
61062306a36Sopenharmony_ci			} else if (cevent->fsn == next_fsn) {
61162306a36Sopenharmony_ci				last_frag = pos;
61262306a36Sopenharmony_ci				next_fsn = 0;
61362306a36Sopenharmony_ci				is_last = 1;
61462306a36Sopenharmony_ci			}
61562306a36Sopenharmony_ci			goto out;
61662306a36Sopenharmony_ci		default:
61762306a36Sopenharmony_ci			goto out;
61862306a36Sopenharmony_ci		}
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ciout:
62262306a36Sopenharmony_ci	if (!first_frag)
62362306a36Sopenharmony_ci		return NULL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(ulpq->asoc->base.net,
62662306a36Sopenharmony_ci					     &ulpq->reasm_uo, first_frag,
62762306a36Sopenharmony_ci					     last_frag);
62862306a36Sopenharmony_ci	if (retval) {
62962306a36Sopenharmony_ci		sin->fsn_uo = next_fsn;
63062306a36Sopenharmony_ci		if (is_last) {
63162306a36Sopenharmony_ci			retval->msg_flags |= MSG_EOR;
63262306a36Sopenharmony_ci			sin->pd_mode_uo = 0;
63362306a36Sopenharmony_ci		}
63462306a36Sopenharmony_ci	}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return retval;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_reassembled_uo(
64062306a36Sopenharmony_ci						struct sctp_ulpq *ulpq,
64162306a36Sopenharmony_ci						struct sctp_ulpevent *event)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct sctp_association *asoc = ulpq->asoc;
64462306a36Sopenharmony_ci	struct sk_buff *pos, *first_frag = NULL;
64562306a36Sopenharmony_ci	struct sctp_ulpevent *retval = NULL;
64662306a36Sopenharmony_ci	struct sk_buff *pd_first = NULL;
64762306a36Sopenharmony_ci	struct sk_buff *pd_last = NULL;
64862306a36Sopenharmony_ci	struct sctp_stream_in *sin;
64962306a36Sopenharmony_ci	__u32 next_fsn = 0;
65062306a36Sopenharmony_ci	__u32 pd_point = 0;
65162306a36Sopenharmony_ci	__u32 pd_len = 0;
65262306a36Sopenharmony_ci	__u32 mid = 0;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm_uo, pos) {
65762306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		if (cevent->stream < event->stream)
66062306a36Sopenharmony_ci			continue;
66162306a36Sopenharmony_ci		if (cevent->stream > event->stream)
66262306a36Sopenharmony_ci			break;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		if (MID_lt(cevent->mid, event->mid))
66562306a36Sopenharmony_ci			continue;
66662306a36Sopenharmony_ci		if (MID_lt(event->mid, cevent->mid))
66762306a36Sopenharmony_ci			break;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
67062306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
67162306a36Sopenharmony_ci			if (!sin->pd_mode_uo) {
67262306a36Sopenharmony_ci				sin->mid_uo = cevent->mid;
67362306a36Sopenharmony_ci				pd_first = pos;
67462306a36Sopenharmony_ci				pd_last = pos;
67562306a36Sopenharmony_ci				pd_len = pos->len;
67662306a36Sopenharmony_ci			}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci			first_frag = pos;
67962306a36Sopenharmony_ci			next_fsn = 0;
68062306a36Sopenharmony_ci			mid = cevent->mid;
68162306a36Sopenharmony_ci			break;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
68462306a36Sopenharmony_ci			if (first_frag && cevent->mid == mid &&
68562306a36Sopenharmony_ci			    cevent->fsn == next_fsn) {
68662306a36Sopenharmony_ci				next_fsn++;
68762306a36Sopenharmony_ci				if (pd_first) {
68862306a36Sopenharmony_ci					pd_last = pos;
68962306a36Sopenharmony_ci					pd_len += pos->len;
69062306a36Sopenharmony_ci				}
69162306a36Sopenharmony_ci			} else {
69262306a36Sopenharmony_ci				first_frag = NULL;
69362306a36Sopenharmony_ci			}
69462306a36Sopenharmony_ci			break;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
69762306a36Sopenharmony_ci			if (first_frag && cevent->mid == mid &&
69862306a36Sopenharmony_ci			    cevent->fsn == next_fsn)
69962306a36Sopenharmony_ci				goto found;
70062306a36Sopenharmony_ci			else
70162306a36Sopenharmony_ci				first_frag = NULL;
70262306a36Sopenharmony_ci			break;
70362306a36Sopenharmony_ci		}
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	if (!pd_first)
70762306a36Sopenharmony_ci		goto out;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	pd_point = sctp_sk(asoc->base.sk)->pd_point;
71062306a36Sopenharmony_ci	if (pd_point && pd_point <= pd_len) {
71162306a36Sopenharmony_ci		retval = sctp_make_reassembled_event(asoc->base.net,
71262306a36Sopenharmony_ci						     &ulpq->reasm_uo,
71362306a36Sopenharmony_ci						     pd_first, pd_last);
71462306a36Sopenharmony_ci		if (retval) {
71562306a36Sopenharmony_ci			sin->fsn_uo = next_fsn;
71662306a36Sopenharmony_ci			sin->pd_mode_uo = 1;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci	goto out;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cifound:
72262306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(asoc->base.net, &ulpq->reasm_uo,
72362306a36Sopenharmony_ci					     first_frag, pos);
72462306a36Sopenharmony_ci	if (retval)
72562306a36Sopenharmony_ci		retval->msg_flags |= MSG_EOR;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ciout:
72862306a36Sopenharmony_ci	return retval;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_reasm_uo(struct sctp_ulpq *ulpq,
73262306a36Sopenharmony_ci						struct sctp_ulpevent *event)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct sctp_ulpevent *retval = NULL;
73562306a36Sopenharmony_ci	struct sctp_stream_in *sin;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) {
73862306a36Sopenharmony_ci		event->msg_flags |= MSG_EOR;
73962306a36Sopenharmony_ci		return event;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	sctp_intl_store_reasm_uo(ulpq, event);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	sin = sctp_stream_in(&ulpq->asoc->stream, event->stream);
74562306a36Sopenharmony_ci	if (sin->pd_mode_uo && event->mid == sin->mid_uo &&
74662306a36Sopenharmony_ci	    event->fsn == sin->fsn_uo)
74762306a36Sopenharmony_ci		retval = sctp_intl_retrieve_partial_uo(ulpq, event);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (!retval)
75062306a36Sopenharmony_ci		retval = sctp_intl_retrieve_reassembled_uo(ulpq, event);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	return retval;
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_first_uo(struct sctp_ulpq *ulpq)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct sctp_stream_in *csin, *sin = NULL;
75862306a36Sopenharmony_ci	struct sk_buff *first_frag = NULL;
75962306a36Sopenharmony_ci	struct sk_buff *last_frag = NULL;
76062306a36Sopenharmony_ci	struct sctp_ulpevent *retval;
76162306a36Sopenharmony_ci	struct sk_buff *pos;
76262306a36Sopenharmony_ci	__u32 next_fsn = 0;
76362306a36Sopenharmony_ci	__u16 sid = 0;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm_uo, pos) {
76662306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci		csin = sctp_stream_in(&ulpq->asoc->stream, cevent->stream);
76962306a36Sopenharmony_ci		if (csin->pd_mode_uo)
77062306a36Sopenharmony_ci			continue;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
77362306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
77462306a36Sopenharmony_ci			if (first_frag)
77562306a36Sopenharmony_ci				goto out;
77662306a36Sopenharmony_ci			first_frag = pos;
77762306a36Sopenharmony_ci			last_frag = pos;
77862306a36Sopenharmony_ci			next_fsn = 0;
77962306a36Sopenharmony_ci			sin = csin;
78062306a36Sopenharmony_ci			sid = cevent->stream;
78162306a36Sopenharmony_ci			sin->mid_uo = cevent->mid;
78262306a36Sopenharmony_ci			break;
78362306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
78462306a36Sopenharmony_ci			if (!first_frag)
78562306a36Sopenharmony_ci				break;
78662306a36Sopenharmony_ci			if (cevent->stream == sid &&
78762306a36Sopenharmony_ci			    cevent->mid == sin->mid_uo &&
78862306a36Sopenharmony_ci			    cevent->fsn == next_fsn) {
78962306a36Sopenharmony_ci				next_fsn++;
79062306a36Sopenharmony_ci				last_frag = pos;
79162306a36Sopenharmony_ci			} else {
79262306a36Sopenharmony_ci				goto out;
79362306a36Sopenharmony_ci			}
79462306a36Sopenharmony_ci			break;
79562306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
79662306a36Sopenharmony_ci			if (first_frag)
79762306a36Sopenharmony_ci				goto out;
79862306a36Sopenharmony_ci			break;
79962306a36Sopenharmony_ci		default:
80062306a36Sopenharmony_ci			break;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (!first_frag)
80562306a36Sopenharmony_ci		return NULL;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ciout:
80862306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(ulpq->asoc->base.net,
80962306a36Sopenharmony_ci					     &ulpq->reasm_uo, first_frag,
81062306a36Sopenharmony_ci					     last_frag);
81162306a36Sopenharmony_ci	if (retval) {
81262306a36Sopenharmony_ci		sin->fsn_uo = next_fsn;
81362306a36Sopenharmony_ci		sin->pd_mode_uo = 1;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return retval;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int sctp_ulpevent_idata(struct sctp_ulpq *ulpq,
82062306a36Sopenharmony_ci			       struct sctp_chunk *chunk, gfp_t gfp)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct sctp_ulpevent *event;
82362306a36Sopenharmony_ci	struct sk_buff_head temp;
82462306a36Sopenharmony_ci	int event_eor = 0;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
82762306a36Sopenharmony_ci	if (!event)
82862306a36Sopenharmony_ci		return -ENOMEM;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	event->mid = ntohl(chunk->subh.idata_hdr->mid);
83162306a36Sopenharmony_ci	if (event->msg_flags & SCTP_DATA_FIRST_FRAG)
83262306a36Sopenharmony_ci		event->ppid = chunk->subh.idata_hdr->ppid;
83362306a36Sopenharmony_ci	else
83462306a36Sopenharmony_ci		event->fsn = ntohl(chunk->subh.idata_hdr->fsn);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (!(event->msg_flags & SCTP_DATA_UNORDERED)) {
83762306a36Sopenharmony_ci		event = sctp_intl_reasm(ulpq, event);
83862306a36Sopenharmony_ci		if (event) {
83962306a36Sopenharmony_ci			skb_queue_head_init(&temp);
84062306a36Sopenharmony_ci			__skb_queue_tail(&temp, sctp_event2skb(event));
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci			if (event->msg_flags & MSG_EOR)
84362306a36Sopenharmony_ci				event = sctp_intl_order(ulpq, event);
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci	} else {
84662306a36Sopenharmony_ci		event = sctp_intl_reasm_uo(ulpq, event);
84762306a36Sopenharmony_ci		if (event) {
84862306a36Sopenharmony_ci			skb_queue_head_init(&temp);
84962306a36Sopenharmony_ci			__skb_queue_tail(&temp, sctp_event2skb(event));
85062306a36Sopenharmony_ci		}
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	if (event) {
85462306a36Sopenharmony_ci		event_eor = (event->msg_flags & MSG_EOR) ? 1 : 0;
85562306a36Sopenharmony_ci		sctp_enqueue_event(ulpq, &temp);
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return event_eor;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic struct sctp_ulpevent *sctp_intl_retrieve_first(struct sctp_ulpq *ulpq)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct sctp_stream_in *csin, *sin = NULL;
86462306a36Sopenharmony_ci	struct sk_buff *first_frag = NULL;
86562306a36Sopenharmony_ci	struct sk_buff *last_frag = NULL;
86662306a36Sopenharmony_ci	struct sctp_ulpevent *retval;
86762306a36Sopenharmony_ci	struct sk_buff *pos;
86862306a36Sopenharmony_ci	__u32 next_fsn = 0;
86962306a36Sopenharmony_ci	__u16 sid = 0;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	skb_queue_walk(&ulpq->reasm, pos) {
87262306a36Sopenharmony_ci		struct sctp_ulpevent *cevent = sctp_skb2event(pos);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		csin = sctp_stream_in(&ulpq->asoc->stream, cevent->stream);
87562306a36Sopenharmony_ci		if (csin->pd_mode)
87662306a36Sopenharmony_ci			continue;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
87962306a36Sopenharmony_ci		case SCTP_DATA_FIRST_FRAG:
88062306a36Sopenharmony_ci			if (first_frag)
88162306a36Sopenharmony_ci				goto out;
88262306a36Sopenharmony_ci			if (cevent->mid == csin->mid) {
88362306a36Sopenharmony_ci				first_frag = pos;
88462306a36Sopenharmony_ci				last_frag = pos;
88562306a36Sopenharmony_ci				next_fsn = 0;
88662306a36Sopenharmony_ci				sin = csin;
88762306a36Sopenharmony_ci				sid = cevent->stream;
88862306a36Sopenharmony_ci			}
88962306a36Sopenharmony_ci			break;
89062306a36Sopenharmony_ci		case SCTP_DATA_MIDDLE_FRAG:
89162306a36Sopenharmony_ci			if (!first_frag)
89262306a36Sopenharmony_ci				break;
89362306a36Sopenharmony_ci			if (cevent->stream == sid &&
89462306a36Sopenharmony_ci			    cevent->mid == sin->mid &&
89562306a36Sopenharmony_ci			    cevent->fsn == next_fsn) {
89662306a36Sopenharmony_ci				next_fsn++;
89762306a36Sopenharmony_ci				last_frag = pos;
89862306a36Sopenharmony_ci			} else {
89962306a36Sopenharmony_ci				goto out;
90062306a36Sopenharmony_ci			}
90162306a36Sopenharmony_ci			break;
90262306a36Sopenharmony_ci		case SCTP_DATA_LAST_FRAG:
90362306a36Sopenharmony_ci			if (first_frag)
90462306a36Sopenharmony_ci				goto out;
90562306a36Sopenharmony_ci			break;
90662306a36Sopenharmony_ci		default:
90762306a36Sopenharmony_ci			break;
90862306a36Sopenharmony_ci		}
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	if (!first_frag)
91262306a36Sopenharmony_ci		return NULL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ciout:
91562306a36Sopenharmony_ci	retval = sctp_make_reassembled_event(ulpq->asoc->base.net,
91662306a36Sopenharmony_ci					     &ulpq->reasm, first_frag,
91762306a36Sopenharmony_ci					     last_frag);
91862306a36Sopenharmony_ci	if (retval) {
91962306a36Sopenharmony_ci		sin->fsn = next_fsn;
92062306a36Sopenharmony_ci		sin->pd_mode = 1;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return retval;
92462306a36Sopenharmony_ci}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic void sctp_intl_start_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct sctp_ulpevent *event;
92962306a36Sopenharmony_ci	struct sk_buff_head temp;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (!skb_queue_empty(&ulpq->reasm)) {
93262306a36Sopenharmony_ci		do {
93362306a36Sopenharmony_ci			event = sctp_intl_retrieve_first(ulpq);
93462306a36Sopenharmony_ci			if (event) {
93562306a36Sopenharmony_ci				skb_queue_head_init(&temp);
93662306a36Sopenharmony_ci				__skb_queue_tail(&temp, sctp_event2skb(event));
93762306a36Sopenharmony_ci				sctp_enqueue_event(ulpq, &temp);
93862306a36Sopenharmony_ci			}
93962306a36Sopenharmony_ci		} while (event);
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (!skb_queue_empty(&ulpq->reasm_uo)) {
94362306a36Sopenharmony_ci		do {
94462306a36Sopenharmony_ci			event = sctp_intl_retrieve_first_uo(ulpq);
94562306a36Sopenharmony_ci			if (event) {
94662306a36Sopenharmony_ci				skb_queue_head_init(&temp);
94762306a36Sopenharmony_ci				__skb_queue_tail(&temp, sctp_event2skb(event));
94862306a36Sopenharmony_ci				sctp_enqueue_event(ulpq, &temp);
94962306a36Sopenharmony_ci			}
95062306a36Sopenharmony_ci		} while (event);
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic void sctp_renege_events(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
95562306a36Sopenharmony_ci			       gfp_t gfp)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	struct sctp_association *asoc = ulpq->asoc;
95862306a36Sopenharmony_ci	__u32 freed = 0;
95962306a36Sopenharmony_ci	__u16 needed;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	needed = ntohs(chunk->chunk_hdr->length) -
96262306a36Sopenharmony_ci		 sizeof(struct sctp_idata_chunk);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
96562306a36Sopenharmony_ci		freed = sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
96662306a36Sopenharmony_ci		if (freed < needed)
96762306a36Sopenharmony_ci			freed += sctp_ulpq_renege_list(ulpq, &ulpq->reasm,
96862306a36Sopenharmony_ci						       needed);
96962306a36Sopenharmony_ci		if (freed < needed)
97062306a36Sopenharmony_ci			freed += sctp_ulpq_renege_list(ulpq, &ulpq->reasm_uo,
97162306a36Sopenharmony_ci						       needed);
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (freed >= needed && sctp_ulpevent_idata(ulpq, chunk, gfp) <= 0)
97562306a36Sopenharmony_ci		sctp_intl_start_pd(ulpq, gfp);
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void sctp_intl_stream_abort_pd(struct sctp_ulpq *ulpq, __u16 sid,
97962306a36Sopenharmony_ci				      __u32 mid, __u16 flags, gfp_t gfp)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct sock *sk = ulpq->asoc->base.sk;
98262306a36Sopenharmony_ci	struct sctp_ulpevent *ev = NULL;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (!sctp_ulpevent_type_enabled(ulpq->asoc->subscribe,
98562306a36Sopenharmony_ci					SCTP_PARTIAL_DELIVERY_EVENT))
98662306a36Sopenharmony_ci		return;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	ev = sctp_ulpevent_make_pdapi(ulpq->asoc, SCTP_PARTIAL_DELIVERY_ABORTED,
98962306a36Sopenharmony_ci				      sid, mid, flags, gfp);
99062306a36Sopenharmony_ci	if (ev) {
99162306a36Sopenharmony_ci		struct sctp_sock *sp = sctp_sk(sk);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci		__skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		if (!sp->data_ready_signalled) {
99662306a36Sopenharmony_ci			sp->data_ready_signalled = 1;
99762306a36Sopenharmony_ci			sk->sk_data_ready(sk);
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic void sctp_intl_reap_ordered(struct sctp_ulpq *ulpq, __u16 sid)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct sctp_stream *stream = &ulpq->asoc->stream;
100562306a36Sopenharmony_ci	struct sctp_ulpevent *cevent, *event = NULL;
100662306a36Sopenharmony_ci	struct sk_buff_head *lobby = &ulpq->lobby;
100762306a36Sopenharmony_ci	struct sk_buff *pos, *tmp;
100862306a36Sopenharmony_ci	struct sk_buff_head temp;
100962306a36Sopenharmony_ci	__u16 csid;
101062306a36Sopenharmony_ci	__u32 cmid;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	skb_queue_head_init(&temp);
101362306a36Sopenharmony_ci	sctp_skb_for_each(pos, lobby, tmp) {
101462306a36Sopenharmony_ci		cevent = (struct sctp_ulpevent *)pos->cb;
101562306a36Sopenharmony_ci		csid = cevent->stream;
101662306a36Sopenharmony_ci		cmid = cevent->mid;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		if (csid > sid)
101962306a36Sopenharmony_ci			break;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		if (csid < sid)
102262306a36Sopenharmony_ci			continue;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci		if (!MID_lt(cmid, sctp_mid_peek(stream, in, csid)))
102562306a36Sopenharmony_ci			break;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci		__skb_unlink(pos, lobby);
102862306a36Sopenharmony_ci		if (!event)
102962306a36Sopenharmony_ci			event = sctp_skb2event(pos);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		__skb_queue_tail(&temp, pos);
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (!event && pos != (struct sk_buff *)lobby) {
103562306a36Sopenharmony_ci		cevent = (struct sctp_ulpevent *)pos->cb;
103662306a36Sopenharmony_ci		csid = cevent->stream;
103762306a36Sopenharmony_ci		cmid = cevent->mid;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		if (csid == sid && cmid == sctp_mid_peek(stream, in, csid)) {
104062306a36Sopenharmony_ci			sctp_mid_next(stream, in, csid);
104162306a36Sopenharmony_ci			__skb_unlink(pos, lobby);
104262306a36Sopenharmony_ci			__skb_queue_tail(&temp, pos);
104362306a36Sopenharmony_ci			event = sctp_skb2event(pos);
104462306a36Sopenharmony_ci		}
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (event) {
104862306a36Sopenharmony_ci		sctp_intl_retrieve_ordered(ulpq, event);
104962306a36Sopenharmony_ci		sctp_enqueue_event(ulpq, &temp);
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic void sctp_intl_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct sctp_stream *stream = &ulpq->asoc->stream;
105662306a36Sopenharmony_ci	__u16 sid;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	for (sid = 0; sid < stream->incnt; sid++) {
105962306a36Sopenharmony_ci		struct sctp_stream_in *sin = SCTP_SI(stream, sid);
106062306a36Sopenharmony_ci		__u32 mid;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci		if (sin->pd_mode_uo) {
106362306a36Sopenharmony_ci			sin->pd_mode_uo = 0;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci			mid = sin->mid_uo;
106662306a36Sopenharmony_ci			sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x1, gfp);
106762306a36Sopenharmony_ci		}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci		if (sin->pd_mode) {
107062306a36Sopenharmony_ci			sin->pd_mode = 0;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci			mid = sin->mid;
107362306a36Sopenharmony_ci			sctp_intl_stream_abort_pd(ulpq, sid, mid, 0, gfp);
107462306a36Sopenharmony_ci			sctp_mid_skip(stream, in, sid, mid);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci			sctp_intl_reap_ordered(ulpq, sid);
107762306a36Sopenharmony_ci		}
107862306a36Sopenharmony_ci	}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/* intl abort pd happens only when all data needs to be cleaned */
108162306a36Sopenharmony_ci	sctp_ulpq_flush(ulpq);
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic inline int sctp_get_skip_pos(struct sctp_ifwdtsn_skip *skiplist,
108562306a36Sopenharmony_ci				    int nskips, __be16 stream, __u8 flags)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	int i;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	for (i = 0; i < nskips; i++)
109062306a36Sopenharmony_ci		if (skiplist[i].stream == stream &&
109162306a36Sopenharmony_ci		    skiplist[i].flags == flags)
109262306a36Sopenharmony_ci			return i;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	return i;
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci#define SCTP_FTSN_U_BIT	0x1
109862306a36Sopenharmony_cistatic void sctp_generate_iftsn(struct sctp_outq *q, __u32 ctsn)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	struct sctp_ifwdtsn_skip ftsn_skip_arr[10];
110162306a36Sopenharmony_ci	struct sctp_association *asoc = q->asoc;
110262306a36Sopenharmony_ci	struct sctp_chunk *ftsn_chunk = NULL;
110362306a36Sopenharmony_ci	struct list_head *lchunk, *temp;
110462306a36Sopenharmony_ci	int nskips = 0, skip_pos;
110562306a36Sopenharmony_ci	struct sctp_chunk *chunk;
110662306a36Sopenharmony_ci	__u32 tsn;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (!asoc->peer.prsctp_capable)
110962306a36Sopenharmony_ci		return;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (TSN_lt(asoc->adv_peer_ack_point, ctsn))
111262306a36Sopenharmony_ci		asoc->adv_peer_ack_point = ctsn;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	list_for_each_safe(lchunk, temp, &q->abandoned) {
111562306a36Sopenharmony_ci		chunk = list_entry(lchunk, struct sctp_chunk, transmitted_list);
111662306a36Sopenharmony_ci		tsn = ntohl(chunk->subh.data_hdr->tsn);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		if (TSN_lte(tsn, ctsn)) {
111962306a36Sopenharmony_ci			list_del_init(lchunk);
112062306a36Sopenharmony_ci			sctp_chunk_free(chunk);
112162306a36Sopenharmony_ci		} else if (TSN_lte(tsn, asoc->adv_peer_ack_point + 1)) {
112262306a36Sopenharmony_ci			__be16 sid = chunk->subh.idata_hdr->stream;
112362306a36Sopenharmony_ci			__be32 mid = chunk->subh.idata_hdr->mid;
112462306a36Sopenharmony_ci			__u8 flags = 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci			if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
112762306a36Sopenharmony_ci				flags |= SCTP_FTSN_U_BIT;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci			asoc->adv_peer_ack_point = tsn;
113062306a36Sopenharmony_ci			skip_pos = sctp_get_skip_pos(&ftsn_skip_arr[0], nskips,
113162306a36Sopenharmony_ci						     sid, flags);
113262306a36Sopenharmony_ci			ftsn_skip_arr[skip_pos].stream = sid;
113362306a36Sopenharmony_ci			ftsn_skip_arr[skip_pos].reserved = 0;
113462306a36Sopenharmony_ci			ftsn_skip_arr[skip_pos].flags = flags;
113562306a36Sopenharmony_ci			ftsn_skip_arr[skip_pos].mid = mid;
113662306a36Sopenharmony_ci			if (skip_pos == nskips)
113762306a36Sopenharmony_ci				nskips++;
113862306a36Sopenharmony_ci			if (nskips == 10)
113962306a36Sopenharmony_ci				break;
114062306a36Sopenharmony_ci		} else {
114162306a36Sopenharmony_ci			break;
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (asoc->adv_peer_ack_point > ctsn)
114662306a36Sopenharmony_ci		ftsn_chunk = sctp_make_ifwdtsn(asoc, asoc->adv_peer_ack_point,
114762306a36Sopenharmony_ci					       nskips, &ftsn_skip_arr[0]);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (ftsn_chunk) {
115062306a36Sopenharmony_ci		list_add_tail(&ftsn_chunk->list, &q->control_chunk_list);
115162306a36Sopenharmony_ci		SCTP_INC_STATS(asoc->base.net, SCTP_MIB_OUTCTRLCHUNKS);
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci#define _sctp_walk_ifwdtsn(pos, chunk, end) \
115662306a36Sopenharmony_ci	for (pos = (void *)(chunk->subh.ifwdtsn_hdr + 1); \
115762306a36Sopenharmony_ci	     (void *)pos <= (void *)(chunk->subh.ifwdtsn_hdr + 1) + (end) - \
115862306a36Sopenharmony_ci			    sizeof(struct sctp_ifwdtsn_skip); pos++)
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci#define sctp_walk_ifwdtsn(pos, ch) \
116162306a36Sopenharmony_ci	_sctp_walk_ifwdtsn((pos), (ch), ntohs((ch)->chunk_hdr->length) - \
116262306a36Sopenharmony_ci					sizeof(struct sctp_ifwdtsn_chunk))
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cistatic bool sctp_validate_fwdtsn(struct sctp_chunk *chunk)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct sctp_fwdtsn_skip *skip;
116762306a36Sopenharmony_ci	__u16 incnt;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (chunk->chunk_hdr->type != SCTP_CID_FWD_TSN)
117062306a36Sopenharmony_ci		return false;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	incnt = chunk->asoc->stream.incnt;
117362306a36Sopenharmony_ci	sctp_walk_fwdtsn(skip, chunk)
117462306a36Sopenharmony_ci		if (ntohs(skip->stream) >= incnt)
117562306a36Sopenharmony_ci			return false;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	return true;
117862306a36Sopenharmony_ci}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cistatic bool sctp_validate_iftsn(struct sctp_chunk *chunk)
118162306a36Sopenharmony_ci{
118262306a36Sopenharmony_ci	struct sctp_ifwdtsn_skip *skip;
118362306a36Sopenharmony_ci	__u16 incnt;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (chunk->chunk_hdr->type != SCTP_CID_I_FWD_TSN)
118662306a36Sopenharmony_ci		return false;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	incnt = chunk->asoc->stream.incnt;
118962306a36Sopenharmony_ci	sctp_walk_ifwdtsn(skip, chunk)
119062306a36Sopenharmony_ci		if (ntohs(skip->stream) >= incnt)
119162306a36Sopenharmony_ci			return false;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return true;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic void sctp_report_fwdtsn(struct sctp_ulpq *ulpq, __u32 ftsn)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	/* Move the Cumulattive TSN Ack ahead. */
119962306a36Sopenharmony_ci	sctp_tsnmap_skip(&ulpq->asoc->peer.tsn_map, ftsn);
120062306a36Sopenharmony_ci	/* purge the fragmentation queue */
120162306a36Sopenharmony_ci	sctp_ulpq_reasm_flushtsn(ulpq, ftsn);
120262306a36Sopenharmony_ci	/* Abort any in progress partial delivery. */
120362306a36Sopenharmony_ci	sctp_ulpq_abort_pd(ulpq, GFP_ATOMIC);
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic void sctp_intl_reasm_flushtsn(struct sctp_ulpq *ulpq, __u32 ftsn)
120762306a36Sopenharmony_ci{
120862306a36Sopenharmony_ci	struct sk_buff *pos, *tmp;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	skb_queue_walk_safe(&ulpq->reasm, pos, tmp) {
121162306a36Sopenharmony_ci		struct sctp_ulpevent *event = sctp_skb2event(pos);
121262306a36Sopenharmony_ci		__u32 tsn = event->tsn;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci		if (TSN_lte(tsn, ftsn)) {
121562306a36Sopenharmony_ci			__skb_unlink(pos, &ulpq->reasm);
121662306a36Sopenharmony_ci			sctp_ulpevent_free(event);
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	skb_queue_walk_safe(&ulpq->reasm_uo, pos, tmp) {
122162306a36Sopenharmony_ci		struct sctp_ulpevent *event = sctp_skb2event(pos);
122262306a36Sopenharmony_ci		__u32 tsn = event->tsn;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		if (TSN_lte(tsn, ftsn)) {
122562306a36Sopenharmony_ci			__skb_unlink(pos, &ulpq->reasm_uo);
122662306a36Sopenharmony_ci			sctp_ulpevent_free(event);
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic void sctp_report_iftsn(struct sctp_ulpq *ulpq, __u32 ftsn)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	/* Move the Cumulattive TSN Ack ahead. */
123462306a36Sopenharmony_ci	sctp_tsnmap_skip(&ulpq->asoc->peer.tsn_map, ftsn);
123562306a36Sopenharmony_ci	/* purge the fragmentation queue */
123662306a36Sopenharmony_ci	sctp_intl_reasm_flushtsn(ulpq, ftsn);
123762306a36Sopenharmony_ci	/* abort only when it's for all data */
123862306a36Sopenharmony_ci	if (ftsn == sctp_tsnmap_get_max_tsn_seen(&ulpq->asoc->peer.tsn_map))
123962306a36Sopenharmony_ci		sctp_intl_abort_pd(ulpq, GFP_ATOMIC);
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic void sctp_handle_fwdtsn(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	struct sctp_fwdtsn_skip *skip;
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/* Walk through all the skipped SSNs */
124762306a36Sopenharmony_ci	sctp_walk_fwdtsn(skip, chunk)
124862306a36Sopenharmony_ci		sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn));
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic void sctp_intl_skip(struct sctp_ulpq *ulpq, __u16 sid, __u32 mid,
125262306a36Sopenharmony_ci			   __u8 flags)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct sctp_stream_in *sin = sctp_stream_in(&ulpq->asoc->stream, sid);
125562306a36Sopenharmony_ci	struct sctp_stream *stream  = &ulpq->asoc->stream;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if (flags & SCTP_FTSN_U_BIT) {
125862306a36Sopenharmony_ci		if (sin->pd_mode_uo && MID_lt(sin->mid_uo, mid)) {
125962306a36Sopenharmony_ci			sin->pd_mode_uo = 0;
126062306a36Sopenharmony_ci			sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x1,
126162306a36Sopenharmony_ci						  GFP_ATOMIC);
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci		return;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	if (MID_lt(mid, sctp_mid_peek(stream, in, sid)))
126762306a36Sopenharmony_ci		return;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (sin->pd_mode) {
127062306a36Sopenharmony_ci		sin->pd_mode = 0;
127162306a36Sopenharmony_ci		sctp_intl_stream_abort_pd(ulpq, sid, mid, 0x0, GFP_ATOMIC);
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	sctp_mid_skip(stream, in, sid, mid);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	sctp_intl_reap_ordered(ulpq, sid);
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_cistatic void sctp_handle_iftsn(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct sctp_ifwdtsn_skip *skip;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* Walk through all the skipped MIDs and abort stream pd if possible */
128462306a36Sopenharmony_ci	sctp_walk_ifwdtsn(skip, chunk)
128562306a36Sopenharmony_ci		sctp_intl_skip(ulpq, ntohs(skip->stream),
128662306a36Sopenharmony_ci			       ntohl(skip->mid), skip->flags);
128762306a36Sopenharmony_ci}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_cistatic int do_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
129062306a36Sopenharmony_ci{
129162306a36Sopenharmony_ci	struct sk_buff_head temp;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	skb_queue_head_init(&temp);
129462306a36Sopenharmony_ci	__skb_queue_tail(&temp, sctp_event2skb(event));
129562306a36Sopenharmony_ci	return sctp_ulpq_tail_event(ulpq, &temp);
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic struct sctp_stream_interleave sctp_stream_interleave_0 = {
129962306a36Sopenharmony_ci	.data_chunk_len		= sizeof(struct sctp_data_chunk),
130062306a36Sopenharmony_ci	.ftsn_chunk_len		= sizeof(struct sctp_fwdtsn_chunk),
130162306a36Sopenharmony_ci	/* DATA process functions */
130262306a36Sopenharmony_ci	.make_datafrag		= sctp_make_datafrag_empty,
130362306a36Sopenharmony_ci	.assign_number		= sctp_chunk_assign_ssn,
130462306a36Sopenharmony_ci	.validate_data		= sctp_validate_data,
130562306a36Sopenharmony_ci	.ulpevent_data		= sctp_ulpq_tail_data,
130662306a36Sopenharmony_ci	.enqueue_event		= do_ulpq_tail_event,
130762306a36Sopenharmony_ci	.renege_events		= sctp_ulpq_renege,
130862306a36Sopenharmony_ci	.start_pd		= sctp_ulpq_partial_delivery,
130962306a36Sopenharmony_ci	.abort_pd		= sctp_ulpq_abort_pd,
131062306a36Sopenharmony_ci	/* FORWARD-TSN process functions */
131162306a36Sopenharmony_ci	.generate_ftsn		= sctp_generate_fwdtsn,
131262306a36Sopenharmony_ci	.validate_ftsn		= sctp_validate_fwdtsn,
131362306a36Sopenharmony_ci	.report_ftsn		= sctp_report_fwdtsn,
131462306a36Sopenharmony_ci	.handle_ftsn		= sctp_handle_fwdtsn,
131562306a36Sopenharmony_ci};
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_cistatic int do_sctp_enqueue_event(struct sctp_ulpq *ulpq,
131862306a36Sopenharmony_ci				 struct sctp_ulpevent *event)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct sk_buff_head temp;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	skb_queue_head_init(&temp);
132362306a36Sopenharmony_ci	__skb_queue_tail(&temp, sctp_event2skb(event));
132462306a36Sopenharmony_ci	return sctp_enqueue_event(ulpq, &temp);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic struct sctp_stream_interleave sctp_stream_interleave_1 = {
132862306a36Sopenharmony_ci	.data_chunk_len		= sizeof(struct sctp_idata_chunk),
132962306a36Sopenharmony_ci	.ftsn_chunk_len		= sizeof(struct sctp_ifwdtsn_chunk),
133062306a36Sopenharmony_ci	/* I-DATA process functions */
133162306a36Sopenharmony_ci	.make_datafrag		= sctp_make_idatafrag_empty,
133262306a36Sopenharmony_ci	.assign_number		= sctp_chunk_assign_mid,
133362306a36Sopenharmony_ci	.validate_data		= sctp_validate_idata,
133462306a36Sopenharmony_ci	.ulpevent_data		= sctp_ulpevent_idata,
133562306a36Sopenharmony_ci	.enqueue_event		= do_sctp_enqueue_event,
133662306a36Sopenharmony_ci	.renege_events		= sctp_renege_events,
133762306a36Sopenharmony_ci	.start_pd		= sctp_intl_start_pd,
133862306a36Sopenharmony_ci	.abort_pd		= sctp_intl_abort_pd,
133962306a36Sopenharmony_ci	/* I-FORWARD-TSN process functions */
134062306a36Sopenharmony_ci	.generate_ftsn		= sctp_generate_iftsn,
134162306a36Sopenharmony_ci	.validate_ftsn		= sctp_validate_iftsn,
134262306a36Sopenharmony_ci	.report_ftsn		= sctp_report_iftsn,
134362306a36Sopenharmony_ci	.handle_ftsn		= sctp_handle_iftsn,
134462306a36Sopenharmony_ci};
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_civoid sctp_stream_interleave_init(struct sctp_stream *stream)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct sctp_association *asoc;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	asoc = container_of(stream, struct sctp_association, stream);
135162306a36Sopenharmony_ci	stream->si = asoc->peer.intl_capable ? &sctp_stream_interleave_1
135262306a36Sopenharmony_ci					     : &sctp_stream_interleave_0;
135362306a36Sopenharmony_ci}
1354