Fix PAWS (Protect Against Wrapped Sequence numbers) in cases when
hz >> 1000 and thus getting outside the timestamp clock frequenceny of 1ms < x < 1s per tick as mandated by RFC1323, leading to connection resets on idle connections. Always use a granularity of 1ms using getmicrouptime() making all but relevant callouts independent of hz. Use getmicrouptime(), not getmicrotime() as the latter may make a jump possibly breaking TCP nfsroot mounts having our timestamps move forward for more than 24.8 days in a second without having been idle for that long. PR: kern/61404 Reviewed by: jhb, mav, rrs Discussed with: silby, lstewart Sponsored by: Sandvine Incorporated (originally in 2011) MFC after: 6 weeks
This commit is contained in:
parent
b99a737923
commit
d8951c8a2f
@ -1493,7 +1493,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
*/
|
||||
if ((to.to_flags & TOF_TS) && (to.to_tsecr != 0)) {
|
||||
to.to_tsecr -= tp->ts_offset;
|
||||
if (TSTMP_GT(to.to_tsecr, ticks))
|
||||
if (TSTMP_GT(to.to_tsecr, tcp_ts_getticks()))
|
||||
to.to_tsecr = 0;
|
||||
}
|
||||
|
||||
@ -1518,7 +1518,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
if (to.to_flags & TOF_TS) {
|
||||
tp->t_flags |= TF_RCVD_TSTMP;
|
||||
tp->ts_recent = to.to_tsval;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent_age = tcp_ts_getticks();
|
||||
}
|
||||
if (to.to_flags & TOF_MSS)
|
||||
tcp_mss(tp, to.to_mss);
|
||||
@ -1562,7 +1562,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
*/
|
||||
if ((to.to_flags & TOF_TS) != 0 &&
|
||||
SEQ_LEQ(th->th_seq, tp->last_ack_sent)) {
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent_age = tcp_ts_getticks();
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -1600,11 +1600,13 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
*/
|
||||
if ((to.to_flags & TOF_TS) != 0 &&
|
||||
to.to_tsecr) {
|
||||
if (!tp->t_rttlow ||
|
||||
tp->t_rttlow > ticks - to.to_tsecr)
|
||||
tp->t_rttlow = ticks - to.to_tsecr;
|
||||
u_int t;
|
||||
|
||||
t = tcp_ts_getticks() - to.to_tsecr;
|
||||
if (!tp->t_rttlow || tp->t_rttlow > t)
|
||||
tp->t_rttlow = t;
|
||||
tcp_xmit_timer(tp,
|
||||
ticks - to.to_tsecr + 1);
|
||||
TCP_TS_TO_TICKS(t) + 1);
|
||||
} else if (tp->t_rtttime &&
|
||||
SEQ_GT(th->th_ack, tp->t_rtseq)) {
|
||||
if (!tp->t_rttlow ||
|
||||
@ -2070,7 +2072,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
||||
|
||||
/* Check to see if ts_recent is over 24 days old. */
|
||||
if (ticks - tp->ts_recent_age > TCP_PAWS_IDLE) {
|
||||
if (tcp_ts_getticks() - 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
|
||||
@ -2229,7 +2231,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
SEQ_LEQ(th->th_seq, tp->last_ack_sent) &&
|
||||
SEQ_LEQ(tp->last_ack_sent, th->th_seq + tlen +
|
||||
((thflags & (TH_SYN|TH_FIN)) != 0))) {
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent_age = tcp_ts_getticks();
|
||||
tp->ts_recent = to.to_tsval;
|
||||
}
|
||||
|
||||
@ -2543,11 +2545,13 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so,
|
||||
* timestamps of 0 or we could calculate a
|
||||
* huge RTT and blow up the retransmit timer.
|
||||
*/
|
||||
if ((to.to_flags & TOF_TS) != 0 &&
|
||||
to.to_tsecr) {
|
||||
if (!tp->t_rttlow || tp->t_rttlow > ticks - to.to_tsecr)
|
||||
tp->t_rttlow = ticks - to.to_tsecr;
|
||||
tcp_xmit_timer(tp, ticks - to.to_tsecr + 1);
|
||||
if ((to.to_flags & TOF_TS) != 0 && to.to_tsecr) {
|
||||
u_int t;
|
||||
|
||||
t = tcp_ts_getticks() - to.to_tsecr;
|
||||
if (!tp->t_rttlow || tp->t_rttlow > t)
|
||||
tp->t_rttlow = t;
|
||||
tcp_xmit_timer(tp, TCP_TS_TO_TICKS(t) + 1);
|
||||
} else if (tp->t_rtttime && SEQ_GT(th->th_ack, tp->t_rtseq)) {
|
||||
if (!tp->t_rttlow || tp->t_rttlow > ticks - tp->t_rtttime)
|
||||
tp->t_rttlow = ticks - tp->t_rtttime;
|
||||
|
@ -680,13 +680,13 @@ tcp_output(struct tcpcb *tp)
|
||||
/* Timestamps. */
|
||||
if ((tp->t_flags & TF_RCVD_TSTMP) ||
|
||||
((flags & TH_SYN) && (tp->t_flags & TF_REQ_TSTMP))) {
|
||||
to.to_tsval = ticks + tp->ts_offset;
|
||||
to.to_tsval = tcp_ts_getticks() + tp->ts_offset;
|
||||
to.to_tsecr = tp->ts_recent;
|
||||
to.to_flags |= TOF_TS;
|
||||
/* Set receive buffer autosizing timestamp. */
|
||||
if (tp->rfbuf_ts == 0 &&
|
||||
(so->so_rcv.sb_flags & SB_AUTOSIZE))
|
||||
tp->rfbuf_ts = ticks;
|
||||
tp->rfbuf_ts = tcp_ts_getticks();
|
||||
}
|
||||
/* Selective ACK's. */
|
||||
if (tp->t_flags & TF_SACK_PERMIT) {
|
||||
|
@ -62,7 +62,34 @@
|
||||
(tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = \
|
||||
(tp)->snd_recover = (tp)->iss
|
||||
|
||||
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * hz)
|
||||
/* timestamp wrap-around time */
|
||||
#ifdef _KERNEL
|
||||
/*
|
||||
* Clock macros for RFC 1323 timestamps.
|
||||
*/
|
||||
#define TCP_TS_TO_TICKS(_t) ((_t) * hz / 1000)
|
||||
|
||||
/* Timestamp wrap-around time, 24 days. */
|
||||
#define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * 1000)
|
||||
|
||||
/*
|
||||
* tcp_ts_getticks() in ms, should be 1ms < x < 1000ms according to RFC 1323.
|
||||
* We always use 1ms granularity independent of hz.
|
||||
*/
|
||||
static __inline u_int
|
||||
tcp_ts_getticks(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
u_long ms;
|
||||
|
||||
/*
|
||||
* getmicrouptime() should be good enough for any 1-1000ms granularity.
|
||||
* Do not use getmicrotime() here as it might break nfsroot/tcp.
|
||||
*/
|
||||
getmicrouptime(&tv);
|
||||
ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
|
||||
return (ms);
|
||||
}
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#endif /* _NETINET_TCP_SEQ_H_ */
|
||||
|
@ -819,7 +819,7 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
|
||||
if (sc->sc_flags & SCF_TIMESTAMP) {
|
||||
tp->t_flags |= TF_REQ_TSTMP|TF_RCVD_TSTMP;
|
||||
tp->ts_recent = sc->sc_tsreflect;
|
||||
tp->ts_recent_age = ticks;
|
||||
tp->ts_recent_age = tcp_ts_getticks();
|
||||
tp->ts_offset = sc->sc_tsoff;
|
||||
}
|
||||
#ifdef TCP_SIGNATURE
|
||||
@ -1226,7 +1226,7 @@ _syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
|
||||
*/
|
||||
if (to->to_flags & TOF_TS) {
|
||||
sc->sc_tsreflect = to->to_tsval;
|
||||
sc->sc_ts = ticks;
|
||||
sc->sc_ts = tcp_ts_getticks();
|
||||
sc->sc_flags |= SCF_TIMESTAMP;
|
||||
}
|
||||
if (to->to_flags & TOF_SCALE) {
|
||||
@ -1667,7 +1667,7 @@ syncookie_generate(struct syncache_head *sch, struct syncache *sc,
|
||||
data |= md5_buffer[2] << 10; /* more digest bits */
|
||||
data ^= md5_buffer[3];
|
||||
sc->sc_ts = data;
|
||||
sc->sc_tsoff = data - ticks; /* after XOR */
|
||||
sc->sc_tsoff = data - tcp_ts_getticks(); /* after XOR */
|
||||
}
|
||||
|
||||
TCPSTAT_INC(tcps_sc_sendcookie);
|
||||
@ -1752,7 +1752,7 @@ syncookie_lookup(struct in_conninfo *inc, struct syncache_head *sch,
|
||||
sc->sc_flags |= SCF_TIMESTAMP;
|
||||
sc->sc_tsreflect = to->to_tsval;
|
||||
sc->sc_ts = to->to_tsecr;
|
||||
sc->sc_tsoff = to->to_tsecr - ticks;
|
||||
sc->sc_tsoff = to->to_tsecr - tcp_ts_getticks();
|
||||
sc->sc_flags |= (data & 0x1) ? SCF_SIGNATURE : 0;
|
||||
sc->sc_flags |= ((data >> 1) & 0x1) ? SCF_SACK : 0;
|
||||
sc->sc_requested_s_scale = min((data >> 2) & 0xf,
|
||||
|
@ -558,7 +558,7 @@ tcp_twrespond(struct tcptw *tw, int flags)
|
||||
*/
|
||||
if (tw->t_recent && flags == TH_ACK) {
|
||||
to.to_flags |= TOF_TS;
|
||||
to.to_tsval = ticks + tw->ts_offset;
|
||||
to.to_tsval = tcp_ts_getticks() + tw->ts_offset;
|
||||
to.to_tsecr = tw->t_recent;
|
||||
}
|
||||
optlen = tcp_addoptions(&to, (u_char *)(th + 1));
|
||||
|
Loading…
Reference in New Issue
Block a user