Index: head/sys/netinet/tcp_input.c =================================================================== --- head/sys/netinet/tcp_input.c +++ head/sys/netinet/tcp_input.c @@ -1165,7 +1165,7 @@ * causes. */ if (thflags & TH_RST) { - syncache_chkrst(&inc, th); + syncache_chkrst(&inc, th, m); goto dropunlock; } /* Index: head/sys/netinet/tcp_syncache.h =================================================================== --- head/sys/netinet/tcp_syncache.h +++ head/sys/netinet/tcp_syncache.h @@ -46,7 +46,7 @@ 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); Index: head/sys/netinet/tcp_syncache.c =================================================================== --- head/sys/netinet/tcp_syncache.c +++ head/sys/netinet/tcp_syncache.c @@ -131,7 +131,7 @@ 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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ /* * 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 @@ } /* - * 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 @@ 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,29 +1747,31 @@ 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));