Index: sys/netinet/tcp_timer.h =================================================================== --- sys/netinet/tcp_timer.h +++ sys/netinet/tcp_timer.h @@ -86,7 +86,7 @@ #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_MAXUNACKTIME 0 /* max time without making progress */ #define TCPTV_FINWAIT2_TIMEOUT (60*hz) /* FIN_WAIT_2 timeout if no receiver */ /* @@ -183,6 +183,9 @@ #define TP_KEEPINTVL(tp) ((tp)->t_keepintvl ? (tp)->t_keepintvl : tcp_keepintvl) #define TP_KEEPCNT(tp) ((tp)->t_keepcnt ? (tp)->t_keepcnt : tcp_keepcnt) #define TP_MAXIDLE(tp) (TP_KEEPCNT(tp) * TP_KEEPINTVL(tp)) +#define TP_MAXUNACKTIME(tp) \ + ((tp)->t_maxunacktime ? (tp)->t_maxunacktime : tcp_maxunacktime) + extern int tcp_persmin; /* minimum persist interval */ extern int tcp_persmax; /* maximum persist interval */ @@ -191,6 +194,7 @@ extern int tcp_keepintvl; /* time between keepalive probes */ extern int tcp_keepcnt; /* number of keepalives */ extern int tcp_delacktime; /* time before sending a delayed ACK */ +extern int tcp_maxunacktime; /* max time without making progress */ extern int tcp_maxpersistidle; extern int tcp_rexmit_initial; extern int tcp_rexmit_min; Index: sys/netinet/tcp_timer.c =================================================================== --- sys/netinet/tcp_timer.c +++ sys/netinet/tcp_timer.c @@ -167,6 +167,12 @@ &tcp_rexmit_drop_options, 0, "Drop TCP options from 3rd and later retransmitted SYN"); +int tcp_maxunacktime = TCPTV_MAXUNACKTIME; +SYSCTL_PROC(_net_inet_tcp, OID_AUTO, maxunacktime, + CTLTYPE_INT|CTLFLAG_RW | CTLFLAG_NEEDGIANT, + &tcp_maxunacktime, 0, sysctl_msec_to_ticks, "I", + "Maximum time (in ms) that a session can linger without making progress"); + VNET_DEFINE(int, tcp_pmtud_blackhole_detect); SYSCTL_INT(_net_inet_tcp, OID_AUTO, pmtud_blackhole_detection, CTLFLAG_RW|CTLFLAG_VNET, @@ -503,12 +509,38 @@ CURVNET_RESTORE(); } +/* + * Has this session exceeded the maximum time without seeing a substantive + * acknowledgement? If so, return true; otherwise false. + */ +static bool +tcp_maxunacktime_check(struct tcpcb *tp) +{ + + /* Are we tracking this timer for this session? */ + if (TP_MAXUNACKTIME(tp) == 0) + return false; + + /* Do we have a current measurement. */ + if (tp->t_acktime == 0) + return false; + + /* Are we within the acceptable range? */ + if (TSTMP_GT(TP_MAXUNACKTIME(tp) + tp->t_acktime, (u_int)ticks)) + return false; + + /* We exceeded the timer. */ + TCPSTAT_INC(tcps_progdrops); + return true; +} + void tcp_timer_persist(void *xtp) { struct tcpcb *tp = xtp; struct inpcb *inp; struct epoch_tracker et; + bool progdrop; int outrv; CURVNET_SET(tp->t_vnet); #ifdef TCPDEBUG @@ -545,10 +577,12 @@ * (no responses to probes) reaches the maximum * backoff that we would use if retransmitting. */ - if (tp->t_rxtshift == TCP_MAXRXTSHIFT && + progdrop = tcp_maxunacktime_check(tp); + if (progdrop || (tp->t_rxtshift == TCP_MAXRXTSHIFT && (ticks - tp->t_rcvtime >= tcp_maxpersistidle || - ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff)) { - TCPSTAT_INC(tcps_persistdrop); + ticks - tp->t_rcvtime >= TCP_REXMTVAL(tp) * tcp_totbackoff))) { + if (!progdrop) + TCPSTAT_INC(tcps_persistdrop); NET_EPOCH_ENTER(et); tp = tcp_drop(tp, ETIMEDOUT); NET_EPOCH_EXIT(et); @@ -626,8 +660,12 @@ * Retransmission timer went off. Message has not * been acked within retransmit interval. Back off * to a longer retransmit interval and retransmit one segment. + * + * If we've either exceeded the maximum number of retransmissions, + * or we've gone long enough without making progress, then drop + * the session. */ - if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { + if (++tp->t_rxtshift > TCP_MAXRXTSHIFT || tcp_maxunacktime_check(tp)) { tp->t_rxtshift = TCP_MAXRXTSHIFT; TCPSTAT_INC(tcps_timeoutdrop); NET_EPOCH_ENTER(et); Index: sys/netinet/tcp_usrreq.c =================================================================== --- sys/netinet/tcp_usrreq.c +++ sys/netinet/tcp_usrreq.c @@ -2375,7 +2375,7 @@ error = ktls_enable_rx(so, &tls); break; #endif - + case TCP_MAXUNACKTIME: case TCP_KEEPIDLE: case TCP_KEEPINTVL: case TCP_KEEPINIT: @@ -2392,6 +2392,10 @@ INP_WLOCK_RECHECK(inp); switch (sopt->sopt_name) { + case TCP_MAXUNACKTIME: + tp->t_maxunacktime = ui; + break; + case TCP_KEEPIDLE: tp->t_keepidle = ui; /* @@ -2658,11 +2662,15 @@ INP_WUNLOCK(inp); error = sooptcopyout(sopt, buf, len + 1); break; + case TCP_MAXUNACKTIME: case TCP_KEEPIDLE: case TCP_KEEPINTVL: case TCP_KEEPINIT: case TCP_KEEPCNT: switch (sopt->sopt_name) { + case TCP_MAXUNACKTIME: + ui = TP_MAXUNACKTIME(tp) / hz; + break; case TCP_KEEPIDLE: ui = TP_KEEPIDLE(tp) / hz; break; Index: sys/netinet/tcp_var.h =================================================================== --- sys/netinet/tcp_var.h +++ sys/netinet/tcp_var.h @@ -707,6 +707,7 @@ uint64_t tcps_keeptimeo; /* keepalive timeouts */ uint64_t tcps_keepprobe; /* keepalive probes sent */ uint64_t tcps_keepdrops; /* connections dropped in keepalive */ + uint64_t tcps_progdrops; /* drops due to no progress */ uint64_t tcps_sndtotal; /* total packets sent */ uint64_t tcps_sndpack; /* data packets sent */