From ebb0cbea752688d7a42c28ad9f32244f2573ee12 Mon Sep 17 00:00:00 2001 From: Paul Traina Date: Mon, 7 Oct 1996 04:32:42 +0000 Subject: [PATCH] Increase robustness of FreeBSD against high-rate connection attempt denial of service attacks. Reviewed by: bde,wollman,olah Inspired by: vjs@sgi.com --- sys/kern/uipc_sockbuf.c | 52 ++++++++++++++++++++++++++++++++++++----- sys/kern/uipc_socket.c | 3 ++- sys/kern/uipc_socket2.c | 52 ++++++++++++++++++++++++++++++++++++----- sys/netinet/tcp_input.c | 38 +++++++++++++++++++----------- sys/netinet/tcp_reass.c | 38 +++++++++++++++++++----------- sys/sys/socketvar.h | 6 ++++- sys/sys/sysctl.h | 6 ++--- 7 files changed, 149 insertions(+), 46 deletions(-) diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c index 0590034d365a..59c8ff1f7abc 100644 --- a/sys/kern/uipc_sockbuf.c +++ b/sys/kern/uipc_sockbuf.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93 - * $Id: uipc_socket2.c,v 1.13 1996/08/19 19:22:26 julian Exp $ + * $Id: uipc_socket2.c,v 1.14 1996/09/19 00:54:36 pst Exp $ */ #include @@ -60,9 +60,6 @@ static u_long sb_efficiency = 8; /* parameter for sbreserve() */ SYSCTL_INT(_kern, OID_AUTO, sockbuf_waste_factor, CTLFLAG_RW, &sb_efficiency, 0, ""); -static int sominqueue = 0; -SYSCTL_INT(_kern, KERN_SOMINQUEUE, sominqueue, CTLFLAG_RW, &sominqueue, 0, ""); - /* * Procedures to manipulate state flags of socket * and do appropriate wakeups. Normal sequence from the @@ -113,6 +110,7 @@ soisconnected(so) if (head && (so->so_state & SS_INCOMP)) { TAILQ_REMOVE(&head->so_incomp, so, so_list); so->so_state &= ~SS_INCOMP; + so->so_incqlen--; TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); so->so_state |= SS_COMP; sorwakeup(head); @@ -148,6 +146,47 @@ soisdisconnected(so) sorwakeup(so); } +/* + * Return a random connection that hasn't been serviced yet and + * is eligible for discard. + * + * This may be used in conjunction with protocol specific queue + * congestion routines. + */ +struct socket * +sodropablereq(head) + register struct socket *head; +{ + register struct socket *so; + unsigned int i, j, qlen; + + static int rnd; + static long old_mono_secs; + static unsigned int cur_cnt, old_cnt; + + so = TAILQ_FIRST(&head->so_incomp); + if (!so) + return (so); + + qlen = head->so_incqlen; + + if ((i = (mono_time.tv_sec - old_mono_secs)) != 0) { + old_mono_secs = mono_time.tv_sec; + old_cnt = cur_cnt / i; + cur_cnt = 0; + } + + if (++cur_cnt > qlen || old_cnt > qlen) { + rnd = (314159 * rnd + 66329) & 0xffff; + j = ((qlen + 1) * rnd) >> 16; + + while (j-- && so) + so = TAILQ_NEXT(so, so_list); + } + + return (so); +} + /* * When an attempt at a new connection is noted on a socket * which accepts connections, sonewconn is called. If the @@ -166,8 +205,7 @@ sonewconn1(head, connstatus) { register struct socket *so; - if ((head->so_qlen > 3 * head->so_qlimit / 2) && - (head->so_qlen > sominqueue)) + if (head->so_qlen > 3 * head->so_qlimit / 2) return ((struct socket *)0); MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT); if (so == NULL) @@ -188,6 +226,7 @@ sonewconn1(head, connstatus) } else { TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list); so->so_state |= SS_INCOMP; + head->so_incqlen++; } head->so_qlen++; if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0)) { @@ -195,6 +234,7 @@ sonewconn1(head, connstatus) TAILQ_REMOVE(&head->so_comp, so, so_list); } else { TAILQ_REMOVE(&head->so_incomp, so, so_list); + head->so_incqlen--; } head->so_qlen--; (void) free((caddr_t)so, M_SOCKET); diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 462191fbfd57..da9e33146408 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 - * $Id: uipc_socket.c,v 1.18 1996/05/09 20:14:57 wollman Exp $ + * $Id: uipc_socket.c,v 1.19 1996/07/11 16:31:56 wollman Exp $ */ #include @@ -144,6 +144,7 @@ sofree(so) if (head != NULL) { if (so->so_state & SS_INCOMP) { TAILQ_REMOVE(&head->so_incomp, so, so_list); + head->so_incqlen--; } else if (so->so_state & SS_COMP) { TAILQ_REMOVE(&head->so_comp, so, so_list); } else { diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 0590034d365a..59c8ff1f7abc 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)uipc_socket2.c 8.1 (Berkeley) 6/10/93 - * $Id: uipc_socket2.c,v 1.13 1996/08/19 19:22:26 julian Exp $ + * $Id: uipc_socket2.c,v 1.14 1996/09/19 00:54:36 pst Exp $ */ #include @@ -60,9 +60,6 @@ static u_long sb_efficiency = 8; /* parameter for sbreserve() */ SYSCTL_INT(_kern, OID_AUTO, sockbuf_waste_factor, CTLFLAG_RW, &sb_efficiency, 0, ""); -static int sominqueue = 0; -SYSCTL_INT(_kern, KERN_SOMINQUEUE, sominqueue, CTLFLAG_RW, &sominqueue, 0, ""); - /* * Procedures to manipulate state flags of socket * and do appropriate wakeups. Normal sequence from the @@ -113,6 +110,7 @@ soisconnected(so) if (head && (so->so_state & SS_INCOMP)) { TAILQ_REMOVE(&head->so_incomp, so, so_list); so->so_state &= ~SS_INCOMP; + so->so_incqlen--; TAILQ_INSERT_TAIL(&head->so_comp, so, so_list); so->so_state |= SS_COMP; sorwakeup(head); @@ -148,6 +146,47 @@ soisdisconnected(so) sorwakeup(so); } +/* + * Return a random connection that hasn't been serviced yet and + * is eligible for discard. + * + * This may be used in conjunction with protocol specific queue + * congestion routines. + */ +struct socket * +sodropablereq(head) + register struct socket *head; +{ + register struct socket *so; + unsigned int i, j, qlen; + + static int rnd; + static long old_mono_secs; + static unsigned int cur_cnt, old_cnt; + + so = TAILQ_FIRST(&head->so_incomp); + if (!so) + return (so); + + qlen = head->so_incqlen; + + if ((i = (mono_time.tv_sec - old_mono_secs)) != 0) { + old_mono_secs = mono_time.tv_sec; + old_cnt = cur_cnt / i; + cur_cnt = 0; + } + + if (++cur_cnt > qlen || old_cnt > qlen) { + rnd = (314159 * rnd + 66329) & 0xffff; + j = ((qlen + 1) * rnd) >> 16; + + while (j-- && so) + so = TAILQ_NEXT(so, so_list); + } + + return (so); +} + /* * When an attempt at a new connection is noted on a socket * which accepts connections, sonewconn is called. If the @@ -166,8 +205,7 @@ sonewconn1(head, connstatus) { register struct socket *so; - if ((head->so_qlen > 3 * head->so_qlimit / 2) && - (head->so_qlen > sominqueue)) + if (head->so_qlen > 3 * head->so_qlimit / 2) return ((struct socket *)0); MALLOC(so, struct socket *, sizeof(*so), M_SOCKET, M_DONTWAIT); if (so == NULL) @@ -188,6 +226,7 @@ sonewconn1(head, connstatus) } else { TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list); so->so_state |= SS_INCOMP; + head->so_incqlen++; } head->so_qlen++; if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0)) { @@ -195,6 +234,7 @@ sonewconn1(head, connstatus) TAILQ_REMOVE(&head->so_comp, so, so_list); } else { TAILQ_REMOVE(&head->so_incomp, so, so_list); + head->so_incqlen--; } head->so_qlen--; (void) free((caddr_t)so, M_SOCKET); diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 35a3ba5eb255..a31fe9f8861b 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 - * $Id: tcp_input.c,v 1.50 1996/09/21 06:30:06 ache Exp $ + * $Id: tcp_input.c,v 1.51 1996/09/21 06:39:20 pst Exp $ */ #ifndef TUBA_INCLUDE @@ -411,20 +411,25 @@ findpcb: if (so->so_options & SO_ACCEPTCONN) { register struct tcpcb *tp0 = tp; struct socket *so2; - /* - * If the attempt to get onto the socket queue failed, - * drop the oldest queue entry and try again. - */ - so2 = sonewconn(so, 0); - if (!so2) { - tcpstat.tcps_listendrop++; - so2 = TAILQ_FIRST(&so->so_incomp); - if (so2) { - tcp_drop(sototcpcb(so2), ETIMEDOUT); - so2 = sonewconn(so, 0); + if ((tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { + /* + * Note: dropwithreset makes sure we don't + * send a RST in response to a RST. + */ + if (tiflags & TH_ACK) { + tcpstat.tcps_badsyn++; + goto dropwithreset; } - if (!so2) - goto drop; + goto drop; + } + so2 = sonewconn(so, 0); + if (so2 == 0) { + tcpstat.tcps_listendrop++; + so2 = sodropablereq(so); + if (so2) + tcp_drop(sototcpcb(so2), ETIMEDOUT); + else + goto drop; } so = so2; /* @@ -753,6 +758,8 @@ findpcb: } /* + * If the state is SYN_RECEIVED: + * do just the ack and RST checks from SYN_SENT state. * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. @@ -764,6 +771,7 @@ findpcb: * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ + case TCPS_SYN_RECEIVED: case TCPS_SYN_SENT: if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; @@ -791,6 +799,8 @@ findpcb: tp = tcp_drop(tp, ECONNREFUSED); goto drop; } + if (tp->t_state == TCPS_SYN_RECEIVED) + break; if ((tiflags & TH_SYN) == 0) goto drop; tp->snd_wnd = ti->ti_win; /* initial send window */ diff --git a/sys/netinet/tcp_reass.c b/sys/netinet/tcp_reass.c index 35a3ba5eb255..a31fe9f8861b 100644 --- a/sys/netinet/tcp_reass.c +++ b/sys/netinet/tcp_reass.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 - * $Id: tcp_input.c,v 1.50 1996/09/21 06:30:06 ache Exp $ + * $Id: tcp_input.c,v 1.51 1996/09/21 06:39:20 pst Exp $ */ #ifndef TUBA_INCLUDE @@ -411,20 +411,25 @@ findpcb: if (so->so_options & SO_ACCEPTCONN) { register struct tcpcb *tp0 = tp; struct socket *so2; - /* - * If the attempt to get onto the socket queue failed, - * drop the oldest queue entry and try again. - */ - so2 = sonewconn(so, 0); - if (!so2) { - tcpstat.tcps_listendrop++; - so2 = TAILQ_FIRST(&so->so_incomp); - if (so2) { - tcp_drop(sototcpcb(so2), ETIMEDOUT); - so2 = sonewconn(so, 0); + if ((tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) { + /* + * Note: dropwithreset makes sure we don't + * send a RST in response to a RST. + */ + if (tiflags & TH_ACK) { + tcpstat.tcps_badsyn++; + goto dropwithreset; } - if (!so2) - goto drop; + goto drop; + } + so2 = sonewconn(so, 0); + if (so2 == 0) { + tcpstat.tcps_listendrop++; + so2 = sodropablereq(so); + if (so2) + tcp_drop(sototcpcb(so2), ETIMEDOUT); + else + goto drop; } so = so2; /* @@ -753,6 +758,8 @@ findpcb: } /* + * If the state is SYN_RECEIVED: + * do just the ack and RST checks from SYN_SENT state. * If the state is SYN_SENT: * if seg contains an ACK, but not for our SYN, drop the input. * if seg contains a RST, then drop the connection. @@ -764,6 +771,7 @@ findpcb: * arrange for segment to be acked (eventually) * continue processing rest of data/controls, beginning with URG */ + case TCPS_SYN_RECEIVED: case TCPS_SYN_SENT: if ((taop = tcp_gettaocache(inp)) == NULL) { taop = &tao_noncached; @@ -791,6 +799,8 @@ findpcb: tp = tcp_drop(tp, ECONNREFUSED); goto drop; } + if (tp->t_state == TCPS_SYN_RECEIVED) + break; if ((tiflags & TH_SYN) == 0) goto drop; tp->snd_wnd = ti->ti_win; /* initial send window */ diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index fbab6781ec9d..968b42886953 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)socketvar.h 8.3 (Berkeley) 2/19/95 - * $Id: socketvar.h,v 1.13 1996/03/11 15:37:44 davidg Exp $ + * $Id: socketvar.h,v 1.14 1996/05/01 01:53:59 bde Exp $ */ #ifndef _SYS_SOCKETVAR_H_ @@ -69,6 +69,8 @@ struct socket { TAILQ_HEAD(, socket) so_comp; /* queue of complete unaccepted connections */ TAILQ_ENTRY(socket) so_list; /* list of unaccepted connections */ short so_qlen; /* number of unaccepted connections */ + short so_incqlen; /* number of unaccepted incomplete + connections */ short so_qlimit; /* max number queued connections */ short so_timeo; /* connection timeout */ u_short so_error; /* error affecting connection */ @@ -255,6 +257,8 @@ void soisconnecting __P((struct socket *so)); void soisdisconnected __P((struct socket *so)); void soisdisconnecting __P((struct socket *so)); int solisten __P((struct socket *so, int backlog)); +struct socket * + sodropablereq __P((struct socket *head)); struct socket * sonewconn1 __P((struct socket *head, int connstatus)); int soreceive __P((struct socket *so, struct mbuf **paddr, struct uio *uio, diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h index 78575a7f0414..b46d7b702709 100644 --- a/sys/sys/sysctl.h +++ b/sys/sys/sysctl.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)sysctl.h 8.1 (Berkeley) 6/2/93 - * $Id: sysctl.h,v 1.44 1996/09/10 23:31:13 bde Exp $ + * $Id: sysctl.h,v 1.45 1996/09/19 00:54:29 pst Exp $ */ #ifndef _SYS_SYSCTL_H_ @@ -223,8 +223,7 @@ int sysctl_handle_opaque SYSCTL_HANDLER_ARGS; #define KERN_MAXSOCKBUF 31 /* int: max size of a socket buffer */ #define KERN_PS_STRINGS 32 /* int: address of PS_STRINGS */ #define KERN_USRSTACK 33 /* int: address of USRSTACK */ -#define KERN_SOMINQUEUE 34 /* int: override socket listen() */ -#define KERN_MAXID 35 /* number of valid kern ids */ +#define KERN_MAXID 34 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ @@ -261,7 +260,6 @@ int sysctl_handle_opaque SYSCTL_HANDLER_ARGS; { "maxsockbuf", CTLTYPE_INT }, \ { "ps_strings", CTLTYPE_INT }, \ { "usrstack", CTLTYPE_INT }, \ - { "sominqueue", CTLTYPE_INT }, \ } /*