Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet/tcp_syncache.c
Show First 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | static uint32_t syncookie_mac(struct in_conninfo *, tcp_seq, uint8_t, | ||||
uint8_t *, uintptr_t); | uint8_t *, uintptr_t); | ||||
static tcp_seq syncookie_generate(struct syncache_head *, struct syncache *); | static tcp_seq syncookie_generate(struct syncache_head *, struct syncache *); | ||||
static struct syncache | static struct syncache | ||||
*syncookie_lookup(struct in_conninfo *, struct syncache_head *, | *syncookie_lookup(struct in_conninfo *, struct syncache_head *, | ||||
struct syncache *, struct tcphdr *, struct tcpopt *, | struct syncache *, struct tcphdr *, struct tcpopt *, | ||||
struct socket *); | struct socket *); | ||||
static void syncookie_reseed(void *); | static void syncookie_reseed(void *); | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
static int syncookie_cmp(struct in_conninfo *inc, struct syncache_head *sch, | static int syncookie_cmp(struct in_conninfo *, struct syncache_head *, | ||||
struct syncache *sc, struct tcphdr *th, struct tcpopt *to, | struct syncache *, struct tcphdr *, struct tcpopt *, | ||||
struct socket *lso); | struct socket *); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Transmit the SYN,ACK fewer times than TCP_MAXRXTSHIFT specifies. | * Transmit the SYN,ACK fewer times than TCP_MAXRXTSHIFT specifies. | ||||
* 3 retransmits corresponds to a timeout of 3 * (1 + 2 + 4 + 8) == 45 seconds, | * 3 retransmits corresponds to a timeout of 3 * (1 + 2 + 4 + 8) == 45 seconds, | ||||
* the odds are that the user has given up attempting to connect by then. | * the odds are that the user has given up attempting to connect by then. | ||||
*/ | */ | ||||
#define SYNCACHE_MAXREXMTS 3 | #define SYNCACHE_MAXREXMTS 3 | ||||
▲ Show 20 Lines • Show All 995 Lines • ▼ Show 20 Lines | if (sc != NULL && sc != &scs) | ||||
syncache_free(sc); | syncache_free(sc); | ||||
if (s != NULL) | if (s != NULL) | ||||
free(s, M_TCPLOG); | free(s, M_TCPLOG); | ||||
*lsop = NULL; | *lsop = NULL; | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef TCP_RFC7413 | #ifdef TCP_RFC7413 | ||||
static void | static struct socket * | ||||
syncache_tfo_expand(struct syncache *sc, struct socket **lsop, struct mbuf *m, | syncache_tfo_expand(struct syncache *sc, struct socket *lso, struct mbuf *m, | ||||
uint64_t response_cookie) | uint64_t response_cookie) | ||||
{ | { | ||||
struct socket *so; | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
unsigned int *pending_counter; | unsigned int *pending_counter; | ||||
/* | /* | ||||
* Global TCP locks are held because we manipulate the PCB lists | * Global TCP locks are held because we manipulate the PCB lists | ||||
* and create a new socket. | * and create a new socket. | ||||
*/ | */ | ||||
INP_INFO_RLOCK_ASSERT(&V_tcbinfo); | INP_INFO_RLOCK_ASSERT(&V_tcbinfo); | ||||
pending_counter = intotcpcb(sotoinpcb(*lsop))->t_tfo_pending; | pending_counter = intotcpcb(sotoinpcb(*lsop))->t_tfo_pending; | ||||
*lsop = syncache_socket(sc, *lsop, m); | so = syncache_socket(sc, lso, m); | ||||
if (*lsop == NULL) { | if (so == NULL) { | ||||
TCPSTAT_INC(tcps_sc_aborted); | TCPSTAT_INC(tcps_sc_aborted); | ||||
atomic_subtract_int(pending_counter, 1); | atomic_subtract_int(pending_counter, 1); | ||||
} else { | } else { | ||||
inp = sotoinpcb(*lsop); | inp = sotoinpcb(so); | ||||
tp = intotcpcb(inp); | tp = intotcpcb(inp); | ||||
tp->t_flags |= TF_FASTOPEN; | tp->t_flags |= TF_FASTOPEN; | ||||
tp->t_tfo_cookie = response_cookie; | tp->t_tfo_cookie = response_cookie; | ||||
tp->snd_max = tp->iss; | tp->snd_max = tp->iss; | ||||
tp->snd_nxt = tp->iss; | tp->snd_nxt = tp->iss; | ||||
tp->t_tfo_pending = pending_counter; | tp->t_tfo_pending = pending_counter; | ||||
TCPSTAT_INC(tcps_sc_completed); | TCPSTAT_INC(tcps_sc_completed); | ||||
} | } | ||||
return (so); | |||||
} | } | ||||
#endif /* TCP_RFC7413 */ | #endif /* TCP_RFC7413 */ | ||||
/* | /* | ||||
* Given a LISTEN socket and an inbound SYN request, add | * Given a LISTEN socket and an inbound SYN request, add | ||||
* this to the syn cache, and send back a segment: | * this to the syn cache, and send back a segment: | ||||
* <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> | * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> | ||||
* to the source. | * to the source. | ||||
* | * | ||||
* IMPORTANT NOTE: We do _NOT_ ACK data that might accompany the SYN. | * IMPORTANT NOTE: We do _NOT_ ACK data that might accompany the SYN. | ||||
* Doing so would require that we hold onto the data and deliver it | * Doing so would require that we hold onto the data and deliver it | ||||
* to the application. However, if we are the target of a SYN-flood | * to the application. However, if we are the target of a SYN-flood | ||||
* DoS attack, an attacker could send data which would eventually | * DoS attack, an attacker could send data which would eventually | ||||
* consume all available buffer space if it were ACKed. By not ACKing | * consume all available buffer space if it were ACKed. By not ACKing | ||||
* the data, we avoid this DoS scenario. | * the data, we avoid this DoS scenario. | ||||
* | * | ||||
* The exception to the above is when a SYN with a valid TCP Fast Open (TFO) | * The exception to the above is when a SYN with a valid TCP Fast Open (TFO) | ||||
* cookie is processed and a new socket is created. In this case, any data | * cookie is processed and a new socket is created. In this case, any data | ||||
* accompanying the SYN will be queued to the socket by tcp_input() and will | * accompanying the SYN will be queued to the socket by tcp_input() and will | ||||
* be ACKed either when the application sends response data or the delayed | * be ACKed either when the application sends response data or the delayed | ||||
* ACK timer expires, whichever comes first. | * ACK timer expires, whichever comes first. | ||||
*/ | */ | ||||
int | int | ||||
syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, | ||||
struct inpcb *inp, struct socket **lsop, struct mbuf *m, void *tod, | struct inpcb *inp, struct socket *so, struct mbuf *m, void *tod, | ||||
void *todctx) | void *todctx) | ||||
{ | { | ||||
struct tcpcb *tp; | struct tcpcb *tp; | ||||
struct socket *so; | |||||
struct syncache *sc = NULL; | struct syncache *sc = NULL; | ||||
struct syncache_head *sch; | struct syncache_head *sch; | ||||
struct mbuf *ipopts = NULL; | struct mbuf *ipopts = NULL; | ||||
u_int ltflags; | u_int ltflags; | ||||
int win, ip_ttl, ip_tos; | int win, ip_ttl, ip_tos; | ||||
char *s; | char *s; | ||||
int rv = 0; | int rv = 0; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
Show All 14 Lines | #endif | ||||
INP_WLOCK_ASSERT(inp); /* listen socket */ | INP_WLOCK_ASSERT(inp); /* listen socket */ | ||||
KASSERT((th->th_flags & (TH_RST|TH_ACK|TH_SYN)) == TH_SYN, | KASSERT((th->th_flags & (TH_RST|TH_ACK|TH_SYN)) == TH_SYN, | ||||
("%s: unexpected tcp flags", __func__)); | ("%s: unexpected tcp flags", __func__)); | ||||
/* | /* | ||||
* Combine all so/tp operations very early to drop the INP lock as | * Combine all so/tp operations very early to drop the INP lock as | ||||
* soon as possible. | * soon as possible. | ||||
*/ | */ | ||||
so = *lsop; | KASSERT(SOLISTENING(so), ("%s: %p not listening", __func__, so)); | ||||
tp = sototcpcb(so); | tp = sototcpcb(so); | ||||
cred = crhold(so->so_cred); | cred = crhold(so->so_cred); | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if ((inc->inc_flags & INC_ISIPV6) && | if ((inc->inc_flags & INC_ISIPV6) && | ||||
(inp->inp_flags & IN6P_AUTOFLOWLABEL)) | (inp->inp_flags & IN6P_AUTOFLOWLABEL)) | ||||
autoflowlabel = 1; | autoflowlabel = 1; | ||||
#endif | #endif | ||||
ip_ttl = inp->inp_ip_ttl; | ip_ttl = inp->inp_ip_ttl; | ||||
ip_tos = inp->inp_ip_tos; | ip_tos = inp->inp_ip_tos; | ||||
win = sbspace(&so->so_rcv); | win = so->sol_sbrcv_hiwat; | ||||
ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE)); | ltflags = (tp->t_flags & (TF_NOOPT | TF_SIGNATURE)); | ||||
#ifdef TCP_RFC7413 | #ifdef TCP_RFC7413 | ||||
if (V_tcp_fastopen_enabled && IS_FASTOPEN(tp->t_flags) && | if (V_tcp_fastopen_enabled && IS_FASTOPEN(tp->t_flags) && | ||||
(tp->t_tfo_pending != NULL) && (to->to_flags & TOF_FASTOPEN)) { | (tp->t_tfo_pending != NULL) && (to->to_flags & TOF_FASTOPEN)) { | ||||
/* | /* | ||||
* Limit the number of pending TFO connections to | * Limit the number of pending TFO connections to | ||||
* approximately half of the queue limit. This prevents TFO | * approximately half of the queue limit. This prevents TFO | ||||
* SYN floods from starving the service by filling the | * SYN floods from starving the service by filling the | ||||
* listen queue with bogus TFO connections. | * listen queue with bogus TFO connections. | ||||
*/ | */ | ||||
if (atomic_fetchadd_int(tp->t_tfo_pending, 1) <= | if (atomic_fetchadd_int(tp->t_tfo_pending, 1) <= | ||||
(so->so_qlimit / 2)) { | (so->sol_qlimit / 2)) { | ||||
int result; | int result; | ||||
result = tcp_fastopen_check_cookie(inc, | result = tcp_fastopen_check_cookie(inc, | ||||
to->to_tfo_cookie, to->to_tfo_len, | to->to_tfo_cookie, to->to_tfo_len, | ||||
&tfo_response_cookie); | &tfo_response_cookie); | ||||
tfo_cookie_valid = (result > 0); | tfo_cookie_valid = (result > 0); | ||||
tfo_response_cookie_valid = (result >= 0); | tfo_response_cookie_valid = (result >= 0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 277 Lines • ▼ Show 20 Lines | if (syncache_respond(sc, sch, 0, m) == 0) { | ||||
TCPSTAT_INC(tcps_sndtotal); | TCPSTAT_INC(tcps_sndtotal); | ||||
} else { | } else { | ||||
if (sc != &scs) | if (sc != &scs) | ||||
syncache_free(sc); | syncache_free(sc); | ||||
TCPSTAT_INC(tcps_sc_dropped); | TCPSTAT_INC(tcps_sc_dropped); | ||||
} | } | ||||
done: | done: | ||||
if (m) { | |||||
*lsop = NULL; | |||||
m_freem(m); | m_freem(m); | ||||
} | |||||
#ifdef TCP_RFC7413 | #ifdef TCP_RFC7413 | ||||
/* | /* | ||||
* If tfo_pending is not NULL here, then a TFO SYN that did not | * If tfo_pending is not NULL here, then a TFO SYN that did not | ||||
* result in a new socket was processed and the associated pending | * result in a new socket was processed and the associated pending | ||||
* counter has not yet been decremented. All such TFO processing paths | * counter has not yet been decremented. All such TFO processing paths | ||||
* transit this point. | * transit this point. | ||||
*/ | */ | ||||
if (tfo_pending != NULL) | if (tfo_pending != NULL) | ||||
▲ Show 20 Lines • Show All 512 Lines • ▼ Show 20 Lines | #endif | ||||
/* Only use wscale if it was enabled in the orignal SYN. */ | /* Only use wscale if it was enabled in the orignal SYN. */ | ||||
if (cookie.flags.wscale_idx > 0) { | if (cookie.flags.wscale_idx > 0) { | ||||
sc->sc_requested_r_scale = wscale; | sc->sc_requested_r_scale = wscale; | ||||
sc->sc_requested_s_scale = tcp_sc_wstab[cookie.flags.wscale_idx]; | sc->sc_requested_s_scale = tcp_sc_wstab[cookie.flags.wscale_idx]; | ||||
sc->sc_flags |= SCF_WINSCALE; | sc->sc_flags |= SCF_WINSCALE; | ||||
} | } | ||||
wnd = sbspace(&lso->so_rcv); | wnd = lso->sol_sbrcv_hiwat; | ||||
wnd = imax(wnd, 0); | wnd = imax(wnd, 0); | ||||
wnd = imin(wnd, TCP_MAXWIN); | wnd = imin(wnd, TCP_MAXWIN); | ||||
sc->sc_wnd = wnd; | sc->sc_wnd = wnd; | ||||
if (cookie.flags.sack_ok) | if (cookie.flags.sack_ok) | ||||
sc->sc_flags |= SCF_SACK; | sc->sc_flags |= SCF_SACK; | ||||
if (to->to_flags & TOF_TS) { | if (to->to_flags & TOF_TS) { | ||||
▲ Show 20 Lines • Show All 130 Lines • Show Last 20 Lines |