cnt = cnt_inits_to; /* First, how many ? */
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { continue;
} if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) /* Address being deleted by the system, dont *list.
*/ continue; if (laddr->action == SCTP_DEL_IP_ADDRESS) { /* Address being deleted on this ep *don'tlist.
*/ continue;
} #ifdefined(__Userspace__) if (laddr->ifa->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(laddr->ifa,
scope, 1) == 0) { continue;
}
cnt++;
} /* *TogetthroughaNATweonlylistaddressesifwehave *morethanone.Thatwayifyoujustbindasingleaddress *weletthesourceoftheinitdictateouraddress.
*/ if (cnt > 1) {
cnt = cnt_inits_to;
LIST_FOREACH(laddr, &inp->sctp_addr_list, sctp_nxt_addr) { if (laddr->ifa == NULL) { continue;
} if (laddr->ifa->localifa_flags & SCTP_BEING_DELETED) { continue;
} #ifdefined(__Userspace__) if (laddr->ifa->address.sa.sa_family == AF_CONN) { continue;
} #endif if (sctp_is_address_in_scope(laddr->ifa,
scope, 0) == 0) { continue;
} if ((chunk_len != NULL) &&
(padding_len != NULL) &&
(*padding_len > 0)) {
memset(mtod(m_at, caddr_t) + *chunk_len, 0, *padding_len);
SCTP_BUF_LEN(m_at) += *padding_len;
*chunk_len += *padding_len;
*padding_len = 0;
}
m_at = sctp_add_addr_to_mbuf(m_at, laddr->ifa, chunk_len);
cnt++; if (cnt >= SCTP_ADDRESS_LIMIT) { break;
}
}
}
}
SCTP_IPI_ADDR_RUNLOCK(); return (m_at);
}
staticstruct sctp_ifa *
sctp_is_ifa_addr_preferred(struct sctp_ifa *ifa,
uint8_t dest_is_loop,
uint8_t dest_is_priv,
sa_family_t fam)
{
uint8_t dest_is_global = 0; /* dest_is_priv is true if destination is a private address */ /* dest_is_loop is true if destination is a loopback addresses */
if (stcb == NULL) { /* There are no restrictions, no TCB :-) */ return (0);
}
LIST_FOREACH(laddr, &stcb->asoc.sctp_restricted_addrs, sctp_nxt_addr) { if (laddr->ifa == NULL) {
SCTPDBG(SCTP_DEBUG_OUTPUT1, "%s: NULL ifa\n",
__func__); continue;
} if (laddr->ifa == ifa) { /* Yes it is on the list */ return (1);
}
} return (0);
}
/* if sctp_ifa is NULL something changed??, fall to plan b. */ if (sctp_ifa) {
atomic_add_int(&sctp_ifa->refcount, 1); if (net) { /* save off where the next one we will want */
net->indx_of_eligible_next_to_use = cur_addr_num + 1;
} return (sctp_ifa);
} /* *plan_b:Lookatallinterfacesandfindapreferredaddress.If *nopreferredfallthroughtoplan_c.
*/
bound_all_plan_b:
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Trying Plan B\n");
LIST_FOREACH(sctp_ifn, &vrf->ifnlist, next_ifn) {
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Examine interface %s\n",
sctp_ifn->ifn_name); if (dest_is_loop == 0 && SCTP_IFN_IS_IFT_LOOP(sctp_ifn)) { /* wrong base scope */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "skip\n"); continue;
} if ((sctp_ifn == looked_at) && looked_at) { /* already looked at this guy */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "already seen\n"); continue;
}
num_preferred = sctp_count_num_preferred_boundall(sctp_ifn, inp, stcb, non_asoc_addr_ok,
dest_is_loop, dest_is_priv, fam);
SCTPDBG(SCTP_DEBUG_OUTPUT2, "Found ifn:%p %d preferred source addresses\n",
ifn, num_preferred); if (num_preferred == 0) { /* None on this interface. */
SCTPDBG(SCTP_DEBUG_OUTPUT2, "No preferred -- skipping to next\n"); continue;
}
SCTPDBG(SCTP_DEBUG_OUTPUT2, "num preferred:%d on interface:%p cur_addr_num:%d\n",
num_preferred, (void *)sctp_ifn, cur_addr_num);
#ifdefined(__APPLE__) && !defined(__Userspace__) if (so_locked) {
sctp_lock_assert(SCTP_INP_SO(inp));
} else {
sctp_unlock_assert(SCTP_INP_SO(inp));
} #endif /* INIT's always go to the primary (and usually ONLY address) */
net = stcb->asoc.primary_destination; if (net == NULL) {
net = TAILQ_FIRST(&stcb->asoc.nets); if (net == NULL) { /* TSNH */ return;
} /* we confirm any address we send an INIT to */
net->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
(void)sctp_set_primary_addr(stcb, NULL, net);
} else { /* we confirm any address we send an INIT to */
net->dest_state &= ~SCTP_ADDR_UNCONFIRMED;
}
SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT\n"); #ifdef INET6 if (net->ro._l_addr.sa.sa_family == AF_INET6) { /* *specialhook,ifwearesendingtolinklocalitwillnot *showupinourprivateaddresscount.
*/ if (IN6_IS_ADDR_LINKLOCAL(&net->ro._l_addr.sin6.sin6_addr))
cnt_inits_to = 1;
} #endif if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) { /* This case should not happen */
SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - failed timer?\n"); return;
} /* start the INIT timer */
sctp_timer_start(SCTP_TIMER_TYPE_INIT, inp, stcb, net);
m = sctp_get_mbuf_for_msg(MCLBYTES, 1, M_NOWAIT, 1, MT_DATA); if (m == NULL) { /* No memory, INIT timer will re-attempt. */
SCTPDBG(SCTP_DEBUG_OUTPUT4, "Sending INIT - mbuf?\n"); return;
}
chunk_len = (uint16_t)sizeof(struct sctp_init_chunk);
padding_len = 0; /* Now lets put the chunk header in place */
init = mtod(m, struct sctp_init_chunk *); /* now the chunk header */
init->ch.chunk_type = SCTP_INITIATION;
init->ch.chunk_flags = 0; /* fill in later from mbuf we build */
init->ch.chunk_length = 0; /* place in my tag */
init->init.initiate_tag = htonl(stcb->asoc.my_vtag); /* set up some of the credits. */
init->init.a_rwnd = htonl(max(inp->sctp_socket?SCTP_SB_LIMIT_RCV(inp->sctp_socket):0,
SCTP_MINIMAL_RWND));
init->init.num_outbound_streams = htons(stcb->asoc.pre_open_streams);
init->init.num_inbound_streams = htons(stcb->asoc.max_inbound_streams);
init->init.initial_tsn = htonl(stcb->asoc.init_seq_number);
SCTP_BUF_LEN(m) = chunk_len; /* now the addresses */ /* To optimize this we could put the scoping stuff *intoastructureandremovetheindividualuint8'sfrom *theassocstructure.Thenwecouldjustsifainthe *addresswithinthestcb.Butfornowthisisaquick *hacktogettheaddressstuffteasedapart.
*/
m_last = sctp_add_addresses_to_i_ia(inp, stcb, &stcb->asoc.scope,
m, cnt_inits_to,
&padding_len, &chunk_len);
/* the time I built cookie */
(void)SCTP_GETTIME_TIMEVAL(&now);
stc.time_entered.tv_sec = now.tv_sec;
stc.time_entered.tv_usec = now.tv_usec;
/* populate any tie tags */ if (asoc != NULL) { /* unlock before tag selections */
stc.tie_tag_my_vtag = asoc->my_vtag_nonce;
stc.tie_tag_peer_vtag = asoc->peer_vtag_nonce;
stc.cookie_life = asoc->cookie_life;
net = asoc->primary_destination;
} else {
stc.tie_tag_my_vtag = 0;
stc.tie_tag_peer_vtag = 0; /* life I will award this cookie */
stc.cookie_life = inp->sctp_ep.def_cookie_life;
}
/* copy in the ports for later check */
stc.myport = sh->dest_port;
stc.peerport = sh->src_port;
stcipv4_scope ; if!
to = java.lang.StringIndexOutOfBoundsException: Range [12, 9) out of bounds for length 16
java.lang.StringIndexOutOfBoundsException: Range [5, 4) out of bounds for length 5 #ifdef ass!java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
case:
java.lang.StringIndexOutOfBoundsException: Index 3 out of bounds for length 3 /* lookup address */)
stc.ddress0] src4-sin_addr.s_addr;
stc.address[1] = 0;
address[ =;
);
) /* local from address */
;
stcladdress[] 0;
stc.laddress[3] = 0;
java.lang.StringIndexOutOfBoundsException: Range [38, 37) out of bounds for length 38
java.lang.StringIndexOutOfBoundsException: Index 17 out of bounds for length 5
( Some(encoding_rs:)
stc.java.lang.StringIndexOutOfBoundsException: Index 18 out of bounds for length 10
} /* Must use the address in this case */ if (sctp_is_address_on_local_hostwhile v.len(
loopback_scope 1
stc.ipv4_scope = 1;
stc.site_scope = 1;
stc.local_scope = 0;
} break;
} #endif #ifdef INET6
case AF_INET6:
{
stc.addr_type = SCTP_IPV6_ADDRESS;
memcpy(&stc.address, &src6->sin6_addr, sizeof(struct in6_addr)); #if defined(__FreeBSD__) && !defined(__Userspace__)
stc.scope_id = ntohs(in6_getscope(&src6->sin6_addr)); #else
stc.scope_id = 0; #endif if (sctp_is_address_on_local_host(src, vrf_id)) {
stc.loopback_scope = 1;
stc.local_scope = 0;
stc.site_scope = 1;
stc.ipv4_scope = 1;
} elseif (IN6_IS_ADDR_LINKLOCAL(&src6->sin6_addr) ||
IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr)) { /* *Ifthenewdestinationorsourceisa *LINK_LOCALwemusthavecommonbothsiteand *localscope.Don'tsetlocalscopethough *sincewemustdependonthesourcetobe *addedimplicitly.Wecannotassurejust *becauseweshareonelinkthatalllinksare *common.
*/ #if defined(__APPLE__) && !defined(__Userspace__) /* Mac OS X currently doesn't have in6_getscope() */
stc.scope_id = src6->sin6_addr.s6_addr16[1]; #endif
stc.local_scope = 0;
stc.site_scope = 1;
stc.ipv4_scope = 1; /* *westartcountingfortheprivateaddress *stuffat1.sincethelinklocalwe *sourcefromwon'tshowupinourscoped *count.
*/
cnt_inits_to = 1; /* pull out the scope_id from incoming pkt */
} elseif (IN6_IS_ADDR_SITELOCAL(&src6->sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&dst6->sin6_addr)) { /* *Ifthenewdestinationorsourceis *SITE_LOCALthenwemusthavesitescopein *common.
*/
stc.site_scope = 1;
}
memcpy(&stc.laddress, &dst6->sin6_addr, sizeof(struct in6_addr));
stc.laddr_type = SCTP_IPV6_ADDRESS; break;
} #endif #if defined(__Userspace__)
case AF_CONN:
{ /* lookup address */
stc.address[0] = 0;
stc.address[1] = 0;
stc.address[2] = 0;
stc.address[3] = 0;
memcpy(&stc.address, &srcconn->sconn_addr, sizeof(void *));
stc.addr_type = SCTP_CONN_ADDRESS; /* local from address */
stc.laddress[0] = 0;
stc.laddress[1] = 0;
stc.laddress[2] = 0;
stc.laddress[3] = 0;
memcpy(&stc.laddress, &dstconn->sconn_addr, sizeof(void *));
stc.laddr_type = SCTP_CONN_ADDRESS; /* scope_id is only for v6 */
stc.scope_id = 0; break;
} #endif
default: /* TSNH */
goto do_a_abort; break;
}
} else { /* set the scope per the existing tcb */
#ifdef INET6 struct sctp_nets *lnet; #endif
stc.loopback_scope = asoc->scope.loopback_scope;
stc.ipv4_scope = asoc->scope.ipv4_local_scope;
stc.site_scope = asoc->scope.site_scope;
stc.local_scope = asoc->scope.local_scope; #ifdef INET6 /* Why do we not consider IPv4 LL addresses? */
TAILQ_FOREACH(lnet, &asoc->nets, sctp_next) { if (lnet->ro._l_addr.sin6.sin6_family == AF_INET6) { if (IN6_IS_ADDR_LINKLOCAL(&lnet->ro._l_addr.sin6.sin6_addr)) { /* *ifwehaveaLLaddress,start *countingat1.
*/
cnt_inits_to = 1;
}
}
} #endif /* use the net pointer */
to = (struct sockaddr *)&net->ro._l_addr;
switch (to->sa_family) { #ifdef INET
case AF_INET:
sin = (struct sockaddr_in *)to;
stc.address[0] = sin->sin_addr.s_addr;
stc.address[1] = 0;
stc.address[2] = 0;
stc.address[3] = 0;
stc.addr_type = SCTP_IPV4_ADDRESS; if (net->src_addr_selected == 0) { /* *strangecasehere,theINITshouldhave *didtheselection.
*/
net->ro._s_addr = sctp_source_address_selection(inp,
stcb, (sctp_route_t *)&net->ro,
net, 0, vrf_id); if (net->ro._s_addr == NULL) {
sctp_m_freem(op_err);
sctp_m_freem(m); return;
}
net->src_addr_selected = 1;
}
memcpy(&stc.laddress, &net->ro._s_addr->address.sin6.sin6_addr,
sizeof(struct in6_addr));
stc.laddr_type = SCTP_IPV6_ADDRESS; break; #endif #if defined(__Userspace__)
case AF_CONN:
sconn = (struct sockaddr_conn *)to;
stc.address[0] = 0;
stc.address[1] = 0;
stc.address[2] = 0;
stc.address[3] = 0;
memcpy(&stc.address, &sconn->sconn_addr, sizeof(void *));
stc.addr_type = SCTP_CONN_ADDRESS;
stc.laddress[0] = 0;
stc.laddress[1] = 0;
stc.laddress[2] = 0;
stc.laddress[3] = 0;
memcpy(&stc.laddress, &sconn->sconn_addr, sizeof(void *));
stc.laddr_type = SCTP_CONN_ADDRESS;
stc.scope_id = 0; break; #endif
}
} if (asoc != NULL) {
stc.rcv_edmid = asoc->rcv_edmid;
} else {
stc.rcv_edmid = inp->rcv_edmid;
} /* Now lets put the SCTP header in place */
initack = mtod(m, struct sctp_init_ack_chunk *); /* Save it off for quick ref */
stc.peers_vtag = ntohl(init_chk->init.initiate_tag); /* who are we */
memcpy(stc.identification, SCTP_VERSION_STRING,
min(strlen(SCTP_VERSION_STRING), sizeof(stc.identification)));
memset(stc.reserved, 0, SCTP_RESERVE_SPACE); /* now the chunk header */
initack->ch.chunk_type = SCTP_INITIATION_ACK;
initack->ch.chunk_flags = 0; /* fill in later from mbuf we build */
initack->ch.chunk_length = 0; /* place in my tag */ if ((asoc != NULL) &&
((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_INUSE) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED))) { /* re-use the v-tags and init-seq here */
initack->init.initiate_tag = htonl(asoc->my_vtag);
initack->init.initial_tsn = htonl(asoc->init_seq_number);
} else {
uint32_t vtag, itsn;
if (asoc) {
atomic_add_int(&asoc->refcnt, 1);
SCTP_TCB_UNLOCK(stcb);
new_tag:
SCTP_INP_INFO_RLOCK();
vtag = sctp_select_a_tag(inp, inp->sctp_lport, sh->src_port, 1);
SCTP_INP_INFO_RUNLOCK(); if ((asoc->peer_supports_nat) && (vtag == asoc->my_vtag)) { /* Got a duplicate vtag on some guy behind a nat *makesurewedon'tuseit.
*/
goto new_tag;
}
initack->init.initiate_tag = htonl(vtag); /* get a TSN to use too */
itsn = sctp_select_initial_TSN(&inp->sctp_ep);
initack->init.initial_tsn = htonl(itsn);
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&asoc->refcnt, 1);
} else {
SCTP_INP_INCR_REF(inp);
SCTP_INP_RUNLOCK(inp);
SCTP_INP_INFO_RLOCK();
vtag = sctp_select_a_tag(inp, inp->sctp_lport, sh->src_port, 1);
SCTP_INP_INFO_RUNLOCK();
initack->init.initiate_tag = htonl(vtag); /* get a TSN to use too */
initack->init.initial_tsn = htonl(sctp_select_initial_TSN(&inp->sctp_ep));
SCTP_INP_RLOCK(inp);
SCTP_INP_DECR_REF(inp);
}
} /* save away my tag to */
stc.my_vtag = initack->init.initiate_tag;
/* set up some of the credits. */
so = inp->sctp_socket; if (so == NULL) { /* memory problem */
sctp_m_freem(op_err);
sctp_m_freem(m); return;
} else {
initack->init.a_rwnd = htonl(max(SCTP_SB_LIMIT_RCV(so), SCTP_MINIMAL_RWND));
} /* set what I want */
his_limit = ntohs(init_chk->init.num_inbound_streams); /* choose what I want */ if (asoc != NULL) { if (asoc->streamoutcnt > asoc->pre_open_streams) {
i_want = asoc->streamoutcnt;
} else {
i_want = asoc->pre_open_streams;
}
} else {
i_want = inp->sctp_ep.pre_open_stream_count;
} if (his_limit < i_want) { /* I Want more :< */
initack->init.num_outbound_streams = init_chk->init.num_inbound_streams;
} else { /* I can have what I want :> */
initack->init.num_outbound_streams = htons(i_want);
} /* tell him his limit. */
initack->init.num_inbound_streams =
htons(inp->sctp_ep.max_open_streams_intome);
SCTP_TCB_LOCK_ASSERT(stcb); if ((asoc->prsctp_supported) &&
(asoc->sent_queue_cnt_removeable > 0)) {
TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) { /* *LookforchunksmarkedwiththePR_SCTPflagAND *thebufferspaceflag.Iftheonebeingsentis *equalorgreaterprioritythenpurgetheoldone *andfreesomespace.
*/ if (PR_SCTP_BUF_ENABLED(chk->flags)) { /* *ThisoneisPR-SCTPANDbufferspace *limitedtype
*/ if (chk->rec.data.timetodrop.tv_sec > (long)srcv->sinfo_timetolive) { /* *Lowernumbersequatestohigher *priority.Soiftheoneweare *lookingathasalargerpriority, *wewanttodropthedataandNOT *retransmitit.
*/ if (chk->data) { /* *Wereleasethebook_size *ifthembufishere
*/
int ret_spc;
uint8_t sent;
if (chk->sent > SCTP_DATAGRAM_UNSENT)
sent = 1; else
sent = 0;
ret_spc = sctp_release_pr_sctp_chunk(stcb, chk,
sent,
SCTP_SO_LOCKED);
freed_spc += ret_spc; if (freed_spc >= dataout) { return;
}
} /* if chunk was present */
} /* if of sufficient priority */
} /* if chunk has enabled */
} /* tailqforeach */
TAILQ_FOREACH_SAFE(chk, &asoc->send_queue, sctp_next, nchk) { /* Here we must move to the sent queue and mark */ if (PR_SCTP_BUF_ENABLED(chk->flags)) { if (chk->rec.data.timetodrop.tv_sec > (long)srcv->sinfo_timetolive) { if (chk->data) { /* *Wereleasethebook_size *ifthembufishere
*/
int ret_spc;
freed_spc += ret_spc; if (freed_spc >= dataout) { return;
}
} /* end if chk->data */
} /* end if right class */
} /* end if chk pr-sctp */
} /* tailqforeachsafe (chk) */
} /* if enabled in asoc */
}
/* Given an mbuf chain, put it *intotheassociationsendqueueand *placeitonthewheel
*/ if (srcv->sinfo_stream >= stcb->asoc.streamoutcnt) { /* Invalid stream number */
SCTP_LTRACE_ERR_RET_PKT(m, NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL; goto out_now;
} if ((stcb->asoc.stream_locked) &&
(stcb->asoc.stream_locked_on != srcv->sinfo_stream)) {
SCTP_LTRACE_ERR_RET_PKT(m, NULL, stcb, net, SCTP_FROM_SCTP_OUTPUT, EINVAL);
error = EINVAL; goto out_now;
} if ((stcb->asoc.strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPEN) &&
(stcb->asoc.strmout[srcv->sinfo_stream].state != SCTP_STREAM_OPENING)) { /* *Can'tqueueanydatawhilestreamresetisunderway.
*/ if (stcb->asoc.strmout[srcv->sinfo_stream].state > SCTP_STREAM_OPEN) {
error = EAGAIN;
} else {
error = EINVAL;
} goto out_now;
} /* Now can we send this? */ if ((SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_SENT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
(stcb->asoc.state & SCTP_STATE_SHUTDOWN_PENDING)) { /* got data while shutting down */
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, EPIPE);
error = EPIPE; goto out_now;
}
sctp_alloc_a_strmoq(stcb, sp); if (sp == NULL) {
SCTP_LTRACE_ERR_RET(NULL, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
error = ENOMEM; goto out_now;
}
sp->sinfo_flags = srcv->sinfo_flags;
sp->timetolive = srcv->sinfo_timetolive;
sp->ppid = srcv->sinfo_ppid;
sp->context = srcv->sinfo_context;
sp->fsn = 0; if (sp->sinfo_flags & SCTP_ADDR_OVER) {
sp->net = net;
atomic_add_int(&sp->net->ref_count, 1);
} else {
sp->net = NULL;
}
(void)SCTP_GETTIME_TIMEVAL(&sp->ts);
sp->sid = srcv->sinfo_stream;
sp->msg_is_complete = 1;
sp->sender_all_done = 1;
sp->some_taken = 0;
sp->data = m;
sp->tail_mbuf = NULL;
sctp_set_prsctp_policy(sp); /* We could in theory (for sendall) sifa the length *in,butwewouldstillhavetohuntthroughthe *chainsinceweneedtosetupthetail_mbuf
*/
sp->length = 0; for (at = m; at; at = SCTP_BUF_NEXT(at)) { if (SCTP_BUF_NEXT(at) == NULL)
sp->tail_mbuf = at;
sp->length += SCTP_BUF_LEN(at);
} if (srcv->sinfo_keynumber_valid) {
sp->auth_keyid = srcv->sinfo_keynumber;
} else {
sp->auth_keyid = stcb->asoc.authinfo.active_keyid;
} if (sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.peer_auth_chunks)) {
sctp_auth_key_acquire(stcb, sp->auth_keyid);
sp->holds_key_ref = 1;
}
strm = &stcb->asoc.strmout[srcv->sinfo_stream];
sctp_snd_sb_alloc(stcb, sp->length);
atomic_add_int(&stcb->asoc.stream_queue_cnt, 1);
TAILQ_INSERT_TAIL(&strm->outqueue, sp, next);
stcb->asoc.ss_functions.sctp_ss_add_to_stream(stcb, &stcb->asoc, strm, sp);
m = NULL;
out_now: if (m) {
sctp_m_freem(m);
} return (error);
}
staticstruct mbuf *
sctp_copy_mbufchain(struct mbuf *clonechain, struct mbuf *outchain, struct mbuf **endofchain, int can_take_mbuf, int sizeofcpy,
uint8_t copy_by_ref)
{ struct mbuf *m; struct mbuf *appendchain;
caddr_t cp; int len;
if (endofchain == NULL) { /* error */
error_out: if (outchain)
sctp_m_freem(outchain); return (NULL);
} if (can_take_mbuf) {
appendchain = clonechain;
} else { if (!copy_by_ref &&
(sizeofcpy <= (int)((((SCTP_BASE_SYSCTL(sctp_mbuf_threshold_count) - 1) * MLEN) + MHLEN)))) { /* Its not in a cluster */ if (*endofchain == NULL) { /* lets get a mbuf cluster */ if (outchain == NULL) { /* This is the general case */
new_mbuf:
outchain = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_HEADER); if (outchain == NULL) { goto error_out;
}
SCTP_BUF_LEN(outchain) = 0;
*endofchain = outchain; /* get the prepend space */
SCTP_BUF_RESV_UF(outchain, (SCTP_FIRST_MBUF_RESV+4));
} else { /* We really should not get a NULL in endofchain */ /* find end */
m = outchain; while (m) { if (SCTP_BUF_NEXT(m) == NULL) {
*endofchain = m; break;
}
m = SCTP_BUF_NEXT(m);
} /* sanity */ if (*endofchain == NULL) { /* huh, TSNH XXX maybe we should panic */
sctp_m_freem(outchain); goto new_mbuf;
}
} /* get the new end of length */
len = (int)M_TRAILINGSPACE(*endofchain);
} else { /* how much is left at the end? */
len = (int)M_TRAILINGSPACE(*endofchain);
} /* Find the end of the data, for appending */
cp = (mtod((*endofchain), caddr_t) + SCTP_BUF_LEN((*endofchain)));
/* Now lets copy it out */ if (len >= sizeofcpy) { /* It all fits, copy it in */
m_copydata(clonechain, 0, sizeofcpy, cp);
SCTP_BUF_LEN((*endofchain)) += sizeofcpy;
} else { /* fill up the end of the chain */ if (len > 0) {
m_copydata(clonechain, 0, len, cp);
SCTP_BUF_LEN((*endofchain)) += len; /* now we need another one */
sizeofcpy -= len;
}
m = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_HEADER); if (m == NULL) { /* We failed */ goto error_out;
}
SCTP_BUF_NEXT((*endofchain)) = m;
*endofchain = m;
cp = mtod((*endofchain), caddr_t);
m_copydata(clonechain, len, sizeofcpy, cp);
SCTP_BUF_LEN((*endofchain)) += sizeofcpy;
} return (outchain);
} else { /* copy the old fashion way */
appendchain = SCTP_M_COPYM(clonechain, 0, M_COPYALL, M_NOWAIT); #ifdef SCTP_MBUF_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) {
sctp_log_mbc(appendchain, SCTP_MBUF_ICOPY);
} #endif
}
} if (appendchain == NULL) { /* error */ if (outchain)
sctp_m_freem(outchain); return (NULL);
} if (outchain) { /* tack on to the end */ if (*endofchain != NULL) {
SCTP_BUF_NEXT(((*endofchain))) = appendchain;
} else {
m = outchain; while (m) { if (SCTP_BUF_NEXT(m) == NULL) {
SCTP_BUF_NEXT(m) = appendchain; break;
}
m = SCTP_BUF_NEXT(m);
}
} /* *saveofftheendandupdatetheend-chain *position
*/
m = appendchain; while (m) { if (SCTP_BUF_NEXT(m) == NULL) {
*endofchain = m; break;
}
m = SCTP_BUF_NEXT(m);
} return (outchain);
} else { /* save off the end and update the end-chain position */
m = appendchain; while (m) { if (SCTP_BUF_NEXT(m) == NULL) {
*endofchain = m; break;
}
m = SCTP_BUF_NEXT(m);
} return (appendchain);
}
}
staticint
sctp_med_chunk_output(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_association *asoc, int *num_out, int *reason_code, int control_only, int from_where, struct timeval *now, int *now_filled,
uint32_t frag_point, int so_locked);
staticvoid
sctp_sendall_iterator(struct sctp_inpcb *inp, struct sctp_tcb *stcb, void *ptr,
uint32_t val SCTP_UNUSED)
{ struct sctp_copy_all *ca; struct mbuf *m; int ret = 0; int added_control = 0; int un_sent, do_chunk_output = 1; struct sctp_association *asoc; struct sctp_nets *net;
ca = (struct sctp_copy_all *)ptr; if (ca->m == NULL) { return;
} if (ca->inp != inp) { /* TSNH */ return;
} if (ca->sndlen > 0) {
m = SCTP_M_COPYM(ca->m, 0, M_COPYALL, M_NOWAIT); if (m == NULL) { /* can't copy so we are done */
ca->cnt_failed++; return;
} #ifdef SCTP_MBUF_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) {
sctp_log_mbc(m, SCTP_MBUF_ICOPY);
} #endif
} else {
m = NULL;
}
SCTP_TCB_LOCK_ASSERT(stcb); if (stcb->asoc.alternate) {
net = stcb->asoc.alternate;
} else {
net = stcb->asoc.primary_destination;
} if (ca->sndrcv.sinfo_flags & SCTP_ABORT) { /* Abort this assoc with m as the user defined reason */ if (m != NULL) {
SCTP_BUF_PREPEND(m, sizeof(struct sctp_paramhdr), M_NOWAIT);
} else {
m = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), 0, M_NOWAIT, 1, MT_DATA); if (m != NULL) {
SCTP_BUF_LEN(m) = sizeof(struct sctp_paramhdr);
}
} if (m != NULL) { struct sctp_paramhdr *ph;
ph = mtod(m, struct sctp_paramhdr *);
ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
ph->param_length = htons((uint16_t)(sizeof(struct sctp_paramhdr) + ca->sndlen));
} /* We add one here to keep the assoc from *dis-appearingonus.
*/
atomic_add_int(&stcb->asoc.refcnt, 1);
sctp_abort_an_association(inp, stcb, m, false, SCTP_SO_NOT_LOCKED); /* sctp_abort_an_association calls sctp_free_asoc() *freeassociationwillNOTfreeitsincewe *incrementedtherefcnt..wedothistoprevent *itbeingfreedandthingsgettingtrickysince *wecouldendup(fromfree_asoc)callinginpcb_free *whichwouldgetarecursivelockcalltothe *iteratorlock..Butasaconsequenceofthatthe *stcbwillreturntousun-locked..sincefree_asoc *returnswitheithernoTCBortheTCBunlocked,we *mustrelock..tounlockintheiteratortimer:-0
*/
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1); goto no_chunk_output;
} else { if (m != NULL) {
ret = sctp_msg_append(stcb, net, m, &ca->sndrcv);
}
asoc = &stcb->asoc; if (ca->sndrcv.sinfo_flags & SCTP_EOF) { /* shutdown this assoc */ if (TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->sent_queue) &&
sctp_is_there_unsent_data(stcb, SCTP_SO_NOT_LOCKED) == 0) { if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) { goto abort_anyway;
} /* there is nothing queued to send, so I'm done... */ if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { /* only send SHUTDOWN the first time through */ if (SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_SENT);
sctp_stop_timers_for_shutdown(stcb);
sctp_send_shutdown(stcb, net);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
net);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
NULL);
added_control = 1;
do_chunk_output = 0;
}
} else { /* *westillgot(orjustgot)datatosend,soset *SHUTDOWN_PENDING
*/ /* *XXXsocketsdraftsaysthatSCTP_EOFshouldbe *sentwithnodata.currently,wewillallowuser *datatobesentfirstandmoveto *SHUTDOWN-PENDING
*/ if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT)) { if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_PARTIAL_MSG_LEFT);
}
SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_SHUTDOWN_PENDING); if (TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->sent_queue) &&
(asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) { struct mbuf *op_err; char msg[SCTP_DIAG_INFO_LEN];
/* now free everything */ if (ca->inp) { /* Lets clear the flag to allow others to run. */
SCTP_INP_WLOCK(ca->inp);
ca->inp->sctp_flags &= ~SCTP_PCB_FLAGS_SND_ITERATOR_UP;
SCTP_INP_WUNLOCK(ca->inp);
}
sctp_m_freem(ca->m);
SCTP_FREE(ca, SCTP_M_COPYAL);
}
for (i = 0; i < bundle_at; i++) { /* off of the send queue */
TAILQ_REMOVE(&asoc->send_queue, data_list[i], sctp_next);
asoc->send_queue_cnt--; if (i > 0) { /* *AnychunkNOT0youzapthetimechunk0gets *zappedorsetbasedonifaRTOmeasurementis *needed.
*/
data_list[i]->do_rtt = 0;
} /* record time */
data_list[i]->sent_rcv_time = net->last_sent_time;
data_list[i]->rec.data.cwnd_at_send = net->cwnd;
data_list[i]->rec.data.fast_retran_tsn = data_list[i]->rec.data.tsn; if (data_list[i]->whoTo == NULL) {
data_list[i]->whoTo = net;
atomic_add_int(&net->ref_count, 1);
} /* on to the sent queue */
tp1 = TAILQ_LAST(&asoc->sent_queue, sctpchunk_listhead); if ((tp1) && SCTP_TSN_GT(tp1->rec.data.tsn, data_list[i]->rec.data.tsn)) { struct sctp_tmit_chunk *tpp;
/* need to move back */
back_up_more:
tpp = TAILQ_PREV(tp1, sctpchunk_listhead, sctp_next); if (tpp == NULL) {
TAILQ_INSERT_BEFORE(tp1, data_list[i], sctp_next); goto all_done;
}
tp1 = tpp; if (SCTP_TSN_GT(tp1->rec.data.tsn, data_list[i]->rec.data.tsn)) { goto back_up_more;
}
TAILQ_INSERT_AFTER(&asoc->sent_queue, tp1, data_list[i], sctp_next);
} else {
TAILQ_INSERT_TAIL(&asoc->sent_queue,
data_list[i],
sctp_next);
}
all_done: /* This does not lower until the cum-ack passes it */
asoc->sent_queue_cnt++; if ((asoc->peers_rwnd <= 0) &&
(asoc->total_flight == 0) &&
(bundle_at == 1)) { /* Mark the chunk as being a window probe */
SCTP_STAT_INCR(sctps_windowprobed);
} #ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xC2, 3); #endif
data_list[i]->sent = SCTP_DATAGRAM_SENT;
data_list[i]->snd_count = 1;
data_list[i]->rec.data.chunk_was_revoked = 0; if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_FLIGHT_LOG_UP,
data_list[i]->whoTo->flight_size,
data_list[i]->book_size,
(uint32_t)(uintptr_t)data_list[i]->whoTo,
data_list[i]->rec.data.tsn);
}
sctp_flight_size_increase(data_list[i]);
sctp_total_flight_increase(stcb, data_list[i]); if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_RWND_ENABLE) {
sctp_log_rwnd(SCTP_DECREASE_PEER_RWND,
asoc->peers_rwnd, data_list[i]->send_size, SCTP_BASE_SYSCTL(sctp_peer_chunk_oh));
}
asoc->peers_rwnd = sctp_sbspace_sub(asoc->peers_rwnd,
(uint32_t) (data_list[i]->send_size + SCTP_BASE_SYSCTL(sctp_peer_chunk_oh))); if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) { /* SWS sender side engages */
asoc->peers_rwnd = 0;
}
} if (asoc->cc_functions.sctp_cwnd_update_packet_transmitted) {
(*asoc->cc_functions.sctp_cwnd_update_packet_transmitted)(stcb, net);
}
}
TAILQ_FOREACH_SAFE(chk, &asoc->control_send_queue, sctp_next, nchk) { if ((chk->rec.chunk_id.id == SCTP_SELECTIVE_ACK) ||
(chk->rec.chunk_id.id == SCTP_NR_SELECTIVE_ACK) || /* EY */
(chk->rec.chunk_id.id == SCTP_HEARTBEAT_REQUEST) ||
(chk->rec.chunk_id.id == SCTP_HEARTBEAT_ACK) ||
(chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) ||
(chk->rec.chunk_id.id == SCTP_SHUTDOWN) ||
(chk->rec.chunk_id.id == SCTP_SHUTDOWN_ACK) ||
(chk->rec.chunk_id.id == SCTP_OPERATION_ERROR) ||
(chk->rec.chunk_id.id == SCTP_PACKET_DROPPED) ||
(chk->rec.chunk_id.id == SCTP_COOKIE_ACK) ||
(chk->rec.chunk_id.id == SCTP_ECN_CWR) ||
(chk->rec.chunk_id.id == SCTP_ASCONF_ACK)) { /* Stray chunks must be cleaned up */
clean_up_anyway:
TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
asoc->ctrl_queue_cnt--; if (chk->data) {
sctp_m_freem(chk->data);
chk->data = NULL;
} if (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) {
asoc->fwd_tsn_cnt--;
}
sctp_free_a_chunk(stcb, chk, so_locked);
} elseif (chk->rec.chunk_id.id == SCTP_STREAM_RESET) { /* special handling, we must look into the param */ if (chk != asoc->str_reset) { goto clean_up_anyway;
}
}
}
}
static uint32_t
sctp_can_we_split_this(struct sctp_tcb *stcb, uint32_t length,
uint32_t space_left, uint32_t frag_point, int eeor_on)
{ /* Make a decision on if I should split a *msgintomultipleparts.Thisisonlyaskedof *incompletemessages.
*/ if (eeor_on) { /* If we are doing EEOR we need to always send *itifitstheentirething,sinceitmight *bealltheguyisputtinginthehopper.
*/ if (space_left >= length) { /*- *Ifwehavedataoutstanding, *wegetanotherchancewhenthesack *arrivestotransmit-waitformoredata
*/ if (stcb->asoc.total_flight == 0) { /* If nothing is in flight, we zero *thepacketcounter.
*/ return (length);
} return (0);
} else { /* You can fill the rest */ return (space_left);
}
} /*- *Forthosestrangefolkthatmakethesendbuffer *smallerthanourfragmentationpoint,wecan't *getafullmsginsowehavetoallowsplitting.
*/ if (SCTP_SB_LIMIT_SND(stcb->sctp_socket) < frag_point) { return (length);
} if ((length <= space_left) ||
((length - space_left) < SCTP_BASE_SYSCTL(sctp_min_residual))) { /* Sub-optimal residual don't split in non-eeor mode. */ return (0);
} /* If we reach here length is larger *thanthespace_left.Dowewishtosplit *itforthesakeofpacketputtingtogether?
*/ if (space_left >= min(SCTP_BASE_SYSCTL(sctp_min_split_point), frag_point)) { /* Its ok to split it */ return (min(space_left, frag_point));
} /* Nope, can't split */ return (0);
}
static uint32_t
sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_stream_out *strq,
uint32_t space_left,
uint32_t frag_point, int *giveup, int eeor_mode, int *bail, int so_locked)
{ /* Move from the stream to the send_queue keeping track of the total */ struct sctp_association *asoc; struct sctp_stream_queue_pending *sp; struct sctp_tmit_chunk *chk; struct sctp_data_chunk *dchkh=NULL; struct sctp_idata_chunk *ndchkh=NULL;
uint32_t to_move, length; int leading;
uint8_t rcv_flags = 0;
uint8_t some_taken;
SCTP_TCB_LOCK_ASSERT(stcb);
asoc = &stcb->asoc;
one_more_time: /*sa_ignore FREED_MEMORY*/
sp = TAILQ_FIRST(&strq->outqueue); if (sp == NULL) {
sp = TAILQ_FIRST(&strq->outqueue); if (sp) { goto one_more_time;
} if ((sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_EXPLICIT_EOR) == 0) &&
(stcb->asoc.idata_supported == 0) &&
(strq->last_msg_incomplete)) {
SCTP_PRINTF("Huh? Stream:%d lm_in_c=%d but queue is NULL\n",
strq->sid,
strq->last_msg_incomplete);
strq->last_msg_incomplete = 0;
}
to_move = 0; goto out_of;
} if ((sp->msg_is_complete) && (sp->length == 0)) { if (sp->sender_all_done) { /* We are doing deferred cleanup. Last *timethroughwhenwetookallthedata *thesender_all_donewasnotset.
*/ if ((sp->put_last_out == 0) && (sp->discard_rest == 0)) {
SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n");
SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d\n",
sp->sender_all_done,
sp->length,
sp->msg_is_complete,
sp->put_last_out);
}
atomic_subtract_int(&asoc->stream_queue_cnt, 1);
TAILQ_REMOVE(&strq->outqueue, sp, next);
stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, strq, sp); if ((strq->state == SCTP_STREAM_RESET_PENDING) &&
(strq->chunks_on_queues == 0) &&
TAILQ_EMPTY(&strq->outqueue)) {
stcb->asoc.trigger_reset = 1;
} if (sp->net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
} if (sp->data) {
sctp_m_freem(sp->data);
sp->data = NULL;
}
sctp_free_a_strmoq(stcb, sp, so_locked); /* back to get the next msg */ goto one_more_time;
} else { /* sender just finished this but *stillholdsareference
*/
*giveup = 1;
to_move = 0; goto out_of;
}
} else { /* is there some to get */ if (sp->length == 0) { /* no */
*giveup = 1;
to_move = 0; goto out_of;
} elseif (sp->discard_rest) { /* Whack down the size */
atomic_subtract_int(&stcb->asoc.total_output_queue_size, sp->length); if ((stcb->sctp_socket != NULL) &&
((stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) ||
(stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL))) {
SCTP_SB_DECR(&stcb->sctp_socket->so_snd, sp->length);
} if (sp->data) {
sctp_m_freem(sp->data);
sp->data = NULL;
sp->tail_mbuf = NULL;
}
sp->length = 0;
sp->some_taken = 1;
*giveup = 1;
to_move = 0; goto out_of;
}
}
some_taken = sp->some_taken;
length = sp->length; if (sp->msg_is_complete) { /* The message is complete */
to_move = min(length, frag_point); if (to_move == length) { /* All of it fits in the MTU */ if (sp->some_taken) {
rcv_flags |= SCTP_DATA_LAST_FRAG;
} else {
rcv_flags |= SCTP_DATA_NOT_FRAG;
}
sp->put_last_out = 1; if (sp->sinfo_flags & SCTP_SACK_IMMEDIATELY) {
rcv_flags |= SCTP_DATA_SACK_IMMEDIATELY;
}
} else { /* Not all of it fits, we fragment */ if (sp->some_taken == 0) {
rcv_flags |= SCTP_DATA_FIRST_FRAG;
}
sp->some_taken = 1;
}
} else {
to_move = sctp_can_we_split_this(stcb, length, space_left, frag_point, eeor_mode); if (to_move > 0) { if (to_move >= length) {
to_move = length;
} if (sp->some_taken == 0) {
rcv_flags |= SCTP_DATA_FIRST_FRAG;
sp->some_taken = 1;
}
} else { /* Nothing to take. */
*giveup = 1;
to_move = 0; goto out_of;
}
}
/* If we reach here, we can copy out a chunk */
sctp_alloc_a_chunk(stcb, chk); if (chk == NULL) { /* No chunk memory */
*giveup = 1;
to_move = 0; goto out_of;
} /* Setup for unordered if needed by looking *attheusersentinfoflags.
*/ if (sp->sinfo_flags & SCTP_UNORDERED) {
rcv_flags |= SCTP_DATA_UNORDERED;
} if (SCTP_BASE_SYSCTL(sctp_enable_sack_immediately) &&
(sp->sinfo_flags & SCTP_EOF) == SCTP_EOF) {
rcv_flags |= SCTP_DATA_SACK_IMMEDIATELY;
} /* clear out the chunk before setting up */
memset(chk, 0, sizeof(*chk));
chk->rec.data.rcv_flags = rcv_flags;
if (to_move >= length) { /* we think we can steal the whole thing */ if (to_move < sp->length) { /* bail, it changed */ goto dont_do_it;
}
chk->data = sp->data;
chk->last_mbuf = sp->tail_mbuf; /* register the stealing */
sp->data = sp->tail_mbuf = NULL;
} else { struct mbuf *m;
dont_do_it:
chk->data = SCTP_M_COPYM(sp->data, 0, to_move, M_NOWAIT);
chk->last_mbuf = NULL; if (chk->data == NULL) {
sp->some_taken = some_taken;
sctp_free_a_chunk(stcb, chk, so_locked);
*bail = 1;
to_move = 0; goto out_of;
} #ifdef SCTP_MBUF_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_MBUF_LOGGING_ENABLE) {
sctp_log_mbc(chk->data, SCTP_MBUF_ICOPY);
} #endif /* Pull off the data */
m_adj(sp->data, to_move); /* Now lets work our way down and compact it */
m = sp->data; while (m && (SCTP_BUF_LEN(m) == 0)) {
sp->data = SCTP_BUF_NEXT(m);
SCTP_BUF_NEXT(m) = NULL; if (sp->tail_mbuf == m) { /*- *Freeingtail?TSNHsince *wesupposedlyweretakingless *thanthesp->length.
*/ #ifdef INVARIANTS
panic("Huh, freeing tail? - TSNH"); #else
SCTP_PRINTF("Huh, freeing tail? - TSNH\n");
sp->tail_mbuf = sp->data = NULL;
sp->length = 0; #endif
}
sctp_m_free(m);
m = sp->data;
}
} if (SCTP_BUF_IS_EXTENDED(chk->data)) {
chk->copy_by_ref = 1;
} else {
chk->copy_by_ref = 0;
} /* get last_mbuf and counts of mb usage *Thisisuglybuthopefullyitsonlyonembuf.
*/ if (chk->last_mbuf == NULL) {
chk->last_mbuf = chk->data; while (SCTP_BUF_NEXT(chk->last_mbuf) != NULL) {
chk->last_mbuf = SCTP_BUF_NEXT(chk->last_mbuf);
}
}
if (to_move > length) { /*- This should not happen either *sincewealwayslowerto_movetothesize *ofsp->lengthifitslarger.
*/ #ifdef INVARIANTS
panic("Huh, how can to_move be larger?"); #else
SCTP_PRINTF("Huh, how can to_move be larger?\n");
sp->length = 0; #endif
} else {
atomic_subtract_int(&sp->length, to_move);
}
leading = SCTP_DATA_CHUNK_OVERHEAD(stcb); if (M_LEADINGSPACE(chk->data) < leading) { /* Not enough room for a chunk header, get some */ struct mbuf *m;
if (total_moved == 0) { if ((stcb->asoc.sctp_cmt_on_off == 0) &&
(net == stcb->asoc.primary_destination)) { /* ran dry for primary network net */
SCTP_STAT_INCR(sctps_primary_randry);
} elseif (stcb->asoc.sctp_cmt_on_off > 0) { /* ran dry with CMT on */
SCTP_STAT_INCR(sctps_cmt_randry);
}
}
}
if (net == NULL) { return;
}
asoc = &stcb->asoc; for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
TAILQ_FOREACH(sp, &stcb->asoc.strmout[i].outqueue, next) { if (sp->net == net) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
}
}
}
TAILQ_FOREACH(chk, &asoc->send_queue, sctp_next) { if (chk->whoTo == net) {
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
}
}
}
int
sctp_med_chunk_output(struct sctp_inpcb *inp, struct sctp_tcb *stcb, struct sctp_association *asoc, int *num_out, int *reason_code, int control_only, int from_where, struct timeval *now, int *now_filled,
uint32_t frag_point, int so_locked)
{ /** *Okthisisthegenericchunkservicequeue.wemustdothe *following: *-Servicethestreamqueuethatisnext,movingany *message(noteImustgetacompletemessagei.e.FIRST/MIDDLEand *LASTtotheoutqueueinonepass)andassigningTSN's.This *onlyappliesthoughifthepeerdoesnotsupportNDATA.ForNDATA *chunksitsoktonotsendtheentiremessage;-) *-Checktoseeifthecwnd/rwndallowsanyoutput,ifsowegoaheadand *formulateandsendthelowlevelchunks.Makingsuretocombine *anycontrolinthecontrolchunkqueuealso.
*/ struct sctp_nets *net, *start_at, *sack_goes_to = NULL, *old_start_at = NULL; struct mbuf *outchain, *endoutchain; struct sctp_tmit_chunk *chk, *nchk;
/* temp arrays for unlinking */ struct sctp_tmit_chunk *data_list[SCTP_MAX_DATA_BUNDLING]; int no_fragmentflg, error; unsignedint max_rwnd_per_dest, max_send_per_dest; int one_chunk, hbflag, skip_data_for_this_net; int asconf, cookie, no_out_cnt; int bundle_at, ctl_cnt, no_data_chunks, eeor_mode; unsignedint mtu, r_mtu, omtu, mx_mtu, to_out; int tsns_sent = 0;
uint32_t auth_offset; struct sctp_auth_chunk *auth;
uint16_t auth_keyid; int override_ok = 1; int skip_fill_up = 0; int data_auth_reqd = 0; /* JRS 5/14/07 - Add flag for whether a heartbeat is sent to
the destination. */ int quit_now = 0; bool use_zero_crc;
void
sctp_send_heartbeat_ack(struct sctp_tcb *stcb,
struct mbuf *m,
int offset,
int chk_length,
struct sctp_nets *net)
{
/*
* take a HB request and make it into a HB ack and send it.
*/
struct mbuf *outchain;
struct sctp_chunkhdr *chdr;
struct sctp_tmit_chunk *chk;
if (net == NULL)
/* must have a net pointer */
return;
void
sctp_send_asconf(struct sctp_tcb *stcb, struct sctp_nets *net, int addr_locked)
{
/*
* formulate and queue an ASCONF to the peer.
* ASCONF parameters should be queued on the assoc queue.
*/
struct sctp_tmit_chunk *chk;
struct mbuf *m_asconf;
int len;
SCTP_TCB_LOCK_ASSERT(stcb);
if ((!TAILQ_EMPTY(&stcb->asoc.asconf_send_queue)) &&
(!sctp_is_feature_on(stcb->sctp_ep, SCTP_PCB_FLAGS_MULTIPLE_ASCONFS))) {
/* can't send a new one if there is one in flight already */
return;
}
/* compose an ASCONF chunk, maximum length is PMTU */
m_asconf = sctp_compose_asconf(stcb, &len, addr_locked);
if (m_asconf == NULL) {
return;
}
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
/* no memory */
sctp_m_freem(m_asconf);
return;
}
void
sctp_send_asconf_ack(struct sctp_tcb *stcb)
{
/*
* formulate and queue a asconf-ack back to sender.
* the asconf-ack must be stored in the tcb.
*/
struct sctp_tmit_chunk *chk;
struct sctp_asconf_ack *ack, *latest_ack;
struct mbuf *m_ack;
struct sctp_nets *net = NULL;
SCTP_TCB_LOCK_ASSERT(stcb);
/* Get the latest ASCONF-ACK */
latest_ack = TAILQ_LAST(&stcb->asoc.asconf_ack_sent, sctp_asconf_ackhead);
if (latest_ack == NULL) {
return;
}
if (latest_ack->last_sent_to != NULL &&
latest_ack->last_sent_to == stcb->asoc.last_control_chunk_from) {
/* we're doing a retransmission */
net = sctp_find_alternate_net(stcb, stcb->asoc.last_control_chunk_from, 0);
if (net == NULL) {
/* no alternate */
if (stcb->asoc.last_control_chunk_from == NULL) {
if (stcb->asoc.alternate) {
net = stcb->asoc.alternate;
} else {
net = stcb->asoc.primary_destination;
}
} else {
net = stcb->asoc.last_control_chunk_from;
}
}
} else {
/* normal case */
if (stcb->asoc.last_control_chunk_from == NULL) {
if (stcb->asoc.alternate) {
net = stcb->asoc.alternate;
} else {
net = stcb->asoc.primary_destination;
}
} else {
net = stcb->asoc.last_control_chunk_from;
}
}
latest_ack->last_sent_to = net;
static int
sctp_chunk_retransmission(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
struct sctp_association *asoc,
int *cnt_out, struct timeval *now, int *now_filled, int *fr_done, int so_locked)
{
/*-
* send out one MTU of retransmission. If fast_retransmit is
* happening we ignore the cwnd. Otherwise we obey the cwnd and
* rwnd. For a Cookie or Asconf in the control chunk queue we
* retransmit them by themselves.
*
* For data chunks we will pick out the lowest TSN's in the sent_queue
* marked for resend and bundle them all together (up to a MTU of
* destination). The address to send to should have been
* selected/changed where the retransmission was marked (i.e. in FR
* or t3-timeout routines).
*/
struct sctp_tmit_chunk *data_list[SCTP_MAX_DATA_BUNDLING];
struct sctp_tmit_chunk *chk, *fwd;
struct mbuf *m, *endofchain;
struct sctp_nets *net = NULL;
uint32_t tsns_sent = 0;
int no_fragmentflg, bundle_at;
unsigned int mtu;
int error, i, one_chunk, fwd_tsn, ctl_cnt, tmr_started;
struct sctp_auth_chunk *auth = NULL;
uint32_t auth_offset = 0;
uint16_t auth_keyid;
int override_ok = 1;
int data_auth_reqd = 0;
uint32_t dmtu = 0;
bool use_zero_crc;
#if defined(__APPLE__) && !defined(__Userspace__)
if (so_locked) {
sctp_lock_assert(SCTP_INP_SO(inp));
} else {
sctp_unlock_assert(SCTP_INP_SO(inp));
}
#endif
SCTP_TCB_LOCK_ASSERT(stcb);
tmr_started = ctl_cnt = 0;
no_fragmentflg = 1;
fwd_tsn = 0;
*cnt_out = 0;
fwd = NULL;
endofchain = m = NULL;
auth_keyid = stcb->asoc.authinfo.active_keyid;
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xC3, 1);
#endif
if ((TAILQ_EMPTY(&asoc->sent_queue)) &&
(TAILQ_EMPTY(&asoc->control_send_queue))) {
SCTPDBG(SCTP_DEBUG_OUTPUT1,"SCTP hits empty queue with cnt set to %d?\n",
asoc->sent_queue_retran_cnt);
asoc->sent_queue_cnt = 0;
asoc->sent_queue_cnt_removeable = 0;
/* send back 0/0 so we enter normal transmission */
*cnt_out = 0;
return (0);
}
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
if ((chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) ||
(chk->rec.chunk_id.id == SCTP_STREAM_RESET) ||
(chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN)) {
if (chk->sent != SCTP_DATAGRAM_RESEND) {
continue;
}
if (chk->rec.chunk_id.id == SCTP_STREAM_RESET) {
if (chk != asoc->str_reset) {
/*
* not eligible for retran if its
* not ours
*/
continue;
}
}
ctl_cnt++;
if (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) {
fwd_tsn = 1;
}
/*
* Add an AUTH chunk, if chunk requires it save the
* offset into the chain for AUTH
*/
if ((auth == NULL) &&
(sctp_auth_is_required_chunk(chk->rec.chunk_id.id,
stcb->asoc.peer_auth_chunks))) {
m = sctp_add_auth_chunk(m, &endofchain,
&auth, &auth_offset,
stcb,
chk->rec.chunk_id.id);
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
}
m = sctp_copy_mbufchain(chk->data, m, &endofchain, 0, chk->send_size, chk->copy_by_ref);
break;
}
}
one_chunk = 0;
/* do we have control chunks to retransmit? */
if (m != NULL) {
/* Start a timer no matter if we succeed or fail */
switch (asoc->snd_edmid) {
case SCTP_EDMID_LOWER_LAYER_DTLS:
use_zero_crc = true;
break;
default:
use_zero_crc = false;
break;
}
if (chk->rec.chunk_id.id == SCTP_COOKIE_ECHO) {
sctp_timer_start(SCTP_TIMER_TYPE_COOKIE, inp, stcb, chk->whoTo);
use_zero_crc = false;
} else if (chk->rec.chunk_id.id == SCTP_ASCONF) {
/* XXXMT: Can this happen? */
sctp_timer_start(SCTP_TIMER_TYPE_ASCONF, inp, stcb, chk->whoTo);
use_zero_crc = false;
}
chk->snd_count++; /* update our count */
if ((error = sctp_lowlevel_chunk_output(inp, stcb, chk->whoTo,
(struct sockaddr *)&chk->whoTo->ro._l_addr, m,
auth_offset, auth, stcb->asoc.authinfo.active_keyid,
no_fragmentflg, 0, 0,
inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag),
chk->whoTo->port, NULL,
#if defined(__FreeBSD__) && !defined(__Userspace__) 0, 0,
#endif
use_zero_crc,
so_locked))) {
SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error);
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
}
return (error);
} else {
asoc->ifp_had_enobuf = 0;
}
endofchain = NULL;
auth = NULL;
auth_offset = 0;
/*
* We don't want to mark the net->sent time here since this
* we use this for HB and retrans cannot measure RTT
*/
/* (void)SCTP_GETTIME_TIMEVAL(&chk->whoTo->last_sent_time); */
*cnt_out += 1;
chk->sent = SCTP_DATAGRAM_SENT;
sctp_ucount_decr(stcb->asoc.sent_queue_retran_cnt);
if (fwd_tsn == 0) {
return (0);
} else {
/* Clean up the fwd-tsn list */
sctp_clean_up_ctl(stcb, asoc, so_locked);
return (0);
}
}
/*
* Ok, it is just data retransmission we need to do or that and a
* fwd-tsn with it all.
*/
if (TAILQ_EMPTY(&asoc->sent_queue)) {
return (SCTP_RETRAN_DONE);
}
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT)) {
/* not yet open, resend the cookie and that is it */
return (1);
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(20, inp, stcb, NULL);
#endif
data_auth_reqd = sctp_auth_is_required_chunk(SCTP_DATA, stcb->asoc.peer_auth_chunks);
TAILQ_FOREACH(chk, &asoc->sent_queue, sctp_next) {
if (chk->sent != SCTP_DATAGRAM_RESEND) {
/* No, not sent to this net or not ready for rtx */
continue;
}
if (chk->data == NULL) {
SCTP_PRINTF("TSN:%x chk->snd_count:%d chk->sent:%d can't retran - no data\n",
chk->rec.data.tsn, chk->snd_count, chk->sent);
continue;
}
if ((SCTP_BASE_SYSCTL(sctp_max_retran_chunk)) &&
(chk->snd_count >= SCTP_BASE_SYSCTL(sctp_max_retran_chunk))) {
struct mbuf *op_err;
char msg[SCTP_DIAG_INFO_LEN];
SCTP_SNPRINTF(msg, sizeof(msg), "TSN %8.8x retransmitted %d times, giving up",
chk->rec.data.tsn, chk->snd_count);
op_err = sctp_generate_cause(SCTP_BASE_SYSCTL(sctp_diag_info_code),
msg);
atomic_add_int(&stcb->asoc.refcnt, 1);
sctp_abort_an_association(stcb->sctp_ep, stcb, op_err,
false, so_locked);
SCTP_TCB_LOCK(stcb);
atomic_subtract_int(&stcb->asoc.refcnt, 1);
return (SCTP_RETRAN_EXIT);
}
/* pick up the net */
net = chk->whoTo;
switch (net->ro._l_addr.sa.sa_family) {
#ifdef INET
case AF_INET:
mtu = net->mtu - SCTP_MIN_V4_OVERHEAD;
break;
#endif
#ifdef INET6
case AF_INET6:
mtu = net->mtu - SCTP_MIN_OVERHEAD;
break;
#endif
#if defined(__Userspace__)
case AF_CONN:
mtu = net->mtu - sizeof(struct sctphdr);
break;
#endif
default:
/* TSNH */
mtu = net->mtu;
break;
}
if ((asoc->peers_rwnd < mtu) && (asoc->total_flight > 0)) {
/* No room in peers rwnd */
uint32_t tsn;
tsn = asoc->last_acked_seq + 1;
if (tsn == chk->rec.data.tsn) {
/*
* we make a special exception for this
* case. The peer has no rwnd but is missing
* the lowest chunk.. which is probably what
* is holding up the rwnd.
*/
goto one_chunk_around;
}
return (1);
}
one_chunk_around:
if (asoc->peers_rwnd < mtu) {
one_chunk = 1;
if ((asoc->peers_rwnd == 0) &&
(asoc->total_flight == 0)) {
chk->window_probe = 1;
chk->whoTo->window_probe = 1;
}
}
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xC3, 2);
#endif
bundle_at = 0;
m = NULL;
net->fast_retran_ip = 0;
if (chk->rec.data.doing_fast_retransmit == 0) {
/*
* if no FR in progress skip destination that have
* flight_size > cwnd.
*/
if (net->flight_size >= net->cwnd) {
continue;
}
} else {
/*
* Mark the destination net to have FR recovery
* limits put on it.
*/
*fr_done = 1;
net->fast_retran_ip = 1;
}
/*
* if no AUTH is yet included and this chunk requires it,
* make sure to account for it. We don't apply the size
* until the AUTH chunk is actually added below in case
* there is no room for this chunk.
*/
if (data_auth_reqd && (auth == NULL)) {
dmtu = sctp_get_auth_chunk_len(stcb->asoc.peer_hmac_id);
} else
dmtu = 0;
if ((chk->send_size <= (mtu - dmtu)) ||
(chk->flags & CHUNK_FLAGS_FRAGMENT_OK)) {
/* ok we will add this one */
if (data_auth_reqd) {
if (auth == NULL) {
m = sctp_add_auth_chunk(m,
&endofchain,
&auth,
&auth_offset,
stcb,
SCTP_DATA);
auth_keyid = chk->auth_keyid;
override_ok = 0;
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
} else if (override_ok) {
auth_keyid = chk->auth_keyid;
override_ok = 0;
} else if (chk->auth_keyid != auth_keyid) {
/* different keyid, so done bundling */
break;
}
}
m = sctp_copy_mbufchain(chk->data, m, &endofchain, 0, chk->send_size, chk->copy_by_ref);
if (m == NULL) {
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
/* Do clear IP_DF ? */
if (chk->flags & CHUNK_FLAGS_FRAGMENT_OK) {
no_fragmentflg = 0;
}
/* update our MTU size */
if (mtu > (chk->send_size + dmtu))
mtu -= (chk->send_size + dmtu);
else
mtu = 0;
data_list[bundle_at++] = chk;
if (one_chunk && (asoc->total_flight <= 0)) {
SCTP_STAT_INCR(sctps_windowprobed);
}
}
if (one_chunk == 0) {
/*
* now are there anymore forward from chk to pick
* up?
*/
for (fwd = TAILQ_NEXT(chk, sctp_next); fwd != NULL; fwd = TAILQ_NEXT(fwd, sctp_next)) {
if (fwd->sent != SCTP_DATAGRAM_RESEND) {
/* Nope, not for retran */
continue;
}
if (fwd->whoTo != net) {
/* Nope, not the net in question */
continue;
}
if (data_auth_reqd && (auth == NULL)) {
dmtu = sctp_get_auth_chunk_len(stcb->asoc.peer_hmac_id);
} else
dmtu = 0;
if (fwd->send_size <= (mtu - dmtu)) {
if (data_auth_reqd) {
if (auth == NULL) {
m = sctp_add_auth_chunk(m,
&endofchain,
&auth,
&auth_offset,
stcb,
SCTP_DATA);
auth_keyid = fwd->auth_keyid;
override_ok = 0;
SCTP_STAT_INCR_COUNTER64(sctps_outcontrolchunks);
} else if (override_ok) {
auth_keyid = fwd->auth_keyid;
override_ok = 0;
} else if (fwd->auth_keyid != auth_keyid) {
/* different keyid, so done bundling */
break;
}
}
m = sctp_copy_mbufchain(fwd->data, m, &endofchain, 0, fwd->send_size, fwd->copy_by_ref);
if (m == NULL) {
SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_OUTPUT, ENOMEM);
return (ENOMEM);
}
/* Do clear IP_DF ? */
if (fwd->flags & CHUNK_FLAGS_FRAGMENT_OK) {
no_fragmentflg = 0;
}
/* update our MTU size */
if (mtu > (fwd->send_size + dmtu))
mtu -= (fwd->send_size + dmtu);
else
mtu = 0;
data_list[bundle_at++] = fwd;
if (bundle_at >= SCTP_MAX_DATA_BUNDLING) {
break;
}
} else {
/* can't fit so we are done */
break;
}
}
}
/* Is there something to send for this destination? */
if (m) {
/*
* No matter if we fail/or succeed we should start a
* timer. A failure is like a lost IP packet :-)
*/
if (!SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
/*
* no timer running on this destination
* restart it.
*/
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
tmr_started = 1;
}
switch (asoc->snd_edmid) {
case SCTP_EDMID_LOWER_LAYER_DTLS:
use_zero_crc = true;
break;
default:
use_zero_crc = false;
break;
}
/* Now lets send it, if there is anything to send :> */
if ((error = sctp_lowlevel_chunk_output(inp, stcb, net,
(struct sockaddr *)&net->ro._l_addr, m,
auth_offset, auth, auth_keyid,
no_fragmentflg, 0, 0,
inp->sctp_lport, stcb->rport, htonl(stcb->asoc.peer_vtag),
net->port, NULL,
#if defined(__FreeBSD__) && !defined(__Userspace__) 0, 0,
#endif
use_zero_crc,
so_locked))) {
/* error, we could not output */
SCTPDBG(SCTP_DEBUG_OUTPUT3, "Gak send error %d\n", error);
if (error == ENOBUFS) {
asoc->ifp_had_enobuf = 1;
SCTP_STAT_INCR(sctps_lowlevelerr);
}
return (error);
} else {
asoc->ifp_had_enobuf = 0;
}
endofchain = NULL;
auth = NULL;
auth_offset = 0;
/* For HB's */
/*
* We don't want to mark the net->sent time here
* since this we use this for HB and retrans cannot
* measure RTT
*/
/* (void)SCTP_GETTIME_TIMEVAL(&net->last_sent_time); */
/* For auto-close */
if (*now_filled == 0) {
(void)SCTP_GETTIME_TIMEVAL(&asoc->time_last_sent);
*now = asoc->time_last_sent;
*now_filled = 1;
} else {
asoc->time_last_sent = *now;
}
*cnt_out += bundle_at;
#ifdef SCTP_AUDITING_ENABLED
sctp_audit_log(0xC4, bundle_at);
#endif
if (bundle_at) {
tsns_sent = data_list[0]->rec.data.tsn;
}
for (i = 0; i < bundle_at; i++) {
SCTP_STAT_INCR(sctps_sendretransdata);
data_list[i]->sent = SCTP_DATAGRAM_SENT;
/*
* When we have a revoked data, and we
* retransmit it, then we clear the revoked
* flag since this flag dictates if we
* subtracted from the fs
*/
if (data_list[i]->rec.data.chunk_was_revoked) {
/* Deflate the cwnd */
data_list[i]->whoTo->cwnd -= data_list[i]->book_size;
data_list[i]->rec.data.chunk_was_revoked = 0;
}
data_list[i]->snd_count++;
sctp_ucount_decr(asoc->sent_queue_retran_cnt);
/* record the time */
data_list[i]->sent_rcv_time = asoc->time_last_sent;
if (data_list[i]->book_size_scale) {
/*
* need to double the book size on
* this one
*/
data_list[i]->book_size_scale = 0;
/* Since we double the booksize, we must
* also double the output queue size, since this
* get shrunk when we free by this amount.
*/
atomic_add_int(&((asoc)->total_output_queue_size), data_list[i]->book_size);
data_list[i]->book_size *= 2;
} else {
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_RWND_ENABLE) {
sctp_log_rwnd(SCTP_DECREASE_PEER_RWND,
asoc->peers_rwnd, data_list[i]->send_size, SCTP_BASE_SYSCTL(sctp_peer_chunk_oh));
}
asoc->peers_rwnd = sctp_sbspace_sub(asoc->peers_rwnd,
(uint32_t) (data_list[i]->send_size +
SCTP_BASE_SYSCTL(sctp_peer_chunk_oh)));
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_FLIGHT_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_FLIGHT_LOG_UP_RSND,
data_list[i]->whoTo->flight_size,
data_list[i]->book_size,
(uint32_t)(uintptr_t)data_list[i]->whoTo,
data_list[i]->rec.data.tsn);
}
sctp_flight_size_increase(data_list[i]);
sctp_total_flight_increase(stcb, data_list[i]);
if (asoc->peers_rwnd < stcb->sctp_ep->sctp_ep.sctp_sws_sender) {
/* SWS sender side engages */
asoc->peers_rwnd = 0;
}
if ((i == 0) &&
(data_list[i]->rec.data.doing_fast_retransmit)) {
SCTP_STAT_INCR(sctps_sendfastretrans);
if ((data_list[i] == TAILQ_FIRST(&asoc->sent_queue)) &&
(tmr_started == 0)) {
/*-
* ok we just fast-retrans'd
* the lowest TSN, i.e the
* first on the list. In
* this case we want to give
* some more time to get a
* SACK back without a
* t3-expiring.
*/
sctp_timer_stop(SCTP_TIMER_TYPE_SEND, inp, stcb, net,
SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_2);
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, net);
}
}
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, net, tsns_sent, SCTP_CWND_LOG_FROM_RESEND);
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(21, inp, stcb, NULL);
#endif
} else {
/* None will fit */
return (1);
}
if (asoc->sent_queue_retran_cnt <= 0) {
/* all done we have no more to retran */
asoc->sent_queue_retran_cnt = 0;
break;
}
if (one_chunk) {
/* No more room in rwnd */
return (1);
}
/* stop the for loop here. we sent out a packet */
break;
}
return (0);
}
/* Validate that a timer is running somewhere */
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
if (SCTP_OS_TIMER_PENDING(&net->rxt_timer.timer)) {
/* Here is a timer */
return;
}
}
SCTP_TCB_LOCK_ASSERT(stcb);
/* Gak, we did not have a timer somewhere */
SCTPDBG(SCTP_DEBUG_OUTPUT3, "Deadlock avoided starting timer on a dest at retran\n");
if (asoc->alternate) {
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->alternate);
} else {
sctp_timer_start(SCTP_TIMER_TYPE_SEND, inp, stcb, asoc->primary_destination);
}
return;
}
void
sctp_chunk_output(struct sctp_inpcb *inp,
struct sctp_tcb *stcb,
int from_where,
int so_locked)
{
/*-
* Ok this is the generic chunk service queue. we must do the
* following:
* - See if there are retransmits pending, if so we must
* do these first.
* - Service the stream queue that is next, moving any
* message (note I must get a complete message i.e.
* FIRST/MIDDLE and LAST to the out queue in one pass) and assigning
* TSN's
* - Check to see if the cwnd/rwnd allows any output, if so we
* go ahead and formulate and send the low level chunks. Making sure
* to combine any control in the control chunk queue also.
*/
struct sctp_association *asoc;
struct sctp_nets *net;
int error = 0, num_out, tot_out = 0, ret = 0, reason_code;
unsigned int burst_cnt = 0;
struct timeval now;
int now_filled = 0;
int nagle_on;
uint32_t frag_point = sctp_get_frag_point(stcb);
int un_sent = 0;
int fr_done;
unsigned int tot_frs = 0;
#if defined(__APPLE__) && !defined(__Userspace__)
if (so_locked) {
sctp_lock_assert(SCTP_INP_SO(inp));
} else {
sctp_unlock_assert(SCTP_INP_SO(inp));
}
#endif
asoc = &stcb->asoc;
do_it_again:
/* The Nagle algorithm is only applied when handling a send call. */
if (from_where == SCTP_OUTPUT_FROM_USR_SEND) {
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NODELAY)) {
nagle_on = 0;
} else {
nagle_on = 1;
}
} else {
nagle_on = 0;
}
SCTP_TCB_LOCK_ASSERT(stcb);
if ((un_sent <= 0) &&
(TAILQ_EMPTY(&asoc->control_send_queue)) &&
(TAILQ_EMPTY(&asoc->asconf_send_queue)) &&
(asoc->sent_queue_retran_cnt == 0) &&
(asoc->trigger_reset == 0)) {
/* Nothing to do unless there is something to be sent left */
return;
}
/* Do we have something to send, data or control AND
* a sack timer running, if so piggy-back the sack.
*/
if (SCTP_OS_TIMER_PENDING(&stcb->asoc.dack_timer.timer)) {
sctp_send_sack(stcb, so_locked);
sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, NULL,
SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_3);
}
while (asoc->sent_queue_retran_cnt) {
/*-
* Ok, it is retransmission time only, we send out only ONE
* packet with a single call off to the retran code.
*/
if (from_where == SCTP_OUTPUT_FROM_COOKIE_ACK) {
/*-
* Special hook for handling cookies discarded
* by peer that carried data. Send cookie-ack only
* and then the next call with get the retran's.
*/
(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1,
from_where,
&now, &now_filled, frag_point, so_locked);
return;
} else if (from_where != SCTP_OUTPUT_FROM_HB_TMR) {
/* if its not from a HB then do it */
fr_done = 0;
ret = sctp_chunk_retransmission(inp, stcb, asoc, &num_out, &now, &now_filled, &fr_done, so_locked);
if (fr_done) {
tot_frs++;
}
} else {
/*
* its from any other place, we don't allow retran
* output (only control)
*/
ret = 1;
}
if (ret > 0) {
/* Can't send anymore */
/*-
* now lets push out control by calling med-level
* output once. this assures that we WILL send HB's
* if queued too.
*/
(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1,
from_where,
&now, &now_filled, frag_point, so_locked);
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(8, inp, stcb, NULL);
#endif
sctp_timer_validation(inp, stcb, asoc);
return;
}
if (ret < 0) {
/*-
* The count was off.. retran is not happening so do
* the normal retransmission.
*/
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(9, inp, stcb, NULL);
#endif
if (ret == SCTP_RETRAN_EXIT) {
return;
}
break;
}
if (from_where == SCTP_OUTPUT_FROM_T3) {
/* Only one transmission allowed out of a timeout */
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(10, inp, stcb, NULL);
#endif
/* Push out any control */
(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out, &reason_code, 1, from_where,
&now, &now_filled, frag_point, so_locked);
return;
}
if ((asoc->fr_max_burst > 0) && (tot_frs >= asoc->fr_max_burst)) {
/* Hit FR burst limit */
return;
}
if ((num_out == 0) && (ret == 0)) {
/* No more retrans to send */
break;
}
}
#ifdef SCTP_AUDITING_ENABLED
sctp_auditing(12, inp, stcb, NULL);
#endif
/* Check for bad destinations, if they exist move chunks around. */
TAILQ_FOREACH(net, &asoc->nets, sctp_next) {
if ((net->dest_state & SCTP_ADDR_REACHABLE) == 0) {
/*-
* if possible move things off of this address we
* still may send below due to the dormant state but
* we try to find an alternate address to send to
* and if we have one we move all queued data on the
* out wheel to this alternate address.
*/
if (net->ref_count > 1)
sctp_move_chunks_from_net(stcb, net);
} else {
/*-
* if ((asoc->sat_network) || (net->addr_is_local))
* { burst_limit = asoc->max_burst *
* SCTP_SAT_NETWORK_BURST_INCR; }
*/
if (asoc->max_burst > 0) {
if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst)) {
if ((net->flight_size + (asoc->max_burst * net->mtu)) < net->cwnd) {
/* JRS - Use the congestion control given in the congestion control module */
asoc->cc_functions.sctp_cwnd_update_after_output(stcb, net, asoc->max_burst);
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
sctp_log_maxburst(stcb, net, 0, asoc->max_burst, SCTP_MAX_BURST_APPLIED);
}
SCTP_STAT_INCR(sctps_maxburstqueued);
}
net->fast_retran_ip = 0;
} else {
if (net->flight_size == 0) {
/* Should be decaying the cwnd here */
;
}
}
}
}
}
burst_cnt = 0;
do {
error = sctp_med_chunk_output(inp, stcb, asoc, &num_out,
&reason_code, 0, from_where,
&now, &now_filled, frag_point, so_locked);
if (error) {
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Error %d was returned from med-c-op\n", error);
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
sctp_log_maxburst(stcb, asoc->primary_destination, error, burst_cnt, SCTP_MAX_BURST_ERROR_STOP);
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, NULL, error, SCTP_SEND_NOW_COMPLETES);
sctp_log_cwnd(stcb, NULL, 0xdeadbeef, SCTP_SEND_NOW_COMPLETES);
}
break;
}
SCTPDBG(SCTP_DEBUG_OUTPUT3, "m-c-o put out %d\n", num_out);
tot_out += num_out;
burst_cnt++;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, NULL, num_out, SCTP_SEND_NOW_COMPLETES);
if (num_out == 0) {
sctp_log_cwnd(stcb, NULL, reason_code, SCTP_SEND_NOW_COMPLETES);
}
}
if (nagle_on) {
/*
* When the Nagle algorithm is used, look at how much
* is unsent, then if its smaller than an MTU and we
* have data in flight we stop, except if we are
* handling a fragmented user message.
*/
un_sent = stcb->asoc.total_output_queue_size - stcb->asoc.total_flight;
if ((un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD)) &&
(stcb->asoc.total_flight > 0)) {
/* && sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR))) {*/
break;
}
}
if (TAILQ_EMPTY(&asoc->control_send_queue) &&
TAILQ_EMPTY(&asoc->send_queue) &&
sctp_is_there_unsent_data(stcb, so_locked) == 0) {
/* Nothing left to send */
break;
}
if ((stcb->asoc.total_output_queue_size - stcb->asoc.total_flight) <= 0) {
/* Nothing left to send */
break;
}
} while (num_out &&
((asoc->max_burst == 0) ||
SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) ||
(burst_cnt < asoc->max_burst)));
if (SCTP_BASE_SYSCTL(sctp_use_cwnd_based_maxburst) == 0) {
if ((asoc->max_burst > 0) && (burst_cnt >= asoc->max_burst)) {
SCTP_STAT_INCR(sctps_maxburstqueued);
asoc->burst_limit_applied = 1;
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_MAXBURST_ENABLE) {
sctp_log_maxburst(stcb, asoc->primary_destination, 0, burst_cnt, SCTP_MAX_BURST_APPLIED);
}
} else {
asoc->burst_limit_applied = 0;
}
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_CWND_LOGGING_ENABLE) {
sctp_log_cwnd(stcb, NULL, tot_out, SCTP_SEND_NOW_COMPLETES);
}
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Ok, we have put out %d chunks\n",
tot_out);
/*-
* Now we need to clean up the control chunk chain if a ECNE is on
* it. It must be marked as UNSENT again so next call will continue
* to send it until such time that we get a CWR, to remove it.
*/
if (stcb->asoc.ecn_echo_cnt_onq)
sctp_fix_ecn_echo(asoc);
if (stcb->asoc.trigger_reset) {
if (sctp_send_stream_reset_out_if_possible(stcb, so_locked) == 0) {
goto do_it_again;
}
}
return;
}
void
send_forward_tsn(struct sctp_tcb *stcb,
struct sctp_association *asoc)
{
struct sctp_tmit_chunk *chk, *at, *tp1, *last;
struct sctp_forward_tsn_chunk *fwdtsn;
struct sctp_strseq *strseq;
struct sctp_strseq_mid *strseq_m;
uint32_t advance_peer_ack_point;
unsigned int cnt_of_space, i, ovh;
unsigned int space_needed;
unsigned int cnt_of_skipped = 0;
SCTP_TCB_LOCK_ASSERT(stcb);
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
if (chk->rec.chunk_id.id == SCTP_FORWARD_CUM_TSN) {
/* mark it to unsent */
chk->sent = SCTP_DATAGRAM_UNSENT;
chk->snd_count = 0;
/* Do we correct its output location? */
if (chk->whoTo) {
sctp_free_remote_addr(chk->whoTo);
chk->whoTo = NULL;
}
goto sctp_fill_in_rest;
}
}
/* Ok if we reach here we must build one */
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
return;
}
asoc->fwd_tsn_cnt++;
chk->copy_by_ref = 0;
/*
* We don't do the old thing here since
* this is used not for on-wire but to
* tell if we are sending a fwd-tsn by
* the stack during output. And if its
* a IFORWARD or a FORWARD it is a fwd-tsn.
*/
chk->rec.chunk_id.id = SCTP_FORWARD_CUM_TSN;
chk->rec.chunk_id.can_take_data = 0;
chk->flags = 0;
chk->asoc = asoc;
chk->whoTo = NULL;
chk->data = sctp_get_mbuf_for_msg(MCLBYTES, 0, M_NOWAIT, 1, MT_DATA);
if (chk->data == NULL) {
sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
return;
}
SCTP_BUF_RESV_UF(chk->data, SCTP_MIN_OVERHEAD);
chk->sent = SCTP_DATAGRAM_UNSENT;
chk->snd_count = 0;
TAILQ_INSERT_TAIL(&asoc->control_send_queue, chk, sctp_next);
asoc->ctrl_queue_cnt++;
sctp_fill_in_rest:
/*-
* Here we go through and fill out the part that deals with
* stream/seq of the ones we skip.
*/
SCTP_BUF_LEN(chk->data) = 0;
TAILQ_FOREACH(at, &asoc->sent_queue, sctp_next) {
if ((at->sent != SCTP_FORWARD_TSN_SKIP) &&
(at->sent != SCTP_DATAGRAM_NR_ACKED)) {
/* no more to look at */
break;
}
if (!asoc->idata_supported && (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED)) {
/* We don't report these */
continue;
}
cnt_of_skipped++;
}
if (asoc->idata_supported) {
space_needed = (sizeof(struct sctp_forward_tsn_chunk) +
(cnt_of_skipped * sizeof(struct sctp_strseq_mid)));
} else {
space_needed = (sizeof(struct sctp_forward_tsn_chunk) +
(cnt_of_skipped * sizeof(struct sctp_strseq)));
}
cnt_of_space = (unsigned int)M_TRAILINGSPACE(chk->data);
if (stcb->sctp_ep->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) {
ovh = SCTP_MIN_OVERHEAD;
} else {
ovh = SCTP_MIN_V4_OVERHEAD;
}
if (cnt_of_space > (asoc->smallest_mtu - ovh)) {
/* trim to a mtu size */
cnt_of_space = asoc->smallest_mtu - ovh;
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) {
sctp_misc_ints(SCTP_FWD_TSN_CHECK, 0xff, 0, cnt_of_skipped,
asoc->advanced_peer_ack_point);
}
advance_peer_ack_point = asoc->advanced_peer_ack_point;
if (cnt_of_space < space_needed) {
/*-
* ok we must trim down the chunk by lowering the
* advance peer ack point.
*/
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) {
sctp_misc_ints(SCTP_FWD_TSN_CHECK, 0xff, 0xff, cnt_of_space,
space_needed);
}
cnt_of_skipped = cnt_of_space - sizeof(struct sctp_forward_tsn_chunk);
if (asoc->idata_supported) {
cnt_of_skipped /= sizeof(struct sctp_strseq_mid);
} else {
cnt_of_skipped /= sizeof(struct sctp_strseq);
}
/*-
* Go through and find the TSN that will be the one
* we report.
*/
at = TAILQ_FIRST(&asoc->sent_queue);
if (at != NULL) {
for (i = 0; i < cnt_of_skipped; i++) {
tp1 = TAILQ_NEXT(at, sctp_next);
if (tp1 == NULL) {
break;
}
at = tp1;
}
}
if (at && SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOG_TRY_ADVANCE) {
sctp_misc_ints(SCTP_FWD_TSN_CHECK, 0xff, cnt_of_skipped, at->rec.data.tsn,
asoc->advanced_peer_ack_point);
}
last = at;
/*-
* last now points to last one I can report, update
* peer ack point
*/
if (last) {
advance_peer_ack_point = last->rec.data.tsn;
}
if (asoc->idata_supported) {
space_needed = sizeof(struct sctp_forward_tsn_chunk) +
cnt_of_skipped * sizeof(struct sctp_strseq_mid);
} else {
space_needed = sizeof(struct sctp_forward_tsn_chunk) +
cnt_of_skipped * sizeof(struct sctp_strseq);
}
}
chk->send_size = space_needed;
/* Setup the chunk */
fwdtsn = mtod(chk->data, struct sctp_forward_tsn_chunk *);
fwdtsn->ch.chunk_length = htons(chk->send_size);
fwdtsn->ch.chunk_flags = 0;
if (asoc->idata_supported) {
fwdtsn->ch.chunk_type = SCTP_IFORWARD_CUM_TSN;
} else {
fwdtsn->ch.chunk_type = SCTP_FORWARD_CUM_TSN;
}
fwdtsn->new_cumulative_tsn = htonl(advance_peer_ack_point);
SCTP_BUF_LEN(chk->data) = chk->send_size;
fwdtsn++;
/*-
* Move pointer to after the fwdtsn and transfer to the
* strseq pointer.
*/
if (asoc->idata_supported) {
strseq_m = (struct sctp_strseq_mid *)fwdtsn;
strseq = NULL;
} else {
strseq = (struct sctp_strseq *)fwdtsn;
strseq_m = NULL;
}
/*-
* Now populate the strseq list. This is done blindly
* without pulling out duplicate stream info. This is
* inefficient but won't harm the process since the peer will
* look at these in sequence and will thus release anything.
* It could mean we exceed the PMTU and chop off some that
* we could have included.. but this is unlikely (aka 1432/4
* would mean 300+ stream seq's would have to be reported in
* one FWD-TSN. With a bit of work we can later FIX this to
* optimize and pull out duplicates.. but it does add more
* overhead. So for now... not!
*/
i = 0;
TAILQ_FOREACH(at, &asoc->sent_queue, sctp_next) {
if (i >= cnt_of_skipped) {
break;
}
if (!asoc->idata_supported && (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED)) {
/* We don't report these */
continue;
}
if (at->rec.data.tsn == advance_peer_ack_point) {
at->rec.data.fwd_tsn_cnt = 0;
}
if (asoc->idata_supported) {
strseq_m->sid = htons(at->rec.data.sid);
if (at->rec.data.rcv_flags & SCTP_DATA_UNORDERED) {
strseq_m->flags = htons(PR_SCTP_UNORDERED_FLAG);
} else {
strseq_m->flags = 0;
}
strseq_m->mid = htonl(at->rec.data.mid);
strseq_m++;
} else {
strseq->sid = htons(at->rec.data.sid);
strseq->ssn = htons((uint16_t)at->rec.data.mid);
strseq++;
}
i++;
}
return;
}
void
sctp_send_sack(struct sctp_tcb *stcb, int so_locked)
{
/*-
* Queue up a SACK or NR-SACK in the control queue.
* We must first check to see if a SACK or NR-SACK is
* somehow on the control queue.
* If so, we will take and and remove the old one.
*/
struct sctp_association *asoc;
struct sctp_tmit_chunk *chk, *a_chk;
struct sctp_sack_chunk *sack;
struct sctp_nr_sack_chunk *nr_sack;
struct sctp_gap_ack_block *gap_descriptor;
const struct sack_track *selector;
int mergeable = 0;
int offset;
caddr_t limit;
uint32_t *dup;
int limit_reached = 0;
unsigned int i, siz, j;
unsigned int num_gap_blocks = 0, num_nr_gap_blocks = 0, space;
int num_dups = 0;
int space_req;
uint32_t highest_tsn;
uint8_t flags;
uint8_t type;
uint8_t tsn_map;
if (stcb->asoc.nrsack_supported == 1) {
type = SCTP_NR_SELECTIVE_ACK;
} else {
type = SCTP_SELECTIVE_ACK;
}
a_chk = NULL;
asoc = &stcb->asoc;
SCTP_TCB_LOCK_ASSERT(stcb);
if (asoc->last_data_chunk_from == NULL) {
/* Hmm we never received anything */
return;
}
sctp_slide_mapping_arrays(stcb);
sctp_set_rwnd(stcb, asoc);
TAILQ_FOREACH(chk, &asoc->control_send_queue, sctp_next) {
if (chk->rec.chunk_id.id == type) {
/* Hmm, found a sack already on queue, remove it */
TAILQ_REMOVE(&asoc->control_send_queue, chk, sctp_next);
asoc->ctrl_queue_cnt--;
a_chk = chk;
if (a_chk->data) {
sctp_m_freem(a_chk->data);
a_chk->data = NULL;
}
if (a_chk->whoTo) {
sctp_free_remote_addr(a_chk->whoTo);
a_chk->whoTo = NULL;
}
break;
}
}
if (a_chk == NULL) {
sctp_alloc_a_chunk(stcb, a_chk);
if (a_chk == NULL) {
/* No memory so we drop the idea, and set a timer */
if (stcb->asoc.delayed_ack) {
sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
stcb->sctp_ep, stcb, NULL,
SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_4);
sctp_timer_start(SCTP_TIMER_TYPE_RECV,
stcb->sctp_ep, stcb, NULL);
} else {
stcb->asoc.send_sack = 1;
}
return;
}
a_chk->copy_by_ref = 0;
a_chk->rec.chunk_id.id = type;
a_chk->rec.chunk_id.can_take_data = 1;
}
/* Clear our pkt counts */
asoc->data_pkts_seen = 0;
a_chk->flags = 0;
a_chk->asoc = asoc;
a_chk->snd_count = 0;
a_chk->send_size = 0; /* fill in later */
a_chk->sent = SCTP_DATAGRAM_UNSENT;
a_chk->whoTo = NULL;
if ((asoc->last_data_chunk_from->dest_state & SCTP_ADDR_REACHABLE) == 0) {
/*-
* Ok, the destination for the SACK is unreachable, lets see if
* we can select an alternate to asoc->last_data_chunk_from
*/
a_chk->whoTo = sctp_find_alternate_net(stcb, asoc->last_data_chunk_from, 0);
if (a_chk->whoTo == NULL) {
/* Nope, no alternate */
a_chk->whoTo = asoc->last_data_chunk_from;
}
} else {
a_chk->whoTo = asoc->last_data_chunk_from;
}
if (a_chk->whoTo) {
atomic_add_int(&a_chk->whoTo->ref_count, 1);
}
if (SCTP_TSN_GT(asoc->highest_tsn_inside_map, asoc->highest_tsn_inside_nr_map)) {
highest_tsn = asoc->highest_tsn_inside_map;
} else {
highest_tsn = asoc->highest_tsn_inside_nr_map;
}
if (highest_tsn == asoc->cumulative_tsn) {
/* no gaps */
if (type == SCTP_SELECTIVE_ACK) {
space_req = sizeof(struct sctp_sack_chunk);
} else {
space_req = sizeof(struct sctp_nr_sack_chunk);
}
} else {
/* gaps get a cluster */
space_req = MCLBYTES;
}
/* Ok now lets formulate a MBUF with our sack */
a_chk->data = sctp_get_mbuf_for_msg(space_req, 0, M_NOWAIT, 1, MT_DATA);
if ((a_chk->data == NULL) ||
(a_chk->whoTo == NULL)) {
/* rats, no mbuf memory */
if (a_chk->data) {
/* was a problem with the destination */
sctp_m_freem(a_chk->data);
a_chk->data = NULL;
}
sctp_free_a_chunk(stcb, a_chk, so_locked);
/* sa_ignore NO_NULL_CHK */
if (stcb->asoc.delayed_ack) {
sctp_timer_stop(SCTP_TIMER_TYPE_RECV,
stcb->sctp_ep, stcb, NULL,
SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_5);
sctp_timer_start(SCTP_TIMER_TYPE_RECV,
stcb->sctp_ep, stcb, NULL);
} else {
stcb->asoc.send_sack = 1;
}
return;
}
/* ok, lets go through and fill it in */
SCTP_BUF_RESV_UF(a_chk->data, SCTP_MIN_OVERHEAD);
space = (unsigned int)M_TRAILINGSPACE(a_chk->data);
if (space > (a_chk->whoTo->mtu - SCTP_MIN_OVERHEAD)) {
space = (a_chk->whoTo->mtu - SCTP_MIN_OVERHEAD);
}
limit = mtod(a_chk->data, caddr_t);
limit += space;
flags = 0;
if ((asoc->sctp_cmt_on_off > 0) &&
SCTP_BASE_SYSCTL(sctp_cmt_use_dac)) {
/*-
* CMT DAC algorithm: If 2 (i.e., 0x10) packets have been
* received, then set high bit to 1, else 0. Reset
* pkts_rcvd.
*/
flags |= (asoc->cmt_dac_pkts_rcvd << 6);
asoc->cmt_dac_pkts_rcvd = 0;
}
#ifdef SCTP_ASOCLOG_OF_TSNS
stcb->asoc.cumack_logsnt[stcb->asoc.cumack_log_atsnt] = asoc->cumulative_tsn;
stcb->asoc.cumack_log_atsnt++;
if (stcb->asoc.cumack_log_atsnt >= SCTP_TSN_LOG_SIZE) {
stcb->asoc.cumack_log_atsnt = 0;
}
#endif
/* reset the readers interpretation */
stcb->freed_by_sorcv_sincelast = 0;
/* get to new offset for the param. */
req_tsn = (struct sctp_stream_reset_tsn_request *)((caddr_t)ch + len); /* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_tsn_request);
req_tsn->ph.param_type = htons(SCTP_STR_RESET_TSN_REQUEST);
req_tsn->ph.param_length = htons(len);
req_tsn->request_seq = htonl(seq);
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->send_size = len + old_len;
chk->book_size = SCTP_SIZE32(chk->send_size);
chk->book_size_scale = 0;
SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size); return;
}
/* get to new offset for the param. */
resp = (struct sctp_stream_reset_response *)((caddr_t)ch + len); /* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_response);
resp->ph.param_type = htons(SCTP_STR_RESET_RESPONSE);
resp->ph.param_length = htons(len);
resp->response_seq = htonl(resp_seq);
resp->result = ntohl(result);
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->book_size = len + old_len;
chk->book_size_scale = 0;
chk->send_size = SCTP_SIZE32(chk->book_size);
SCTP_BUF_LEN(chk->data) = chk->send_size; return;
}
/* get to new offset for the param. */
resp = (struct sctp_stream_reset_response_tsn *)((caddr_t)ch + len);
/* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_response_tsn);
resp->ph.param_type = htons(SCTP_STR_RESET_RESPONSE);
resp->ph.param_length = htons(len);
resp->response_seq = htonl(resp_seq);
resp->result = htonl(result);
resp->senders_next_tsn = htonl(send_una);
resp->receivers_next_tsn = htonl(recv_next);
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->book_size = len + old_len;
chk->send_size = SCTP_SIZE32(chk->book_size);
chk->book_size_scale = 0;
SCTP_BUF_LEN(chk->data) = chk->send_size;
return;
}
/* get to new offset for the param. */
addstr = (struct sctp_stream_reset_add_strm *)((caddr_t)ch + len);
/* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_add_strm);
/* Fill it out. */
addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_OUT_STREAMS);
addstr->ph.param_length = htons(len);
addstr->request_seq = htonl(seq);
addstr->number_of_streams = htons(adding);
addstr->reserved = 0;
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->send_size = len + old_len;
chk->book_size = SCTP_SIZE32(chk->send_size);
chk->book_size_scale = 0;
SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
return;
}
/* get to new offset for the param. */
addstr = (struct sctp_stream_reset_add_strm *)((caddr_t)ch + len);
/* now how long will this param be? */
len = sizeof(struct sctp_stream_reset_add_strm);
/* Fill it out. */
addstr->ph.param_type = htons(SCTP_STR_RESET_ADD_IN_STREAMS);
addstr->ph.param_length = htons(len);
addstr->request_seq = htonl(seq);
addstr->number_of_streams = htons(adding);
addstr->reserved = 0;
/* now fix the chunk length */
ch->chunk_length = htons(len + old_len);
chk->send_size = len + old_len;
chk->book_size = SCTP_SIZE32(chk->send_size);
chk->book_size_scale = 0;
SCTP_BUF_LEN(chk->data) = SCTP_SIZE32(chk->send_size);
return;
}
int
sctp_send_stream_reset_out_if_possible(struct sctp_tcb *stcb, int so_locked)
{
struct sctp_association *asoc;
struct sctp_tmit_chunk *chk;
struct sctp_chunkhdr *ch;
uint32_t seq;
m = m_uiotombuf(uio, M_WAITOK, max_send_len, 0,
(M_PKTHDR | (user_marks_eor ? M_EOR : 0)));
if (m == NULL) {
/* The only possible error is EFAULT. */
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
*error = EFAULT;
} else {
*sndout = m_length(m, NULL);
*new_tail = m_last(m);
}
return (m);
#else
int left, cancpy, willcpy;
struct mbuf *m, *head;
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
left = (int)min(uio->uio_resid, max_send_len);
#else
left = (int)min(uio_resid(uio), max_send_len);
#endif
#else
left = (int)min(uio->uio_resid, max_send_len);
#endif
/* Always get a header just in case */
head = sctp_get_mbuf_for_msg(left, 0, M_WAITOK, 0, MT_DATA);
if (head == NULL) {
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
*error = ENOBUFS;
return (NULL);
}
cancpy = (int)M_TRAILINGSPACE(head);
willcpy = min(cancpy, left);
*error = uiomove(mtod(head, caddr_t), willcpy, uio);
if (*error != 0) {
sctp_m_freem(head);
return (NULL);
}
*sndout += willcpy;
left -= willcpy;
SCTP_BUF_LEN(head) = willcpy;
m = head;
*new_tail = head;
while (left > 0) {
/* move in user data */
SCTP_BUF_NEXT(m) = sctp_get_mbuf_for_msg(left, 0, M_WAITOK, 0, MT_DATA);
if (SCTP_BUF_NEXT(m) == NULL) {
sctp_m_freem(head);
*new_tail = NULL;
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
*error = ENOBUFS;
return (NULL);
}
m = SCTP_BUF_NEXT(m);
cancpy = (int)M_TRAILINGSPACE(m);
willcpy = min(cancpy, left);
*error = uiomove(mtod(m, caddr_t), willcpy, uio);
if (*error != 0) {
sctp_m_freem(head);
*new_tail = NULL;
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, *error);
return (NULL);
}
SCTP_BUF_LEN(m) = willcpy;
left -= willcpy;
*sndout += willcpy;
*new_tail = m;
if (left == 0) {
SCTP_BUF_NEXT(m) = NULL;
}
}
return (head);
#endif
}
static int
sctp_copy_one(struct sctp_stream_queue_pending *sp,
struct uio *uio,
int resv_upfront)
{
#if defined(__FreeBSD__) || defined(__Userspace__)
sp->data = m_uiotombuf(uio, M_WAITOK, sp->length, resv_upfront, 0);
if (sp->data == NULL) {
/* The only possible error is EFAULT. */
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, EFAULT);
return (EFAULT);
}
sp->tail_mbuf = m_last(sp->data);
return (0);
#else
int left;
int cancpy, willcpy, error;
struct mbuf *m, *head;
int cpsz = 0;
/* First one gets a header */
left = sp->length;
head = m = sctp_get_mbuf_for_msg((left + resv_upfront), 0, M_WAITOK, 0, MT_DATA);
if (m == NULL) {
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
return (ENOBUFS);
}
/*-
* Add this one for m in now, that way if the alloc fails we won't
* have a bad cnt.
*/
SCTP_BUF_RESV_UF(m, resv_upfront);
cancpy = (int)M_TRAILINGSPACE(m);
willcpy = min(cancpy, left);
while (left > 0) {
/* move in user data */
error = uiomove(mtod(m, caddr_t), willcpy, uio);
if (error) {
sctp_m_freem(head);
return (error);
}
SCTP_BUF_LEN(m) = willcpy;
left -= willcpy;
cpsz += willcpy;
if (left > 0) {
SCTP_BUF_NEXT(m) = sctp_get_mbuf_for_msg(left, 0, M_WAITOK, 0, MT_DATA);
if (SCTP_BUF_NEXT(m) == NULL) {
/*
* the head goes back to caller, he can free
* the rest
*/
sctp_m_freem(head);
SCTP_LTRACE_ERR_RET(NULL, NULL, NULL, SCTP_FROM_SCTP_OUTPUT, ENOBUFS);
return (ENOBUFS);
}
m = SCTP_BUF_NEXT(m);
cancpy = (int)M_TRAILINGSPACE(m);
willcpy = min(cancpy, left);
} else {
sp->tail_mbuf = m;
SCTP_BUF_NEXT(m) = NULL;
}
}
sp->data = head;
sp->length = cpsz;
return (0);
#endif
}
{
/*-
* This routine must be very careful in its work. Protocol
* processing is up and running so care must be taken to spl...()
* when you need to do something that may effect the stcb/asoc. The
* sb is locked however. When data is copied the protocol processing
* should be enabled since this is a slower operation...
*/
struct sctp_stream_queue_pending *sp;
int resv_in_first;
t_inp = inp = (struct sctp_inpcb *)so->so_pcb;
if (inp == NULL) {
error = EINVAL;
goto out_unlocked;
}
user_marks_eor = sctp_is_feature_on(inp, SCTP_PCB_FLAGS_EXPLICIT_EOR);
if ((uio == NULL) && (user_marks_eor != 0)) {
/*-
* We do not support eeor mode for
* sending with mbuf chains (like sendfile).
*/
error = EINVAL;
goto out_unlocked;
}
if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) &&
SCTP_IS_LISTENING(inp)) {
/* The listener can NOT send. */
error = EINVAL;
goto out_unlocked;
}
atomic_add_int(&inp->total_sends, 1);
if (srcv != NULL) {
sndrcvninfo = (struct sctp_nonpad_sndrcvinfo *)srcv;
sinfo_assoc_id = sndrcvninfo->sinfo_assoc_id;
sinfo_flags = sndrcvninfo->sinfo_flags;
if (INVALID_SINFO_FLAG(sinfo_flags) ||
PR_SCTP_INVALID_POLICY(sinfo_flags)) {
error = EINVAL;
goto out_unlocked;
}
if (sinfo_flags != 0) {
SCTP_STAT_INCR(sctps_sends_with_flags);
}
} else {
sndrcvninfo = NULL;
sinfo_flags = inp->def_send.sinfo_flags;
sinfo_assoc_id = inp->def_send.sinfo_assoc_id;
}
#if defined(__FreeBSD__) && !defined(__Userspace__)
if (flags & MSG_EOR) {
sinfo_flags |= SCTP_EOR;
}
if (flags & MSG_EOF) {
sinfo_flags |= SCTP_EOF;
}
#endif
if ((sinfo_flags & SCTP_ADDR_OVER) && (addr == NULL)) {
error = EINVAL;
goto out_unlocked;
}
SCTP_INP_RLOCK(inp);
if ((sinfo_flags & SCTP_SENDALL) &&
(inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE)) {
SCTP_INP_RUNLOCK(inp);
error = sctp_sendall(inp, uio, top, sndrcvninfo);
top = NULL;
goto out_unlocked;
}
/* Now we must find the association. */
if ((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_IN_TCPPOOL)) {
stcb = LIST_FIRST(&inp->sctp_asoc_list);
if (stcb != NULL) {
SCTP_TCB_LOCK(stcb);
}
SCTP_INP_RUNLOCK(inp);
} else if (sinfo_assoc_id > SCTP_ALL_ASSOC) {
stcb = sctp_findasoc_ep_asocid_locked(inp, sinfo_assoc_id, 1);
SCTP_INP_RUNLOCK(inp);
if (stcb != NULL) {
SCTP_TCB_LOCK_ASSERT(stcb);
}
} else if (addr != NULL) {
/*-
* Since we did not use findep we must
* increment it, and if we don't find a tcb
* decrement it.
*/
SCTP_INP_INCR_REF(inp);
SCTP_INP_RUNLOCK(inp);
stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL);
if (stcb == NULL) {
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
} else {
SCTP_TCB_LOCK_ASSERT(stcb);
}
} else {
SCTP_INP_RUNLOCK(inp);
}
#ifdef INVARIANTS
if (stcb != NULL) {
SCTP_TCB_LOCK_ASSERT(stcb);
}
#endif
if ((stcb == NULL) && (addr != NULL)) {
/* Possible implicit send? */
SCTP_ASOC_CREATE_LOCK(inp);
create_lock_applied = true;
if ((inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_GONE) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_SOCKET_ALLGONE)) {
error = EINVAL;
goto out_unlocked;
}
if (((inp->sctp_flags & SCTP_PCB_FLAGS_BOUND_V6) == 0) &&
(addr->sa_family == AF_INET6)) {
error = EINVAL;
goto out_unlocked;
}
SCTP_INP_WLOCK(inp);
SCTP_INP_INCR_REF(inp);
SCTP_INP_WUNLOCK(inp);
/* With the lock applied look again */
stcb = sctp_findassociation_ep_addr(&t_inp, addr, &net, NULL, NULL);
#if defined(INET) || defined(INET6)
if ((stcb == NULL) && (control != NULL) && (port > 0)) {
stcb = sctp_findassociation_cmsgs(&t_inp, port, control, &net, &error);
}
#endif
if (stcb == NULL) {
SCTP_INP_WLOCK(inp);
SCTP_INP_DECR_REF(inp);
SCTP_INP_WUNLOCK(inp);
} else {
SCTP_TCB_LOCK_ASSERT(stcb);
SCTP_ASOC_CREATE_UNLOCK(inp);
create_lock_applied = false;
}
if (error != 0) {
goto out_unlocked;
}
if (t_inp != inp) {
error = ENOTCONN;
goto out_unlocked;
}
}
if (stcb == NULL) {
if (addr == NULL) {
error = ENOENT;
goto out_unlocked;
} else {
/* We must go ahead and start the INIT process */
uint32_t vrf_id;
if ((sinfo_flags & SCTP_ABORT) ||
((sinfo_flags & SCTP_EOF) && (sndlen == 0))) {
/*-
* User asks to abort a non-existent assoc,
* or EOF a non-existent assoc with no data
*/
error = ENOENT;
goto out_unlocked;
}
/* get an asoc/stcb struct */
vrf_id = inp->def_vrf_id;
KASSERT(create_lock_applied, ("create_lock_applied is false"));
stcb = sctp_aloc_assoc_connected(inp, addr, &error, 0, 0, vrf_id,
inp->sctp_ep.pre_open_stream_count,
inp->sctp_ep.port,
#if !defined(__Userspace__)
p,
#else
(struct proc *)NULL,
#endif
SCTP_INITIALIZE_AUTH_PARAMS);
if (stcb == NULL) {
/* error is setup for us in the call. */
KASSERT(error != 0, ("error is 0 although stcb is NULL"));
goto out_unlocked;
}
SCTP_TCB_LOCK_ASSERT(stcb);
SCTP_ASOC_CREATE_UNLOCK(inp);
create_lock_applied = false;
/* Turn on queue only flag to prevent data from being sent */
queue_only = 1;
SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
(void)SCTP_GETTIME_TIMEVAL(&stcb->asoc.time_entered);
if (control != NULL) {
if (sctp_process_cmsgs_for_init(stcb, control, &error)) {
sctp_free_assoc(inp, stcb, SCTP_NORMAL_PROC,
SCTP_FROM_SCTP_OUTPUT + SCTP_LOC_6);
stcb = NULL;
KASSERT(error != 0,
("error is 0 although sctp_process_cmsgs_for_init() indicated an error"));
goto out_unlocked;
}
}
/* out with the INIT */
queue_only_for_init = 1;
/*-
* we may want to dig in after this call and adjust the MTU
* value. It defaulted to 1500 (constant) but the ro
* structure may now have an update and thus we may need to
* change it BEFORE we append the message.
*/
}
}
KASSERT(!create_lock_applied, ("create_lock_applied is true"));
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
asoc = &stcb->asoc;
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out_unlocked;
}
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) {
queue_only = 1;
}
/* Keep the stcb from being freed under our feet. */
atomic_add_int(&asoc->refcnt, 1);
free_cnt_applied = true;
if (sndrcvninfo == NULL) {
/* Use a local copy to have a consistent view. */
sndrcvninfo_buf = asoc->def_send;
sndrcvninfo = &sndrcvninfo_buf;
sinfo_flags = sndrcvninfo->sinfo_flags;
#if defined(__FreeBSD__) && !defined(__Userspace__)
if (flags & MSG_EOR) {
sinfo_flags |= SCTP_EOR;
}
if (flags & MSG_EOF) {
sinfo_flags |= SCTP_EOF;
}
#endif
}
/* Are we aborting? */
if (sinfo_flags & SCTP_ABORT) {
struct mbuf *mm;
struct sctp_paramhdr *ph;
ssize_t tot_demand, tot_out = 0, max_out;
SCTP_STAT_INCR(sctps_sends_with_abort);
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_WAIT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_COOKIE_ECHOED)) {
/* It has to be up before we abort. */
error = EINVAL;
goto out_unlocked;
}
/* How big is the user initiated abort? */
if (top != NULL) {
struct mbuf *cntm;
if (sndlen != 0) {
for (cntm = top; cntm; cntm = SCTP_BUF_NEXT(cntm)) {
tot_out += SCTP_BUF_LEN(cntm);
}
}
mm = sctp_get_mbuf_for_msg(sizeof(struct sctp_paramhdr), 0, M_NOWAIT, 1, MT_DATA);
} else {
/* Must fit in a MTU */
tot_out = sndlen;
tot_demand = (tot_out + sizeof(struct sctp_paramhdr));
if (tot_demand > SCTP_DEFAULT_ADD_MORE) {
error = EMSGSIZE;
goto out_unlocked;
}
mm = sctp_get_mbuf_for_msg((unsigned int)tot_demand, 0, M_NOWAIT, 1, MT_DATA);
}
if (mm == NULL) {
error = ENOMEM;
goto out_unlocked;
}
max_out = asoc->smallest_mtu - sizeof(struct sctp_paramhdr);
max_out -= sizeof(struct sctp_abort_msg);
if (tot_out > max_out) {
tot_out = max_out;
}
ph = mtod(mm, struct sctp_paramhdr *);
ph->param_type = htons(SCTP_CAUSE_USER_INITIATED_ABT);
ph->param_length = htons((uint16_t)(sizeof(struct sctp_paramhdr) + tot_out));
ph++;
SCTP_BUF_LEN(mm) = (int)(tot_out + sizeof(struct sctp_paramhdr));
if (top == NULL) {
SCTP_TCB_UNLOCK(stcb);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(so, 0);
#endif
error = uiomove((caddr_t)ph, (int)tot_out, uio);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(so, 0);
#endif
SCTP_TCB_LOCK(stcb);
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
sctp_m_freem(mm);
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out_unlocked;
}
if (error != 0) {
/*-
* Here if we can't get his data we
* still abort we just don't get to
* send the users note :-0
*/
sctp_m_freem(mm);
mm = NULL;
error = 0;
}
} else {
if (sndlen != 0) {
SCTP_BUF_NEXT(mm) = top;
}
}
atomic_subtract_int(&asoc->refcnt, 1);
free_cnt_applied = false;
/* release this lock, otherwise we hang on ourselves */
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
sctp_abort_an_association(stcb->sctp_ep, stcb, mm, false, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
stcb = NULL;
/* In this case top is already chained to mm
* avoid double free, since we free it below if
* top != NULL and driver would free it after sending
* the packet out
*/
if (sndlen != 0) {
top = NULL;
}
goto out_unlocked;
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
if (sinfo_flags & SCTP_ADDR_OVER) {
if (addr != NULL) {
net = sctp_findnet(stcb, addr);
} else {
net = NULL;
}
if ((net == NULL) ||
((port != 0) && (port != stcb->rport))) {
error = EINVAL;
goto out_unlocked;
}
} else {
if (asoc->alternate != NULL) {
net = asoc->alternate;
} else {
net = asoc->primary_destination;
}
}
if (sndlen == 0) {
if (sinfo_flags & SCTP_EOF) {
got_all_of_the_send = true;
goto dataless_eof;
} else {
error = EINVAL;
goto out_unlocked;
}
}
if (sctp_is_feature_on(inp, SCTP_PCB_FLAGS_NO_FRAGMENT)) {
if (sndlen > (ssize_t)asoc->smallest_mtu) {
error = EMSGSIZE;
goto out_unlocked;
}
}
sinfo_stream = sndrcvninfo->sinfo_stream;
/* Is the stream no. valid? */
if (sinfo_stream >= asoc->streamoutcnt) {
/* Invalid stream number */
error = EINVAL;
goto out_unlocked;
}
if ((asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPEN) &&
(asoc->strmout[sinfo_stream].state != SCTP_STREAM_OPENING)) {
/*
* Can't queue any data while stream reset is underway.
*/
if (asoc->strmout[sinfo_stream].state > SCTP_STREAM_OPEN) {
error = EAGAIN;
} else {
error = EINVAL;
}
goto out_unlocked;
}
atomic_add_int(&stcb->total_sends, 1);
#if defined(__Userspace__)
if (inp->recv_callback != NULL) {
non_blocking = true;
}
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
if (SCTP_SO_IS_NBIO(so) || (flags & (MSG_NBIO | MSG_DONTWAIT)) != 0) {
#else
if (SCTP_SO_IS_NBIO(so)) {
#endif
non_blocking = true;
}
if (non_blocking) {
ssize_t amount;
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
/* Ok, we will attempt a msgsnd :> */
#if !(defined(_WIN32) || defined(__Userspace__))
if (p != NULL) {
#if defined(__FreeBSD__)
p->td_ru.ru_msgsnd++;
#else
p->p_stats->p_ru.ru_msgsnd++;
#endif
}
#endif
/* Calculate the maximum we can send */
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) {
max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes;
} else {
max_len = 0;
}
/* Unless E_EOR mode is on, we must make a send FIT in one call. */
if ((user_marks_eor == 0) &&
(sndlen > (ssize_t)SCTP_SB_LIMIT_SND(stcb->sctp_socket))) {
/* It will NEVER fit. */
error = EMSGSIZE;
goto out_unlocked;
}
if (user_marks_eor != 0) {
local_add_more = (ssize_t)min(SCTP_SB_LIMIT_SND(so), SCTP_BASE_SYSCTL(sctp_add_more_threshold));
} else {
/*-
* For non-eeor the whole message must fit in
* the socket send buffer.
*/
local_add_more = sndlen;
}
if (non_blocking) {
goto skip_preblock;
}
if (((max_len <= local_add_more) && ((ssize_t)SCTP_SB_LIMIT_SND(so) >= local_add_more)) ||
(max_len == 0) ||
((asoc->chunks_on_out_queue + asoc->stream_queue_cnt) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) {
/* No room right now! */
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
SOCKBUF_LOCK(&so->so_snd);
while ((SCTP_SB_LIMIT_SND(so) < (inqueue_bytes + local_add_more)) ||
((asoc->stream_queue_cnt + asoc->chunks_on_out_queue) >= SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue))) {
SCTPDBG(SCTP_DEBUG_OUTPUT1,"pre_block limit:%u <(inq:%d + %zd) || (%d+%d > %d)\n",
(unsigned int)SCTP_SB_LIMIT_SND(so),
inqueue_bytes,
local_add_more,
asoc->stream_queue_cnt,
asoc->chunks_on_out_queue,
SCTP_BASE_SYSCTL(sctp_max_chunks_on_queue));
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLKA, asoc, sndlen);
}
be.error = 0;
#if !(defined(_WIN32) && !defined(__Userspace__))
stcb->block_entry = &be;
#endif
SCTP_TCB_UNLOCK(stcb);
#if defined(__FreeBSD__) && !defined(__Userspace__)
error = sbwait(so, SO_SND);
#else
error = sbwait(&so->so_snd);
#endif
if (error == 0) {
if (so->so_error != 0) {
error = so->so_error;
}
if (be.error != 0) {
error = be.error;
}
}
SOCKBUF_UNLOCK(&so->so_snd);
SCTP_TCB_LOCK(stcb);
stcb->block_entry = NULL;
if (error != 0) {
goto out_unlocked;
}
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out_unlocked;
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
sctp_log_block(SCTP_BLOCK_LOG_OUTOF_BLK,
asoc, asoc->total_output_queue_size);
}
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
SOCKBUF_LOCK(&so->so_snd);
}
if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) {
max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes;
} else {
max_len = 0;
}
SOCKBUF_UNLOCK(&so->so_snd);
}
skip_preblock:
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
#if defined(__APPLE__) && !defined(__Userspace__)
error = sblock(&so->so_snd, SBLOCKWAIT(flags));
if (error != 0) {
goto out_unlocked;
}
#endif
/* sndlen covers for mbuf case
* uio_resid covers for the non-mbuf case
* NOTE: uio will be null when top/mbuf is passed
*/
if (top == NULL) {
struct sctp_stream_queue_pending *sp;
struct sctp_stream_out *strm;
uint32_t sndout;
if ((asoc->stream_locked) &&
(asoc->stream_locked_on != sinfo_stream)) {
error = EINVAL;
goto out;
}
strm = &asoc->strmout[sinfo_stream];
if (strm->last_msg_incomplete == 0) {
do_a_copy_in:
SCTP_TCB_UNLOCK(stcb);
sp = sctp_copy_it_in(stcb, asoc, sndrcvninfo, uio, net, max_len, user_marks_eor, &error);
SCTP_TCB_LOCK(stcb);
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out;
}
if (error != 0) {
goto out;
}
/*
* Reject the sending of a new user message, if the
* association is about to be shut down.
*/
if ((SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_SENT) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_RECEIVED) ||
(SCTP_GET_STATE(stcb) == SCTP_STATE_SHUTDOWN_ACK_SENT) ||
(asoc->state & SCTP_STATE_SHUTDOWN_PENDING)) {
if (sp->data != 0) {
sctp_m_freem(sp->data);
sp->data = NULL;
sp->tail_mbuf = NULL;
sp->length = 0;
}
if (sp->net != NULL) {
sctp_free_remote_addr(sp->net);
sp->net = NULL;
}
sctp_free_a_strmoq(stcb, sp, SCTP_SO_LOCKED);
error = EPIPE;
goto out_unlocked;
}
/* The out streams might be reallocated. */
strm = &asoc->strmout[sinfo_stream];
if (sp->msg_is_complete) {
strm->last_msg_incomplete = 0;
asoc->stream_locked = 0;
} else {
/* Just got locked to this guy in
* case of an interrupt.
*/
strm->last_msg_incomplete = 1;
if (asoc->idata_supported == 0) {
asoc->stream_locked = 1;
asoc->stream_locked_on = sinfo_stream;
}
sp->sender_all_done = 0;
}
sctp_snd_sb_alloc(stcb, sp->length);
atomic_add_int(&asoc->stream_queue_cnt, 1);
if (sinfo_flags & SCTP_UNORDERED) {
SCTP_STAT_INCR(sctps_sends_with_unord);
}
sp->processing = 1;
TAILQ_INSERT_TAIL(&strm->outqueue, sp, next);
asoc->ss_functions.sctp_ss_add_to_stream(stcb, asoc, strm, sp);
} else {
sp = TAILQ_LAST(&strm->outqueue, sctp_streamhead);
if (sp == NULL) {
/* ???? Huh ??? last msg is gone */
#ifdef INVARIANTS
panic("Warning: Last msg marked incomplete, yet nothing left?");
#else
SCTP_PRINTF("Warning: Last msg marked incomplete, yet nothing left?\n");
strm->last_msg_incomplete = 0;
#endif
goto do_a_copy_in;
}
if (sp->processing != 0) {
error = EINVAL;
goto out;
} else {
sp->processing = 1;
}
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
while (uio->uio_resid > 0) {
#else
while (uio_resid(uio) > 0) {
#endif
#else
while (uio->uio_resid > 0) {
#endif
/* How much room do we have? */
struct mbuf *new_tail, *mm;
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) {
max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes;
} else {
max_len = 0;
}
if ((max_len > (ssize_t)SCTP_BASE_SYSCTL(sctp_add_more_threshold)) ||
((max_len > 0 ) && (SCTP_SB_LIMIT_SND(so) < SCTP_BASE_SYSCTL(sctp_add_more_threshold))) ||
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
(uio->uio_resid <= max_len)) {
#else
(uio_resid(uio) <= max_len)) {
#endif
#else
(uio->uio_resid <= max_len)) {
#endif
SCTP_TCB_UNLOCK(stcb);
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_UNLOCK(so, 0);
#endif
sndout = 0;
new_tail = NULL;
#if defined(__FreeBSD__) || defined(__Userspace__)
mm = sctp_copy_resume(uio, (int)max_len, user_marks_eor, &error, &sndout, &new_tail);
#else
mm = sctp_copy_resume(uio, (int)max_len, &error, &sndout, &new_tail);
#endif
#if defined(__APPLE__) && !defined(__Userspace__)
SCTP_SOCKET_LOCK(so, 0);
#endif
SCTP_TCB_LOCK(stcb);
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
/* We need to get out.
* Peer probably aborted.
*/
sctp_m_freem(mm);
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out;
}
if ((mm == NULL) || (error != 0)) {
if (mm != NULL) {
sctp_m_freem(mm);
}
if (sp != NULL) {
sp->processing = 0;
}
goto out;
}
/* Update the mbuf and count */
if (sp->tail_mbuf != NULL) {
/* Tack it to the end. */
SCTP_BUF_NEXT(sp->tail_mbuf) = mm;
} else {
/* A stolen mbuf. */
sp->data = mm;
}
sp->tail_mbuf = new_tail;
sctp_snd_sb_alloc(stcb, sndout);
atomic_add_int(&sp->length, sndout);
if (sinfo_flags & SCTP_SACK_IMMEDIATELY) {
sp->sinfo_flags |= SCTP_SACK_IMMEDIATELY;
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
if (uio->uio_resid == 0) {
#else
if (uio_resid(uio) == 0) {
#endif
#else
if (uio->uio_resid == 0) {
#endif
/* got it all? */
continue;
}
/* PR-SCTP? */
if ((asoc->prsctp_supported) && (asoc->sent_queue_cnt_removeable > 0)) {
/* This is ugly but we must assure locking order */
sctp_prune_prsctp(stcb, asoc, sndrcvninfo, (int)sndlen);
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes)
max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes;
else
max_len = 0;
if (max_len > 0) {
continue;
}
}
/* wait for space now */
if (non_blocking) {
/* Non-blocking io in place out */
if (sp != NULL) {
sp->processing = 0;
}
goto skip_out_eof;
}
/* What about the INIT, send it maybe */
if (queue_only_for_init) {
if (SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) {
/* a collision took us forward? */
queue_only = 0;
} else {
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
sctp_send_initiate(inp, stcb, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
queue_only = 1;
}
}
if ((net->flight_size > net->cwnd) &&
(asoc->sctp_cmt_on_off == 0)) {
SCTP_STAT_INCR(sctps_send_cwnd_avoid);
queue_only = 1;
} else if (asoc->ifp_had_enobuf) {
SCTP_STAT_INCR(sctps_ifnomemqueued);
if (net->flight_size > (2 * net->mtu)) {
queue_only = 1;
}
asoc->ifp_had_enobuf = 0;
}
un_sent = asoc->total_output_queue_size - asoc->total_flight;
if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) &&
(asoc->total_flight > 0) &&
(asoc->stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) &&
(un_sent < (int)(asoc->smallest_mtu - SCTP_MIN_OVERHEAD))) {
/*-
* Ok, Nagle is set on and we have data outstanding.
* Don't send anything and let SACKs drive out the
* data unless we have a "full" segment to send.
*/
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_NAGLE_LOGGING_ENABLE) {
sctp_log_nagle_event(stcb, SCTP_NAGLE_APPLIED);
}
SCTP_STAT_INCR(sctps_naglequeued);
nagle_applies = 1;
} else {
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_NAGLE_LOGGING_ENABLE) {
if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY))
sctp_log_nagle_event(stcb, SCTP_NAGLE_SKIPPED);
}
SCTP_STAT_INCR(sctps_naglesent);
nagle_applies = 0;
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_CWNDLOG_PRESEND, queue_only_for_init, queue_only,
nagle_applies, un_sent);
sctp_misc_ints(SCTP_CWNDLOG_PRESEND, asoc->total_output_queue_size,
asoc->total_flight,
asoc->chunks_on_out_queue, asoc->total_flight_count);
}
if (queue_only_for_init) {
queue_only_for_init = 0;
}
if ((queue_only == 0) && (nagle_applies == 0)) {
/*-
* need to start chunk output
* before blocking.. note that if
* a lock is already applied, then
* the input via the net is happening
* and I don't need to start output :-D
*/
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
sctp_chunk_output(inp, stcb,
SCTP_OUTPUT_FROM_USR_SEND, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
}
/*-
* This is a bit strange, but I think it will
* work. The total_output_queue_size is locked and
* protected by the TCB_LOCK, which we just released.
* There is a race that can occur between releasing it
* above, and me getting the socket lock, where sacks
* come in but we have not put the SB_WAIT on the
* so_snd buffer to get the wakeup. After the LOCK
* is applied the sack_processing will also need to
* LOCK the so->so_snd to do the actual sowwakeup(). So
* once we have the socket buffer lock if we recheck the
* size we KNOW we will get to sleep safely with the
* wakeup flag in place.
*/
inqueue_bytes = asoc->total_output_queue_size - (asoc->chunks_on_out_queue * SCTP_DATA_CHUNK_OVERHEAD(stcb));
SOCKBUF_LOCK(&so->so_snd);
if (SCTP_SB_LIMIT_SND(so) <= (inqueue_bytes +
min(SCTP_BASE_SYSCTL(sctp_add_more_threshold), SCTP_SB_LIMIT_SND(so)))) {
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK,
asoc, uio->uio_resid);
#else
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK,
asoc, uio_resid(uio));
#endif
#else
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLK,
asoc, uio->uio_resid);
#endif
}
be.error = 0;
#if !(defined(_WIN32) && !defined(__Userspace__))
stcb->block_entry = &be;
#endif
SCTP_TCB_UNLOCK(stcb);
#if defined(__APPLE__) && !defined(__Userspace__)
sbunlock(&so->so_snd, 1);
#endif
#if defined(__FreeBSD__) && !defined(__Userspace__)
error = sbwait(so, SO_SND);
#else
error = sbwait(&so->so_snd);
#endif
if (error == 0) {
if (so->so_error != 0)
error = so->so_error;
if (be.error != 0) {
error = be.error;
}
}
SOCKBUF_UNLOCK(&so->so_snd);
SCTP_TCB_LOCK(stcb);
stcb->block_entry = NULL;
if ((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) ||
(asoc->state & SCTP_STATE_WAS_ABORTED)) {
if (asoc->state & SCTP_STATE_WAS_ABORTED) {
/* XXX: Could also be ECONNABORTED, not enough info. */
error = ECONNRESET;
} else {
error = ENOTCONN;
}
goto out_unlocked;
}
if (error != 0) {
if (sp != NULL) {
sp->processing = 0;
}
goto out_unlocked;
}
#if defined(__APPLE__) && !defined(__Userspace__)
error = sblock(&so->so_snd, SBLOCKWAIT(flags));
if (error != 0) {
goto out_unlocked;
}
#endif
} else {
SOCKBUF_UNLOCK(&so->so_snd);
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
sctp_log_block(SCTP_BLOCK_LOG_OUTOF_BLK,
asoc, asoc->total_output_queue_size);
}
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
/* The out streams might be reallocated. */
strm = &asoc->strmout[sinfo_stream];
if (sp != NULL) {
if (sp->msg_is_complete == 0) {
strm->last_msg_incomplete = 1;
if (asoc->idata_supported == 0) {
asoc->stream_locked = 1;
asoc->stream_locked_on = sinfo_stream;
}
} else {
sp->sender_all_done = 1;
strm->last_msg_incomplete = 0;
asoc->stream_locked = 0;
}
sp->processing = 0;
} else {
SCTP_PRINTF("Huh no sp TSNH?\n");
strm->last_msg_incomplete = 0;
asoc->stream_locked = 0;
}
#if defined(__APPLE__) && !defined(__Userspace__)
#if defined(APPLE_LEOPARD)
if (uio->uio_resid == 0) {
#else
if (uio_resid(uio) == 0) {
#endif
#else
if (uio->uio_resid == 0) {
#endif
got_all_of_the_send = true;
}
} else {
error = sctp_msg_append(stcb, net, top, sndrcvninfo);
top = NULL;
if ((sinfo_flags & SCTP_EOF) != 0) {
got_all_of_the_send = true;
}
}
if (error != 0) {
goto out;
}
dataless_eof:
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
/* EOF thing ? */
if ((sinfo_flags & SCTP_EOF) && got_all_of_the_send) {
SCTP_STAT_INCR(sctps_sends_with_eof);
error = 0;
if (TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->sent_queue) &&
sctp_is_there_unsent_data(stcb, SCTP_SO_LOCKED) == 0) {
if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
goto abort_anyway;
}
/* there is nothing queued to send, so I'm done... */
if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
struct sctp_nets *netp;
/* only send SHUTDOWN the first time through */
if (SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) {
SCTP_STAT_DECR_GAUGE32(sctps_currestab);
}
SCTP_SET_STATE(stcb, SCTP_STATE_SHUTDOWN_SENT);
sctp_stop_timers_for_shutdown(stcb);
if (asoc->alternate != NULL) {
netp = asoc->alternate;
} else {
netp = asoc->primary_destination;
}
sctp_send_shutdown(stcb, netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWN, stcb->sctp_ep, stcb,
netp);
sctp_timer_start(SCTP_TIMER_TYPE_SHUTDOWNGUARD, stcb->sctp_ep, stcb,
NULL);
}
} else {
/*-
* we still got (or just got) data to send, so set
* SHUTDOWN_PENDING
*/
/*-
* XXX sockets draft says that SCTP_EOF should be
* sent with no data. currently, we will allow user
* data to be sent first and move to
* SHUTDOWN-PENDING
*/
if ((SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_SENT) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
(SCTP_GET_STATE(stcb) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
if ((*asoc->ss_functions.sctp_ss_is_user_msgs_incomplete)(stcb, asoc)) {
SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_PARTIAL_MSG_LEFT);
}
SCTP_ADD_SUBSTATE(stcb, SCTP_STATE_SHUTDOWN_PENDING);
if (TAILQ_EMPTY(&asoc->send_queue) &&
TAILQ_EMPTY(&asoc->sent_queue) &&
(asoc->state & SCTP_STATE_PARTIAL_MSG_LEFT)) {
struct mbuf *op_err;
char msg[SCTP_DIAG_INFO_LEN];
skip_out_eof:
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
some_on_control = !TAILQ_EMPTY(&asoc->control_send_queue);
if (queue_only_for_init) {
if (SCTP_GET_STATE(stcb) == SCTP_STATE_OPEN) {
/* a collision took us forward? */
queue_only = 0;
} else {
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
sctp_send_initiate(inp, stcb, SCTP_SO_LOCKED);
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
SCTP_SET_STATE(stcb, SCTP_STATE_COOKIE_WAIT);
queue_only = 1;
}
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
if ((net->flight_size > net->cwnd) &&
(asoc->sctp_cmt_on_off == 0)) {
SCTP_STAT_INCR(sctps_send_cwnd_avoid);
queue_only = 1;
} else if (asoc->ifp_had_enobuf) {
SCTP_STAT_INCR(sctps_ifnomemqueued);
if (net->flight_size > (2 * net->mtu)) {
queue_only = 1;
}
asoc->ifp_had_enobuf = 0;
}
un_sent = asoc->total_output_queue_size - asoc->total_flight;
if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) &&
(asoc->total_flight > 0) &&
(asoc->stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) &&
(un_sent < (int)(asoc->smallest_mtu - SCTP_MIN_OVERHEAD))) {
/*-
* Ok, Nagle is set on and we have data outstanding.
* Don't send anything and let SACKs drive out the
* data unless wen have a "full" segment to send.
*/
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_NAGLE_LOGGING_ENABLE) {
sctp_log_nagle_event(stcb, SCTP_NAGLE_APPLIED);
}
SCTP_STAT_INCR(sctps_naglequeued);
nagle_applies = 1;
} else {
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_NAGLE_LOGGING_ENABLE) {
if (sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY))
sctp_log_nagle_event(stcb, SCTP_NAGLE_SKIPPED);
}
SCTP_STAT_INCR(sctps_naglesent);
nagle_applies = 0;
}
if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_BLK_LOGGING_ENABLE) {
sctp_misc_ints(SCTP_CWNDLOG_PRESEND, queue_only_for_init, queue_only,
nagle_applies, un_sent);
sctp_misc_ints(SCTP_CWNDLOG_PRESEND, asoc->total_output_queue_size,
asoc->total_flight,
asoc->chunks_on_out_queue, asoc->total_flight_count);
}
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_ENTER(et);
#endif
if ((queue_only == 0) && (nagle_applies == 0) && (asoc->peers_rwnd && un_sent)) {
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_USR_SEND, SCTP_SO_LOCKED);
} else if ((queue_only == 0) &&
(asoc->peers_rwnd == 0) &&
(asoc->total_flight == 0)) {
/* We get to have a probe outstanding */
sctp_chunk_output(inp, stcb, SCTP_OUTPUT_FROM_USR_SEND, SCTP_SO_LOCKED);
} else if (some_on_control) {
int num_out, reason;
/* Here we do control only */
(void)sctp_med_chunk_output(inp, stcb, asoc, &num_out,
&reason, 1, 1, &now, &now_filled,
sctp_get_frag_point(stcb),
SCTP_SO_LOCKED);
}
#if defined(__FreeBSD__) && !defined(__Userspace__)
NET_EPOCH_EXIT(et);
#endif
SCTPDBG(SCTP_DEBUG_OUTPUT1, "USR Send complete qo:%d prw:%d unsent:%d tf:%d cooq:%d toqs:%d err:%d\n",
queue_only, asoc->peers_rwnd, un_sent,
asoc->total_flight, asoc->chunks_on_out_queue,
asoc->total_output_queue_size, error);
KASSERT(stcb != NULL, ("stcb is NULL"));
SCTP_TCB_LOCK_ASSERT(stcb);
KASSERT((asoc->state & SCTP_STATE_ABOUT_TO_BE_FREED) == 0,
("Association about to be freed"));
KASSERT((asoc->state & SCTP_STATE_WAS_ABORTED) == 0,
("Association was aborted"));
out:
#if defined(__APPLE__) && !defined(__Userspace__)
sbunlock(&so->so_snd, 1);
#endif
out_unlocked:
if (create_lock_applied) {
SCTP_ASOC_CREATE_UNLOCK(inp);
}
if (stcb != NULL) {
if (local_soresv) {
atomic_subtract_int(&asoc->sb_send_resv, (int)sndlen);
}
if (free_cnt_applied) {
atomic_subtract_int(&asoc->refcnt, 1);
}
SCTP_TCB_UNLOCK(stcb);
}
if (top != NULL) {
sctp_m_freem(top);
}
if (control != NULL) {
sctp_m_freem(control);
}
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, error);
return (error);
}
if (stcb->asoc.auth_supported == 0) { return (m);
} /* does the requested chunk require auth? */ if (!sctp_auth_is_required_chunk(chunk, stcb->asoc.peer_auth_chunks)) { return (m);
}
m_auth = sctp_get_mbuf_for_msg(sizeof(*auth), 0, M_NOWAIT, 1, MT_HEADER); if (m_auth == NULL) { /* no mbuf's */ return (m);
} /* reserve some space if this will be the first mbuf */ if (m == NULL)
SCTP_BUF_RESV_UF(m_auth, SCTP_MIN_OVERHEAD); /* fill in the AUTH chunk details */
auth = mtod(m_auth, struct sctp_auth_chunk *);
memset(auth, 0, sizeof(*auth));
auth->ch.chunk_type = SCTP_AUTHENTICATION;
auth->ch.chunk_flags = 0;
chunk_len = sizeof(*auth) +
sctp_get_hmac_digest_len(stcb->asoc.peer_hmac_id);
auth->ch.chunk_length = htons(chunk_len);
auth->hmac_id = htons(stcb->asoc.peer_hmac_id); /* key id and hmac digest will be computed and filled in upon send */
/* save the offset where the auth was inserted into the chain */
*offset = 0; for (cn = m; cn; cn = SCTP_BUF_NEXT(cn)) {
*offset += SCTP_BUF_LEN(cn);
}
/* update length and return pointer to the auth chunk */
SCTP_BUF_LEN(m_auth) = chunk_len;
m = sctp_copy_mbufchain(m_auth, m, m_end, 1, chunk_len, 0); if (auth_ret != NULL)
*auth_ret = auth;
#if defined(__FreeBSD__)
sin = &ro->ro_nh->gw4_sa; #else
sin = (struct sockaddr_in *)ro->ro_rt->rt_gateway; #endif
gwnetaddr.s_addr = (sin->sin_addr.s_addr & mask->sin_addr.s_addr);
SCTPDBG(SCTP_DEBUG_OUTPUT1, "match_nexthop4: nexthop is "); #if defined(__FreeBSD__)
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, &ro->ro_nh->gw_sa); #else
SCTPDBG_ADDR(SCTP_DEBUG_OUTPUT2, ro->ro_rt->rt_gateway); #endif
SCTPDBG(SCTP_DEBUG_OUTPUT1, "network address is %x\n", gwnetaddr.s_addr); if (srcnetaddr.s_addr == gwnetaddr.s_addr) { return (1);
} #endif return (0);
} #elif defined(__Userspace__) /* TODO __Userspace__ versions of sctp_vXsrc_match_nexthop(). */
int
sctp_v6src_match_nexthop(struct sockaddr_in6 *src6, sctp_route_t *ro)
{ return (0);
}
int
sctp_v4src_match_nexthop(struct sctp_ifa *sifa, sctp_route_t *ro)
{ return (0);
}
#endif
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.1.98Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.