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>