Increase robustness of FreeBSD against high-rate connection attempt

denial of service attacks.

Reviewed by:	bde,wollman,olah
Inspired by:	vjs@sgi.com
This commit is contained in:
Paul Traina 1996-10-07 04:32:42 +00:00
parent 9f202f0cde
commit ebb0cbea75
7 changed files with 149 additions and 46 deletions

View File

@ -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 <sys/param.h>
@ -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);

View File

@ -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 <sys/param.h>
@ -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 {

View File

@ -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 <sys/param.h>
@ -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);

View File

@ -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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
}
/*
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 */

View File

@ -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 @@ tcp_input(m, iphlen)
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 @@ tcp_input(m, iphlen)
}
/*
* 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 @@ tcp_input(m, iphlen)
* 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 @@ tcp_input(m, iphlen)
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 */

View File

@ -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,

View File

@ -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 }, \
}
/*