/* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Author: Venkata N. Padmanabhan (padmanab@cs.berkeley.edu) */ /* * TCP Session is a compatible extension of standard TCP that treats * the set of connections between a pair of hosts as a "session". * Certain functionality, that usually resides in the TCP layer, is * moved over to the TCP session layer. These include: * 1. Integrated congestion control, and * 2. Integrated loss recovery. * * In addition, TCP session also does * 3. Scheduling of connections within the session. * * Together, these avoid competition between concurrent connections, * cut down packet losses in the face of congestion, and improve loss * recovery decreasing the frequency of timeouts. * * For questions/comments, contact: * Venkata N. Padmanabhan (padmanab@cs.berkeley.edu) * http://www.cs.berkeley.edu/~padmanab */ #ifdef TCP_SESSION #include void session_init() { LIST_INIT(&session_list); } struct sessionpcb * session_newpcb(tcp) register struct tcpcb *tcp; { struct sessionpcb *sp = malloc(sizeof(struct sessionpcb), M_PCB, M_NOWAIT); if (sp == NULL) { printf("In session_newpcb(): unable to malloc sessionpcb\n"); return NULL; } sp->tp = malloc(sizeof(struct tcpcb), M_PCB, M_NOWAIT); if (sp->tp == NULL) { printf("In session_newpcb(): unable to malloc tcpcb\n"); return NULL; } /* copy over relevant fields from the TCP PCB */ sp->inp_laddr.s_addr = tcp->t_inpcb->inp_laddr.s_addr; sp->inp_faddr.s_addr = tcp->t_inpcb->inp_faddr.s_addr; /* * Copy over the entire TCP PCB though only a subset of the * fields is relevant for the TCP session. * XXX left something out? */ bcopy((char *) tcp, (char *) sp->tp, sizeof(struct tcpcb)); /* initialize other fields */ sp->ownd = 0; sp->ownd_correction = 0; sp->highest_echoed_ts = 0; sp->numconn = 0; sp->numseg = 0; sp->dupack_ok = 0; sp->session_seqno = 0; sp->recover = 0; sp->max_bytes_snd = 0; /* * XXX in the case of a server, the transition from listen state could * cause the socket/tcpcb to change!! */ sp->curconn = tcp; LIST_INIT(&sp->connlist); TAILQ_INIT(&sp->seglist); sp->tp->session = 0; /* enable/disable stats for TCP session */ if (sp->tp->t_xflags & TXF_ENABLE_SESSION_STATS) { sp->tp->t_flags |= TF_ENABLE_STATS; tcp_stats_init(sp->tp,sp->tp->session_stats_flags); } else sp->tp->t_flags &= ~TF_ENABLE_STATS; return sp; } struct sessionpcb * session_addconn(tcp) register struct tcpcb *tcp; { struct sessionpcb *sp; sp = LIST_FIRST(&session_list); while (sp) { if (CONN_IN_SESSION(tcp,sp)) break; sp = LIST_NEXT(sp,next_session); } if (!sp) { sp = session_newpcb(tcp); if (!sp) { printf("In session_addconn(): session_newpcb() failed\n"); return; } else { LIST_INSERT_HEAD(&session_list,sp,next_session); } } sp->numconn++; /* XXX need to update other stuff such as ssthresh ? */ /* XXX need to check le_prev? */ LIST_INSERT_HEAD(&sp->connlist, tcp, session_nextconn); tcp->session = sp; tcp->num_thresh_dupack_segs = 0; if (SESSION_INCR_CWND(tcp)) { sp->tp->snd_cwnd += sp->tp->t_maxseg; #ifdef TCP_STATS if ((sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & TCP_STATS_SND_CWND)) tcp_stats_record(TCP_STATS_SND_CWND, sp->tp); #endif } return tcp->session; } void session_delconn(tcp) register struct tcpcb *tcp; { struct sessionpcb *sp = tcp->session; struct segment *seg; struct segment *nxtseg; if (!sp) { printf("In session_delconn(): conn not in known session\n"); return; } /* XXX should we free the segments (if any) of this connection? */ seg = TAILQ_FIRST(&sp->seglist); LIST_REMOVE(tcp,session_nextconn); tcp->session = NULL; /* * If no connections left in session, delete the session. * XXX This will change with persistent state (e.g., fast start). */ if (--sp->numconn == 0) { while (seg) { nxtseg = TAILQ_NEXT(seg,nextseg); TAILQ_REMOVE(&sp->seglist,seg,nextseg); sp->numseg--; free(seg,M_PCB); seg = nxtseg; } free(sp->tp,M_PCB); LIST_REMOVE(sp,next_session); free(sp,M_PCB); } } void session_opencwnd(sp, acked) register struct sessionpcb *sp; int acked; { register u_int cw = sp->tp->snd_cwnd; register u_int incr = sp->tp->t_maxseg; u_long old_snd_cwnd = sp->tp->snd_cwnd; if (cw > sp->tp->snd_ssthresh) incr = incr * acked / (2*cw); else incr = max(incr, (incr*acked / (2 * sp->tp->t_maxseg))); sp->tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<tp->snd_scale); #ifdef TCP_STATS if (sp->tp->snd_cwnd != old_snd_cwnd && (sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & TCP_STATS_SND_CWND)) tcp_stats_record(TCP_STATS_SND_CWND, sp->tp); #endif } void session_closecwnd(sp,how) register struct sessionpcb *sp; int how; { /* * XXX we use snd_cwnd rather than sp->ownd here. This may not * be appropriate if snd_cwnd > ownd (i.e., the sender does * not fill up the available window). */ int win = max(2, sp->tp->snd_cwnd/2/sp->tp->t_maxseg); sp->tp->snd_ssthresh = win * sp->tp->t_maxseg; switch (how) { case TIMEOUT: sp->tp->snd_cwnd = sp->tp->t_maxseg; break; case FAST_REXMT: sp->tp->snd_cwnd = sp->tp->snd_ssthresh; break; } #ifdef TCP_STATS if ((sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & (TCP_STATS_SND_CWND|TCP_STATS_SND_SSTHRESH))) tcp_stats_record(TCP_STATS_SND_CWND|TCP_STATS_SND_SSTHRESH, sp->tp); #endif } void session_adjust_ownd(sp,size) register struct sessionpcb *sp; u_long size; { if (sp->ownd_correction < size) sp->ownd -= min(sp->ownd, size - sp->ownd_correction); sp->ownd_correction -= min(sp->ownd_correction, size); if (sp->ownd < 0 || sp->ownd_correction < 0) printf("In session_adjust_ownd(): ownd=%ld ownd_correction=%ld\n", sp->ownd, sp->ownd_correction); } void session_xmit_timer(tcp, rtt) register struct tcpcb *tcp; short rtt; { /* make sure that we are operating on a TCP conn, not a TCP session */ if (tcp->session) tcp_xmit_timer(tcp->session->tp,rtt); } void session_xmit_timer_exact(tcp, rtt_exact) register struct tcpcb *tcp; u_long rtt_exact; { /* make sure that we are operating on a TCP conn, not a TCP session */ if (tcp->session) tcp_xmit_timer_exact(tcp->session->tp,rtt_exact); } struct segment * session_rexmt_oneseg(sp,oldseg) register struct sessionpcb *sp; register struct segment *oldseg; { register struct tcpcb *tcp = oldseg->tp; tcp_seq old_snd_nxt = tcp->snd_nxt; struct segment *newseg; if (SEQ_LT(oldseg->startseq, oldseg->tp->snd_una)) printf("unnecessary rtx: %lu\n", (u_long) (oldseg->startseq - oldseg->tp->iss)); tcp->t_rtt = 0; tcp->snd_nxt = oldseg->startseq; sp->max_bytes_snd = (u_long) (oldseg->endseq - oldseg->startseq); /* XXX need a way of specifying exactly how much to send */ newseg = session_output_oneseg(sp,tcp,1); sp->max_bytes_snd = 0; if (!newseg) { tcp->snd_nxt = old_snd_nxt; printf("In session_rexmt_oneseg(): rexmt attempt failed\n"); return (NULL); } newseg->flags |= SGF_RXMITTED; newseg->session_seqno = oldseg->session_seqno; session_adjust_ownd(sp,(u_long) (oldseg->endseq - oldseg->startseq)); return (newseg); } void session_rexmt(sp) register struct sessionpcb *sp; { struct segment *seg = TAILQ_FIRST(&sp->seglist); struct segment *nxtseg; tcp_seq old_snd_nxt; struct timeval curtime; while (seg) { /* * Retransmit the segment if: * 1. it is at the left edge of the unacked window * AND * 2.a. at least a threshold # of dup/later acks * AND * 2.a.1. at least one dupack * OR * 2.a.2. threshold dup/later acks for 2 or more segs in conn * OR * 3. partial new ack for this segment * * The indentation is supposed to indicate the location of * the parenthesis -- (1 AND ((2.a AND (2.a.1 OR 2.a.2)) OR 3)) * * XXX for now, we ignore the case of all packets from the * tail of a connection getting dropped * * XXX got rid of the check involving * DELACK_THRESH_GEQ(sp->highest_echoed_ts,seg->ts) * because it is not a good idea to depend on the timing of * delayed acks. */ if ((seg->startseq == seg->tp->snd_una) && ((seg->dupacks + seg->later_acks >= tcprexmtthresh && (seg->dupacks > 0 || seg->tp->num_thresh_dupack_segs > 1)) || SEG_PARTIALACK(seg))) { if (SEG_THRESH_DUPACK(seg)) { seg->flags &= ~SGF_THRESH_DUPACK; seg->tp->num_thresh_dupack_segs--; } else if (!SEG_PARTIALACK(seg)) printf("In session_rexmt(): thresh_dupacks/partialack not set for seg being rxmitted\n"); /* * if another loss in same loss win, stop adjusting * ownd */ if (SEQ_LEQ(seg->session_seqno, sp->recover)) sp->flags |= SSF_DONT_ADJUST_OWND; /* new loss window => cut down cwnd */ if (SEQ_GT(seg->session_seqno, sp->recover)) { session_closecwnd(sp,FAST_REXMT); sp->recover = sp->session_seqno; sp->flags &= ~SSF_DONT_ADJUST_OWND; } if (SEQ_GT(seg->endseq, seg->tp->snd_last)) seg->tp->snd_last = seg->tp->snd_max; old_snd_nxt = seg->tp->snd_nxt; if (session_rexmt_oneseg(sp,seg)) { #ifdef TCP_STATS if ((sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & TCP_STATS_DUPACK_RXT)) tcp_stats_record(TCP_STATS_DUPACK_RXT, sp->tp); #endif /* adjust ownd to account for dupacks */ if (!SESSION_DONT_ADJUST_OWND(sp)) { sp->ownd_correction += min(sp->ownd, seg->dupacks*sp->tp->t_maxseg); sp->ownd -= min(sp->ownd, seg->dupacks*sp->tp->t_maxseg); } /* restore old snd_nxt if it was greater */ if (SEQ_GT(old_snd_nxt, seg->tp->snd_nxt)) seg->tp->snd_nxt = old_snd_nxt; /* set rexmt timer afresh */ sp->tp->t_timer[TCPT_REXMT] = sp->tp->t_rxtcur; /* remove old copy of the segment from queue */ nxtseg = TAILQ_NEXT(seg,nextseg); TAILQ_REMOVE(&sp->seglist,seg,nextseg); sp->numseg--; free(seg,M_PCB); seg = nxtseg; continue; } } seg = TAILQ_NEXT(seg,nextseg); } } void session_clean_segs(sp, tcp, ti, oi, acked) register struct sessionpcb *sp; register struct tcpcb *tcp; register struct tcpiphdr *ti; register struct tcp_opt_info *oi; register int acked; { struct segment *seg = TAILQ_FIRST(&sp->seglist); struct segment *nxtseg; u_long echoed_ts; int new_data_acked = 0; /* XXX ok because in the end we only compare with sp->recover */ tcp_seq latestLossSusp = sp->recover; #ifdef FINE_GRAINED_TSTAMP echoed_ts = oi->ts_ecr_exact; #else echoed_ts = oi->ts_ecr; #endif sp->flags &= ~SSF_LOSS_SUSPECTED; while (seg) { /* later ack for packet of different connection */ if (seg->tp != tcp && TSTMP_GT(echoed_ts,seg->ts)) { seg->later_acks++; /* * XXX SSF_LOSS_SUSPECTED is not set here because a * later ack is perfectly normal when a receiver * employs the delayed ack policy. */ } /* ack for same connection */ else if (seg->tp == tcp) { /* new data acked */ if (SEQ_GT(tcp->snd_una,seg->startseq)) { new_data_acked = 1; if (SEQ_GEQ(tcp->snd_una,seg->endseq)) { session_adjust_ownd(sp, (u_long) (seg->endseq - seg->startseq)); nxtseg = TAILQ_NEXT(seg,nextseg); TAILQ_REMOVE(&sp->seglist,seg,nextseg); sp->numseg--; if (SEG_THRESH_DUPACK(seg)) seg->tp->num_thresh_dupack_segs--; free(seg,M_PCB); seg = nxtseg; continue; } else { session_adjust_ownd(sp,(u_long) (tcp->snd_una - seg->startseq)); seg->startseq = tcp->snd_una; } } /* partial new ack => rexmt immediately */ /* * XXX we assume that snd_una has already been updated * and that recover is only set by fast rtx (not by * timeout) */ else if (acked && seg->startseq == tcp->snd_una && SEQ_LT(seg->startseq,tcp->snd_last)) { seg->flags |= SGF_PARTIALACK; latestLossSusp = SEQ_MAX(latestLossSusp, seg->session_seqno); sp->flags |= SSF_LOSS_SUSPECTED; } /* dupack */ else if (!acked && !SEG_RXMITTED(seg) && ((tcp->snd_una == seg->startseq && sp->dupack_ok) || TSTMP_GT(echoed_ts,seg->ts))) { seg->dupacks++; latestLossSusp = SEQ_MAX(latestLossSusp, seg->session_seqno); sp->flags |= SSF_LOSS_SUSPECTED; } } if (seg->dupacks + seg->later_acks >= tcprexmtthresh && !SEG_THRESH_DUPACK(seg)) { seg->flags |= SGF_THRESH_DUPACK; seg->tp->num_thresh_dupack_segs++; } seg = TAILQ_NEXT(seg,nextseg); } /* * assume (at most) one segment left the pipe if: * 1. the received pkt is an ack AND * 2. no new data was acked AND * 3. the ack does not have SYN/FIN/RST set AND * 4. we don't suspect a loss (because of reordering) that is beyond * the most recent loss epoch AND * 5. DONT_ADJUST_OWND is not set (i.e., there isn't another loss in * the same loss win) * * XXX do we need both 4 and 5? */ if ((ti->ti_flags & TH_ACK) && !new_data_acked && (ti->ti_flags & (TH_SYN|TH_FIN|TH_RST))==0 && SEQ_LEQ(latestLossSusp,sp->recover) && !SESSION_DONT_ADJUST_OWND(sp)) { sp->ownd_correction += min(sp->ownd, sp->tp->t_maxseg); sp->ownd -= min(sp->ownd, sp->tp->t_maxseg); /* sanity check */ if (acked > 0 && tcp->t_state == TCPS_ESTABLISHED) printf("In session_clean_segs(): acked and new_data_acked are contradictory: state=%d\n", tcp->t_state); } } void session_timeout(sp) register struct sessionpcb *sp; { struct tcpcb *tcp; struct segment *seg, *nxtseg; int rexmt; /* clear out unacked segments from cache */ /* * XXX unlike in ns, we don't try to preserve the old session seqno * space. We also don't set recover upon timeout, there there is the * danger of false fast rtx */ seg = TAILQ_FIRST(&sp->seglist); if (seg) seg->tp->num_thresh_dupack_segs = 0; while (seg) { nxtseg = TAILQ_NEXT(seg,nextseg); TAILQ_REMOVE(&sp->seglist,seg,nextseg); sp->numseg--; free(seg,M_PCB); seg = nxtseg; } /* reset ownd */ sp->ownd = 0; sp->ownd_correction = 0; /* sanity check */ if (sp->numseg != 0) { printf("In session_timeout(): cleared all segs but numseg=%d\n", sp->numseg); sp->numseg = 0; } /* back off timer and recompute rxtcur */ if (++sp->tp->t_rxtshift > TCP_MAXRXTSHIFT) { /* XXX to do */ printf("In session_timeout(): dropping all connections\n"); return; } rexmt = TCP_REXMTVAL(sp->tp) * tcp_backoff[sp->tp->t_rxtshift]; TCPT_RANGESET(sp->tp->t_rxtcur, rexmt, sp->tp->t_rttmin, TCPTV_REXMTMAX); sp->tp->t_timer[TCPT_REXMT] = sp->tp->t_rxtcur; /* * If backed off a lot, ask lower layer to try for a better route. * Also, clobber srtt. */ if (sp->tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { if (tcp = LIST_FIRST(&sp->connlist)) in_losing(tcp->t_inpcb); sp->tp->t_rttvar += (sp->tp->t_srtt >> TCP_RTT_SHIFT); sp->tp->t_srtt = 0; } /* reset each connection in the session */ tcp = LIST_FIRST(&sp->connlist); while (tcp) { tcp->snd_nxt = tcp->snd_una; tcp->t_rtt = 0; tcp->snd_last = tcp->snd_una; tcp = LIST_NEXT(tcp,session_nextconn); } /* shut down window */ session_closecwnd(sp,TIMEOUT); #ifdef TCP_STATS if ((sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & TCP_STATS_COARSE_RXT)) tcp_stats_record(TCP_STATS_COARSE_RXT, sp->tp); #endif /* start retransmitting packets */ session_output(sp); } struct segment * session_output_oneseg(sp,tcp,fast_rexmt_flag) register struct sessionpcb *sp; register struct tcpcb *tcp; int fast_rexmt_flag; { struct segment *seg; struct timeval curtime; tcp_seq old_snd_max = tcp->snd_max; /* XXX should we adjust snd_nxt? */ seg = malloc(sizeof(struct segment),M_PCB,M_NOWAIT); seg->startseq = tcp->snd_nxt; if (SEQ_LT(seg->startseq, tcp->snd_una)) printf("In s_o_o(): startseq: %lu snd_una: %lu iss: %lu\n", (u_long) (seg->startseq - tcp->iss), (u_long) (tcp->snd_una - tcp->iss), tcp->iss); /* XXX will calling tcp_output force sending of an ack? */ TCP_OUTPUT(tcp); seg->endseq = tcp->snd_nxt; /* if zero bytes of data were sent, free the segment */ if (!SEQ_GT(seg->endseq,seg->startseq)) { free(seg,M_PCB); return (NULL); } if (SEQ_GT(seg->endseq,old_snd_max)) { sp->session_seqno += (u_long) (seg->endseq - old_snd_max); seg->session_seqno = sp->session_seqno; } else if (!fast_rexmt_flag) { /* slow start following timeout => compute session_seqno afresh */ sp->session_seqno += (u_long) (seg->endseq - seg->startseq); seg->session_seqno = sp->session_seqno; /* seg->session_seqno = 0; printf("In session_output_oneseg(): setting session_seqno to 0\n");*/ } seg->flags = 0; seg->dupacks = 0; seg->later_acks = 0; #ifdef FINE_GRAINED_TSTAMP microtime(&curtime); seg->ts = EXACT_TIME(&curtime); #else seg->ts = tcp_now; #endif seg->tp = tcp; TAILQ_INSERT_TAIL(&sp->seglist,seg,nextseg); sp->numseg++; /* adjust outstanding window */ sp->ownd += (u_long) (seg->endseq - seg->startseq); /* if there is unacked data, make sure rexmt timer is set */ if (sp->numseg > 0 && sp->tp->t_timer[TCPT_REXMT] == 0) sp->tp->t_timer[TCPT_REXMT] = sp->tp->t_rxtcur; /* XXX should we return the return-value of tcp_output ? */ return (seg); } void session_output(sp) register struct sessionpcb *sp; { int count = 0; if (sp->ownd < 0 || sp->ownd_correction < 0) printf("In session_output(): ownd=%ld ownd_correction=%ld\n", sp->ownd, sp->ownd_correction); while ((sp->ownd < sp->tp->snd_cwnd) && (count++ < sp->numconn)) { if (session_output_oneseg(sp,sp->curconn,0)) count = 0; if (!(sp->curconn = LIST_NEXT(sp->curconn, session_nextconn))) sp->curconn = LIST_FIRST(&sp->connlist); } } void session_input(tcp, ti, oi, acked) register struct tcpcb *tcp; register struct tcpiphdr *ti; register struct tcp_opt_info *oi; int acked; { register struct sessionpcb *sp; u_long echoed_ts; #ifdef FINE_GRAINED_TSTAMP echoed_ts = oi->ts_ecr_exact; #else echoed_ts = oi->ts_ecr; #endif /* * Don't do any session-related operations when in either the LISTEN * or the TIME_WAIT state. This is because (a) the socket (and tcpcb) * changes when we move from LISTEN to SYN_RECEIVED state, and * (b) the connection would have been removed from the session before * it gets into the TIME_WAIT state (this is done in tcp_close()). */ if (tcp && !tcp->session) { if (tcp->t_state == TCPS_LISTEN || tcp->t_state == TCPS_TIME_WAIT) return; else sp = session_addconn(tcp); } else sp = tcp->session; if (TSTMP_GT(echoed_ts,sp->highest_echoed_ts) || sp->highest_echoed_ts==0) { sp->highest_echoed_ts = echoed_ts; if (sp->highest_echoed_ts == 0) /* make initial value (0) unique */ sp->highest_echoed_ts = 1; } /* if the received segment is an ack, use it to detect pkt reordering */ if (ti->ti_flags & TH_ACK) session_clean_segs(sp,tcp,ti,oi,acked); sp->dupack_ok = 0; session_rexmt(sp); /* XXX okay to increase window even for partial ack ? */ if (acked > 0) { /* * XXX make sure that opencwnd is not called as soon as fast * recovery terminates */ if (!(sp->tp->t_xflags & TXF_COUNT_BYTES_ACKED)) acked = min(acked, sp->tp->t_maxseg); if (!SESSION_LOSS_SUSPECTED(sp)) session_opencwnd(sp,acked); sp->flags &= ~SSF_LOSS_SUSPECTED; /* if there is still unacked data, set REXMT timer afresh */ if (sp->numseg > 0) sp->tp->t_timer[TCPT_REXMT] = sp->tp->t_rxtcur; else /* otherwise cancel REXMT timer */ sp->tp->t_timer[TCPT_REXMT] = 0; } session_output(sp); } void session_rcvd_syn_ack(tcp, ti) register struct tcpcb *tcp; register struct tcpiphdr *ti; { register struct sessionpcb *sp; if (tcp && !tcp->session) { /* * XXX session_rcvd_syn_ack() should be called only when we move * from SYN_SENT to ESTABLISHED state. I am not sure why I added * the check for TIME_WAIT. Anyway, it should be harmless, so * it stays for now. */ if (tcp->t_state == TCPS_TIME_WAIT) return; else sp = session_addconn(tcp); } else { sp = tcp->session; sp->tp->snd_cwnd += sp->tp->t_maxseg; #ifdef TCP_STATS if ((sp->tp->t_flags & TF_ENABLE_STATS) && (tcp_stats_flags & TCP_STATS_SND_CWND)) tcp_stats_record(TCP_STATS_SND_CWND, sp->tp); #endif } } #endif /* TCP_SESSION */