#include "opt_inet6.h"
#include "opt_tcpdebug.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/protosw.h>
#include <sys/thread.h>
#include <sys/globaldata.h>
#include <sys/thread2.h>
#include <sys/msgport2.h>
#include <machine/cpu.h>
#include <net/route.h>
#include <net/netmsg2.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_pcb.h>
#ifdef INET6
#include <netinet6/in6_pcb.h>
#endif
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_timer2.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#ifdef TCPDEBUG
#include <netinet/tcp_debug.h>
#endif
#define TCP_TIMER_REXMT 0x01
#define TCP_TIMER_PERSIST 0x02
#define TCP_TIMER_KEEP 0x04
#define TCP_TIMER_2MSL 0x08
#define TCP_TIMER_DELACK 0x10
static struct tcpcb *tcp_timer_rexmt_handler(struct tcpcb *);
static struct tcpcb *tcp_timer_persist_handler(struct tcpcb *);
static struct tcpcb *tcp_timer_keep_handler(struct tcpcb *);
static struct tcpcb *tcp_timer_2msl_handler(struct tcpcb *);
static struct tcpcb *tcp_timer_delack_handler(struct tcpcb *);
static const struct tcp_timer {
uint32_t tt_task;
struct tcpcb *(*tt_handler)(struct tcpcb *);
} tcp_timer_handlers[] = {
{ TCP_TIMER_DELACK, tcp_timer_delack_handler },
{ TCP_TIMER_REXMT, tcp_timer_rexmt_handler },
{ TCP_TIMER_PERSIST, tcp_timer_persist_handler },
{ TCP_TIMER_KEEP, tcp_timer_keep_handler },
{ TCP_TIMER_2MSL, tcp_timer_2msl_handler },
{ 0, NULL }
};
static int
sysctl_msec_to_ticks(SYSCTL_HANDLER_ARGS)
{
int error, s, tt;
tt = *(int *)oidp->oid_arg1;
s = (int)((int64_t)tt * 1000 / hz);
error = sysctl_handle_int(oidp, &s, 0, req);
if (error || !req->newptr)
return (error);
tt = (int)((int64_t)s * hz / 1000);
if (tt < 1)
return (EINVAL);
*(int *)oidp->oid_arg1 = tt;
return (0);
}
int tcp_keepinit;
SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINIT, keepinit, CTLTYPE_INT|CTLFLAG_RW,
&tcp_keepinit, 0, sysctl_msec_to_ticks, "I", "Time to establish TCP connection");
int tcp_keepidle;
SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPIDLE, keepidle, CTLTYPE_INT|CTLFLAG_RW,
&tcp_keepidle, 0, sysctl_msec_to_ticks, "I", "Time before TCP keepalive probes begin");
int tcp_keepintvl;
SYSCTL_PROC(_net_inet_tcp, TCPCTL_KEEPINTVL, keepintvl, CTLTYPE_INT|CTLFLAG_RW,
&tcp_keepintvl, 0, sysctl_msec_to_ticks, "I", "Time between TCP keepalive probes");
int tcp_delacktime;
SYSCTL_PROC(_net_inet_tcp, TCPCTL_DELACKTIME, delacktime,
CTLTYPE_INT|CTLFLAG_RW, &tcp_delacktime, 0, sysctl_msec_to_ticks, "I",
"Time before a delayed ACK is sent");
int tcp_msl;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, msl, CTLTYPE_INT|CTLFLAG_RW,
&tcp_msl, 0, sysctl_msec_to_ticks, "I", "Maximum segment lifetime");
int tcp_rexmit_min;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_min, CTLTYPE_INT|CTLFLAG_RW,
&tcp_rexmit_min, 0, sysctl_msec_to_ticks, "I", "Minimum Retransmission Timeout");
int tcp_rexmit_slop;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, rexmit_slop, CTLTYPE_INT|CTLFLAG_RW,
&tcp_rexmit_slop, 0, sysctl_msec_to_ticks, "I",
"Retransmission Timer Slop");
static int always_keepalive = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, always_keepalive, CTLFLAG_RW,
&always_keepalive , 0, "Assume SO_KEEPALIVE on all TCP connections");
int tcp_keepcnt = TCPTV_KEEPCNT;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, keepcnt, CTLFLAG_RW,
&tcp_keepcnt, 0, "Maximum number of keepalive probes to be sent");
static int tcp_do_eifel_response = 1;
SYSCTL_INT(_net_inet_tcp, OID_AUTO, eifel_response, CTLFLAG_RW,
&tcp_do_eifel_response, 0, "Eifel response algorithm (RFC 4015)");
int tcp_eifel_rtoinc = 2;
SYSCTL_PROC(_net_inet_tcp, OID_AUTO, eifel_rtoinc, CTLTYPE_INT|CTLFLAG_RW,
&tcp_eifel_rtoinc, 0, sysctl_msec_to_ticks, "I",
"Eifel response RTO increment");
int tcp_maxpersistidle;
void
tcp_canceltimers(struct tcpcb *tp)
{
tcp_callout_stop(tp, tp->tt_2msl);
tcp_callout_stop(tp, tp->tt_persist);
tcp_callout_stop(tp, tp->tt_keep);
tcp_callout_stop(tp, tp->tt_rexmt);
}
static void
tcp_send_timermsg(struct tcpcb *tp, uint32_t task)
{
struct netmsg_tcp_timer *tmsg = tp->tt_msg;
KKASSERT(tmsg != NULL && tmsg->tt_cpuid == mycpuid &&
tmsg->tt_tcb != NULL);
tmsg->tt_tasks |= task;
if (tmsg->tt_msg.lmsg.ms_flags & MSGF_DONE)
lwkt_sendmsg_oncpu(tmsg->tt_msgport, &tmsg->tt_msg.lmsg);
}
int tcp_syn_backoff[TCP_MAXRXTSHIFT + 1] =
{ 1, 1, 1, 1, 1, 2, 4, 8, 16, 32, 64, 64, 64 };
int tcp_syn_backoff_low[TCP_MAXRXTSHIFT + 1] =
{ 1, 1, 2, 4, 8, 8, 16, 16, 32, 64, 64, 64, 64 };
int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
static int tcp_totbackoff = 511;
static struct tcpcb *
tcp_timer_delack_handler(struct tcpcb *tp)
{
tp->t_flags |= TF_ACKNOW;
tcpstat.tcps_delack++;
tcp_output(tp);
return tp;
}
void
tcp_timer_delack(void *xtp)
{
struct tcpcb *tp = xtp;
struct callout *co = &tp->tt_delack->tc_callout;
crit_enter();
if (callout_pending(co) || !callout_active(co)) {
crit_exit();
return;
}
callout_deactivate(co);
tcp_send_timermsg(tp, TCP_TIMER_DELACK);
crit_exit();
}
static struct tcpcb *
tcp_timer_2msl_handler(struct tcpcb *tp)
{
#ifdef TCPDEBUG
int ostate;
#endif
#ifdef TCPDEBUG
ostate = tp->t_state;
#endif
if (tp->t_state != TCPS_TIME_WAIT &&
(ticks - tp->t_rcvtime) <= tp->t_maxidle) {
tcp_callout_reset(tp, tp->tt_2msl, tp->t_keepintvl,
tcp_timer_2msl);
} else {
tp = tcp_close(tp);
}
#ifdef TCPDEBUG
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO);
#endif
return tp;
}
void
tcp_timer_2msl(void *xtp)
{
struct tcpcb *tp = xtp;
struct callout *co = &tp->tt_2msl->tc_callout;
crit_enter();
if (callout_pending(co) || !callout_active(co)) {
crit_exit();
return;
}
callout_deactivate(co);
tcp_send_timermsg(tp, TCP_TIMER_2MSL);
crit_exit();
}
static struct tcpcb *
tcp_timer_keep_handler(struct tcpcb *tp)
{
struct tcptemp *t_template;
#ifdef TCPDEBUG
int ostate = tp->t_state;
#endif
tcpstat.tcps_keeptimeo++;
if (tp->t_state < TCPS_ESTABLISHED)
goto dropit;
if ((always_keepalive || (tp->t_flags & TF_KEEPALIVE) ||
(tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE)) &&
tp->t_state <= TCPS_CLOSING) {
if ((ticks - tp->t_rcvtime) >= tp->t_keepidle + tp->t_maxidle)
goto dropit;
tcpstat.tcps_keepprobe++;
t_template = tcp_maketemplate(tp);
if (t_template) {
tcp_respond(tp, t_template->tt_ipgen,
&t_template->tt_t, NULL,
tp->rcv_nxt, tp->snd_una - 1, 0);
tcp_freetemplate(t_template);
}
tcp_callout_reset(tp, tp->tt_keep, tp->t_keepintvl,
tcp_timer_keep);
} else {
tcp_callout_reset(tp, tp->tt_keep, tp->t_keepidle,
tcp_timer_keep);
}
#ifdef TCPDEBUG
if (tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO);
#endif
return tp;
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, NULL, NULL, PRU_SLOWTIMO);
#endif
return tp;
}
void
tcp_timer_keep(void *xtp)
{
struct tcpcb *tp = xtp;
struct callout *co = &tp->tt_keep->tc_callout;
crit_enter();
if (callout_pending(co) || !callout_active(co)) {
crit_exit();
return;
}
callout_deactivate(co);
tcp_send_timermsg(tp, TCP_TIMER_KEEP);
crit_exit();
}
static struct tcpcb *
tcp_timer_persist_handler(struct tcpcb *tp)
{
#ifdef TCPDEBUG
int ostate;
#endif
#ifdef TCPDEBUG
ostate = tp->t_state;
#endif
tcpstat.tcps_persisttimeo++;
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_flags |= TF_FORCE;
tcp_output(tp);
tp->t_flags &= ~TF_FORCE;
out:
#ifdef TCPDEBUG
if (tp && tp->t_inpcb->inp_socket->so_options & SO_DEBUG)
tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO);
#endif
return tp;
}
void
tcp_timer_persist(void *xtp)
{
struct tcpcb *tp = xtp;
struct callout *co = &tp->tt_persist->tc_callout;
crit_enter();
if (callout_pending(co) || !callout_active(co)){
crit_exit();
return;
}
callout_deactivate(co);
tcp_send_timermsg(tp, TCP_TIMER_PERSIST);
crit_exit();
}
void
tcp_save_congestion_state(struct tcpcb *tp)
{
tp->snd_cwnd_prev = tp->snd_cwnd;
tp->snd_wacked_prev = tp->snd_wacked;
tp->snd_ssthresh_prev = tp->snd_ssthresh;
tp->snd_recover_prev = tp->snd_recover;
tp->snd_max_prev = tp->snd_max;
if (IN_FASTRECOVERY(tp))
tp->rxt_flags |= TRXT_F_WASFRECOVERY;
else
tp->rxt_flags &= ~TRXT_F_WASFRECOVERY;
if (tp->t_flags & TF_RCVD_TSTMP) {
tp->t_rexmtTS = ticks;
tp->rxt_flags |= TRXT_F_FIRSTACCACK;
}
#ifdef later
tcp_sack_save_scoreboard(&tp->scb);
#endif
}
void
tcp_revert_congestion_state(struct tcpcb *tp)
{
tp->snd_cwnd = tp->snd_cwnd_prev;
tp->snd_wacked = tp->snd_wacked_prev;
tp->snd_ssthresh = tp->snd_ssthresh_prev;
tp->snd_recover = tp->snd_recover_prev;
if (tp->rxt_flags & TRXT_F_WASFRECOVERY)
ENTER_FASTRECOVERY(tp);
if (tp->rxt_flags & TRXT_F_FASTREXMT) {
++tcpstat.tcps_sndfastrexmitbad;
if (tp->rxt_flags & TRXT_F_EARLYREXMT)
++tcpstat.tcps_sndearlyrexmitbad;
} else {
++tcpstat.tcps_sndrtobad;
tp->snd_last = ticks;
if (tcp_do_eifel_response)
tp->rxt_flags |= TRXT_F_REBASERTO;
}
tp->t_badrxtwin = 0;
tp->t_rxtshift = 0;
tp->snd_nxt = tp->snd_max;
#ifdef later
tcp_sack_revert_scoreboard(&tp->scb, tp->snd_una);
#endif
}
static struct tcpcb *
tcp_timer_rexmt_handler(struct tcpcb *tp)
{
int rexmt;
#ifdef TCPDEBUG
int ostate;
#endif
#ifdef TCPDEBUG
ostate = tp->t_state;
#endif
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) {
tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1));
tp->t_rxtcur_prev = tp->t_rxtcur;
tp->t_srtt_prev = tp->t_srtt +
(tcp_eifel_rtoinc << TCP_RTT_SHIFT);
tp->t_rttvar_prev = tp->t_rttvar;
tcp_save_congestion_state(tp);
tp->rxt_flags &= ~(TRXT_F_FASTREXMT | TRXT_F_EARLYREXMT |
TRXT_F_REBASERTO);
}
if (tp->t_state == TCPS_SYN_SENT || tp->t_state == TCPS_SYN_RECEIVED) {
tp->t_rxtsyn += tp->t_rxtcur;
}
tcp_sack_discard(tp);
tcpstat.tcps_rexmttimeo++;
if (tp->t_state == TCPS_SYN_SENT) {
if (tcp_low_rtobase) {
rexmt = TCP_REXMTVAL(tp) *
tcp_syn_backoff_low[tp->t_rxtshift];
} else {
rexmt = TCP_REXMTVAL(tp) *
tcp_syn_backoff[tp->t_rxtshift];
}
} else {
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
}
TCPT_RANGESET(tp->t_rxtcur, rexmt,
tp->t_rttmin, TCPTV_REXMTMAX);
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
#ifdef INET6
if (INP_ISIPV6(tp->t_inpcb))
in6_losing(tp->t_inpcb);
else
#endif
in_losing(tp->t_inpcb);
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
tp->t_srtt = 0;
}
tp->snd_nxt = tp->snd_una;
tp->snd_recover = tp->snd_max;
tp->t_flags |= TF_ACKNOW;
tp->t_rtttime = 0;
{
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_wacked = 0;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->t_dupacks = 0;
}
EXIT_FASTRECOVERY(tp);
tcp_output(tp);
out:
#ifdef TCPDEBUG
if (tp && (tp->t_inpcb->inp_socket->so_options & SO_DEBUG))
tcp_trace(TA_USER, ostate, tp, NULL, NULL, PRU_SLOWTIMO);
#endif
return tp;
}
void
tcp_timer_rexmt(void *xtp)
{
struct tcpcb *tp = xtp;
struct callout *co = &tp->tt_rexmt->tc_callout;
crit_enter();
if (callout_pending(co) || !callout_active(co)) {
crit_exit();
return;
}
callout_deactivate(co);
tcp_send_timermsg(tp, TCP_TIMER_REXMT);
crit_exit();
}
static void
tcp_timer_handler(netmsg_t msg)
{
struct netmsg_tcp_timer *tmsg = (struct netmsg_tcp_timer *)msg;
const struct tcp_timer *tt;
struct tcpcb *tp;
crit_enter();
KKASSERT(tmsg->tt_cpuid == mycpuid && tmsg->tt_tcb != NULL);
tp = tmsg->tt_tcb;
tmsg->tt_running_tasks = tmsg->tt_tasks;
tmsg->tt_prev_tasks = tmsg->tt_tasks;
tmsg->tt_tasks = 0;
lwkt_replymsg(&tmsg->tt_msg.lmsg, 0);
if (tmsg->tt_running_tasks == 0) {
crit_exit();
return;
}
for (tt = tcp_timer_handlers; tt->tt_handler != NULL; ++tt) {
if ((tmsg->tt_running_tasks & tt->tt_task) == 0)
continue;
tmsg->tt_running_tasks &= ~tt->tt_task;
tp = tt->tt_handler(tp);
if (tp == NULL)
break;
if (tmsg->tt_running_tasks == 0)
break;
}
crit_exit();
}
void
tcp_create_timermsg(struct tcpcb *tp, struct lwkt_port *msgport)
{
struct netmsg_tcp_timer *tmsg = tp->tt_msg;
netmsg_init(&tmsg->tt_msg, NULL, &netisr_adone_rport,
MSGF_DROPABLE | MSGF_PRIORITY, tcp_timer_handler);
tmsg->tt_cpuid = mycpuid;
tmsg->tt_msgport = msgport;
tmsg->tt_tcb = tp;
tmsg->tt_tasks = 0;
}
void
tcp_destroy_timermsg(struct tcpcb *tp)
{
struct netmsg_tcp_timer *tmsg = tp->tt_msg;
if (tmsg == NULL ||
tmsg->tt_tcb == NULL)
return;
KKASSERT(tmsg->tt_cpuid == mycpuid);
crit_enter();
if ((tmsg->tt_msg.lmsg.ms_flags & MSGF_DONE) == 0) {
lwkt_dropmsg(&tmsg->tt_msg.lmsg);
}
crit_exit();
}
static __inline void
tcp_callout_init(struct tcp_callout *tc, uint32_t task)
{
callout_init_mp(&tc->tc_callout);
tc->tc_task = task;
}
void
tcp_inittimers(struct tcpcb *tp)
{
tcp_callout_init(tp->tt_rexmt, TCP_TIMER_REXMT);
tcp_callout_init(tp->tt_persist, TCP_TIMER_PERSIST);
tcp_callout_init(tp->tt_keep, TCP_TIMER_KEEP);
tcp_callout_init(tp->tt_2msl, TCP_TIMER_2MSL);
tcp_callout_init(tp->tt_delack, TCP_TIMER_DELACK);
}