diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index bbb031439fc8..c4a4746d4218 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -1165,7 +1165,7 @@ tfo_socket_result:
 		 * causes.
 		 */
 		if (thflags & TH_RST) {
-			syncache_chkrst(&inc, th);
+			syncache_chkrst(&inc, th, m);
 			goto dropunlock;
 		}
 		/*
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index 9eefd0d948d7..4480ed43c238 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -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. */
diff --git a/sys/netinet/tcp_syncache.h b/sys/netinet/tcp_syncache.h
index 0104e528e0d3..3067b7efeded 100644
--- a/sys/netinet/tcp_syncache.h
+++ b/sys/netinet/tcp_syncache.h
@@ -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);