The handling of RST segments in the SYN-RCVD state exists in the

code paths. Both are not consistent and the one on the syn cache code
does not conform to the relevant specifications (Page 69 of RFC 793
and Section 4.2 of RFC 5961).

This patch fixes this:
* The sequence numbers checks are fixed as specified on
  page Page 69 RFC 793.
* The sysctl variable net.inet.tcp.insecure_rst is now honoured
  and the behaviour as specified in Section 4.2 of RFC 5961.

Approved by:		re (gjb@)
Reviewed by:		bz@, glebius@, rrs@,
Differential Revision:	https://reviews.freebsd.org/D17595
Sponsored by:		Netflix, Inc.
This commit is contained in:
Michael Tuexen 2018-10-18 19:21:18 +00:00
parent d485128bd0
commit 93899d10b4
3 changed files with 63 additions and 40 deletions

@ -1165,7 +1165,7 @@ tfo_socket_result:
* causes.
*/
if (thflags & TH_RST) {
syncache_chkrst(&inc, th);
syncache_chkrst(&inc, th, m);
goto dropunlock;
}
/*

@ -131,7 +131,7 @@ static void syncache_drop(struct syncache *, struct syncache_head *);
static void syncache_free(struct syncache *);
static void syncache_insert(struct syncache *, struct syncache_head *);
static int syncache_respond(struct syncache *, struct syncache_head *,
const struct mbuf *);
const struct mbuf *, int);
static struct socket *syncache_socket(struct syncache *, struct socket *,
struct mbuf *m);
static void syncache_timeout(struct syncache *sc, struct syncache_head *sch,
@ -489,7 +489,7 @@ syncache_timer(void *xsch)
free(s, M_TCPLOG);
}
syncache_respond(sc, sch, NULL);
syncache_respond(sc, sch, NULL, TH_SYN|TH_ACK);
TCPSTAT_INC(tcps_sc_retransmitted);
syncache_timeout(sc, sch, 0);
}
@ -537,9 +537,10 @@ syncache_lookup(struct in_conninfo *inc, struct syncache_head **schp)
* This function is called when we get a RST for a
* non-existent connection, so that we can see if the
* connection is in the syn cache. If it is, zap it.
* If required send a challenge ACK.
*/
void
syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th)
syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m)
{
struct syncache *sc;
struct syncache_head *sch;
@ -590,19 +591,36 @@ syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th)
* send a reset with the sequence number at the rightmost edge
* of our receive window, and we have to handle this case.
*/
if (SEQ_GEQ(th->th_seq, sc->sc_irs) &&
SEQ_LEQ(th->th_seq, sc->sc_irs + sc->sc_wnd)) {
syncache_drop(sc, sch);
if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
log(LOG_DEBUG, "%s; %s: Our SYN|ACK was rejected, "
"connection attempt aborted by remote endpoint\n",
s, __func__);
TCPSTAT_INC(tcps_sc_reset);
if ((SEQ_GEQ(th->th_seq, sc->sc_irs + 1) &&
SEQ_LT(th->th_seq, sc->sc_irs + 1 + sc->sc_wnd)) ||
(sc->sc_wnd == 0 && th->th_seq == sc->sc_irs + 1)) {
if (V_tcp_insecure_rst ||
th->th_seq == sc->sc_irs + 1) {
syncache_drop(sc, sch);
if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
log(LOG_DEBUG,
"%s; %s: Our SYN|ACK was rejected, "
"connection attempt aborted by remote "
"endpoint\n",
s, __func__);
TCPSTAT_INC(tcps_sc_reset);
} else {
TCPSTAT_INC(tcps_badrst);
/* Send challenge ACK. */
if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
log(LOG_DEBUG, "%s; %s: RST with invalid "
" SEQ %u != NXT %u (+WND %u), "
"sending challenge ACK\n",
s, __func__,
th->th_seq, sc->sc_irs + 1, sc->sc_wnd);
syncache_respond(sc, sch, m, TH_ACK);
}
} else {
if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
log(LOG_DEBUG, "%s; %s: RST with invalid SEQ %u != "
"IRS %u (+WND %u), segment ignored\n",
s, __func__, th->th_seq, sc->sc_irs, sc->sc_wnd);
"NXT %u (+WND %u), segment ignored\n",
s, __func__,
th->th_seq, sc->sc_irs + 1, sc->sc_wnd);
TCPSTAT_INC(tcps_badrst);
}
@ -1413,7 +1431,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
s, __func__);
free(s, M_TCPLOG);
}
if (syncache_respond(sc, sch, m) == 0) {
if (syncache_respond(sc, sch, m, TH_SYN|TH_ACK) == 0) {
sc->sc_rxmits = 0;
syncache_timeout(sc, sch, 1);
TCPSTAT_INC(tcps_sndacks);
@ -1577,7 +1595,7 @@ skip_alloc:
/*
* Do a standard 3-way handshake.
*/
if (syncache_respond(sc, sch, m) == 0) {
if (syncache_respond(sc, sch, m, TH_SYN|TH_ACK) == 0) {
if (V_tcp_syncookies && V_tcp_syncookiesonly && sc != &scs)
syncache_free(sc);
else if (sc != &scs)
@ -1618,12 +1636,12 @@ tfo_expanded:
}
/*
* Send SYN|ACK to the peer. Either in response to the peer's SYN,
* Send SYN|ACK or ACK to the peer. Either in response to a peer's segment,
* i.e. m0 != NULL, or upon 3WHS ACK timeout, i.e. m0 == NULL.
*/
static int
syncache_respond(struct syncache *sc, struct syncache_head *sch,
const struct mbuf *m0)
const struct mbuf *m0, int flags)
{
struct ip *ip = NULL;
struct mbuf *m;
@ -1709,15 +1727,18 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch,
th->th_sport = sc->sc_inc.inc_lport;
th->th_dport = sc->sc_inc.inc_fport;
th->th_seq = htonl(sc->sc_iss);
if (flags & TH_SYN)
th->th_seq = htonl(sc->sc_iss);
else
th->th_seq = htonl(sc->sc_iss + 1);
th->th_ack = htonl(sc->sc_irs + 1);
th->th_off = sizeof(struct tcphdr) >> 2;
th->th_x2 = 0;
th->th_flags = TH_SYN|TH_ACK;
th->th_flags = flags;
th->th_win = htons(sc->sc_wnd);
th->th_urp = 0;
if (sc->sc_flags & SCF_ECN) {
if ((flags & TH_SYN) && (sc->sc_flags & SCF_ECN)) {
th->th_flags |= TH_ECE;
TCPSTAT_INC(tcps_ecn_shs);
}
@ -1726,30 +1747,32 @@ syncache_respond(struct syncache *sc, struct syncache_head *sch,
if ((sc->sc_flags & SCF_NOOPT) == 0) {
to.to_flags = 0;
to.to_mss = mssopt;
to.to_flags = TOF_MSS;
if (sc->sc_flags & SCF_WINSCALE) {
to.to_wscale = sc->sc_requested_r_scale;
to.to_flags |= TOF_SCALE;
if (flags & TH_SYN) {
to.to_mss = mssopt;
to.to_flags = TOF_MSS;
if (sc->sc_flags & SCF_WINSCALE) {
to.to_wscale = sc->sc_requested_r_scale;
to.to_flags |= TOF_SCALE;
}
if (sc->sc_flags & SCF_SACK)
to.to_flags |= TOF_SACKPERM;
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (sc->sc_flags & SCF_SIGNATURE)
to.to_flags |= TOF_SIGNATURE;
#endif
if (sc->sc_tfo_cookie) {
to.to_flags |= TOF_FASTOPEN;
to.to_tfo_len = TCP_FASTOPEN_COOKIE_LEN;
to.to_tfo_cookie = sc->sc_tfo_cookie;
/* don't send cookie again when retransmitting response */
sc->sc_tfo_cookie = NULL;
}
}
if (sc->sc_flags & SCF_TIMESTAMP) {
to.to_tsval = sc->sc_tsoff + tcp_ts_getticks();
to.to_tsecr = sc->sc_tsreflect;
to.to_flags |= TOF_TS;
}
if (sc->sc_flags & SCF_SACK)
to.to_flags |= TOF_SACKPERM;
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
if (sc->sc_flags & SCF_SIGNATURE)
to.to_flags |= TOF_SIGNATURE;
#endif
if (sc->sc_tfo_cookie) {
to.to_flags |= TOF_FASTOPEN;
to.to_tfo_len = TCP_FASTOPEN_COOKIE_LEN;
to.to_tfo_cookie = sc->sc_tfo_cookie;
/* don't send cookie again when retransmitting response */
sc->sc_tfo_cookie = NULL;
}
optlen = tcp_addoptions(&to, (u_char *)(th + 1));
/* Adjust headers by option size. */

@ -46,7 +46,7 @@ int syncache_expand(struct in_conninfo *, struct tcpopt *,
int syncache_add(struct in_conninfo *, struct tcpopt *,
struct tcphdr *, struct inpcb *, struct socket **, struct mbuf *,
void *, void *);
void syncache_chkrst(struct in_conninfo *, struct tcphdr *);
void syncache_chkrst(struct in_conninfo *, struct tcphdr *, struct mbuf *);
void syncache_badack(struct in_conninfo *);
int syncache_pcblist(struct sysctl_req *req, int max_pcbs, int *pcbs_exported);