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:
parent
d485128bd0
commit
93899d10b4
sys/netinet
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user