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:
parent
9f202f0cde
commit
ebb0cbea75
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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 }, \
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user