aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet/tcp_syncache.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/tcp_syncache.c')
-rw-r--r--sys/netinet/tcp_syncache.c161
1 files changed, 88 insertions, 73 deletions
diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c
index 1ee6c6e31f33..6c072e0fec38 100644
--- a/sys/netinet/tcp_syncache.c
+++ b/sys/netinet/tcp_syncache.c
@@ -122,6 +122,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 *, const struct mbuf *, int);
+static void syncache_send_challenge_ack(struct syncache *, struct mbuf *);
static struct socket *syncache_socket(struct syncache *, struct socket *,
struct mbuf *m);
static void syncache_timeout(struct syncache *sc, struct syncache_head *sch,
@@ -534,6 +535,10 @@ syncache_timer(void *xsch)
TCPSTAT_INC(tcps_sndtotal);
TCPSTAT_INC(tcps_sc_retransmitted);
} else {
+ /*
+ * Most likely we are memory constrained, so free
+ * resources.
+ */
syncache_drop(sc, sch);
TCPSTAT_INC(tcps_sc_dropped);
}
@@ -694,13 +699,7 @@ syncache_chkrst(struct in_conninfo *inc, struct tcphdr *th, struct mbuf *m,
"sending challenge ACK\n",
s, __func__,
th->th_seq, sc->sc_irs + 1, sc->sc_wnd);
- if (syncache_respond(sc, m, TH_ACK) == 0) {
- TCPSTAT_INC(tcps_sndacks);
- TCPSTAT_INC(tcps_sndtotal);
- } else {
- syncache_drop(sc, sch);
- TCPSTAT_INC(tcps_sc_dropped);
- }
+ syncache_send_challenge_ack(sc, m);
}
} else {
if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
@@ -718,23 +717,6 @@ done:
}
void
-syncache_badack(struct in_conninfo *inc, uint16_t port)
-{
- struct syncache *sc;
- struct syncache_head *sch;
-
- if (syncache_cookiesonly())
- return;
- sc = syncache_lookup(inc, &sch); /* returns locked sch */
- SCH_LOCK_ASSERT(sch);
- if ((sc != NULL) && (sc->sc_port == port)) {
- syncache_drop(sc, sch);
- TCPSTAT_INC(tcps_sc_badack);
- }
- SCH_UNLOCK(sch);
-}
-
-void
syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq, uint16_t port)
{
struct syncache *sc;
@@ -756,7 +738,7 @@ syncache_unreach(struct in_conninfo *inc, tcp_seq th_seq, uint16_t port)
goto done;
/*
- * If we've rertransmitted 3 times and this is our second error,
+ * If we've retransmitted 3 times and this is our second error,
* we remove the entry. Otherwise, we allow it to continue on.
* This prevents us from incorrectly nuking an entry during a
* spurious network outage.
@@ -963,6 +945,10 @@ syncache_socket(struct syncache *sc, struct socket *lso, struct mbuf *m)
if (sc->sc_rxmits > 1)
tp->snd_cwnd = 1;
+ /* Copy over the challenge ACK state. */
+ tp->t_challenge_ack_end = sc->sc_challenge_ack_end;
+ tp->t_challenge_ack_cnt = sc->sc_challenge_ack_cnt;
+
#ifdef TCP_OFFLOAD
/*
* Allow a TOE driver to install its hooks. Note that we hold the
@@ -1047,6 +1033,8 @@ abort:
*
* On syncache_socket() success the newly created socket
* has its underlying inp locked.
+ *
+ * *lsop is updated, if and only if 1 is returned.
*/
int
syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
@@ -1095,12 +1083,14 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
*/
SCH_UNLOCK(sch);
TCPSTAT_INC(tcps_sc_spurcookie);
- if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Spurious ACK, "
"segment rejected "
"(syncookies disabled)\n",
s, __func__);
- goto failed;
+ free(s, M_TCPLOG);
+ }
+ return (0);
}
if (sch->sch_last_overflow <
time_uptime - SYNCOOKIE_LIFETIME) {
@@ -1110,12 +1100,14 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
*/
SCH_UNLOCK(sch);
TCPSTAT_INC(tcps_sc_spurcookie);
- if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Spurious ACK, "
"segment rejected "
"(no syncache entry)\n",
s, __func__);
- goto failed;
+ free(s, M_TCPLOG);
+ }
+ return (0);
}
SCH_UNLOCK(sch);
}
@@ -1129,11 +1121,13 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
TCPSTAT_INC(tcps_sc_recvcookie);
} else {
TCPSTAT_INC(tcps_sc_failcookie);
- if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG, "%s; %s: Segment failed "
"SYNCOOKIE authentication, segment rejected "
"(probably spoofed)\n", s, __func__);
- goto failed;
+ free(s, M_TCPLOG);
+ }
+ return (0);
}
#if defined(IPSEC_SUPPORT) || defined(TCP_SIGNATURE)
/* If received ACK has MD5 signature, check it. */
@@ -1161,7 +1155,7 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
/*
* If listening socket requested TCP digests, check that
* received ACK has signature and it is correct.
- * If not, drop the ACK and leave sc entry in th cache,
+ * If not, drop the ACK and leave sc entry in the cache,
* because SYN was received with correct signature.
*/
if (sc->sc_flags & SCF_SIGNATURE) {
@@ -1202,14 +1196,14 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
*/
if (sc->sc_flags & SCF_TIMESTAMP && to->to_flags & TOF_TS &&
TSTMP_LT(to->to_tsval, sc->sc_tsreflect)) {
- SCH_UNLOCK(sch);
if ((s = tcp_log_addrs(inc, th, NULL, NULL))) {
log(LOG_DEBUG,
"%s; %s: SEG.TSval %u < TS.Recent %u, "
"segment dropped\n", s, __func__,
to->to_tsval, sc->sc_tsreflect);
- free(s, M_TCPLOG);
}
+ SCH_UNLOCK(sch);
+ free(s, M_TCPLOG);
return (-1); /* Do not send RST */
}
@@ -1226,7 +1220,6 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
"expected, segment processed normally\n",
s, __func__);
free(s, M_TCPLOG);
- s = NULL;
}
}
@@ -1258,6 +1251,38 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
return (-1); /* Do not send RST */
}
}
+
+ /*
+ * SEG.SEQ validation:
+ * The SEG.SEQ must be in the window starting at our
+ * initial receive sequence number + 1.
+ */
+ if (SEQ_LEQ(th->th_seq, sc->sc_irs) ||
+ SEQ_GT(th->th_seq, sc->sc_irs + sc->sc_wnd)) {
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ log(LOG_DEBUG, "%s; %s: SEQ %u != IRS+1 %u, "
+ "sending challenge ACK\n",
+ s, __func__, th->th_seq, sc->sc_irs + 1);
+ syncache_send_challenge_ack(sc, m);
+ SCH_UNLOCK(sch);
+ free(s, M_TCPLOG);
+ return (-1); /* Do not send RST */
+ }
+
+ /*
+ * SEG.ACK validation:
+ * SEG.ACK must match our initial send sequence number + 1.
+ */
+ if (th->th_ack != sc->sc_iss + 1) {
+ if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
+ log(LOG_DEBUG, "%s; %s: ACK %u != ISS+1 %u, "
+ "segment rejected\n",
+ s, __func__, th->th_ack, sc->sc_iss + 1);
+ SCH_UNLOCK(sch);
+ free(s, M_TCPLOG);
+ return (0); /* Do send RST, do not free sc. */
+ }
+
TAILQ_REMOVE(&sch->sch_bucket, sc, sc_hash);
sch->sch_length--;
#ifdef TCP_OFFLOAD
@@ -1270,29 +1295,6 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
SCH_UNLOCK(sch);
}
- /*
- * Segment validation:
- * ACK must match our initial sequence number + 1 (the SYN|ACK).
- */
- if (th->th_ack != sc->sc_iss + 1) {
- if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
- log(LOG_DEBUG, "%s; %s: ACK %u != ISS+1 %u, segment "
- "rejected\n", s, __func__, th->th_ack, sc->sc_iss);
- goto failed;
- }
-
- /*
- * The SEQ must fall in the window starting at the received
- * initial receive sequence number + 1 (the SYN).
- */
- if (SEQ_LEQ(th->th_seq, sc->sc_irs) ||
- SEQ_GT(th->th_seq, sc->sc_irs + sc->sc_wnd)) {
- if ((s = tcp_log_addrs(inc, th, NULL, NULL)))
- log(LOG_DEBUG, "%s; %s: SEQ %u != IRS+1 %u, segment "
- "rejected\n", s, __func__, th->th_seq, sc->sc_irs);
- goto failed;
- }
-
*lsop = syncache_socket(sc, *lsop, m);
if (__predict_false(*lsop == NULL)) {
@@ -1304,16 +1306,6 @@ syncache_expand(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
if (sc != &scs)
syncache_free(sc);
return (1);
-failed:
- if (sc != NULL) {
- TCPSTATES_DEC(TCPS_SYN_RECEIVED);
- if (sc != &scs)
- syncache_free(sc);
- }
- if (s != NULL)
- free(s, M_TCPLOG);
- *lsop = NULL;
- return (0);
}
static struct socket *
@@ -1375,6 +1367,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
struct tcpcb *tp;
struct socket *rv = NULL;
struct syncache *sc = NULL;
+ struct ucred *cred;
struct syncache_head *sch;
struct mbuf *ipopts = NULL;
u_int ltflags;
@@ -1403,6 +1396,7 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
*/
KASSERT(SOLISTENING(so), ("%s: %p not listening", __func__, so));
tp = sototcpcb(so);
+ cred = V_tcp_syncache.see_other ? NULL : crhold(so->so_cred);
#ifdef INET6
if (inc->inc_flags & INC_ISIPV6) {
@@ -1572,6 +1566,10 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
TCPSTAT_INC(tcps_sndacks);
TCPSTAT_INC(tcps_sndtotal);
} else {
+ /*
+ * Most likely we are memory constrained, so free
+ * resources.
+ */
syncache_drop(sc, sch);
TCPSTAT_INC(tcps_sc_dropped);
}
@@ -1631,16 +1629,16 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
/*
* sc_cred is only used in syncache_pcblist() to list TCP endpoints in
* TCPS_SYN_RECEIVED state when V_tcp_syncache.see_other is false.
- * Therefore, store the credentials and take a reference count only
- * when needed:
+ * Therefore, store the credentials only when needed:
* - sc is allocated from the zone and not using the on stack instance.
* - the sysctl variable net.inet.tcp.syncache.see_other is false.
* The reference count is decremented when a zone allocated sc is
* freed in syncache_free().
*/
- if (sc != &scs && !V_tcp_syncache.see_other)
- sc->sc_cred = crhold(so->so_cred);
- else
+ if (sc != &scs && !V_tcp_syncache.see_other) {
+ sc->sc_cred = cred;
+ cred = NULL;
+ } else
sc->sc_cred = NULL;
sc->sc_port = port;
sc->sc_ipopts = ipopts;
@@ -1757,6 +1755,9 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th,
TCPSTAT_INC(tcps_sndacks);
TCPSTAT_INC(tcps_sndtotal);
} else {
+ /*
+ * Most likely we are memory constrained, so free resources.
+ */
if (sc != &scs)
syncache_free(sc);
TCPSTAT_INC(tcps_sc_dropped);
@@ -1778,6 +1779,8 @@ donenoprobe:
tcp_fastopen_decrement_counter(tfo_pending);
tfo_expanded:
+ if (cred != NULL)
+ crfree(cred);
if (sc == NULL || sc == &scs) {
#ifdef MAC
mac_syncache_destroy(&maclabel);
@@ -2053,6 +2056,18 @@ syncache_respond(struct syncache *sc, const struct mbuf *m0, int flags)
return (error);
}
+static void
+syncache_send_challenge_ack(struct syncache *sc, struct mbuf *m)
+{
+ if (tcp_challenge_ack_check(&sc->sc_challenge_ack_end,
+ &sc->sc_challenge_ack_cnt)) {
+ if (syncache_respond(sc, m, TH_ACK) == 0) {
+ TCPSTAT_INC(tcps_sndacks);
+ TCPSTAT_INC(tcps_sndtotal);
+ }
+ }
+}
+
/*
* The purpose of syncookies is to handle spoofed SYN flooding DoS attacks
* that exceed the capacity of the syncache by avoiding the storage of any