Restructure TCP timeout handling:
- eliminate the fast/slow timeout lists for TCP and instead use a callout entry for each timer. - increase the TCP timer granularity to HZ - implement "bad retransmit" recovery, as presented in "On Estimating End-to-End Network Path Properties", by Allman and Paxson. Submitted by: jlemon, wollmann
This commit is contained in:
parent
e0eed6c8af
commit
9b8b58e033
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=50673
@ -125,8 +125,7 @@ softclock()
|
||||
c_links.sle);
|
||||
} else {
|
||||
c->c_flags =
|
||||
(c->c_flags & ~CALLOUT_PENDING)
|
||||
| CALLOUT_FIRED;
|
||||
(c->c_flags & ~CALLOUT_PENDING);
|
||||
}
|
||||
splx(s);
|
||||
c_func(c_arg);
|
||||
@ -218,11 +217,11 @@ callout_handle_init(struct callout_handle *handle)
|
||||
* callout_init() - initialize a callout structure so that it can
|
||||
* safely be passed to callout_reset() and callout_stop()
|
||||
*
|
||||
* <sys/callout.h> defines two convenience macros:
|
||||
* <sys/callout.h> defines three convenience macros:
|
||||
*
|
||||
* callout_pending() - returns number of ticks until callout fires, or 0
|
||||
* if not scheduled
|
||||
* callout_fired() - returns truth if callout has already been fired
|
||||
* callout_active() - returns truth if callout has not been serviced
|
||||
* callout_pending() - returns truth if callout is still waiting for timeout
|
||||
* callout_deactivate() - marks the callout as having been serviced
|
||||
*/
|
||||
void
|
||||
callout_reset(c, to_ticks, ftn, arg)
|
||||
@ -240,13 +239,13 @@ callout_reset(c, to_ticks, ftn, arg)
|
||||
/*
|
||||
* We could spl down here and back up at the TAILQ_INSERT_TAIL,
|
||||
* but there's no point since doing this setup doesn't take much
|
||||
^ time.
|
||||
* time.
|
||||
*/
|
||||
if (to_ticks <= 0)
|
||||
to_ticks = 1;
|
||||
|
||||
c->c_arg = arg;
|
||||
c->c_flags = (c->c_flags & ~CALLOUT_FIRED) | CALLOUT_PENDING;
|
||||
c->c_flags |= (CALLOUT_ACTIVE | CALLOUT_PENDING);
|
||||
c->c_func = ftn;
|
||||
c->c_time = ticks + to_ticks;
|
||||
TAILQ_INSERT_TAIL(&callwheel[c->c_time & callwheelmask],
|
||||
@ -266,10 +265,11 @@ callout_stop(c)
|
||||
* Don't attempt to delete a callout that's not on the queue.
|
||||
*/
|
||||
if (!(c->c_flags & CALLOUT_PENDING)) {
|
||||
c->c_flags &= ~CALLOUT_ACTIVE;
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
c->c_flags &= ~CALLOUT_PENDING;
|
||||
c->c_flags &= ~(CALLOUT_ACTIVE | CALLOUT_PENDING);
|
||||
|
||||
if (nextsoftcheck == c) {
|
||||
nextsoftcheck = TAILQ_NEXT(c, c_links.tqe);
|
||||
|
@ -92,7 +92,7 @@ struct protosw inetsw[] = {
|
||||
PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD,
|
||||
tcp_input, 0, tcp_ctlinput, tcp_ctloutput,
|
||||
0,
|
||||
tcp_init, tcp_fasttimo, tcp_slowtimo, tcp_drain,
|
||||
tcp_init, 0, tcp_slowtimo, tcp_drain,
|
||||
&tcp_usrreqs
|
||||
},
|
||||
{ SOCK_RAW, &inetdomain, IPPROTO_RAW, PR_ATOMIC|PR_ADDR,
|
||||
|
@ -93,7 +93,6 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,
|
||||
&tcp_delack_enabled, 0,
|
||||
"Delay ACK to try and piggyback it onto a data packet");
|
||||
|
||||
u_long tcp_now;
|
||||
struct inpcbhead tcb;
|
||||
struct inpcbinfo tcbinfo;
|
||||
|
||||
@ -120,7 +119,8 @@ static void tcp_xmit_timer __P((struct tcpcb *, int));
|
||||
(tp)->t_segq == NULL && \
|
||||
(tp)->t_state == TCPS_ESTABLISHED) { \
|
||||
if (tcp_delack_enabled) \
|
||||
tp->t_flags |= TF_DELACK; \
|
||||
callout_reset(tp->tt_delack, tcp_delacktime, \
|
||||
tcp_timer_delack, tp); \
|
||||
else \
|
||||
tp->t_flags |= TF_ACKNOW; \
|
||||
(tp)->rcv_nxt += (ti)->ti_len; \
|
||||
@ -523,9 +523,9 @@ tcp_input(m, iphlen)
|
||||
* Segment received on connection.
|
||||
* Reset idle time and keep-alive timer.
|
||||
*/
|
||||
tp->t_idle = 0;
|
||||
tp->t_rcvtime = ticks;
|
||||
if (TCPS_HAVEESTABLISHED(tp->t_state))
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
|
||||
|
||||
/*
|
||||
* Process options if not in LISTEN state,
|
||||
@ -575,7 +575,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if ((to.to_flag & TOF_TS) != 0 &&
|
||||
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -588,12 +588,23 @@ tcp_input(m, iphlen)
|
||||
* this is a pure ack for outstanding data.
|
||||
*/
|
||||
++tcpstat.tcps_predack;
|
||||
/*
|
||||
* "bad retransmit" recovery
|
||||
*/
|
||||
if (tp->t_rxtshift == 1 &&
|
||||
ticks < tp->t_badrxtwin) {
|
||||
tp->snd_cwnd = tp->snd_cwnd_prev;
|
||||
tp->snd_ssthresh =
|
||||
tp->snd_ssthresh_prev;
|
||||
tp->snd_nxt = tp->snd_max;
|
||||
tp->t_badrxtwin = 0;
|
||||
}
|
||||
if ((to.to_flag & TOF_TS) != 0)
|
||||
tcp_xmit_timer(tp,
|
||||
tcp_now - to.to_tsecr + 1);
|
||||
else if (tp->t_rtt &&
|
||||
ticks - to.to_tsecr + 1);
|
||||
else if (tp->t_rtttime &&
|
||||
SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp, tp->t_rtt);
|
||||
tcp_xmit_timer(tp, ticks - tp->t_rtttime);
|
||||
acked = ti->ti_ack - tp->snd_una;
|
||||
tcpstat.tcps_rcvackpack++;
|
||||
tcpstat.tcps_rcvackbyte += acked;
|
||||
@ -611,9 +622,11 @@ tcp_input(m, iphlen)
|
||||
* decide between more output or persist.
|
||||
*/
|
||||
if (tp->snd_una == tp->snd_max)
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
else if (tp->t_timer[TCPT_PERSIST] == 0)
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
else if (!callout_active(tp->tt_persist))
|
||||
callout_reset(tp->tt_rexmt,
|
||||
tp->t_rxtcur,
|
||||
tcp_timer_rexmt, tp);
|
||||
|
||||
sowwakeup(so);
|
||||
if (so->so_snd.sb_cc)
|
||||
@ -638,7 +651,8 @@ tcp_input(m, iphlen)
|
||||
sbappend(&so->so_rcv, m);
|
||||
sorwakeup(so);
|
||||
if (tcp_delack_enabled) {
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
} else {
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tcp_output(tp);
|
||||
@ -760,6 +774,7 @@ tcp_input(m, iphlen)
|
||||
taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) {
|
||||
|
||||
taop->tao_cc = to.to_cc;
|
||||
tp->t_starttime = ticks;
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
|
||||
/*
|
||||
@ -771,9 +786,11 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if (tcp_delack_enabled && ((tiflags & TH_FIN) ||
|
||||
(ti->ti_len != 0 &&
|
||||
in_localaddr(inp->inp_faddr))))
|
||||
tp->t_flags |= (TF_DELACK | TF_NEEDSYN);
|
||||
else
|
||||
in_localaddr(inp->inp_faddr)))) {
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
tp->t_flags |= TF_NEEDSYN;
|
||||
} else
|
||||
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
|
||||
|
||||
/*
|
||||
@ -784,7 +801,8 @@ tcp_input(m, iphlen)
|
||||
tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN);
|
||||
tcpstat.tcps_connects++;
|
||||
soisconnected(so);
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
|
||||
callout_reset(tp->tt_keep, tcp_keepinit,
|
||||
tcp_timer_keep, tp);
|
||||
dropsocket = 0; /* committed to socket */
|
||||
tcpstat.tcps_accepts++;
|
||||
goto trimthenstep6;
|
||||
@ -803,7 +821,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->t_state = TCPS_SYN_RECEIVED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
|
||||
callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
|
||||
dropsocket = 0; /* committed to socket */
|
||||
tcpstat.tcps_accepts++;
|
||||
goto trimthenstep6;
|
||||
@ -903,7 +921,8 @@ tcp_input(m, iphlen)
|
||||
* ACKNOW will be turned on later.
|
||||
*/
|
||||
if (tcp_delack_enabled && ti->ti_len != 0)
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
else
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
/*
|
||||
@ -912,13 +931,15 @@ tcp_input(m, iphlen)
|
||||
* SYN_SENT --> ESTABLISHED
|
||||
* SYN_SENT* --> FIN_WAIT_1
|
||||
*/
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
tiflags &= ~TH_SYN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle,
|
||||
tcp_timer_keep, tp);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -931,7 +952,7 @@ tcp_input(m, iphlen)
|
||||
* If there was no CC option, clear cached CC value.
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
if (to.to_flag & TOF_CC) {
|
||||
if (taop->tao_cc != 0 &&
|
||||
CC_GT(to.to_cc, taop->tao_cc)) {
|
||||
@ -941,13 +962,16 @@ tcp_input(m, iphlen)
|
||||
* SYN-SENT* -> FIN-WAIT-1*
|
||||
*/
|
||||
taop->tao_cc = to.to_cc;
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] =
|
||||
tcp_keepidle;
|
||||
callout_reset(tp->tt_keep,
|
||||
tcp_keepidle,
|
||||
tcp_timer_keep,
|
||||
tp);
|
||||
}
|
||||
tp->t_flags |= TF_NEEDSYN;
|
||||
} else
|
||||
@ -1006,7 +1030,7 @@ tcp_input(m, iphlen)
|
||||
if ((tiflags & TH_SYN) &&
|
||||
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
|
||||
if (tp->t_state == TCPS_TIME_WAIT &&
|
||||
tp->t_duration > TCPTV_MSL)
|
||||
(ticks - tp->t_starttime) > tcp_msl)
|
||||
goto dropwithreset;
|
||||
if (CC_GT(to.to_cc, tp->cc_recv)) {
|
||||
tp = tcp_close(tp);
|
||||
@ -1115,7 +1139,7 @@ tcp_input(m, iphlen)
|
||||
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
||||
|
||||
/* Check to see if ts_recent is over 24 days old. */
|
||||
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
||||
if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
||||
/*
|
||||
* Invalidate ts_recent. If this segment updates
|
||||
* ts_recent, the age will be reset later and ts_recent
|
||||
@ -1262,7 +1286,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if ((to.to_flag & TOF_TS) != 0 &&
|
||||
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -1322,12 +1346,14 @@ tcp_input(m, iphlen)
|
||||
* SYN-RECEIVED -> ESTABLISHED
|
||||
* SYN-RECEIVED* -> FIN-WAIT-1
|
||||
*/
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle,
|
||||
tcp_timer_keep, tp);
|
||||
}
|
||||
/*
|
||||
* If segment contains data or ACK, will call tcp_reass()
|
||||
@ -1382,7 +1408,7 @@ tcp_input(m, iphlen)
|
||||
* to keep a constant cwnd packets in the
|
||||
* network.
|
||||
*/
|
||||
if (tp->t_timer[TCPT_REXMT] == 0 ||
|
||||
if (!callout_active(tp->tt_rexmt) ||
|
||||
ti->ti_ack != tp->snd_una)
|
||||
tp->t_dupacks = 0;
|
||||
else if (++tp->t_dupacks == tcprexmtthresh) {
|
||||
@ -1394,8 +1420,8 @@ tcp_input(m, iphlen)
|
||||
if (win < 2)
|
||||
win = 2;
|
||||
tp->snd_ssthresh = win * tp->t_maxseg;
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
tp->t_rtt = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
tp->t_rtttime = 0;
|
||||
tp->snd_nxt = ti->ti_ack;
|
||||
tp->snd_cwnd = tp->t_maxseg;
|
||||
(void) tcp_output(tp);
|
||||
@ -1452,6 +1478,20 @@ tcp_input(m, iphlen)
|
||||
tcpstat.tcps_rcvackpack++;
|
||||
tcpstat.tcps_rcvackbyte += acked;
|
||||
|
||||
/*
|
||||
* If we just performed our first retransmit, and the ACK
|
||||
* arrives within our recovery window, then it was a mistake
|
||||
* to do the retransmit in the first place. Recover our
|
||||
* original cwnd and ssthresh, and proceed to transmit where
|
||||
* we left off.
|
||||
*/
|
||||
if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {
|
||||
tp->snd_cwnd = tp->snd_cwnd_prev;
|
||||
tp->snd_ssthresh = tp->snd_ssthresh_prev;
|
||||
tp->snd_nxt = tp->snd_max;
|
||||
tp->t_badrxtwin = 0; /* XXX probably not required */
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a timestamp reply, update smoothed
|
||||
* round trip time. If no timestamp is present but
|
||||
@ -1462,9 +1502,9 @@ tcp_input(m, iphlen)
|
||||
* Recompute the initial retransmit timer.
|
||||
*/
|
||||
if (to.to_flag & TOF_TS)
|
||||
tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1);
|
||||
else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp,tp->t_rtt);
|
||||
tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
|
||||
else if (tp->t_rtttime && SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp, ticks - tp->t_rtttime);
|
||||
|
||||
/*
|
||||
* If all outstanding data is acked, stop retransmit
|
||||
@ -1473,10 +1513,11 @@ tcp_input(m, iphlen)
|
||||
* timer, using current (possibly backed-off) value.
|
||||
*/
|
||||
if (ti->ti_ack == tp->snd_max) {
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
needoutput = 1;
|
||||
} else if (tp->t_timer[TCPT_PERSIST] == 0)
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
} else if (!callout_active(tp->tt_persist))
|
||||
callout_reset(tp->tt_rexmt, tp->t_rxtcur,
|
||||
tcp_timer_rexmt, tp);
|
||||
|
||||
/*
|
||||
* If no data (only SYN) was ACK'd,
|
||||
@ -1532,7 +1573,8 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if (so->so_state & SS_CANTRCVMORE) {
|
||||
soisdisconnected(so);
|
||||
tp->t_timer[TCPT_2MSL] = tcp_maxidle;
|
||||
callout_reset(tp->tt_2msl, tcp_maxidle,
|
||||
tcp_timer_2msl, tp);
|
||||
}
|
||||
tp->t_state = TCPS_FIN_WAIT_2;
|
||||
}
|
||||
@ -1550,11 +1592,14 @@ tcp_input(m, iphlen)
|
||||
tcp_canceltimers(tp);
|
||||
/* Shorten TIME_WAIT [RFC-1644, p.28] */
|
||||
if (tp->cc_recv != 0 &&
|
||||
tp->t_duration < TCPTV_MSL)
|
||||
tp->t_timer[TCPT_2MSL] =
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC;
|
||||
(ticks - tp->t_starttime) < tcp_msl)
|
||||
callout_reset(tp->tt_2msl,
|
||||
tp->t_rxtcur *
|
||||
TCPTV_TWTRUNC,
|
||||
tcp_timer_2msl, tp);
|
||||
else
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
soisdisconnected(so);
|
||||
}
|
||||
break;
|
||||
@ -1578,7 +1623,8 @@ tcp_input(m, iphlen)
|
||||
* it and restart the finack timer.
|
||||
*/
|
||||
case TCPS_TIME_WAIT:
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
goto dropafterack;
|
||||
}
|
||||
}
|
||||
@ -1702,7 +1748,8 @@ tcp_input(m, iphlen)
|
||||
* more input can be expected, send ACK now.
|
||||
*/
|
||||
if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN))
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
else
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->rcv_nxt++;
|
||||
@ -1714,6 +1761,8 @@ tcp_input(m, iphlen)
|
||||
* enter the CLOSE_WAIT state.
|
||||
*/
|
||||
case TCPS_SYN_RECEIVED:
|
||||
tp->t_starttime = ticks;
|
||||
/*FALLTHROUGH*/
|
||||
case TCPS_ESTABLISHED:
|
||||
tp->t_state = TCPS_CLOSE_WAIT;
|
||||
break;
|
||||
@ -1736,14 +1785,16 @@ tcp_input(m, iphlen)
|
||||
tcp_canceltimers(tp);
|
||||
/* Shorten TIME_WAIT [RFC-1644, p.28] */
|
||||
if (tp->cc_recv != 0 &&
|
||||
tp->t_duration < TCPTV_MSL) {
|
||||
tp->t_timer[TCPT_2MSL] =
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC;
|
||||
(ticks - tp->t_starttime) < tcp_msl) {
|
||||
callout_reset(tp->tt_2msl,
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC,
|
||||
tcp_timer_2msl, tp);
|
||||
/* For transaction client, force ACK now. */
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
}
|
||||
else
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
soisdisconnected(so);
|
||||
break;
|
||||
|
||||
@ -1751,7 +1802,8 @@ tcp_input(m, iphlen)
|
||||
* In TIME_WAIT state restart the 2 MSL time_wait timer.
|
||||
*/
|
||||
case TCPS_TIME_WAIT:
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1900,7 +1952,7 @@ tcp_dooptions(tp, cp, cnt, ti, to)
|
||||
if (ti->ti_flags & TH_SYN) {
|
||||
tp->t_flags |= TF_RCVD_TSTMP;
|
||||
tp->ts_recent = to->to_tsval;
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
}
|
||||
break;
|
||||
case TCPOPT_CC:
|
||||
@ -1988,7 +2040,7 @@ tcp_pulloutofband(so, ti, m)
|
||||
static void
|
||||
tcp_xmit_timer(tp, rtt)
|
||||
register struct tcpcb *tp;
|
||||
short rtt;
|
||||
int rtt;
|
||||
{
|
||||
register int delta;
|
||||
|
||||
@ -2032,7 +2084,7 @@ tcp_xmit_timer(tp, rtt)
|
||||
tp->t_srtt = rtt << TCP_RTT_SHIFT;
|
||||
tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
|
||||
}
|
||||
tp->t_rtt = 0;
|
||||
tp->t_rtttime = 0;
|
||||
tp->t_rxtshift = 0;
|
||||
|
||||
/*
|
||||
@ -2143,12 +2195,12 @@ tcp_mss(tp, offer)
|
||||
* is also a minimum value; this is subject to time.
|
||||
*/
|
||||
if (rt->rt_rmx.rmx_locks & RTV_RTT)
|
||||
tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
|
||||
tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
|
||||
tp->t_rttmin = rtt / (RTM_RTTUNIT / hz);
|
||||
tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
|
||||
tcpstat.tcps_usedrtt++;
|
||||
if (rt->rt_rmx.rmx_rttvar) {
|
||||
tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
|
||||
tcpstat.tcps_usedrttvar++;
|
||||
} else {
|
||||
/* default variation is +- 1 rtt */
|
||||
@ -2156,8 +2208,8 @@ tcp_mss(tp, offer)
|
||||
tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
|
||||
}
|
||||
TCPT_RANGESET(tp->t_rxtcur,
|
||||
((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
}
|
||||
/*
|
||||
* if there's an mtu associated with the route, use it
|
||||
@ -2231,11 +2283,15 @@ tcp_mss(tp, offer)
|
||||
bufsize = sb_max;
|
||||
(void)sbreserve(&so->so_rcv, bufsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't force slow-start on local network.
|
||||
* Set the slow-start flight size depending on whether this
|
||||
* is a local network or not.
|
||||
*/
|
||||
if (!in_localaddr(inp->inp_faddr))
|
||||
tp->snd_cwnd = mss;
|
||||
if (in_localaddr(inp->inp_faddr))
|
||||
tp->snd_cwnd = mss * ss_fltsz_local;
|
||||
else
|
||||
tp->snd_cwnd = mss * ss_fltsz;
|
||||
|
||||
if (rt->rt_rmx.rmx_ssthresh) {
|
||||
/*
|
||||
|
@ -73,6 +73,13 @@ static int path_mtu_discovery = 1;
|
||||
SYSCTL_INT(_net_inet_tcp, OID_AUTO, path_mtu_discovery, CTLFLAG_RW,
|
||||
&path_mtu_discovery, 1, "Enable Path MTU Discovery");
|
||||
|
||||
int ss_fltsz = 1;
|
||||
SYSCTL_INT(_net_inet_tcp, OID_AUTO, slowstart_flightsize, CTLFLAG_RW,
|
||||
&ss_fltsz, 1, "Slow start flight size");
|
||||
|
||||
int ss_fltsz_local = TCP_MAXWIN; /* something large */
|
||||
SYSCTL_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW,
|
||||
&ss_fltsz_local, 1, "Slow start flight size for local networks");
|
||||
|
||||
/*
|
||||
* Tcp output routine: figure out what should be sent and send it.
|
||||
@ -99,13 +106,20 @@ tcp_output(tp)
|
||||
* to send, then transmit; otherwise, investigate further.
|
||||
*/
|
||||
idle = (tp->snd_max == tp->snd_una);
|
||||
if (idle && tp->t_idle >= tp->t_rxtcur)
|
||||
if (idle && (ticks - tp->t_rcvtime) >= tp->t_rxtcur) {
|
||||
/*
|
||||
* We have been idle for "a while" and no acks are
|
||||
* expected to clock out any data we send --
|
||||
* slow start to get ack "clock" running again.
|
||||
*/
|
||||
tp->snd_cwnd = tp->t_maxseg;
|
||||
*
|
||||
* Set the slow-start flight size depending on whether
|
||||
* this is a local network or not.
|
||||
*/
|
||||
if (in_localaddr(tp->t_inpcb->inp_faddr))
|
||||
tp->snd_cwnd = tp->t_maxseg * ss_fltsz_local;
|
||||
else
|
||||
tp->snd_cwnd = tp->t_maxseg * ss_fltsz;
|
||||
}
|
||||
again:
|
||||
sendalot = 0;
|
||||
off = tp->snd_nxt - tp->snd_una;
|
||||
@ -149,7 +163,7 @@ tcp_output(tp)
|
||||
flags &= ~TH_FIN;
|
||||
win = 1;
|
||||
} else {
|
||||
tp->t_timer[TCPT_PERSIST] = 0;
|
||||
callout_stop(tp->tt_persist);
|
||||
tp->t_rxtshift = 0;
|
||||
}
|
||||
}
|
||||
@ -200,10 +214,10 @@ tcp_output(tp)
|
||||
*/
|
||||
len = 0;
|
||||
if (win == 0) {
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
tp->t_rxtshift = 0;
|
||||
tp->snd_nxt = tp->snd_una;
|
||||
if (tp->t_timer[TCPT_PERSIST] == 0)
|
||||
if (!callout_active(tp->tt_persist))
|
||||
tcp_setpersist(tp);
|
||||
}
|
||||
}
|
||||
@ -291,11 +305,11 @@ tcp_output(tp)
|
||||
* persisting to move a small or zero window
|
||||
* (re)transmitting and thereby not persisting
|
||||
*
|
||||
* tp->t_timer[TCPT_PERSIST]
|
||||
* is set when we are in persist state.
|
||||
* callout_active(tp->tt_persist)
|
||||
* is true when we are in persist state.
|
||||
* tp->t_force
|
||||
* is set when we are called to send a persist packet.
|
||||
* tp->t_timer[TCPT_REXMT]
|
||||
* callout_active(tp->tt_rexmt)
|
||||
* is set when we are retransmitting
|
||||
* The output side is idle when both timers are zero.
|
||||
*
|
||||
@ -305,8 +319,8 @@ tcp_output(tp)
|
||||
* if window is nonzero, transmit what we can,
|
||||
* otherwise force out a byte.
|
||||
*/
|
||||
if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
|
||||
tp->t_timer[TCPT_PERSIST] == 0) {
|
||||
if (so->so_snd.sb_cc && !callout_active(tp->tt_rexmt) &&
|
||||
!callout_active(tp->tt_persist)) {
|
||||
tp->t_rxtshift = 0;
|
||||
tcp_setpersist(tp);
|
||||
}
|
||||
@ -364,7 +378,7 @@ tcp_output(tp)
|
||||
|
||||
/* Form timestamp option as shown in appendix A of RFC 1323. */
|
||||
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
|
||||
*lp++ = htonl(tcp_now);
|
||||
*lp++ = htonl(ticks);
|
||||
*lp = htonl(tp->ts_recent);
|
||||
optlen += TCPOLEN_TSTAMP_APPA;
|
||||
}
|
||||
@ -569,7 +583,8 @@ tcp_output(tp)
|
||||
* case, since we know we aren't doing a retransmission.
|
||||
* (retransmit and persist are mutually exclusive...)
|
||||
*/
|
||||
if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
|
||||
if (len || (flags & (TH_SYN|TH_FIN))
|
||||
|| callout_active(tp->tt_persist))
|
||||
ti->ti_seq = htonl(tp->snd_nxt);
|
||||
else
|
||||
ti->ti_seq = htonl(tp->snd_max);
|
||||
@ -615,7 +630,7 @@ tcp_output(tp)
|
||||
* In transmit state, time the transmission and arrange for
|
||||
* the retransmit. In persist state, just set snd_max.
|
||||
*/
|
||||
if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
|
||||
if (tp->t_force == 0 || !callout_active(tp->tt_persist)) {
|
||||
tcp_seq startseq = tp->snd_nxt;
|
||||
|
||||
/*
|
||||
@ -636,8 +651,8 @@ tcp_output(tp)
|
||||
* Time this transmission if not a retransmission and
|
||||
* not currently timing anything.
|
||||
*/
|
||||
if (tp->t_rtt == 0) {
|
||||
tp->t_rtt = 1;
|
||||
if (tp->t_rtttime == 0) {
|
||||
tp->t_rtttime = ticks;
|
||||
tp->t_rtseq = startseq;
|
||||
tcpstat.tcps_segstimed++;
|
||||
}
|
||||
@ -651,11 +666,12 @@ tcp_output(tp)
|
||||
* Initialize shift counter which is used for backoff
|
||||
* of retransmit time.
|
||||
*/
|
||||
if (tp->t_timer[TCPT_REXMT] == 0 &&
|
||||
if (!callout_active(tp->tt_rexmt) &&
|
||||
tp->snd_nxt != tp->snd_una) {
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
if (tp->t_timer[TCPT_PERSIST]) {
|
||||
tp->t_timer[TCPT_PERSIST] = 0;
|
||||
callout_reset(tp->tt_rexmt, tp->t_rxtcur,
|
||||
tcp_timer_rexmt, tp);
|
||||
if (callout_active(tp->tt_persist)) {
|
||||
callout_stop(tp->tt_persist);
|
||||
tp->t_rxtshift = 0;
|
||||
}
|
||||
}
|
||||
@ -733,7 +749,9 @@ tcp_output(tp)
|
||||
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
|
||||
tp->rcv_adv = tp->rcv_nxt + win;
|
||||
tp->last_ack_sent = tp->rcv_nxt;
|
||||
tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
|
||||
tp->t_flags &= ~TF_ACKNOW;
|
||||
if (tcp_delack_enabled)
|
||||
callout_stop(tp->tt_delack);
|
||||
if (sendalot)
|
||||
goto again;
|
||||
return (0);
|
||||
@ -743,16 +761,17 @@ void
|
||||
tcp_setpersist(tp)
|
||||
register struct tcpcb *tp;
|
||||
{
|
||||
register int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
|
||||
int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
|
||||
int tt;
|
||||
|
||||
if (tp->t_timer[TCPT_REXMT])
|
||||
panic("tcp_output REXMT");
|
||||
if (callout_active(tp->tt_rexmt))
|
||||
panic("tcp_setpersist: retransmit pending");
|
||||
/*
|
||||
* Start/restart persistance timer.
|
||||
*/
|
||||
TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
|
||||
t * tcp_backoff[tp->t_rxtshift],
|
||||
TCPTV_PERSMIN, TCPTV_PERSMAX);
|
||||
TCPT_RANGESET(tt, t * tcp_backoff[tp->t_rxtshift],
|
||||
TCPTV_PERSMIN, TCPTV_PERSMAX);
|
||||
callout_reset(tp->tt_persist, tt, tcp_timer_persist, tp);
|
||||
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
|
||||
tp->t_rxtshift++;
|
||||
}
|
||||
|
@ -93,7 +93,6 @@ SYSCTL_INT(_net_inet_tcp, OID_AUTO, delayed_ack, CTLFLAG_RW,
|
||||
&tcp_delack_enabled, 0,
|
||||
"Delay ACK to try and piggyback it onto a data packet");
|
||||
|
||||
u_long tcp_now;
|
||||
struct inpcbhead tcb;
|
||||
struct inpcbinfo tcbinfo;
|
||||
|
||||
@ -120,7 +119,8 @@ static void tcp_xmit_timer __P((struct tcpcb *, int));
|
||||
(tp)->t_segq == NULL && \
|
||||
(tp)->t_state == TCPS_ESTABLISHED) { \
|
||||
if (tcp_delack_enabled) \
|
||||
tp->t_flags |= TF_DELACK; \
|
||||
callout_reset(tp->tt_delack, tcp_delacktime, \
|
||||
tcp_timer_delack, tp); \
|
||||
else \
|
||||
tp->t_flags |= TF_ACKNOW; \
|
||||
(tp)->rcv_nxt += (ti)->ti_len; \
|
||||
@ -523,9 +523,9 @@ tcp_input(m, iphlen)
|
||||
* Segment received on connection.
|
||||
* Reset idle time and keep-alive timer.
|
||||
*/
|
||||
tp->t_idle = 0;
|
||||
tp->t_rcvtime = ticks;
|
||||
if (TCPS_HAVEESTABLISHED(tp->t_state))
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
|
||||
|
||||
/*
|
||||
* Process options if not in LISTEN state,
|
||||
@ -575,7 +575,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if ((to.to_flag & TOF_TS) != 0 &&
|
||||
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -588,12 +588,23 @@ tcp_input(m, iphlen)
|
||||
* this is a pure ack for outstanding data.
|
||||
*/
|
||||
++tcpstat.tcps_predack;
|
||||
/*
|
||||
* "bad retransmit" recovery
|
||||
*/
|
||||
if (tp->t_rxtshift == 1 &&
|
||||
ticks < tp->t_badrxtwin) {
|
||||
tp->snd_cwnd = tp->snd_cwnd_prev;
|
||||
tp->snd_ssthresh =
|
||||
tp->snd_ssthresh_prev;
|
||||
tp->snd_nxt = tp->snd_max;
|
||||
tp->t_badrxtwin = 0;
|
||||
}
|
||||
if ((to.to_flag & TOF_TS) != 0)
|
||||
tcp_xmit_timer(tp,
|
||||
tcp_now - to.to_tsecr + 1);
|
||||
else if (tp->t_rtt &&
|
||||
ticks - to.to_tsecr + 1);
|
||||
else if (tp->t_rtttime &&
|
||||
SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp, tp->t_rtt);
|
||||
tcp_xmit_timer(tp, ticks - tp->t_rtttime);
|
||||
acked = ti->ti_ack - tp->snd_una;
|
||||
tcpstat.tcps_rcvackpack++;
|
||||
tcpstat.tcps_rcvackbyte += acked;
|
||||
@ -611,9 +622,11 @@ tcp_input(m, iphlen)
|
||||
* decide between more output or persist.
|
||||
*/
|
||||
if (tp->snd_una == tp->snd_max)
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
else if (tp->t_timer[TCPT_PERSIST] == 0)
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
else if (!callout_active(tp->tt_persist))
|
||||
callout_reset(tp->tt_rexmt,
|
||||
tp->t_rxtcur,
|
||||
tcp_timer_rexmt, tp);
|
||||
|
||||
sowwakeup(so);
|
||||
if (so->so_snd.sb_cc)
|
||||
@ -638,7 +651,8 @@ tcp_input(m, iphlen)
|
||||
sbappend(&so->so_rcv, m);
|
||||
sorwakeup(so);
|
||||
if (tcp_delack_enabled) {
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
} else {
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tcp_output(tp);
|
||||
@ -760,6 +774,7 @@ tcp_input(m, iphlen)
|
||||
taop->tao_cc != 0 && CC_GT(to.to_cc, taop->tao_cc)) {
|
||||
|
||||
taop->tao_cc = to.to_cc;
|
||||
tp->t_starttime = ticks;
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
|
||||
/*
|
||||
@ -771,9 +786,11 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if (tcp_delack_enabled && ((tiflags & TH_FIN) ||
|
||||
(ti->ti_len != 0 &&
|
||||
in_localaddr(inp->inp_faddr))))
|
||||
tp->t_flags |= (TF_DELACK | TF_NEEDSYN);
|
||||
else
|
||||
in_localaddr(inp->inp_faddr)))) {
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
tp->t_flags |= TF_NEEDSYN;
|
||||
} else
|
||||
tp->t_flags |= (TF_ACKNOW | TF_NEEDSYN);
|
||||
|
||||
/*
|
||||
@ -784,7 +801,8 @@ tcp_input(m, iphlen)
|
||||
tp->rcv_adv += min(tp->rcv_wnd, TCP_MAXWIN);
|
||||
tcpstat.tcps_connects++;
|
||||
soisconnected(so);
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
|
||||
callout_reset(tp->tt_keep, tcp_keepinit,
|
||||
tcp_timer_keep, tp);
|
||||
dropsocket = 0; /* committed to socket */
|
||||
tcpstat.tcps_accepts++;
|
||||
goto trimthenstep6;
|
||||
@ -803,7 +821,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->t_state = TCPS_SYN_RECEIVED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
|
||||
callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
|
||||
dropsocket = 0; /* committed to socket */
|
||||
tcpstat.tcps_accepts++;
|
||||
goto trimthenstep6;
|
||||
@ -903,7 +921,8 @@ tcp_input(m, iphlen)
|
||||
* ACKNOW will be turned on later.
|
||||
*/
|
||||
if (tcp_delack_enabled && ti->ti_len != 0)
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
else
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
/*
|
||||
@ -912,13 +931,15 @@ tcp_input(m, iphlen)
|
||||
* SYN_SENT --> ESTABLISHED
|
||||
* SYN_SENT* --> FIN_WAIT_1
|
||||
*/
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
tiflags &= ~TH_SYN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle,
|
||||
tcp_timer_keep, tp);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@ -931,7 +952,7 @@ tcp_input(m, iphlen)
|
||||
* If there was no CC option, clear cached CC value.
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
if (to.to_flag & TOF_CC) {
|
||||
if (taop->tao_cc != 0 &&
|
||||
CC_GT(to.to_cc, taop->tao_cc)) {
|
||||
@ -941,13 +962,16 @@ tcp_input(m, iphlen)
|
||||
* SYN-SENT* -> FIN-WAIT-1*
|
||||
*/
|
||||
taop->tao_cc = to.to_cc;
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] =
|
||||
tcp_keepidle;
|
||||
callout_reset(tp->tt_keep,
|
||||
tcp_keepidle,
|
||||
tcp_timer_keep,
|
||||
tp);
|
||||
}
|
||||
tp->t_flags |= TF_NEEDSYN;
|
||||
} else
|
||||
@ -1006,7 +1030,7 @@ tcp_input(m, iphlen)
|
||||
if ((tiflags & TH_SYN) &&
|
||||
(to.to_flag & TOF_CC) && tp->cc_recv != 0) {
|
||||
if (tp->t_state == TCPS_TIME_WAIT &&
|
||||
tp->t_duration > TCPTV_MSL)
|
||||
(ticks - tp->t_starttime) > tcp_msl)
|
||||
goto dropwithreset;
|
||||
if (CC_GT(to.to_cc, tp->cc_recv)) {
|
||||
tp = tcp_close(tp);
|
||||
@ -1115,7 +1139,7 @@ tcp_input(m, iphlen)
|
||||
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
||||
|
||||
/* Check to see if ts_recent is over 24 days old. */
|
||||
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
||||
if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
||||
/*
|
||||
* Invalidate ts_recent. If this segment updates
|
||||
* ts_recent, the age will be reset later and ts_recent
|
||||
@ -1262,7 +1286,7 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if ((to.to_flag & TOF_TS) != 0 &&
|
||||
SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)) {
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -1322,12 +1346,14 @@ tcp_input(m, iphlen)
|
||||
* SYN-RECEIVED -> ESTABLISHED
|
||||
* SYN-RECEIVED* -> FIN-WAIT-1
|
||||
*/
|
||||
tp->t_starttime = ticks;
|
||||
if (tp->t_flags & TF_NEEDFIN) {
|
||||
tp->t_state = TCPS_FIN_WAIT_1;
|
||||
tp->t_flags &= ~TF_NEEDFIN;
|
||||
} else {
|
||||
tp->t_state = TCPS_ESTABLISHED;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
callout_reset(tp->tt_keep, tcp_keepidle,
|
||||
tcp_timer_keep, tp);
|
||||
}
|
||||
/*
|
||||
* If segment contains data or ACK, will call tcp_reass()
|
||||
@ -1382,7 +1408,7 @@ tcp_input(m, iphlen)
|
||||
* to keep a constant cwnd packets in the
|
||||
* network.
|
||||
*/
|
||||
if (tp->t_timer[TCPT_REXMT] == 0 ||
|
||||
if (!callout_active(tp->tt_rexmt) ||
|
||||
ti->ti_ack != tp->snd_una)
|
||||
tp->t_dupacks = 0;
|
||||
else if (++tp->t_dupacks == tcprexmtthresh) {
|
||||
@ -1394,8 +1420,8 @@ tcp_input(m, iphlen)
|
||||
if (win < 2)
|
||||
win = 2;
|
||||
tp->snd_ssthresh = win * tp->t_maxseg;
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
tp->t_rtt = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
tp->t_rtttime = 0;
|
||||
tp->snd_nxt = ti->ti_ack;
|
||||
tp->snd_cwnd = tp->t_maxseg;
|
||||
(void) tcp_output(tp);
|
||||
@ -1452,6 +1478,20 @@ tcp_input(m, iphlen)
|
||||
tcpstat.tcps_rcvackpack++;
|
||||
tcpstat.tcps_rcvackbyte += acked;
|
||||
|
||||
/*
|
||||
* If we just performed our first retransmit, and the ACK
|
||||
* arrives within our recovery window, then it was a mistake
|
||||
* to do the retransmit in the first place. Recover our
|
||||
* original cwnd and ssthresh, and proceed to transmit where
|
||||
* we left off.
|
||||
*/
|
||||
if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {
|
||||
tp->snd_cwnd = tp->snd_cwnd_prev;
|
||||
tp->snd_ssthresh = tp->snd_ssthresh_prev;
|
||||
tp->snd_nxt = tp->snd_max;
|
||||
tp->t_badrxtwin = 0; /* XXX probably not required */
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have a timestamp reply, update smoothed
|
||||
* round trip time. If no timestamp is present but
|
||||
@ -1462,9 +1502,9 @@ tcp_input(m, iphlen)
|
||||
* Recompute the initial retransmit timer.
|
||||
*/
|
||||
if (to.to_flag & TOF_TS)
|
||||
tcp_xmit_timer(tp, tcp_now - to.to_tsecr + 1);
|
||||
else if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp,tp->t_rtt);
|
||||
tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
|
||||
else if (tp->t_rtttime && SEQ_GT(ti->ti_ack, tp->t_rtseq))
|
||||
tcp_xmit_timer(tp, ticks - tp->t_rtttime);
|
||||
|
||||
/*
|
||||
* If all outstanding data is acked, stop retransmit
|
||||
@ -1473,10 +1513,11 @@ tcp_input(m, iphlen)
|
||||
* timer, using current (possibly backed-off) value.
|
||||
*/
|
||||
if (ti->ti_ack == tp->snd_max) {
|
||||
tp->t_timer[TCPT_REXMT] = 0;
|
||||
callout_stop(tp->tt_rexmt);
|
||||
needoutput = 1;
|
||||
} else if (tp->t_timer[TCPT_PERSIST] == 0)
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
} else if (!callout_active(tp->tt_persist))
|
||||
callout_reset(tp->tt_rexmt, tp->t_rxtcur,
|
||||
tcp_timer_rexmt, tp);
|
||||
|
||||
/*
|
||||
* If no data (only SYN) was ACK'd,
|
||||
@ -1532,7 +1573,8 @@ tcp_input(m, iphlen)
|
||||
*/
|
||||
if (so->so_state & SS_CANTRCVMORE) {
|
||||
soisdisconnected(so);
|
||||
tp->t_timer[TCPT_2MSL] = tcp_maxidle;
|
||||
callout_reset(tp->tt_2msl, tcp_maxidle,
|
||||
tcp_timer_2msl, tp);
|
||||
}
|
||||
tp->t_state = TCPS_FIN_WAIT_2;
|
||||
}
|
||||
@ -1550,11 +1592,14 @@ tcp_input(m, iphlen)
|
||||
tcp_canceltimers(tp);
|
||||
/* Shorten TIME_WAIT [RFC-1644, p.28] */
|
||||
if (tp->cc_recv != 0 &&
|
||||
tp->t_duration < TCPTV_MSL)
|
||||
tp->t_timer[TCPT_2MSL] =
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC;
|
||||
(ticks - tp->t_starttime) < tcp_msl)
|
||||
callout_reset(tp->tt_2msl,
|
||||
tp->t_rxtcur *
|
||||
TCPTV_TWTRUNC,
|
||||
tcp_timer_2msl, tp);
|
||||
else
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
soisdisconnected(so);
|
||||
}
|
||||
break;
|
||||
@ -1578,7 +1623,8 @@ tcp_input(m, iphlen)
|
||||
* it and restart the finack timer.
|
||||
*/
|
||||
case TCPS_TIME_WAIT:
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
goto dropafterack;
|
||||
}
|
||||
}
|
||||
@ -1702,7 +1748,8 @@ tcp_input(m, iphlen)
|
||||
* more input can be expected, send ACK now.
|
||||
*/
|
||||
if (tcp_delack_enabled && (tp->t_flags & TF_NEEDSYN))
|
||||
tp->t_flags |= TF_DELACK;
|
||||
callout_reset(tp->tt_delack, tcp_delacktime,
|
||||
tcp_timer_delack, tp);
|
||||
else
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tp->rcv_nxt++;
|
||||
@ -1714,6 +1761,8 @@ tcp_input(m, iphlen)
|
||||
* enter the CLOSE_WAIT state.
|
||||
*/
|
||||
case TCPS_SYN_RECEIVED:
|
||||
tp->t_starttime = ticks;
|
||||
/*FALLTHROUGH*/
|
||||
case TCPS_ESTABLISHED:
|
||||
tp->t_state = TCPS_CLOSE_WAIT;
|
||||
break;
|
||||
@ -1736,14 +1785,16 @@ tcp_input(m, iphlen)
|
||||
tcp_canceltimers(tp);
|
||||
/* Shorten TIME_WAIT [RFC-1644, p.28] */
|
||||
if (tp->cc_recv != 0 &&
|
||||
tp->t_duration < TCPTV_MSL) {
|
||||
tp->t_timer[TCPT_2MSL] =
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC;
|
||||
(ticks - tp->t_starttime) < tcp_msl) {
|
||||
callout_reset(tp->tt_2msl,
|
||||
tp->t_rxtcur * TCPTV_TWTRUNC,
|
||||
tcp_timer_2msl, tp);
|
||||
/* For transaction client, force ACK now. */
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
}
|
||||
else
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
soisdisconnected(so);
|
||||
break;
|
||||
|
||||
@ -1751,7 +1802,8 @@ tcp_input(m, iphlen)
|
||||
* In TIME_WAIT state restart the 2 MSL time_wait timer.
|
||||
*/
|
||||
case TCPS_TIME_WAIT:
|
||||
tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
|
||||
callout_reset(tp->tt_2msl, 2 * tcp_msl,
|
||||
tcp_timer_2msl, tp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1900,7 +1952,7 @@ tcp_dooptions(tp, cp, cnt, ti, to)
|
||||
if (ti->ti_flags & TH_SYN) {
|
||||
tp->t_flags |= TF_RCVD_TSTMP;
|
||||
tp->ts_recent = to->to_tsval;
|
||||
tp->ts_recent_age = tcp_now;
|
||||
tp->ts_recent_age = ticks;
|
||||
}
|
||||
break;
|
||||
case TCPOPT_CC:
|
||||
@ -1988,7 +2040,7 @@ tcp_pulloutofband(so, ti, m)
|
||||
static void
|
||||
tcp_xmit_timer(tp, rtt)
|
||||
register struct tcpcb *tp;
|
||||
short rtt;
|
||||
int rtt;
|
||||
{
|
||||
register int delta;
|
||||
|
||||
@ -2032,7 +2084,7 @@ tcp_xmit_timer(tp, rtt)
|
||||
tp->t_srtt = rtt << TCP_RTT_SHIFT;
|
||||
tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
|
||||
}
|
||||
tp->t_rtt = 0;
|
||||
tp->t_rtttime = 0;
|
||||
tp->t_rxtshift = 0;
|
||||
|
||||
/*
|
||||
@ -2143,12 +2195,12 @@ tcp_mss(tp, offer)
|
||||
* is also a minimum value; this is subject to time.
|
||||
*/
|
||||
if (rt->rt_rmx.rmx_locks & RTV_RTT)
|
||||
tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
|
||||
tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
|
||||
tp->t_rttmin = rtt / (RTM_RTTUNIT / hz);
|
||||
tp->t_srtt = rtt / (RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
|
||||
tcpstat.tcps_usedrtt++;
|
||||
if (rt->rt_rmx.rmx_rttvar) {
|
||||
tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
|
||||
tcpstat.tcps_usedrttvar++;
|
||||
} else {
|
||||
/* default variation is +- 1 rtt */
|
||||
@ -2156,8 +2208,8 @@ tcp_mss(tp, offer)
|
||||
tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
|
||||
}
|
||||
TCPT_RANGESET(tp->t_rxtcur,
|
||||
((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
}
|
||||
/*
|
||||
* if there's an mtu associated with the route, use it
|
||||
@ -2231,11 +2283,15 @@ tcp_mss(tp, offer)
|
||||
bufsize = sb_max;
|
||||
(void)sbreserve(&so->so_rcv, bufsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't force slow-start on local network.
|
||||
* Set the slow-start flight size depending on whether this
|
||||
* is a local network or not.
|
||||
*/
|
||||
if (!in_localaddr(inp->inp_faddr))
|
||||
tp->snd_cwnd = mss;
|
||||
if (in_localaddr(inp->inp_faddr))
|
||||
tp->snd_cwnd = mss * ss_fltsz_local;
|
||||
else
|
||||
tp->snd_cwnd = mss * ss_fltsz;
|
||||
|
||||
if (rt->rt_rmx.rmx_ssthresh) {
|
||||
/*
|
||||
|
@ -75,7 +75,7 @@
|
||||
(tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
|
||||
(tp)->iss
|
||||
|
||||
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
|
||||
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * hz)
|
||||
/* timestamp wrap-around time */
|
||||
|
||||
#ifdef KERNEL
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/malloc.h>
|
||||
@ -74,9 +75,11 @@ int tcp_mssdflt = TCP_MSS;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
|
||||
&tcp_mssdflt , 0, "Default TCP Maximum Segment Size");
|
||||
|
||||
#if 0
|
||||
static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW,
|
||||
&tcp_rttdflt , 0, "Default maximum TCP Round Trip Time");
|
||||
#endif
|
||||
|
||||
static int tcp_do_rfc1323 = 1;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW,
|
||||
@ -122,6 +125,8 @@ struct inp_tp {
|
||||
char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1];
|
||||
} inp_tp_u;
|
||||
struct tcpcb tcb;
|
||||
struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl;
|
||||
struct callout inp_tp_delack;
|
||||
};
|
||||
#undef ALIGNMENT
|
||||
#undef ALIGNM1
|
||||
@ -137,6 +142,14 @@ tcp_init()
|
||||
tcp_iss = random(); /* wrong, but better than a constant */
|
||||
tcp_ccgen = 1;
|
||||
tcp_cleartaocache();
|
||||
|
||||
tcp_delacktime = TCPTV_DELACK;
|
||||
tcp_keepinit = TCPTV_KEEP_INIT;
|
||||
tcp_keepidle = TCPTV_KEEP_IDLE;
|
||||
tcp_keepintvl = TCPTV_KEEPINTVL;
|
||||
tcp_maxpersistidle = TCPTV_KEEP_IDLE;
|
||||
tcp_msl = TCPTV_MSL;
|
||||
|
||||
LIST_INIT(&tcb);
|
||||
tcbinfo.listhead = &tcb;
|
||||
TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize);
|
||||
@ -150,6 +163,7 @@ tcp_init()
|
||||
&tcbinfo.porthashmask);
|
||||
tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets,
|
||||
ZONE_INTERRUPT, 0);
|
||||
|
||||
if (max_protohdr < sizeof(struct tcpiphdr))
|
||||
max_protohdr = sizeof(struct tcpiphdr);
|
||||
if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
|
||||
@ -304,6 +318,13 @@ tcp_newtcpcb(inp)
|
||||
tp->t_segq = NULL;
|
||||
tp->t_maxseg = tp->t_maxopd = tcp_mssdflt;
|
||||
|
||||
/* Set up our timeouts. */
|
||||
callout_init(tp->tt_rexmt = &it->inp_tp_rexmt);
|
||||
callout_init(tp->tt_persist = &it->inp_tp_persist);
|
||||
callout_init(tp->tt_keep = &it->inp_tp_keep);
|
||||
callout_init(tp->tt_2msl = &it->inp_tp_2msl);
|
||||
callout_init(tp->tt_delack = &it->inp_tp_delack);
|
||||
|
||||
if (tcp_do_rfc1323)
|
||||
tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);
|
||||
if (tcp_do_rfc1644)
|
||||
@ -320,6 +341,7 @@ tcp_newtcpcb(inp)
|
||||
tp->t_rxtcur = TCPTV_RTOBASE;
|
||||
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
||||
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
||||
tp->t_rcvtime = ticks;
|
||||
inp->inp_ip_ttl = ip_defttl;
|
||||
inp->inp_ppcb = (caddr_t)tp;
|
||||
return (tp); /* XXX */
|
||||
@ -366,6 +388,16 @@ tcp_close(tp)
|
||||
register struct rtentry *rt;
|
||||
int dosavessthresh;
|
||||
|
||||
/*
|
||||
* Make sure that all of our timers are stopped before we
|
||||
* delete the PCB.
|
||||
*/
|
||||
callout_stop(tp->tt_rexmt);
|
||||
callout_stop(tp->tt_persist);
|
||||
callout_stop(tp->tt_keep);
|
||||
callout_stop(tp->tt_2msl);
|
||||
callout_stop(tp->tt_delack);
|
||||
|
||||
/*
|
||||
* If we got enough samples through the srtt filter,
|
||||
* save the rtt and rttvar in the routing entry.
|
||||
@ -384,7 +416,7 @@ tcp_close(tp)
|
||||
|
||||
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
|
||||
i = tp->t_srtt *
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
|
||||
if (rt->rt_rmx.rmx_rtt && i)
|
||||
/*
|
||||
* filter this update to half the old & half
|
||||
@ -400,7 +432,7 @@ tcp_close(tp)
|
||||
}
|
||||
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
|
||||
i = tp->t_rttvar *
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
|
||||
if (rt->rt_rmx.rmx_rttvar && i)
|
||||
rt->rt_rmx.rmx_rttvar =
|
||||
(rt->rt_rmx.rmx_rttvar + i) / 2;
|
||||
@ -734,7 +766,7 @@ tcp_mtudisc(inp, errno)
|
||||
tp->t_maxseg = mss;
|
||||
|
||||
tcpstat.tcps_mturesent++;
|
||||
tp->t_rtt = 0;
|
||||
tp->t_rtttime = 0;
|
||||
tp->snd_nxt = tp->snd_una;
|
||||
tcp_output(tp);
|
||||
}
|
||||
|
@ -63,116 +63,55 @@
|
||||
#include <netinet/tcp_debug.h>
|
||||
#endif
|
||||
|
||||
int tcp_keepinit = TCPTV_KEEP_INIT;
|
||||
int tcp_keepinit;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit,
|
||||
CTLFLAG_RW, &tcp_keepinit , 0, "");
|
||||
|
||||
int tcp_keepidle = TCPTV_KEEP_IDLE;
|
||||
int tcp_keepidle;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle,
|
||||
CTLFLAG_RW, &tcp_keepidle , 0, "");
|
||||
|
||||
static int tcp_keepintvl = TCPTV_KEEPINTVL;
|
||||
int tcp_keepintvl;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl,
|
||||
CTLFLAG_RW, &tcp_keepintvl , 0, "");
|
||||
|
||||
int tcp_delacktime;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime, CTLFLAG_RW,
|
||||
&tcp_delacktime, 0, "Time before a delayed ACK is sent");
|
||||
|
||||
int tcp_msl;
|
||||
SYSCTL_INT(_net_inet_tcp, OID_AUTO, msl, CTLFLAG_RW,
|
||||
&tcp_msl, 0, "Maximum segment lifetime");
|
||||
|
||||
static int always_keepalive = 0;
|
||||
SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW,
|
||||
&always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections");
|
||||
|
||||
static int tcp_keepcnt = TCPTV_KEEPCNT;
|
||||
/* max idle probes */
|
||||
static int tcp_maxpersistidle = TCPTV_KEEP_IDLE;
|
||||
int tcp_maxpersistidle;
|
||||
/* max idle time in persist */
|
||||
int tcp_maxidle;
|
||||
|
||||
/*
|
||||
* Fast timeout routine for processing delayed acks
|
||||
*/
|
||||
void
|
||||
tcp_fasttimo()
|
||||
{
|
||||
register struct inpcb *inp;
|
||||
register struct tcpcb *tp;
|
||||
int s;
|
||||
|
||||
if (tcp_delack_enabled) {
|
||||
s = splnet();
|
||||
for (inp = tcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
|
||||
if ((tp = (struct tcpcb *)inp->inp_ppcb) &&
|
||||
(tp->t_flags & TF_DELACK)) {
|
||||
tp->t_flags &= ~TF_DELACK;
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tcpstat.tcps_delack++;
|
||||
(void) tcp_output(tp);
|
||||
}
|
||||
}
|
||||
splx(s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tcp protocol timeout routine called every 500 ms.
|
||||
* Updates the timers in all active tcb's and
|
||||
* Updates timestamps used for TCP
|
||||
* causes finite state machine actions if timers expire.
|
||||
*/
|
||||
void
|
||||
tcp_slowtimo()
|
||||
{
|
||||
register struct inpcb *ip, *ipnxt;
|
||||
register struct tcpcb *tp;
|
||||
register int i;
|
||||
int s;
|
||||
#ifdef TCPDEBUG
|
||||
int ostate;
|
||||
#endif
|
||||
|
||||
s = splnet();
|
||||
|
||||
tcp_maxidle = tcp_keepcnt * tcp_keepintvl;
|
||||
|
||||
ip = tcb.lh_first;
|
||||
if (ip == NULL) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Search through tcb's and update active timers.
|
||||
*/
|
||||
for (; ip != NULL; ip = ipnxt) {
|
||||
ipnxt = ip->inp_list.le_next;
|
||||
tp = intotcpcb(ip);
|
||||
if (tp == 0 || tp->t_state == TCPS_LISTEN)
|
||||
continue;
|
||||
for (i = 0; i < TCPT_NTIMERS; i++) {
|
||||
if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
|
||||
#ifdef TCPDEBUG
|
||||
ostate = tp->t_state;
|
||||
#endif
|
||||
tp = tcp_timers(tp, i);
|
||||
if (tp == NULL)
|
||||
goto tpgone;
|
||||
#ifdef TCPDEBUG
|
||||
if (tp->t_inpcb->inp_socket->so_options
|
||||
& SO_DEBUG)
|
||||
tcp_trace(TA_USER, ostate, tp,
|
||||
(struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
tp->t_idle++;
|
||||
tp->t_duration++;
|
||||
if (tp->t_rtt)
|
||||
tp->t_rtt++;
|
||||
tpgone:
|
||||
;
|
||||
}
|
||||
tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
|
||||
#ifdef TCP_COMPAT_42
|
||||
if ((int)tcp_iss < 0)
|
||||
tcp_iss = TCP_ISSINCR; /* XXX */
|
||||
#endif
|
||||
tcp_now++; /* for timestamps */
|
||||
splx(s);
|
||||
}
|
||||
|
||||
@ -183,10 +122,10 @@ void
|
||||
tcp_canceltimers(tp)
|
||||
struct tcpcb *tp;
|
||||
{
|
||||
register int i;
|
||||
|
||||
for (i = 0; i < TCPT_NTIMERS; i++)
|
||||
tp->t_timer[i] = 0;
|
||||
callout_stop(tp->tt_2msl);
|
||||
callout_stop(tp->tt_persist);
|
||||
callout_stop(tp->tt_keep);
|
||||
callout_stop(tp->tt_rexmt);
|
||||
}
|
||||
|
||||
int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
|
||||
@ -197,175 +136,300 @@ static int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
|
||||
/*
|
||||
* TCP timer processing.
|
||||
*/
|
||||
struct tcpcb *
|
||||
tcp_timers(tp, timer)
|
||||
register struct tcpcb *tp;
|
||||
int timer;
|
||||
void
|
||||
tcp_timer_delack(xtp)
|
||||
void *xtp;
|
||||
{
|
||||
register int rexmt;
|
||||
struct tcpcb *tp = xtp;
|
||||
int s;
|
||||
|
||||
switch (timer) {
|
||||
s = splnet();
|
||||
if (callout_pending(tp->tt_delack)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
callout_deactivate(tp->tt_delack);
|
||||
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
tcpstat.tcps_delack++;
|
||||
(void) tcp_output(tp);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
tcp_timer_2msl(xtp)
|
||||
void *xtp;
|
||||
{
|
||||
struct tcpcb *tp = xtp;
|
||||
int s;
|
||||
#ifdef TCPDEBUG
|
||||
int ostate;
|
||||
|
||||
ostate = tp->t_state;
|
||||
#endif
|
||||
s = splnet();
|
||||
if (callout_pending(tp->tt_2msl)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
callout_deactivate(tp->tt_2msl);
|
||||
/*
|
||||
* 2 MSL timeout in shutdown went off. If we're closed but
|
||||
* still waiting for peer to close and connection has been idle
|
||||
* too long, or if 2MSL time is up from TIME_WAIT, delete connection
|
||||
* control block. Otherwise, check again in a bit.
|
||||
*/
|
||||
case TCPT_2MSL:
|
||||
if (tp->t_state != TCPS_TIME_WAIT &&
|
||||
tp->t_idle <= tcp_maxidle)
|
||||
tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
|
||||
else
|
||||
tp = tcp_close(tp);
|
||||
break;
|
||||
if (tp->t_state != TCPS_TIME_WAIT &&
|
||||
(ticks - tp->t_rcvtime) <= tcp_maxidle)
|
||||
callout_reset(tp->tt_2msl, tcp_keepintvl,
|
||||
tcp_timer_2msl, tp);
|
||||
else
|
||||
tp = tcp_close(tp);
|
||||
|
||||
#ifdef TCPDEBUG
|
||||
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
|
||||
tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
tcp_timer_keep(xtp)
|
||||
void *xtp;
|
||||
{
|
||||
struct tcpcb *tp = xtp;
|
||||
int s;
|
||||
#ifdef TCPDEBUG
|
||||
int ostate;
|
||||
|
||||
ostate = tp->t_state;
|
||||
#endif
|
||||
s = splnet();
|
||||
if (callout_pending(tp->tt_keep)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
callout_deactivate(tp->tt_keep);
|
||||
/*
|
||||
* Keep-alive timer went off; send something
|
||||
* or drop connection if idle for too long.
|
||||
*/
|
||||
tcpstat.tcps_keeptimeo++;
|
||||
if (tp->t_state < TCPS_ESTABLISHED)
|
||||
goto dropit;
|
||||
if ((always_keepalive ||
|
||||
tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) &&
|
||||
tp->t_state <= TCPS_CLOSING) {
|
||||
if ((ticks - tp->t_rcvtime) >= tcp_keepidle + tcp_maxidle)
|
||||
goto dropit;
|
||||
/*
|
||||
* Send a packet designed to force a response
|
||||
* if the peer is up and reachable:
|
||||
* either an ACK if the connection is still alive,
|
||||
* or an RST if the peer has closed the connection
|
||||
* due to timeout or reboot.
|
||||
* Using sequence number tp->snd_una-1
|
||||
* causes the transmitted zero-length segment
|
||||
* to lie outside the receive window;
|
||||
* by the protocol spec, this requires the
|
||||
* correspondent TCP to respond.
|
||||
*/
|
||||
tcpstat.tcps_keepprobe++;
|
||||
#ifdef TCP_COMPAT_42
|
||||
/*
|
||||
* The keepalive packet must have nonzero length
|
||||
* to get a 4.2 host to respond.
|
||||
*/
|
||||
tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
|
||||
tp->rcv_nxt - 1, tp->snd_una - 1, 0);
|
||||
#else
|
||||
tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
|
||||
tp->rcv_nxt, tp->snd_una - 1, 0);
|
||||
#endif
|
||||
callout_reset(tp->tt_keep, tcp_keepintvl, tcp_timer_keep, tp);
|
||||
} else
|
||||
callout_reset(tp->tt_keep, tcp_keepidle, tcp_timer_keep, tp);
|
||||
|
||||
#ifdef TCPDEBUG
|
||||
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
|
||||
tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
splx(s);
|
||||
return;
|
||||
|
||||
dropit:
|
||||
tcpstat.tcps_keepdrops++;
|
||||
tp = tcp_drop(tp, ETIMEDOUT);
|
||||
|
||||
#ifdef TCPDEBUG
|
||||
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
|
||||
tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
tcp_timer_persist(xtp)
|
||||
void *xtp;
|
||||
{
|
||||
struct tcpcb *tp = xtp;
|
||||
int s;
|
||||
#ifdef TCPDEBUG
|
||||
int ostate;
|
||||
|
||||
ostate = tp->t_state;
|
||||
#endif
|
||||
s = splnet();
|
||||
if (callout_pending(tp->tt_persist)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
callout_deactivate(tp->tt_persist);
|
||||
/*
|
||||
* Persistance timer into zero window.
|
||||
* Force a byte to be output, if possible.
|
||||
*/
|
||||
tcpstat.tcps_persisttimeo++;
|
||||
/*
|
||||
* Hack: if the peer is dead/unreachable, we do not
|
||||
* time out if the window is closed. After a full
|
||||
* backoff, drop the connection if the idle time
|
||||
* (no responses to probes) reaches the maximum
|
||||
* backoff that we would use if retransmitting.
|
||||
*/
|
||||
if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
|
||||
((ticks - tp->t_rcvtime) >= tcp_maxpersistidle ||
|
||||
(ticks - tp->t_rcvtime) >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
|
||||
tcpstat.tcps_persistdrop++;
|
||||
tp = tcp_drop(tp, ETIMEDOUT);
|
||||
goto out;
|
||||
}
|
||||
tcp_setpersist(tp);
|
||||
tp->t_force = 1;
|
||||
(void) tcp_output(tp);
|
||||
tp->t_force = 0;
|
||||
|
||||
out:
|
||||
#ifdef TCPDEBUG
|
||||
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
|
||||
tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
splx(s);
|
||||
}
|
||||
|
||||
void
|
||||
tcp_timer_rexmt(xtp)
|
||||
void *xtp;
|
||||
{
|
||||
struct tcpcb *tp = xtp;
|
||||
int s;
|
||||
int rexmt;
|
||||
#ifdef TCPDEBUG
|
||||
int ostate;
|
||||
|
||||
ostate = tp->t_state;
|
||||
#endif
|
||||
s = splnet();
|
||||
if (callout_pending(tp->tt_rexmt)) {
|
||||
splx(s);
|
||||
return;
|
||||
}
|
||||
callout_deactivate(tp->tt_rexmt);
|
||||
/*
|
||||
* Retransmission timer went off. Message has not
|
||||
* been acked within retransmit interval. Back off
|
||||
* to a longer retransmit interval and retransmit one segment.
|
||||
*/
|
||||
case TCPT_REXMT:
|
||||
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
|
||||
tp->t_rxtshift = TCP_MAXRXTSHIFT;
|
||||
tcpstat.tcps_timeoutdrop++;
|
||||
tp = tcp_drop(tp, tp->t_softerror ?
|
||||
tp->t_softerror : ETIMEDOUT);
|
||||
break;
|
||||
}
|
||||
tcpstat.tcps_rexmttimeo++;
|
||||
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
|
||||
TCPT_RANGESET(tp->t_rxtcur, rexmt,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
|
||||
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
|
||||
tp->t_rxtshift = TCP_MAXRXTSHIFT;
|
||||
tcpstat.tcps_timeoutdrop++;
|
||||
tp = tcp_drop(tp, tp->t_softerror ?
|
||||
tp->t_softerror : ETIMEDOUT);
|
||||
goto out;
|
||||
}
|
||||
if (tp->t_rxtshift == 1) {
|
||||
/*
|
||||
* If losing, let the lower level know and try for
|
||||
* a better route. Also, if we backed off this far,
|
||||
* our srtt estimate is probably bogus. Clobber it
|
||||
* so we'll take the next rtt measurement as our srtt;
|
||||
* move the current srtt into rttvar to keep the current
|
||||
* retransmit times until then.
|
||||
* first retransmit; record ssthresh and cwnd so they can
|
||||
* be recovered if this turns out to be a "bad" retransmit.
|
||||
* A retransmit is considered "bad" if an ACK for this
|
||||
* segment is received within RTT/2 interval; the assumption
|
||||
* here is that the ACK was already in flight. See
|
||||
* "On Estimating End-to-End Network Path Properties" by
|
||||
* Allman and Paxson for more details.
|
||||
*/
|
||||
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
|
||||
in_losing(tp->t_inpcb);
|
||||
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
|
||||
tp->t_srtt = 0;
|
||||
}
|
||||
tp->snd_nxt = tp->snd_una;
|
||||
/*
|
||||
* Force a segment to be sent.
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
/*
|
||||
* If timing a segment in this window, stop the timer.
|
||||
*/
|
||||
tp->t_rtt = 0;
|
||||
/*
|
||||
* Close the congestion window down to one segment
|
||||
* (we'll open it by one segment for each ack we get).
|
||||
* Since we probably have a window's worth of unacked
|
||||
* data accumulated, this "slow start" keeps us from
|
||||
* dumping all that data as back-to-back packets (which
|
||||
* might overwhelm an intermediate gateway).
|
||||
*
|
||||
* There are two phases to the opening: Initially we
|
||||
* open by one mss on each ack. This makes the window
|
||||
* size increase exponentially with time. If the
|
||||
* window is larger than the path can handle, this
|
||||
* exponential growth results in dropped packet(s)
|
||||
* almost immediately. To get more time between
|
||||
* drops but still "push" the network to take advantage
|
||||
* of improving conditions, we switch from exponential
|
||||
* to linear window opening at some threshhold size.
|
||||
* For a threshhold, we use half the current window
|
||||
* size, truncated to a multiple of the mss.
|
||||
*
|
||||
* (the minimum cwnd that will give us exponential
|
||||
* growth is 2 mss. We don't allow the threshhold
|
||||
* to go below this.)
|
||||
*/
|
||||
{
|
||||
tp->snd_cwnd_prev = tp->snd_cwnd;
|
||||
tp->snd_ssthresh_prev = tp->snd_ssthresh;
|
||||
tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1));
|
||||
}
|
||||
tcpstat.tcps_rexmttimeo++;
|
||||
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
|
||||
TCPT_RANGESET(tp->t_rxtcur, rexmt,
|
||||
tp->t_rttmin, TCPTV_REXMTMAX);
|
||||
/*
|
||||
* If losing, let the lower level know and try for
|
||||
* a better route. Also, if we backed off this far,
|
||||
* our srtt estimate is probably bogus. Clobber it
|
||||
* so we'll take the next rtt measurement as our srtt;
|
||||
* move the current srtt into rttvar to keep the current
|
||||
* retransmit times until then.
|
||||
*/
|
||||
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
|
||||
in_losing(tp->t_inpcb);
|
||||
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
|
||||
tp->t_srtt = 0;
|
||||
}
|
||||
tp->snd_nxt = tp->snd_una;
|
||||
/*
|
||||
* Force a segment to be sent.
|
||||
*/
|
||||
tp->t_flags |= TF_ACKNOW;
|
||||
/*
|
||||
* If timing a segment in this window, stop the timer.
|
||||
*/
|
||||
tp->t_rtttime = 0;
|
||||
/*
|
||||
* Close the congestion window down to one segment
|
||||
* (we'll open it by one segment for each ack we get).
|
||||
* Since we probably have a window's worth of unacked
|
||||
* data accumulated, this "slow start" keeps us from
|
||||
* dumping all that data as back-to-back packets (which
|
||||
* might overwhelm an intermediate gateway).
|
||||
*
|
||||
* There are two phases to the opening: Initially we
|
||||
* open by one mss on each ack. This makes the window
|
||||
* size increase exponentially with time. If the
|
||||
* window is larger than the path can handle, this
|
||||
* exponential growth results in dropped packet(s)
|
||||
* almost immediately. To get more time between
|
||||
* drops but still "push" the network to take advantage
|
||||
* of improving conditions, we switch from exponential
|
||||
* to linear window opening at some threshhold size.
|
||||
* For a threshhold, we use half the current window
|
||||
* size, truncated to a multiple of the mss.
|
||||
*
|
||||
* (the minimum cwnd that will give us exponential
|
||||
* growth is 2 mss. We don't allow the threshhold
|
||||
* to go below this.)
|
||||
*/
|
||||
{
|
||||
u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
|
||||
if (win < 2)
|
||||
win = 2;
|
||||
tp->snd_cwnd = tp->t_maxseg;
|
||||
tp->snd_ssthresh = win * tp->t_maxseg;
|
||||
tp->t_dupacks = 0;
|
||||
}
|
||||
(void) tcp_output(tp);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Persistance timer into zero window.
|
||||
* Force a byte to be output, if possible.
|
||||
*/
|
||||
case TCPT_PERSIST:
|
||||
tcpstat.tcps_persisttimeo++;
|
||||
/*
|
||||
* Hack: if the peer is dead/unreachable, we do not
|
||||
* time out if the window is closed. After a full
|
||||
* backoff, drop the connection if the idle time
|
||||
* (no responses to probes) reaches the maximum
|
||||
* backoff that we would use if retransmitting.
|
||||
*/
|
||||
if (tp->t_rxtshift == TCP_MAXRXTSHIFT &&
|
||||
(tp->t_idle >= tcp_maxpersistidle ||
|
||||
tp->t_idle >= TCP_REXMTVAL(tp) * tcp_totbackoff)) {
|
||||
tcpstat.tcps_persistdrop++;
|
||||
tp = tcp_drop(tp, ETIMEDOUT);
|
||||
break;
|
||||
}
|
||||
tcp_setpersist(tp);
|
||||
tp->t_force = 1;
|
||||
(void) tcp_output(tp);
|
||||
tp->t_force = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
* Keep-alive timer went off; send something
|
||||
* or drop connection if idle for too long.
|
||||
*/
|
||||
case TCPT_KEEP:
|
||||
tcpstat.tcps_keeptimeo++;
|
||||
if (tp->t_state < TCPS_ESTABLISHED)
|
||||
goto dropit;
|
||||
if ((always_keepalive ||
|
||||
tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE) &&
|
||||
tp->t_state <= TCPS_CLOSING) {
|
||||
if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
|
||||
goto dropit;
|
||||
/*
|
||||
* Send a packet designed to force a response
|
||||
* if the peer is up and reachable:
|
||||
* either an ACK if the connection is still alive,
|
||||
* or an RST if the peer has closed the connection
|
||||
* due to timeout or reboot.
|
||||
* Using sequence number tp->snd_una-1
|
||||
* causes the transmitted zero-length segment
|
||||
* to lie outside the receive window;
|
||||
* by the protocol spec, this requires the
|
||||
* correspondent TCP to respond.
|
||||
*/
|
||||
tcpstat.tcps_keepprobe++;
|
||||
#ifdef TCP_COMPAT_42
|
||||
/*
|
||||
* The keepalive packet must have nonzero length
|
||||
* to get a 4.2 host to respond.
|
||||
*/
|
||||
tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
|
||||
tp->rcv_nxt - 1, tp->snd_una - 1, 0);
|
||||
#else
|
||||
tcp_respond(tp, tp->t_template, (struct mbuf *)NULL,
|
||||
tp->rcv_nxt, tp->snd_una - 1, 0);
|
||||
#endif
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
|
||||
} else
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
|
||||
break;
|
||||
dropit:
|
||||
tcpstat.tcps_keepdrops++;
|
||||
tp = tcp_drop(tp, ETIMEDOUT);
|
||||
break;
|
||||
}
|
||||
return (tp);
|
||||
(void) tcp_output(tp);
|
||||
|
||||
out:
|
||||
#ifdef TCPDEBUG
|
||||
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
|
||||
tcp_trace(TA_USER, ostate, tp, (struct tcpiphdr *)0,
|
||||
PRU_SLOWTIMO);
|
||||
#endif
|
||||
splx(s);
|
||||
}
|
||||
|
@ -37,17 +37,6 @@
|
||||
#ifndef _NETINET_TCP_TIMER_H_
|
||||
#define _NETINET_TCP_TIMER_H_
|
||||
|
||||
/*
|
||||
* Definitions of the TCP timers. These timers are counted
|
||||
* down PR_SLOWHZ times a second.
|
||||
*/
|
||||
#define TCPT_NTIMERS 4
|
||||
|
||||
#define TCPT_REXMT 0 /* retransmit */
|
||||
#define TCPT_PERSIST 1 /* retransmit persistence */
|
||||
#define TCPT_KEEP 2 /* keep alive */
|
||||
#define TCPT_2MSL 3 /* 2*msl quiet time timer */
|
||||
|
||||
/*
|
||||
* The TCPT_REXMT timer is used to force retransmissions.
|
||||
* The TCP has the TCPT_REXMT timer set whenever segments
|
||||
@ -87,22 +76,22 @@
|
||||
/*
|
||||
* Time constants.
|
||||
*/
|
||||
#define TCPTV_MSL ( 30*PR_SLOWHZ) /* max seg lifetime (hah!) */
|
||||
#define TCPTV_MSL ( 30*hz) /* max seg lifetime (hah!) */
|
||||
#define TCPTV_SRTTBASE 0 /* base roundtrip time;
|
||||
if 0, no idea yet */
|
||||
#define TCPTV_RTOBASE ( 3*PR_SLOWHZ) /* assumed RTO if no info */
|
||||
#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
|
||||
#define TCPTV_RTOBASE ( 3*hz) /* assumed RTO if no info */
|
||||
#define TCPTV_SRTTDFLT ( 3*hz) /* assumed RTT if no info */
|
||||
|
||||
#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
|
||||
#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
|
||||
#define TCPTV_PERSMIN ( 5*hz) /* retransmit persistence */
|
||||
#define TCPTV_PERSMAX ( 60*hz) /* maximum persist interval */
|
||||
|
||||
#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
|
||||
#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
|
||||
#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
|
||||
#define TCPTV_KEEP_INIT ( 75*hz) /* initial connect keepalive */
|
||||
#define TCPTV_KEEP_IDLE (120*60*hz) /* dflt time before probing */
|
||||
#define TCPTV_KEEPINTVL ( 75*hz) /* default probe interval */
|
||||
#define TCPTV_KEEPCNT 8 /* max probes before drop */
|
||||
|
||||
#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
|
||||
#define TCPTV_REXMTMAX ( 64*PR_SLOWHZ) /* max allowable REXMT value */
|
||||
#define TCPTV_MIN ( 1*hz) /* minimum allowable value */
|
||||
#define TCPTV_REXMTMAX ( 64*hz) /* max allowable REXMT value */
|
||||
|
||||
#define TCPTV_TWTRUNC 8 /* RTO factor to truncate TW */
|
||||
|
||||
@ -110,6 +99,8 @@
|
||||
|
||||
#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
|
||||
|
||||
#define TCPTV_DELACK (hz / PR_FASTHZ) /* 200mS timeout */
|
||||
|
||||
#ifdef TCPTIMERS
|
||||
static char *tcptimers[] =
|
||||
{ "REXMT", "PERSIST", "KEEP", "2MSL" };
|
||||
@ -118,20 +109,39 @@ static char *tcptimers[] =
|
||||
/*
|
||||
* Force a time value to be in a certain range.
|
||||
*/
|
||||
#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
|
||||
#define TCPT_RANGESET(tv, value, tvmin, tvmax) do { \
|
||||
(tv) = (value); \
|
||||
if ((u_long)(tv) < (u_long)(tvmin)) \
|
||||
(tv) = (tvmin); \
|
||||
else if ((u_long)(tv) > (u_long)(tvmax)) \
|
||||
(tv) = (tvmax); \
|
||||
}
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* Convert slow-timeout ticks to timer ticks. We don't really want to do
|
||||
* this as it is rather expensive, so this is only a transitional stage
|
||||
* until we are able to update all the code which counts timer ticks.
|
||||
*/
|
||||
#define TCPT_TICKS(stt) ((stt) * hz / PR_SLOWHZ)
|
||||
#define TCPT_SLOWHZ(tt) (((tt) * PR_SLOWHZ) / hz)
|
||||
|
||||
#ifdef KERNEL
|
||||
extern int tcp_keepinit; /* time to establish connection */
|
||||
extern int tcp_keepidle; /* time before keepalive probes begin */
|
||||
extern int tcp_keepintvl; /* time between keepalive probes */
|
||||
extern int tcp_maxidle; /* time to drop after starting probes */
|
||||
extern int tcp_delacktime; /* time before sending a delayed ACK */
|
||||
extern int tcp_maxpersistidle;
|
||||
extern int tcp_msl;
|
||||
extern int tcp_ttl; /* time to live for TCP segs */
|
||||
extern int tcp_backoff[];
|
||||
#endif
|
||||
|
||||
#endif
|
||||
void tcp_timer_2msl __P((void *xtp));
|
||||
void tcp_timer_keep __P((void *xtp));
|
||||
void tcp_timer_persist __P((void *xtp));
|
||||
void tcp_timer_rexmt __P((void *xtp));
|
||||
void tcp_timer_delack __P((void *xtp));
|
||||
|
||||
#endif /* KERNEL */
|
||||
|
||||
#endif /* !_NETINET_TCP_TIMER_H_ */
|
||||
|
@ -39,6 +39,7 @@
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/callout.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/malloc.h>
|
||||
@ -74,9 +75,11 @@ int tcp_mssdflt = TCP_MSS;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_MSSDFLT, mssdflt, CTLFLAG_RW,
|
||||
&tcp_mssdflt , 0, "Default TCP Maximum Segment Size");
|
||||
|
||||
#if 0
|
||||
static int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_RTTDFLT, rttdflt, CTLFLAG_RW,
|
||||
&tcp_rttdflt , 0, "Default maximum TCP Round Trip Time");
|
||||
#endif
|
||||
|
||||
static int tcp_do_rfc1323 = 1;
|
||||
SYSCTL_INT(_net_inet_tcp, TCPCTL_DO_RFC1323, rfc1323, CTLFLAG_RW,
|
||||
@ -122,6 +125,8 @@ struct inp_tp {
|
||||
char align[(sizeof(struct inpcb) + ALIGNM1) & ~ALIGNM1];
|
||||
} inp_tp_u;
|
||||
struct tcpcb tcb;
|
||||
struct callout inp_tp_rexmt, inp_tp_persist, inp_tp_keep, inp_tp_2msl;
|
||||
struct callout inp_tp_delack;
|
||||
};
|
||||
#undef ALIGNMENT
|
||||
#undef ALIGNM1
|
||||
@ -137,6 +142,14 @@ tcp_init()
|
||||
tcp_iss = random(); /* wrong, but better than a constant */
|
||||
tcp_ccgen = 1;
|
||||
tcp_cleartaocache();
|
||||
|
||||
tcp_delacktime = TCPTV_DELACK;
|
||||
tcp_keepinit = TCPTV_KEEP_INIT;
|
||||
tcp_keepidle = TCPTV_KEEP_IDLE;
|
||||
tcp_keepintvl = TCPTV_KEEPINTVL;
|
||||
tcp_maxpersistidle = TCPTV_KEEP_IDLE;
|
||||
tcp_msl = TCPTV_MSL;
|
||||
|
||||
LIST_INIT(&tcb);
|
||||
tcbinfo.listhead = &tcb;
|
||||
TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", TCBHASHSIZE, hashsize);
|
||||
@ -150,6 +163,7 @@ tcp_init()
|
||||
&tcbinfo.porthashmask);
|
||||
tcbinfo.ipi_zone = zinit("tcpcb", sizeof(struct inp_tp), maxsockets,
|
||||
ZONE_INTERRUPT, 0);
|
||||
|
||||
if (max_protohdr < sizeof(struct tcpiphdr))
|
||||
max_protohdr = sizeof(struct tcpiphdr);
|
||||
if (max_linkhdr + sizeof(struct tcpiphdr) > MHLEN)
|
||||
@ -304,6 +318,13 @@ tcp_newtcpcb(inp)
|
||||
tp->t_segq = NULL;
|
||||
tp->t_maxseg = tp->t_maxopd = tcp_mssdflt;
|
||||
|
||||
/* Set up our timeouts. */
|
||||
callout_init(tp->tt_rexmt = &it->inp_tp_rexmt);
|
||||
callout_init(tp->tt_persist = &it->inp_tp_persist);
|
||||
callout_init(tp->tt_keep = &it->inp_tp_keep);
|
||||
callout_init(tp->tt_2msl = &it->inp_tp_2msl);
|
||||
callout_init(tp->tt_delack = &it->inp_tp_delack);
|
||||
|
||||
if (tcp_do_rfc1323)
|
||||
tp->t_flags = (TF_REQ_SCALE|TF_REQ_TSTMP);
|
||||
if (tcp_do_rfc1644)
|
||||
@ -320,6 +341,7 @@ tcp_newtcpcb(inp)
|
||||
tp->t_rxtcur = TCPTV_RTOBASE;
|
||||
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
||||
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
|
||||
tp->t_rcvtime = ticks;
|
||||
inp->inp_ip_ttl = ip_defttl;
|
||||
inp->inp_ppcb = (caddr_t)tp;
|
||||
return (tp); /* XXX */
|
||||
@ -366,6 +388,16 @@ tcp_close(tp)
|
||||
register struct rtentry *rt;
|
||||
int dosavessthresh;
|
||||
|
||||
/*
|
||||
* Make sure that all of our timers are stopped before we
|
||||
* delete the PCB.
|
||||
*/
|
||||
callout_stop(tp->tt_rexmt);
|
||||
callout_stop(tp->tt_persist);
|
||||
callout_stop(tp->tt_keep);
|
||||
callout_stop(tp->tt_2msl);
|
||||
callout_stop(tp->tt_delack);
|
||||
|
||||
/*
|
||||
* If we got enough samples through the srtt filter,
|
||||
* save the rtt and rttvar in the routing entry.
|
||||
@ -384,7 +416,7 @@ tcp_close(tp)
|
||||
|
||||
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
|
||||
i = tp->t_srtt *
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTT_SCALE));
|
||||
if (rt->rt_rmx.rmx_rtt && i)
|
||||
/*
|
||||
* filter this update to half the old & half
|
||||
@ -400,7 +432,7 @@ tcp_close(tp)
|
||||
}
|
||||
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
|
||||
i = tp->t_rttvar *
|
||||
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
|
||||
(RTM_RTTUNIT / (hz * TCP_RTTVAR_SCALE));
|
||||
if (rt->rt_rmx.rmx_rttvar && i)
|
||||
rt->rt_rmx.rmx_rttvar =
|
||||
(rt->rt_rmx.rmx_rttvar + i) / 2;
|
||||
@ -734,7 +766,7 @@ tcp_mtudisc(inp, errno)
|
||||
tp->t_maxseg = mss;
|
||||
|
||||
tcpstat.tcps_mturesent++;
|
||||
tp->t_rtt = 0;
|
||||
tp->t_rtttime = 0;
|
||||
tp->snd_nxt = tp->snd_una;
|
||||
tcp_output(tp);
|
||||
}
|
||||
|
@ -537,7 +537,7 @@ tcp_connect(tp, nam, p)
|
||||
if (oinp) {
|
||||
if (oinp != inp && (otp = intotcpcb(oinp)) != NULL &&
|
||||
otp->t_state == TCPS_TIME_WAIT &&
|
||||
otp->t_duration < TCPTV_MSL &&
|
||||
(ticks - otp->t_starttime) < tcp_msl &&
|
||||
(otp->t_flags & TF_RCVD_CC))
|
||||
otp = tcp_close(otp);
|
||||
else
|
||||
@ -563,7 +563,7 @@ tcp_connect(tp, nam, p)
|
||||
soisconnecting(so);
|
||||
tcpstat.tcps_connattempt++;
|
||||
tp->t_state = TCPS_SYN_SENT;
|
||||
tp->t_timer[TCPT_KEEP] = tcp_keepinit;
|
||||
callout_reset(tp->tt_keep, tcp_keepinit, tcp_timer_keep, tp);
|
||||
tp->iss = tcp_iss; tcp_iss += TCP_ISSINCR/2;
|
||||
tcp_sendseqinit(tp);
|
||||
|
||||
@ -810,7 +810,8 @@ tcp_usrclosed(tp)
|
||||
soisdisconnected(tp->t_inpcb->inp_socket);
|
||||
/* To prevent the connection hanging in FIN_WAIT_2 forever. */
|
||||
if (tp->t_state == TCPS_FIN_WAIT_2)
|
||||
tp->t_timer[TCPT_2MSL] = tcp_maxidle;
|
||||
callout_reset(tp->tt_2msl, tcp_maxidle,
|
||||
tcp_timer_2msl, tp);
|
||||
}
|
||||
return (tp);
|
||||
}
|
||||
|
@ -49,7 +49,11 @@ struct tcpcb {
|
||||
int t_dupacks; /* consecutive dup acks recd */
|
||||
struct tcpiphdr *t_template; /* skeletal packet for transmit */
|
||||
|
||||
int t_timer[TCPT_NTIMERS]; /* tcp timers */
|
||||
struct callout *tt_rexmt; /* retransmit timer */
|
||||
struct callout *tt_persist; /* retransmit persistence */
|
||||
struct callout *tt_keep; /* keepalive */
|
||||
struct callout *tt_2msl; /* 2*msl TIME_WAIT timer */
|
||||
struct callout *tt_delack; /* delayed ACK timer */
|
||||
|
||||
struct inpcb *t_inpcb; /* back pointer to internet pcb */
|
||||
int t_state; /* state of this connection */
|
||||
@ -98,12 +102,12 @@ struct tcpcb {
|
||||
*/
|
||||
u_int t_maxopd; /* mss plus options */
|
||||
|
||||
u_int t_idle; /* inactivity time */
|
||||
u_long t_duration; /* connection duration */
|
||||
int t_rtt; /* round trip time */
|
||||
u_long t_rcvtime; /* inactivity time */
|
||||
u_long t_starttime; /* time connection was established */
|
||||
int t_rtttime; /* round trip time */
|
||||
tcp_seq t_rtseq; /* sequence number being timed */
|
||||
|
||||
int t_rxtcur; /* current retransmit value */
|
||||
int t_rxtcur; /* current retransmit value (ticks) */
|
||||
u_int t_maxseg; /* maximum segment size */
|
||||
int t_srtt; /* smoothed round-trip time */
|
||||
int t_rttvar; /* variance in round-trip time */
|
||||
@ -131,6 +135,10 @@ struct tcpcb {
|
||||
/* RFC 1644 variables */
|
||||
tcp_cc cc_send; /* send connection count */
|
||||
tcp_cc cc_recv; /* receive connection count */
|
||||
/* experimental */
|
||||
u_long snd_cwnd_prev; /* cwnd prior to retransmit */
|
||||
u_long snd_ssthresh_prev; /* ssthresh prior to retransmit */
|
||||
u_long t_badrxtwin; /* window for retransmit recovery */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -305,7 +313,8 @@ struct xtcpcb {
|
||||
#define TCPCTL_RECVSPACE 9 /* receive buffer space */
|
||||
#define TCPCTL_KEEPINIT 10 /* receive buffer space */
|
||||
#define TCPCTL_PCBLIST 11 /* list of all outstanding PCBs */
|
||||
#define TCPCTL_MAXID 12
|
||||
#define TCPCTL_DELACKTIME 12 /* time before sending delayed ACK */
|
||||
#define TCPCTL_MAXID 13
|
||||
|
||||
#define TCPCTL_NAMES { \
|
||||
{ 0, 0 }, \
|
||||
@ -320,6 +329,7 @@ struct xtcpcb {
|
||||
{ "recvspace", CTLTYPE_INT }, \
|
||||
{ "keepinit", CTLTYPE_INT }, \
|
||||
{ "pcblist", CTLTYPE_STRUCT }, \
|
||||
{ "delacktime", CTLTYPE_INT }, \
|
||||
}
|
||||
|
||||
|
||||
@ -332,8 +342,9 @@ extern struct inpcbhead tcb; /* head of queue of active tcpcb's */
|
||||
extern struct inpcbinfo tcbinfo;
|
||||
extern struct tcpstat tcpstat; /* tcp statistics */
|
||||
extern int tcp_mssdflt; /* XXX */
|
||||
extern u_long tcp_now; /* for RFC 1323 timestamps */
|
||||
extern int tcp_delack_enabled;
|
||||
extern int ss_fltsz;
|
||||
extern int ss_fltsz_local;
|
||||
|
||||
void tcp_canceltimers __P((struct tcpcb *));
|
||||
struct tcpcb *
|
||||
|
@ -59,8 +59,8 @@ struct callout {
|
||||
};
|
||||
|
||||
#define CALLOUT_LOCAL_ALLOC 0x0001 /* was allocated from callfree */
|
||||
#define CALLOUT_PENDING 0x0002 /* callout is currently active */
|
||||
#define CALLOUT_FIRED 0x0004 /* callout has been fired */
|
||||
#define CALLOUT_ACTIVE 0x0002 /* callout is currently active */
|
||||
#define CALLOUT_PENDING 0x0004 /* callout is waiting for timeout */
|
||||
|
||||
struct callout_handle {
|
||||
struct callout *callout;
|
||||
@ -73,10 +73,10 @@ extern int ncallout;
|
||||
extern struct callout_tailq *callwheel;
|
||||
extern int callwheelsize, callwheelbits, callwheelmask, softticks;
|
||||
|
||||
#define callout_fired(c) ((c)->c_flags & CALLOUT_FIRED)
|
||||
#define callout_active(c) ((c)->c_flags & CALLOUT_ACTIVE)
|
||||
#define callout_deactivate(c) ((c)->c_flags &= ~CALLOUT_ACTIVE)
|
||||
void callout_init __P((struct callout *));
|
||||
#define callout_pending(c) (((c)->c_flags & CALLOUT_PENDING) ? \
|
||||
((c)->c_time - ticks) : 0)
|
||||
#define callout_pending(c) ((c)->c_flags & CALLOUT_PENDING)
|
||||
void callout_reset __P((struct callout *, int, void (*)(void *), void *));
|
||||
void callout_stop __P((struct callout *));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user