sctp_output.c revision e6f13f867aee09b686a93cafd9c45389c0997ca6
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define _SUN_TPI_VERSION 2
#include <sys/socketvar.h>
/* swilly code in sys/socketvar.h turns off DEBUG */
#ifdef __lint
#define DEBUG
#endif
#include <inet/ipclassifier.h>
/*
* PR-SCTP comments.
*
* A message can expire before it gets to the transmit list (i.e. it is still
* in the unsent list - unchunked), after it gets to the transmit list, but
* before transmission has actually started, or after transmission has begun.
* Accordingly, we check for the status of a message in sctp_chunkify() when
* the message is being transferred from the unsent list to the transmit list;
* in sctp_get_msg_to_send(), when we get the next chunk from the transmit
* list and in sctp_rexmit() when we get the next chunk to be (re)transmitted.
* When we nuke a message in sctp_chunkify(), all we need to do is take it
* out of the unsent list and update sctp_unsent; when a message is deemed
* timed-out in sctp_get_msg_to_send() we can just take it out of the transmit
* list, update sctp_unsent IFF transmission for the message has not yet begun
* (i.e. !SCTP_CHUNK_ISSENT(meta->b_cont)). However, if transmission for the
* message has started, then we cannot just take it out of the list, we need
* to send Forward TSN chunk to the peer so that the peer can clear its
* fragment list for this message. However, we cannot just send the Forward
* TSN in sctp_get_msg_to_send() because there might be unacked chunks for
* messages preceeding this abandoned message. So, we send a Forward TSN
* IFF all messages prior to this abandoned message has been SACKd, if not
* we defer sending the Forward TSN to sctp_cumack(), which will check for
* this condition and send the Forward TSN via sctp_check_abandoned_msg(). In
* sctp_rexmit() when we check for retransmissions, we need to determine if
* the advanced peer ack point can be moved ahead, and if so, send a Forward
* TSN to the peer instead of retransmitting the chunk. Note that when
* we send a Forward TSN for a message, there may be yet unsent chunks for
* this message; we need to mark all such chunks as abandoned, so that
* sctp_cumack() can take the message out of the transmit list, additionally
* sctp_unsent need to be adjusted. Whenever sctp_unsent is updated (i.e.
* be notified so that it can adjust its idea of the queued message.
*/
#include "sctp_impl.h"
static struct kmem_cache *sctp_kmem_ftsn_set_cache;
#ifdef DEBUG
#endif
/*
* Called to allocate a header mblk when sending data to SCTP.
* Data will follow in b_cont of this mblk.
*/
mblk_t *
int flags)
{
struct T_unitdata_req *tudr;
int error;
if (flags & SCTP_CAN_BLOCK) {
} else {
}
if (mp) {
if (nlen > 0)
if (clen > 0)
}
return (mp);
}
/*ARGSUSED2*/
int
{
struct T_unitdata_req *tudr;
int error = 0;
/* Get destination address, if specified */
if (tudr->DEST_length > 0) {
sin = (struct sockaddr_in *)
switch (sin->sin_family) {
case AF_INET:
return (EINVAL);
}
break;
case AF_INET6:
return (EINVAL);
}
sin6 = (struct sockaddr_in6 *)
break;
default:
return (EAFNOSUPPORT);
}
return (EINVAL);
}
}
/* Ancillary Data? */
if (tudr->OPT_length > 0) {
char *cend;
struct sctp_sndrcvinfo *sndrcv;
for (;;) {
break;
}
return (EINVAL);
}
break;
}
else
break;
}
}
if (!pump) {
return (ENOMEM);
}
}
goto process_sendq;
}
goto done;
/* Reject any new data requests if we are shutting down */
goto unlock_done;
}
/* Re-use the mproto to store relevant info. */
/* User requested specific destination */
/* Send sendfail event */
B_FALSE);
goto unlock_done;
}
/* no data */
if (msg_len == 0) {
goto unlock_done;
}
/* Add it to the unsent list */
} else {
}
return (0);
done:
return (error);
}
void
{
int count;
int chunksize;
sizeof (*sdc);
else
sizeof (*sdc);
/*
* If this partially chunked, we ignore the first_len for now
* and use the one already present. For the unchunked bits, we
* use the length of the last chunk.
*/
if (SCTP_IS_MSG_CHUNKED(mdblk)) {
int chunk_len;
else
} else {
/*
* Update ULP the amount of queued data, which is
* sent-unack'ed + unsent.
*/
if (!SCTP_IS_DETACHED(sctp)) {
}
goto try_next;
}
}
chunk_tail = NULL;
/* Skip as many mblk's as we need */
}
/* Split the chain, if needed */
if (count > 0) {
} else {
}
return;
}
if (chunk_tail != NULL) {
} else {
}
count = 0;
} else if (chunk_tail == NULL) {
goto next;
} else {
}
}
/* Alloc chunk hdr, if needed */
} else {
}
return;
}
} else {
}
/*
* We defer assigning the SSN just before sending the chunk, else
* if we drop the chunk in sctp_get_msg_to_send(), we would need
* to send a Forward TSN to let the peer know. Some more comments
* about this in sctp_impl.h for SCTP_CHUNK_SENT.
*/
} else {
}
next:
goto nextchunk;
}
}
} else {
}
if ((count > 0) &&
} else {
sizeof (*sdc);
}
} else {
else
}
goto nextmsg;
}
}
void
{
}
}
mblk_t *
int *error)
{
int hdrlen;
char *hdr;
*error = 0;
if (isv4) {
} else {
}
/*
* A null fp->ire could mean that the address is 'down'. Similarly,
* it is possible that the address went down, we tried to send an
* heartbeat and ended up setting fp->saddr as unspec because we
* didn't have any usable source address. In either case
* sctp_get_ire() will try find an IRE, if available, and set
* the source address, if needed. If we still don't have any
* usable source address, fp->state will be SCTP_FADDRS_UNREACH and
* we return EHOSTUNREACH.
*/
*error = EHOSTUNREACH;
return (NULL);
}
}
/* Copy in IP header. */
/*
* This can happen if IP headers are adjusted after
* data was moved into chunks, or during retransmission,
* or things like snoop is running.
*/
return (NULL);
}
} else {
}
if (sacklen) {
}
/* change addresses in header */
if (isv4) {
} else if (sctp->sctp_bound_to_all) {
}
} else {
} else if (sctp->sctp_bound_to_all) {
}
}
}
/*
* IP will not free this IRE if it is condemned. SCTP needs to
* free it.
*/
}
/* Stash the conn and ire ptr info for IP */
return (mp);
}
/*
* SCTP requires every chunk to be padded so that the total length
* is a multiple of SCTP_ALIGN. This function returns a mblk with
* the specified pad length.
*/
static mblk_t *
{
return (fill);
}
/*
* The memory saving path of reusing the sctp_pad_mp
* fails may be because it has been dupb() too
* many times (DBLK_REFMAX). Use the memory consuming
* path of allocating the pad mblk.
*/
/* Zero it out. SCTP_ALIGN is sizeof (int32_t) */
}
return (fill);
}
static mblk_t *
{
int msglen;
int extra;
if (SCTP_IS_MSG_ABANDONED(meta) ||
continue;
}
if (SCTP_CHUNK_WANT_REXMIT(mp)) {
/*
* Use the same peer address to do fast
* retransmission. If the original peer
* address is dead, switch to the current
* one. Record the old one so that we
* will pick the chunks sent to the old
* one for fast retransmission.
*/
}
continue;
continue;
}
}
/*
* We still return at least the first message
* even if that message cannot fit in as
* PMTU may have changed.
*/
return (start_mp);
}
return (start_mp);
if (extra > 0) {
} else {
return (start_mp);
}
}
} else {
}
}
}
}
/* Clear the flag as there is no more message to be fast rexmitted. */
return (start_mp);
}
/* A debug function just to make sure that a mblk chain is not broken */
#ifdef DEBUG
static boolean_t
{
return (B_TRUE);
return (B_TRUE);
}
return (B_FALSE);
}
#endif
/*
* Gets the next unsent chunk to transmit. Messages that are abandoned are
* skipped. A message can be abandoned if it has a non-zero timetolive and
* transmission has not yet started or if it is a partially reliable
* message and its time is up (assuming we are PR-SCTP aware).
* 'cansend' is used to determine if need to try and chunkify messages from
* the unsent list, if any, and also as an input to sctp_chunkify() if so.
* When called from sctp_rexmit(), we don't want to chunkify, so 'cansend'
* will be set to 0.
*/
mblk_t *
{
*error = 0;
if (SCTP_IS_MSG_ABANDONED(meta))
goto next_msg;
if (SCTP_CHUNK_CANSEND(mp1)) {
#ifdef DEBUG
#endif
return (meta);
}
}
goto next_msg;
}
/*
* If we come here and the first chunk is sent, then we
* we are PR-SCTP aware, in which case if the cumulative
* TSN has moved upto or beyond the first chunk (which
* means all the previous messages have been cumulative
* SACK'd), then we send a Forward TSN with the last
* chunk that was sent in this message. If we can't send
* a Forward TSN because previous non-abandoned messages
* have not been acked then we will defer the Forward TSN
* to sctp_rexmit() or sctp_cumack().
*/
if (SCTP_CHUNK_ISSENT(mp1)) {
if (*error != 0) {
#ifdef DEBUG
sctp->sctp_xmit_tail));
#endif
return (NULL);
}
goto next_msg;
}
} else {
}
/*
* Update ULP the amount of queued data, which is
* sent-unack'ed + unsent.
*/
if (!SCTP_IS_DETACHED(sctp)) {
}
}
/* chunkify, if needed */
} else {
/*
* If user specified destination, try to honor that.
*/
goto chunk_done;
}
}
goto chunk_done;
/*
* sctp_chunkify() won't advance sctp_xmit_tail if it adds
* new chunk(s) to the tail, so we need to skip the
* sctp_xmit_tail, which would have already been processed.
* This could happen when there is unacked chunks, but
* nothing new to send.
* When sctp_chunkify() is called when the transmit queue
* is empty then we need to start from sctp_xmit_tail.
*/
#ifdef DEBUG
}
#endif
goto chunk_done;
}
goto chunkified;
}
#ifdef DEBUG
#endif
return (NULL);
}
void
{
int pktlen = 0;
return;
}
return;
}
}
}
void
{
int extra;
int error;
sacklen = 0;
} else {
/* send a SACK chunk */
sacklen = sizeof (sctp_chunk_hdr_t) +
sizeof (sctp_sack_chunk_t) +
}
sctp->sctp_unacked &&
!sctp->sctp_ndelay) {
goto unsent_data;
}
pad = 0;
/*
* Find first segment eligible for transmit.
*/
if (SCTP_CHUNK_CANSEND(mp))
break;
}
goto unsent_data;
}
}
/*
* Check rwnd.
*/
goto unsent_data;
}
/*
* Pick destination address, and check cwnd.
*/
/*
* Only include SACK chunk if it can be bundled
* with a data chunk, and sent to sctp_lastdata.
*/
goto unsent_data;
}
&error);
/*
* If none of the source addresses are
* available (i.e error == EHOSTUNREACH),
* pretend we have sent the data. We will
* eventually time out trying to retramsmit
* the data if the interface never comes up.
* If we have already sent some stuff (i.e.,
* notsent is B_FALSE) then we are fine, else
* just mark this packet as sent.
*/
}
goto unsent_data;
}
sacklen = 0;
} else {
/*
* If we haven't sent data to this destination for
* a while, do slow start again.
*/
}
goto unsent_data;
}
goto unsent_data;
}
/*
* If none of the source addresses are
* available (i.e error == EHOSTUNREACH),
* pretend we have sent the data. We will
* eventually time out trying to retramsmit
* the data if the interface never comes up.
* If we have already sent some stuff (i.e.,
* notsent is B_FALSE) then we are fine, else
* just mark this packet as sent.
*/
}
goto unsent_data;
}
}
/* Use this chunk to measure RTT? */
if (sctp->sctp_out_time == 0) {
}
if (extra > 0) {
} else {
goto unsent_data;
}
}
/* See if we can bundle more. */
if (SCTP_CHUNK_CANSEND(mp))
break;
}
break;
}
break;
}
break;
}
break;
if (extra > 0) {
} else {
break;
}
}
}
/*
* Path MTU is different from what we thought it would
* be when we created chunks, or IP headers have grown.
* Need to clear the DF bit.
*/
}
/* xmit segment */
"ssn %d to %p (rwnd %d, cansend %d, lastack_rxd %x)\n",
/* arm rto timer (if not set) */
if (!fp->timer_running)
}
return;
/* arm persist timer (if rto timer not set) */
if (!fp->timer_running)
}
/*
* The following two functions initialize and destroy the cache
* associated with the sets used for PR-SCTP.
*/
void
sctp_ftsn_sets_init(void)
{
NULL, 0);
}
void
sctp_ftsn_sets_fini(void)
{
}
/* Free PR-SCTP sets */
void
{
sctp_ftsn_set_t *p;
while (s != NULL) {
p = s->next;
s = p;
}
}
/*
* Given a message meta block, meta, this routine creates or modifies
* the set that will be used to generate a Forward TSN chunk. If the
* entry for stream id, sid, for this message already exists, the
* sequence number, ssn, is updated if it is greater than the existing
* one. If an entry for this sid does not exist, one is created if
* the size does not exceed fp->sfa_pmss. We return false in case
* or an error.
*/
{
sctp_ftsn_set_t *p;
/* msg_hdr->smh_ssn is already in NBO */
if (*s == NULL) {
if (*s == NULL)
return (B_FALSE);
*nsets = 1;
return (B_TRUE);
}
return (B_TRUE);
}
}
/* the last one */
} else {
return (B_FALSE);
return (B_FALSE);
p = p->next;
(*nsets)++;
}
return (B_TRUE);
}
/*
* Given a set of stream id - sequence number pairs, this routing creates
* a Forward TSN chunk. The cumulative TSN (advanced peer ack point)
* for the chunk is obtained from sctp->sctp_adv_pap. The caller
*/
mblk_t *
{
seglen += sizeof (sctp_chunk_hdr_t);
else
return (NULL);
/*
* The cast here should not be an issue since seglen is
* the length of the Forward TSN chunk.
*/
while (nsets > 0) {
ftsn_entry++;
nsets--;
}
return (ftsn_mp);
}
/*
* Given a starting message, the routine steps through all the
* messages whose TSN is less than sctp->sctp_adv_pap and creates
* ftsn sets. The ftsn sets is then used to create an Forward TSN
* chunk. All the messages, that have chunks that are included in the
* ftsn sets, are flagged abandonded. If a message is partially sent
* and is deemed abandoned, all remaining unsent chunks are marked
* abandoned and are deducted from sctp_unsent.
*/
void
{
/*
* Skip adding FTSN sets for un-ordered messages as they do
* not have SSNs.
*/
if (!ubit &&
goto ftsn_done;
}
}
if (!SCTP_CHUNK_ISSENT(mp1))
break;
}
}
/*
* Can't compare with sets == NULL, since we don't add any
* sets for un-ordered messages.
*/
return;
return;
sacklen = 0;
} else {
sacklen = sizeof (sctp_chunk_hdr_t) +
sizeof (sctp_sack_chunk_t) +
/* piggybacked SACK doesn't fit */
sacklen = 0;
} else {
}
}
return;
}
/*
* XXXNeed to optimise this, the reason it is done here is so
* that we don't have to undo in case of failure.
*/
if (!SCTP_IS_MSG_ABANDONED(meta_head))
if (!SCTP_CHUNK_ISACKED(mp1)) {
}
}
if (!SCTP_CHUNK_ABANDONED(mp1)) {
}
}
if (!SCTP_CHUNK_ISSENT(mp1))
break;
}
}
if (unsent > 0) {
/*
* Update ULP the amount of queued data, which is
* sent-unack'ed + unsent.
*/
if (!SCTP_IS_DETACHED(sctp)) {
}
}
}
/*
* This function steps through messages starting at meta and checks if
* the message is abandoned. It stops when it hits an unsent chunk or
* a message that has all its chunk acked. This is the only place
* where the sctp_adv_pap is moved forward to indicated abandoned
* messages.
*/
void
{
if (!SCTP_IS_MSG_ABANDONED(meta) &&
return;
}
}
break;
/*
* We continue checking for successive messages only if there
* is a chunk marked for retransmission. Else, we might
* end up sending FTSN prematurely for chunks that have been
* sent, but not yet acked.
*/
if (!SCTP_IS_MSG_ABANDONED(meta) &&
break;
}
if (!SCTP_CHUNK_ISSENT(mp)) {
return;
}
if (SCTP_CHUNK_WANT_REXMIT(mp))
break;
}
break;
}
}
}
/*
* Determine if we should bundle a data chunk with the chunk being
* retransmitted. We bundle if
*
* - the chunk is sent to the same destination and unack'ed.
*
* OR
*
* - the chunk is unsent, i.e. new data.
*/
(!SCTP_CHUNK_ABANDONED((mp)) && \
!SCTP_CHUNK_ISACKED(mp))) || \
/*
* Retransmit first segment which hasn't been acked with cumtsn or send
* a Forward TSN chunk, if appropriate.
*/
void
{
int extra;
if (!SCTP_CHUNK_ISSENT(mp))
goto window_probe;
/*
* We break in the following cases -
*
* if the advanced peer ack point includes the next
* chunk to be retransmited - possibly the Forward
* TSN was lost.
*
* if we are PRSCTP aware and the next chunk to be
* retransmitted is now abandoned
*
* if the next chunk to be retransmitted is for
* the dest on which the timer went off. (this
* message is not abandoned).
*
* We check for Forward TSN only for the first
* eligible chunk to be retransmitted. The reason
* being if the first eligible chunk is skipped (say
* it was sent to a destination other than oldfp)
* then we cannot advance the cum TSN via Forward
* TSN chunk.
*
* Also, ftsn_check is B_TRUE only for the first
* eligible chunk, it will be B_FALSE for all
* subsequent candidate messages for retransmission.
*/
goto out;
} else {
adv_pap)) {
goto out;
}
}
}
goto out;
}
}
}
}
}
/*
* Retransmit fired for a destination which didn't have
* any unacked data pending.
*/
/*
* Send a window probe. Inflate frwnd to allow
* sending one segment.
*/
/* next TSN to send */
/*
* The above sctp_frwnd adjustment is coarse. The "changed"
* sctp_frwnd may allow us to send more than 1 packet. So
* tell sctp_output() to send only 1 packet.
*/
/* Last sent TSN */
}
return;
out:
/*
* After a time out, assume that everything has left the network. So
* we can clear rxt_unacked for the original peer address.
*/
oldfp->rxt_unacked = 0;
/*
* If we were probing for zero window, don't adjust retransmission
* variables, but the timer is still backed off.
*/
if (sctp->sctp_zero_win_probe) {
/*
* Get the Zero Win Probe for retrasmission, sctp_rxt_nxttsn
* and sctp_rxt_maxtsn will specify the ZWP packet.
*/
} else {
}
/*
* The strikes will be clear by sctp_faddr_alive() when the
* other side sends us an ack.
*/
sctp->sctp_strikes++;
return;
}
/*
* Enter slowstart for this destination
*/
if (do_ftsn) {
goto restart_timer;
}
/*
* Move to the next unabandoned chunk. XXXCheck if meta will
* always be marked abandoned.
*/
else
goto try_bundle;
}
/* Find out if we need to piggyback SACK. */
sacklen = 0;
} else {
sacklen = sizeof (sctp_chunk_hdr_t) +
sizeof (sctp_sack_chunk_t) +
/* piggybacked SACK doesn't fit */
sacklen = 0;
} else {
/*
* OK, we have room to send SACK back. But we
* should send it back to the last fp where we
* receive data from, unless sctp_lastdata equals
* oldfp, then we should probably not send it
* back to that fp. Also we should check that
* the fp is alive.
*/
}
}
}
/*
* Cancel RTT measurement if the retransmitted TSN is before the
* TSN used for timimg.
*/
if (sctp->sctp_out_time != 0 &&
sctp->sctp_out_time = 0;
}
/* Clear the counter as the RTT calculation may be off. */
fp->rtt_updates = 0;
oldfp->rtt_updates = 0;
/*
* After a timeout, we should change the current faddr so that
* new chunks will be sent to the alternate address.
*/
goto restart_timer;
if (extra > 0) {
} else {
goto restart_timer;
}
}
goto restart_timer;
}
/* We can at least and at most send 1 packet at timeout. */
/* Go through the list to find more chunks to be bundled. */
/* Check if the chunk can be bundled. */
break;
}
/* Go to the next message. */
if (SCTP_IS_MSG_ABANDONED(meta) ||
sctp)) {
continue;
}
goto try_bundle;
}
/* No more chunk to be bundled. */
break;
}
break;
break;
if (extra > 0) {
} else {
break;
}
}
}
/*
* Path MTU is different from path we thought it would
* be when we created chunks, or IP headers have grown.
* Need to clear the DF bit.
*/
}
"ssn %d to %p (rwnd %d, lastack_rxd %x)\n",
/*
* Restart the oldfp timer with exponential backoff and
* the new fp timer for the retransmitted chunks.
*/
sctp->sctp_strikes++;
/*
* If there is still some data in the oldfp, restart the
* retransmission timer. If there is no data, the heartbeat will
* continue to run so it will do its job in checking the reachability
* of the oldfp.
*/
/*
* Should we restart the timer of the new fp? If there is
* outstanding data to the new fp, the timer should be
* running already. So restarting it means that the timer
* will fire later for those outstanding data. But if
* we don't restart it, the timer will fire too early for the
* just retransmitted chunks to the new fp. The reason is that we
* don't keep a timestamp on when a chunk is retransmitted.
* So when the timer fires, it will just search for the
* chunk with the earliest TSN sent to new fp. This probably
* is the chunk we just retransmitted. So for now, let's
* be conservative and restart the timer of the new fp.
*/
}
/*
* This function is called by sctp_ss_rexmit() to create a packet
* to be retransmitted to the given fp. The given meta and mp
* parameters are respectively the sctp_msg_hdr_t and the mblk of the
* first chunk to be retransmitted. This is also called when we want
* to retransmit a zero window probe from sctp_rexmit() or when we
* want to retransmit the zero window probe after the window has
* opened from sctp_got_sack().
*/
mblk_t *
{
int extra;
return (NULL);
if (extra > 0) {
} else {
return (NULL);
}
}
return (NULL);
}
/*
* Don't update the TSN if we are doing a Zero Win Probe.
*/
if (!sctp->sctp_zero_win_probe)
/*
* Go through the list to find more chunks to be bundled.
* We should only retransmit sent by unack'ed chunks. Since
* they were sent before, the peer's receive window should
* be able to receive them.
*/
/* Check if the chunk can be bundled. */
break;
}
/* Go to the next message. */
if (SCTP_IS_MSG_ABANDONED(*meta) ||
sctp)) {
continue;
}
goto try_bundle;
}
/* No more chunk to be bundled. */
break;
}
/* Don't bundle chunks beyond sctp_rxt_maxtsn. */
break;
break;
break;
if (extra > 0) {
} else {
break;
}
}
/*
* Don't update the TSN if we are doing a Zero Win Probe.
*/
if (!sctp->sctp_zero_win_probe)
}
*packet_len = seglen;
return (head);
}
/*
* sctp_ss_rexmit() is called when we get a SACK after a timeout which
* advances the cum_tsn but the cum_tsn is still less than what we have sent
* (sctp_rxt_maxtsn) at the time of the timeout. This SACK is a "partial"
* SACK. We retransmit unacked chunks without having to wait for another
* timeout. The rationale is that the SACK should not be "partial" if all the
* lost chunks have been retransmitted. Since the SACK is "partial,"
* the chunks between the cum_tsn and the sctp_rxt_maxtsn should still
* be missing. It is better for us to retransmit them now instead
* of waiting for a timeout.
*/
void
{
int burst;
/*
* If the last cum ack is smaller than what we have just
* retransmitted, simply return.
*/
else
return;
/*
* After a timer fires, sctp_current should be set to the new
* fp where the retransmitted chunks are sent.
*/
/*
* Since we are retransmitting, we only need to use cwnd to determine
* how much we can send as we were allowed (by peer's receive window)
* to send those retransmitted chunks previously when they are first
* sent. If we record how much we have retransmitted but
* unacknowledged using rxt_unacked, then the amount we can now send
* is equal to cwnd minus rxt_unacked.
*
* The field rxt_unacked is incremented when we retransmit a packet
* and decremented when we got a SACK acknowledging something. And
* it is reset when the retransmission timer fires as we assume that
* all packets have left the network after a timeout. If this
* assumption is not true, it means that after a timeout, we can
* get a SACK acknowledging more than rxt_unacked (its value only
* contains what is retransmitted when the timer fires). So
* rxt_unacked will become very big (it is an unsiged int so going
* negative means that the value is huge). This is the reason we
* always send at least 1 MSS bytes.
*
* The reason why we do not have an accurate count is that we
* only know how many packets are outstanding (using the TSN numbers).
* But we do not know how many bytes those packets contain. To
* have an accurate count, we need to walk through the send list.
* As it is not really important to have an accurate count during
* retransmission, we skip this walk to save some time. This should
* not make the retransmission too aggressive to cause congestion.
*/
else
/* Find the first unack'ed chunk */
if (SCTP_IS_MSG_ABANDONED(meta) ||
continue;
}
/* Again, this may not be possible */
if (!SCTP_CHUNK_ISSENT(mp))
return;
goto found_msg;
}
}
/* Everything is abandoned... */
return;
if (!fp->timer_running)
return;
}
/*
* Path MTU is different from path we thought it would
* be when we created chunks, or IP headers have grown.
* Need to clear the DF bit.
*/
}
/* Check and see if there is more chunk to be retransmitted. */
return;
return;
/* Retransmit another packet if the window allows. */
/* Again, this may not be possible */
if (!SCTP_CHUNK_ISSENT(mp))
return;
if (!SCTP_CHUNK_ISACKED(mp))
goto found_msg;
}
}
}