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