Skip site navigation (1)Skip section navigation (2)
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>