Date: Mon, 30 Nov 1998 22:07:47 -0800 (PST) From: Matthew Dillon <dillon@apollo.backplane.com> To: freebsd-current@FreeBSD.ORG Subject: D.O.S. attack protection enhancements commit (ICMP_BANDLIM) Message-ID: <199812010607.WAA03051@apollo.backplane.com>
next in thread | raw e-mail | index | archive | help
I would like to make a commit to -current similar to the following
(the one below is actually relative to -stable, but we are in final
testing of the same patch to our -current tree).
The commit I want to commit is just the ICMP_BANDLIM section. I am
not planning on commiting the UDP_BANDLIM section yet.
The ICMP rate limiter limits the number of ICMP error responses the
machine will return. There are currently several D.O.S. attacks that
can cause the machine to crash or become totally unusable due to it
trying to send an ICMP response to each one. This rate limiter has
been installed on BEST machines for several months and has probably saved
us two or three dozen crashes from attacks.
Since it only limits ICMP error responses, the default rate limit is set
relatively low at 100 packets per second. The only time I have ever
hit the limit has been during an attack, even on our most heavily loaded
web boxes. I believe 100 to be an excellent default value.
I would like a sign-off on the ICMP_BANDLIM portion of the patch so I can
commit it.
The UDP_BANDLIM section is presented for discussion, but I do not plan
to request a commit for it in its current incarnation. This section was
designed to prevent hostile users from being able to perpetrate attacks
or take down a machine from a hacked account and it does in fact do a
pretty good job at it, but there are obvious ways to circumvent the
problem. I really need to change the hack and add a new per-user resource
limit 'maximum allowed network I/O bandwidth' that actually rate-limits
network traffic from a user rather then dropping excessive packet traffic.
-Matt
Matthew Dillon Engineering, HiWay Technologies, Inc. & BEST Internet
Communications & God knows what else.
<dillon@backplane.com> (Please include original email in any response)
diff -r -c /static/src/sys/netinet/tcp_input.c ./netinet/tcp_input.c
*** /static/src/sys/netinet/tcp_input.c Thu Sep 17 15:46:23 1998
--- ./netinet/tcp_input.c Fri Sep 18 13:29:21 1998
***************
*** 382,387 ****
--- 382,394 ----
buf, ntohs(ti->ti_dport),
inet_ntoa(ti->ti_src), ntohs(ti->ti_sport));
}
+ #ifdef ICMP_BANDLIM
+ {
+ extern int badport_bandlim(int); /* hack */
+ if (badport_bandlim(1) < 0)
+ goto drop;
+ }
+ #endif
goto dropwithreset;
}
tp = intotcpcb(inp);
diff -r -c /static/src/sys/netinet/udp_usrreq.c ./netinet/udp_usrreq.c
*** /static/src/sys/netinet/udp_usrreq.c Thu Oct 23 14:19:24 1997
--- ./netinet/udp_usrreq.c Fri Jul 10 19:34:26 1998
***************
*** 47,52 ****
--- 47,53 ----
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+ #include <sys/proc.h>
#include <net/if.h>
#include <net/route.h>
***************
*** 70,75 ****
--- 71,77 ----
#else
static int udpcksum = 0; /* XXX */
#endif
+
SYSCTL_INT(_net_inet_udp, UDPCTL_CHECKSUM, checksum, CTLFLAG_RW,
&udpcksum, 0, "");
***************
*** 77,85 ****
--- 79,105 ----
SYSCTL_INT(_net_inet_udp, OID_AUTO, log_in_vain, CTLFLAG_RW,
&log_in_vain, 0, "");
+ #ifdef UDP_BANDLIM
+ static int udpbandlim = 200000; /* bytes/sec */
+ static int udpbanddebug = 0;
+ SYSCTL_INT(_net_inet_udp, UDPCTL_BANDLIM, bandlim, CTLFLAG_RW,
+ &udpbandlim, 0, "");
+ SYSCTL_INT(_net_inet_udp, UDPCTL_BANDDEBUG, banddebug, CTLFLAG_RW,
+ &udpbanddebug, 0, "");
+ #endif
+ #ifdef ICMP_BANDLIM
+ static int icmplim = 100;
+ SYSCTL_INT(_net_inet_udp, UDPCTL_ICMPLIM, icmplim, CTLFLAG_RW,
+ &icmplim, 0, "");
+ #endif
+
static struct inpcbhead udb; /* from udp_var.h */
static struct inpcbinfo udbinfo;
+ #ifdef ICMP_BANDLIM
+ int badport_bandlim(int which);
+ #endif
+
#ifndef UDBHASHSIZE
#define UDBHASHSIZE 64
#endif
***************
*** 91,99 ****
--- 111,122 ----
static struct sockaddr_in udp_in = { sizeof(udp_in), AF_INET };
static void udp_detach __P((struct inpcb *));
+ static void udp_dealloc_ppcb(struct inpcb *inp);
+ static int udp_alloc_ppcb(struct inpcb *inp);
static int udp_output __P((struct inpcb *, struct mbuf *, struct mbuf *,
struct mbuf *));
static void udp_notify __P((struct inpcb *, int));
+ static int udp_rate_limit(struct inpcb *inp, int packetLen);
void
udp_init()
***************
*** 296,302 ****
--- 319,332 ----
goto bad;
}
*ip = save_ip;
+ #ifdef ICMP_BANDLIM
+ if (badport_bandlim(0) == 0)
+ icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
+ else
+ goto bad;
+ #else
icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
+ #endif
return;
}
***************
*** 379,384 ****
--- 409,425 ----
goto release;
}
+ /*
+ * Check rate limit
+ */
+
+ if (udpbandlim > 0 &&
+ udp_rate_limit(inp, len + sizeof(struct udpiphdr)) < 0
+ ) {
+ error = ENOBUFS;
+ goto release;
+ }
+
if (addr) {
laddr = inp->inp_laddr;
if (inp->inp_faddr.s_addr != INADDR_ANY) {
***************
*** 502,507 ****
--- 543,552 ----
if (error)
break;
((struct inpcb *) so->so_pcb)->inp_ip_ttl = ip_defttl;
+
+ inp = sotoinpcb(so);
+ if ((error = udp_alloc_ppcb(inp)) != 0)
+ break;
break;
case PRU_DETACH:
***************
*** 608,613 ****
--- 653,801 ----
{
int s = splnet();
+ udp_dealloc_ppcb(inp);
+
in_pcbdetach(inp);
splx(s);
}
+
+ /*
+ * UDP RATE LIMITING - used to protect against floods and such
+ *
+ * Only applies to non-root-owned processes.
+ */
+
+ typedef struct udp_rate {
+ int ur_Bytes[2];
+ int ur_Packets;
+ int ur_Ticks;
+ int ur_BandLimited;
+ int ur_Printed;
+ int ur_SUid;
+ } udp_rate;
+
+ static int
+ udp_alloc_ppcb(struct inpcb *inp)
+ {
+ int error = 0;
+ struct proc *p = curproc;
+
+ if (
+ curproc &&
+ inp->inp_ppcb == 0 &&
+ suser(p->p_ucred, &p->p_acflag) != 0
+ ) {
+ udp_rate *ur = malloc(sizeof(udp_rate), M_PCB, M_NOWAIT);
+ if (ur == NULL) {
+ error = ENOBUFS;
+ } else {
+ bzero(ur, sizeof(*ur));
+ ur->ur_Ticks = ticks;
+ inp->inp_ppcb = (caddr_t)ur;
+ }
+ }
+ return(error);
+ }
+
+ static void
+ udp_dealloc_ppcb(struct inpcb *inp)
+ {
+ udp_rate *ur;
+
+ if ((ur = (udp_rate *)inp->inp_ppcb) != 0) {
+ free(ur, M_PCB);
+ inp->inp_ppcb = 0;
+ }
+ }
+
+ static int
+ udp_rate_limit(struct inpcb *inp, int packetLen)
+ {
+ udp_rate *ur;
+ int r = 0;
+ int uticks = ticks;
+
+ if ((ur = (udp_rate *)inp->inp_ppcb) != 0) {
+ int dticks;
+
+ if (udpbanddebug > 0) {
+ printf("UR TEST %d BL=%d bytes=%d/%d pkts=%d\n",
+ (int)(uticks - ur->ur_Ticks),
+ ur->ur_BandLimited,
+ ur->ur_Bytes[0] + ur->ur_Bytes[1],
+ ur->ur_Packets
+ );
+ }
+
+ /*
+ * get delta ticks, accumulation over a one second period.
+ * rate limit holds for one second after going over. Kernel
+ * printf only occurs once per second.
+ */
+
+ if ((dticks = uticks - ur->ur_Ticks) < 0 || dticks >= hz) {
+ ur->ur_Ticks = uticks;
+ ur->ur_Printed = 0;
+ ur->ur_Bytes[1] = ur->ur_Bytes[0];
+ ur->ur_Bytes[0] = 0;
+ }
+ ur->ur_Bytes[0] += packetLen;
+ ++ur->ur_Packets;
+
+ if (ur->ur_Bytes[0] > udpbandlim ||
+ ur->ur_Bytes[1] > udpbandlim
+ ) {
+ r = -1;
+
+ ur->ur_BandLimited;
+ if (ur->ur_Printed == 0) {
+ ur->ur_Printed = 1;
+ if (udpbanddebug >= 0) {
+ printf("udp band limited src %s:%d",
+ inet_ntoa(inp->inp_laddr),
+ (int)ntohs(inp->inp_fport)
+ );
+ printf(" dst %s:%d %d/%d\n",
+ inet_ntoa(inp->inp_faddr),
+ (int)ntohs(inp->inp_lport),
+ ur->ur_Bytes[0] + ur->ur_Bytes[1],
+ ur->ur_Packets
+ );
+ }
+ }
+ } else {
+ ur->ur_BandLimited = 0;
+ }
+ }
+ return(r);
+ }
+
+ #ifdef ICMP_BANDLIM
+
+ int
+ badport_bandlim(int which)
+ {
+ static int lticks[2];
+ static int lpackets[2];
+ int dticks;
+
+ if (which >= 2 || which < 0)
+ return(0);
+ dticks = ticks - lticks[which];
+ if ((unsigned int)dticks > hz) {
+ if (lpackets[which] >= icmplim) {
+ printf("icmp-response bandwidth limit %d/%d pps\n",
+ lpackets[which],
+ icmplim
+ );
+ }
+ lticks[which] = ticks;
+ lpackets[which] = 0;
+ }
+ if (++lpackets[which] >= icmplim)
+ return(-1);
+ return(0);
+ }
+
+ #endif
+
diff -r -c /static/src/sys/netinet/udp_var.h ./netinet/udp_var.h
*** /static/src/sys/netinet/udp_var.h Wed Jun 5 10:20:35 1996
--- ./netinet/udp_var.h Fri Jul 10 19:00:35 1998
***************
*** 79,84 ****
--- 79,101 ----
#define UDPCTL_MAXDGRAM 3 /* max datagram size */
#define UDPCTL_RECVSPACE 4 /* default receive buffer space */
#define UDPCTL_MAXID 5
+ #ifdef UDP_BANDLIM
+ #define UDPCTL_BANDLIM 6
+ #define UDPCTL_BANDDEBUG 7
+ #endif
+ #ifdef ICMP_BANDLIM
+ #define UDPCTL_ICMPLIM 8
+ #endif
+
+ #ifdef UDP_BANDLIM
+ #define UDP_BANDLIM_INFO \
+ { "bandlim", CTLTYPE_INT }, \
+ { "banddebug", CTLTYPE_INT },
+ #endif
+ #ifdef ICMP_BANDLIM
+ #define ICMP_BANDLIM_INFO \
+ { "icmplim", CTLTYPE_INT },
+ #endif
#define UDPCTL_NAMES { \
{ 0, 0 }, \
***************
*** 86,91 ****
--- 103,110 ----
{ "stats", CTLTYPE_STRUCT }, \
{ "maxdgram", CTLTYPE_INT }, \
{ "recvspace", CTLTYPE_INT }, \
+ UDP_BANDLIM_INFO \
+ ICMP_BANDLIM_INFO \
}
#ifdef KERNEL
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199812010607.WAA03051>
