📄 tcpin.c
字号:
/* Swap port numbers */
tmp = seg->source;
seg->source = seg->dest;
seg->dest = tmp;
if(seg->flags.ack){
/* This reset is being sent to clear a half-open connection.
* Set the sequence number of the RST to the incoming ACK
* so it will be acceptable.
*/
seg->flags.ack = 0;
seg->seq = seg->ack;
seg->ack = 0;
} else {
/* We're rejecting a connect request (SYN) from TCP_LISTEN state
* so we have to "acknowledge" their SYN.
*/
seg->flags.ack = 1;
seg->ack = seg->seq;
seg->seq = 0;
if(seg->flags.syn)
seg->ack++;
}
/* Set remaining parts of packet */
seg->flags.urg = 0;
seg->flags.psh = 0;
seg->flags.rst = 1;
seg->flags.syn = 0;
seg->flags.fin = 0;
seg->flags.mss = 0;
seg->flags.wscale = 0;
seg->flags.tstamp = 0;
seg->wnd = 0;
seg->up = 0;
seg->checksum = 0; /* force recomputation */
hbp = ambufw(TCP_HDR_PAD); /* Prealloc room for headers */
hbp->data += TCP_HDR_PAD;
htontcp(seg,&hbp,ip->dest,ip->source);
/* Ship it out (note swap of addresses) */
ip_send(ip->dest,ip->source,TCP_PTCL,ip->tos,0,&hbp,len_p(hbp),0,0);
tcpOutRsts++;
}
/* Process an incoming acknowledgement and window indication.
* From page 72.
*/
static void
update(tcb,seg,length)
register struct tcb *tcb;
register struct tcp *seg;
uint16 length;
{
int32 acked;
int winupd = 0;
int32 swind; /* Incoming window, scaled (non-SYN only) */
long rtt; /* measured round trip time */
int32 abserr; /* abs(rtt - srtt) */
acked = 0;
if(seq_gt(seg->ack,tcb->snd.nxt)){
tcb->flags.force = 1; /* Acks something not yet sent */
return;
}
/* Decide if we need to do a window update.
* This is always checked whenever a legal ACK is received,
* even if it doesn't actually acknowledge anything,
* because it might be a spontaneous window reopening.
*/
if(seq_gt(seg->seq,tcb->snd.wl1) || ((seg->seq == tcb->snd.wl1)
&& seq_ge(seg->ack,tcb->snd.wl2))){
if(seg->flags.syn || !tcb->flags.ws_ok)
swind = seg->wnd;
else
swind = seg->wnd << tcb->snd.wind_scale;
if(swind > tcb->snd.wnd){
winupd = 1; /* Don't count as duplicate ack */
/* If the window had been closed, crank back the send
* pointer so we'll immediately resume transmission.
* Otherwise we'd have to wait until the next probe.
*/
if(tcb->snd.wnd == 0)
tcb->snd.ptr = tcb->snd.una;
}
/* Remember for next time */
tcb->snd.wnd = swind;
tcb->snd.wl1 = seg->seq;
tcb->snd.wl2 = seg->ack;
}
/* See if anything new is being acknowledged */
if(seq_lt(seg->ack,tcb->snd.una))
return; /* Old ack, ignore */
if(seg->ack == tcb->snd.una){
/* Ack current, but doesn't ack anything */
if(tcb->sndcnt == 0 || winupd || length != 0 || seg->flags.syn || seg->flags.fin){
/* Either we have nothing in the pipe, this segment
* was sent to update the window, or it carries
* data/syn/fin. In any of these cases we
* wouldn't necessarily expect an ACK.
*/
return;
}
/* Van Jacobson "fast recovery" code */
if(++tcb->dupacks == TCPDUPACKS){
/* We've had a burst of do-nothing acks, so
* we almost certainly lost a packet.
* Resend it now to avoid a timeout. (This is
* Van Jacobson's 'quick recovery' algorithm.)
*/
int32 ptrsave;
/* Knock the threshold down just as though
* this were a timeout, since we've had
* network congestion.
*/
tcb->ssthresh = tcb->cwind/2;
tcb->ssthresh = max(tcb->ssthresh,tcb->mss);
/* Manipulate the machinery in tcp_output() to
* retransmit just the missing packet
*/
ptrsave = tcb->snd.ptr;
tcb->snd.ptr = tcb->snd.una;
tcb->cwind = tcb->mss;
tcp_output(tcb);
tcb->snd.ptr = ptrsave;
/* "Inflate" the congestion window, pretending as
* though the duplicate acks were normally acking
* the packets beyond the one that was lost.
*/
tcb->cwind = tcb->ssthresh + TCPDUPACKS*tcb->mss;
} else if(tcb->dupacks > TCPDUPACKS){
/* Continue to inflate the congestion window
* until the acks finally get "unstuck".
*/
tcb->cwind += tcb->mss;
}
/* Clamp the congestion window at the amount currently
* on the send queue, with a minimum of one packet.
* This keeps us from increasing the cwind beyond what
* we're actually putting in the pipe; otherwise a big
* burst of data could overwhelm the net.
*/
tcb->cwind = min(tcb->cwind,tcb->sndcnt);
tcb->cwind = max(tcb->cwind,tcb->mss);
return;
}
/* We're here, so the ACK must have actually acked something */
if(tcb->dupacks >= TCPDUPACKS && tcb->cwind > tcb->ssthresh){
/* The acks have finally gotten "unstuck". So now we
* can "deflate" the congestion window, i.e. take it
* back down to where it would be after slow start
* finishes.
*/
tcb->cwind = tcb->ssthresh;
}
tcb->dupacks = 0;
acked = seg->ack - tcb->snd.una;
/* Expand congestion window if not already at limit and if
* this packet wasn't retransmitted
*/
if(tcb->cwind < tcb->snd.wnd && !tcb->flags.retran){
if(tcb->cwind < tcb->ssthresh){
/* Still doing slow start/CUTE, expand by amount acked */
tcb->cwind += min(acked,tcb->mss);
} else {
/* Steady-state test of extra path capacity */
tcb->cwind += ((long)tcb->mss * tcb->mss) / tcb->cwind;
}
/* Don't expand beyond the offered window */
if(tcb->cwind > tcb->snd.wnd)
tcb->cwind = tcb->snd.wnd;
}
tcb->cwind = min(tcb->cwind,tcb->sndcnt); /* Clamp */
tcb->cwind = max(tcb->cwind,tcb->mss);
/* Round trip time estimation */
rtt = -1; /* Init to invalid value */
if(tcb->flags.ts_ok && seg->flags.tstamp){
/* Determine RTT from timestamp echo */
rtt = msclock() - seg->tsecr;
} else if(tcb->flags.rtt_run && seq_ge(seg->ack,tcb->rttseq)){
/* use standard round trip timing */
/* A timed sequence number has been acked */
tcb->flags.rtt_run = 0;
if(!(tcb->flags.retran)){
/* This packet was sent only once and now
* it's been acked, so process the round trip time
*/
rtt = msclock() - tcb->rtt_time;
}
}
if(rtt >= 0){
tcb->rtt = rtt; /* Save for display */
abserr = (rtt > tcb->srtt) ? rtt - tcb->srtt : tcb->srtt - rtt;
/* Run SRTT and MDEV integrators, with rounding */
tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt + (AGAIN/2)) >> LAGAIN;
tcb->mdev = ((DGAIN-1)*tcb->mdev + abserr + (DGAIN/2)) >> LDGAIN;
rtt_add(tcb->conn.remote.address,rtt);
/* Reset the backoff level */
tcb->backoff = 0;
/* Update our tx throughput estimate */
if(rtt != 0) /* Avoid division by zero */
tcb->txbw = 1000*(seg->ack - tcb->rttack)/rtt;
}
tcb->sndcnt -= acked; /* Update virtual byte count on snd queue */
tcb->snd.una = seg->ack;
/* If we're waiting for an ack of our SYN, note it and adjust count */
if(!(tcb->flags.synack)){
tcb->flags.synack = 1;
acked--; /* One less byte to pull from real snd queue */
}
/* Remove acknowledged bytes from the send queue and update the
* unacknowledged pointer. If a FIN is being acked,
* pullup won't be able to remove it from the queue, but that
* causes no harm.
*/
pullup(&tcb->sndq,NULL,(uint16)acked);
/* Stop retransmission timer, but restart it if there is still
* unacknowledged data.
*/
stop_timer(&tcb->timer);
if(tcb->snd.una != tcb->snd.nxt)
start_timer(&tcb->timer);
/* If retransmissions have been occurring, make sure the
* send pointer doesn't repeat ancient history
*/
if(seq_lt(tcb->snd.ptr,tcb->snd.una))
tcb->snd.ptr = tcb->snd.una;
/* Clear the retransmission flag since the oldest
* unacknowledged segment (the only one that is ever retransmitted)
* has now been acked.
*/
tcb->flags.retran = 0;
/* If outgoing data was acked, notify the user so he can send more
* unless we've already sent a FIN.
*/
if(acked != 0 && tcb->t_upcall
&& (tcb->state == TCP_ESTABLISHED || tcb->state == TCP_CLOSE_WAIT)){
(*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
}
}
/* Determine if the given sequence number is in our receiver window.
* NB: must not be used when window is closed!
*/
static
int
in_window(tcb,seq)
struct tcb *tcb;
int32 seq;
{
return seq_within(seq,tcb->rcv.nxt,(int32)(tcb->rcv.nxt+tcb->rcv.wnd-1));
}
/* Process an incoming SYN */
static void
proc_syn(tcb,tos,seg)
register struct tcb *tcb;
uint8 tos;
struct tcp *seg;
{
uint16 mtu;
struct tcp_rtt *tp;
tcb->flags.force = 1; /* Always send a response */
/* Note: It's not specified in RFC 793, but SND.WL1 and
* SND.WND are initialized here since it's possible for the
* window update routine in update() to fail depending on the
* IRS if they are left unitialized.
*/
/* Check incoming precedence and increase if higher */
if(PREC(tos) > PREC(tcb->tos))
tcb->tos = tos;
tcb->rcv.nxt = seg->seq + 1; /* p 68 */
tcb->snd.wl1 = tcb->irs = seg->seq;
tcb->snd.wnd = seg->wnd; /* Never scaled in a SYN */
if(seg->flags.mss)
tcb->mss = seg->mss;
if(seg->flags.wscale){
tcb->snd.wind_scale = seg->wsopt;
tcb->rcv.wind_scale = DEF_WSCALE;
tcb->flags.ws_ok = 1;
}
if(seg->flags.tstamp && Tcp_tstamps){
tcb->flags.ts_ok = 1;
tcb->ts_recent = seg->tsval;
}
/* Check the MTU of the interface we'll use to reach this guy
* and lower the MSS so that unnecessary fragmentation won't occur
*/
if((mtu = ip_mtu(tcb->conn.remote.address)) != 0){
/* Allow space for the TCP and IP headers */
if(tcb->flags.ts_ok)
mtu -= (TSTAMP_LENGTH + TCPLEN + IPLEN + 3) & ~3;
else
mtu -= TCPLEN + IPLEN;
tcb->cwind = tcb->mss = min(mtu,tcb->mss);
}
/* See if there's round-trip time experience */
if((tp = rtt_get(tcb->conn.remote.address)) != NULL){
tcb->srtt = tp->srtt;
tcb->mdev = tp->mdev;
}
}
/* Generate an initial sequence number and put a SYN on the send queue */
void
send_syn(tcb)
register struct tcb *tcb;
{
tcb->iss = geniss();
tcb->rttseq = tcb->snd.wl2 = tcb->snd.una = tcb->iss;
tcb->snd.ptr = tcb->snd.nxt = tcb->rttseq;
tcb->sndcnt++;
tcb->flags.force = 1;
}
/* Add an entry to the resequencing queue in the proper place */
static void
add_reseq(
struct tcb *tcb,
uint8 tos,
struct tcp *seg,
struct mbuf **bpp,
uint16 length
){
register struct reseq *rp,*rp1;
/* Allocate reassembly descriptor */
if((rp = (struct reseq *)malloc(sizeof (struct reseq))) == NULL){
/* No space, toss on floor */
free_p(bpp);
return;
}
ASSIGN(rp->seg,*seg);
rp->tos = tos;
rp->bp = (*bpp);
*bpp = NULL;
rp->length = length;
/* Place on reassembly list sorting by starting seq number */
rp1 = tcb->reseq;
if(rp1 == NULL || seq_lt(seg->seq,rp1->seg.seq)){
/* Either the list is empty, or we're less than all other
* entries; insert at beginning.
*/
rp->next = rp1;
tcb->reseq = rp;
} else {
/* Find the last entry less than us */
for(;;){
if(rp1->next == NULL || seq_lt(seg->seq,rp1->next->seg.seq)){
/* We belong just after this one */
rp->next = rp1->next;
rp1->next = rp;
break;
}
rp1 = rp1->next;
}
}
}
/* Fetch the first entry off the resequencing queue */
static void
get_reseq(
register struct tcb *tcb,
uint8 *tos,
struct tcp *seg,
struct mbuf **bp,
uint16 *length
){
register struct reseq *rp;
if((rp = tcb->reseq) == NULL)
return;
tcb->reseq = rp->next;
*tos = rp->tos;
ASSIGN(*seg,rp->seg);
*bp = rp->bp;
*length = rp->length;
free(rp);
}
/* Trim segment to fit window. Return 0 if OK, -1 if segment is
* unacceptable.
*/
static int
trim(
register struct tcb *tcb,
register struct tcp *seg,
struct mbuf **bpp,
uint16 *length
){
long dupcnt,excess;
uint16 len; /* Segment length including flags */
char accept = 0;
len = *length;
if(seg->flags.syn)
len++;
if(seg->flags.fin)
len++;
/* Segment acceptability tests */
if(tcb->rcv.wnd == 0){
/* If our window is closed, then the other end is
* probably probing us. If so, they might send us acks
* with seg.seq > rcv.nxt. Be sure to accept these
*/
if(len == 0 && seq_within(seg->seq,tcb->rcv.nxt,tcb->rcv.nxt+tcb->window))
return 0;
return -1; /* reject all others */
}
if(tcb->rcv.wnd > 0){
/* Some part of the segment must be in the window */
if(in_window(tcb,seg->seq)){
accept++; /* Beginning is */
} else if(len != 0){
if(in_window(tcb,(int32)(seg->seq+len-1)) || /* End is */
seq_within(tcb->rcv.nxt,seg->seq,(int32)(seg->seq+len-1))){ /* Straddles */
accept++;
}
}
}
if(!accept){
tcb->rerecv += len;
free_p(bpp);
return -1;
}
if((dupcnt = tcb->rcv.nxt - seg->seq) > 0){
tcb->rerecv += dupcnt;
/* Trim off SYN if present */
if(seg->flags.syn){
/* SYN is before first data byte */
seg->flags.syn = 0;
seg->seq++;
dupcnt--;
}
if(dupcnt > 0){
pullup(bpp,NULL,(uint16)dupcnt);
seg->seq += dupcnt;
*length -= dupcnt;
}
}
if((excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd)) > 0){
tcb->rerecv += excess;
/* Trim right edge */
*length -= excess;
trim_mbuf(bpp,*length);
seg->flags.fin = 0; /* FIN follows last data byte */
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -