Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 11 Dec 2004 11:42:47 +0100
From:      Michal Mertl <mime@traveller.cz>
To:        Suleiman Souhlal <ssouhlal@FreeBSD.org>
Cc:        Andre Oppermann <andre@FreeBSD.org>
Subject:   Re: New ICMP limits
Message-ID:  <41BACF27.4080008@traveller.cz>
In-Reply-To: <1102735360.5281.6.camel@localhost>
References:  <41B714DA.6090505@traveller.cz> <1102735360.5281.6.camel@localhost>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------090602040503050508090206
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Suleiman Souhlal napsal(a):
> Hi,
> 
> On Wed, 2004-12-08 at 09:51, Michal Mertl wrote:
> 
>>What do you think?
> 
> 
> Wouldn't it be better to move all the calls to badport_bandlim() to
> inside icmp_error()?

It makes sense, yes. Unfortunately it isn't possible in most cases - 
echo/tstamp call icmp_reflect instead of icmp_errror from inside icmp_input. 
TCP RSTs aren't in fact ICMP packets. The only case, where it's easily 
doable is in udp_input for ICMP_PORT_UNREACHABLES.

I changed my patch quite a bit after Andre's suggestions. I'm sending you 
the new version.

-- 
Michal Mertl

--------------090602040503050508090206
Content-Type: text/plain;
 name="icmp_limits.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="icmp_limits.patch"

Index: icmp_var.h
===================================================================
RCS file: /home/fcvs/cvs/src/sys/netinet/icmp_var.h,v
retrieving revision 1.24
diff -u -r1.24 icmp_var.h
--- icmp_var.h	16 Aug 2004 18:32:07 -0000	1.24
+++ icmp_var.h	10 Dec 2004 16:57:49 -0000
@@ -57,32 +57,16 @@
 	u_long	icps_noroute;		/* no route back */
 };
 
-/*
- * Names for ICMP sysctl objects
- */
-#define	ICMPCTL_MASKREPL	1	/* allow replies to netmask requests */
-#define	ICMPCTL_STATS		2	/* statistics (read-only) */
-#define ICMPCTL_ICMPLIM		3
-#define ICMPCTL_MAXID		4
-
-#define ICMPCTL_NAMES { \
-	{ 0, 0 }, \
-	{ "maskrepl", CTLTYPE_INT }, \
-	{ "stats", CTLTYPE_STRUCT }, \
-	{ "icmplim", CTLTYPE_INT }, \
-}
-
 #ifdef _KERNEL
 SYSCTL_DECL(_net_inet_icmp);
 extern struct icmpstat icmpstat;	/* icmp statistics */
 extern int badport_bandlim(int);
 #define BANDLIM_UNLIMITED -1
-#define BANDLIM_ICMP_UNREACH 0
-#define BANDLIM_ICMP_ECHO 1
-#define BANDLIM_ICMP_TSTAMP 2
-#define BANDLIM_RST_CLOSEDPORT 3 /* No connection, and no listeners */
-#define BANDLIM_RST_OPENPORT 4   /* No connection, listener */
-#define BANDLIM_MAX 4
+#define BANDLIM_ICMP_UNREACH_PORT 0
+#define BANDLIM_ICMP_UNREACH 1
+#define BANDLIM_ICMP_ECHO 2
+#define BANDLIM_RST_CLOSEDPORT 3	/* No connection, and no listeners */
+#define BANDLIM_RST_OPENPORT 4		/* No connection, listener */
 #endif
 
 #endif
Index: ip_icmp.c
===================================================================
RCS file: /home/fcvs/cvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.97
diff -u -r1.97 ip_icmp.c
--- ip_icmp.c	15 Sep 2004 20:13:26 -0000	1.97
+++ ip_icmp.c	10 Dec 2004 16:59:19 -0000
@@ -79,11 +79,11 @@
  */
 
 struct	icmpstat icmpstat;
-SYSCTL_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RW,
+SYSCTL_STRUCT(_net_inet_icmp, OID_AUTO, stats, CTLFLAG_RW,
 	&icmpstat, icmpstat, "");
 
 static int	icmpmaskrepl = 0;
-SYSCTL_INT(_net_inet_icmp, ICMPCTL_MASKREPL, maskrepl, CTLFLAG_RW,
+SYSCTL_INT(_net_inet_icmp, OID_AUTO, maskrepl, CTLFLAG_RW,
 	&icmpmaskrepl, 0, "Reply to ICMP Address Mask Request packets.");
 
 static u_int	icmpmaskfake = 0;
@@ -98,9 +98,28 @@
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, log_redirect, CTLFLAG_RW,
 	&log_redirect, 0, "");
 
-static int      icmplim = 200;
-SYSCTL_INT(_net_inet_icmp, ICMPCTL_ICMPLIM, icmplim, CTLFLAG_RW,
-	&icmplim, 0, "");
+SYSCTL_NODE(_net_inet_icmp, OID_AUTO, limit, CTLFLAG_RW, 0,
+    "ICMP replies limits");
+
+static int      icmplim_unreach = 200;
+SYSCTL_INT(_net_inet_icmp_limit, OID_AUTO, unreach,
+	CTLFLAG_RW, &icmplim_unreach, 0,
+	"Maximum rate of ICMP unreachables");
+
+static int      icmplim_echo = 200;
+SYSCTL_INT(_net_inet_icmp_limit, OID_AUTO, echo,
+	CTLFLAG_RW, &icmplim_echo, 0,
+	"Maximum rate of ICMP echo/tstamp replies");
+
+static int      icmplim_tcp = 200;
+SYSCTL_INT(_net_inet_icmp_limit, OID_AUTO, tcp,
+	CTLFLAG_RW, &icmplim_tcp, 0,
+	"Maximum rate of TCP RSTs");
+
+static int      icmplim_udp = 200;
+SYSCTL_INT(_net_inet_icmp_limit, OID_AUTO, udp,
+	CTLFLAG_RW, &icmplim_udp, 0,
+	"Maximum rate of ICMP port unreachables");
 
 static int	icmplim_output = 1;
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW,
@@ -171,6 +190,23 @@
 	/* Don't send error in response to a multicast or broadcast packet */
 	if (n->m_flags & (M_BCAST|M_MCAST))
 		goto freeit;
+
+	if (type == ICMP_UNREACH && code != ICMP_UNREACH_NEEDFRAG)
+	/*
+	 * Limit sending of ICMP unreachable messages except of
+	 * ICMP_UNREACH_NEEDFRAG.
+	 * This includes packets generated by firewall packages when
+	 * dropping a packet.
+	 * E.g.: "ipfw add 110 unreach filter-prohib tcp from any to any 25"
+	 * It also limits sending of ICMP host unreachable messages
+	 * generated if we are acting as a router and someone is doing a sweep
+	 * scan (eg. nmap and/or numerous windows worms) for destinations
+	 * we are the gateway for but are not reachable (ie. a /24 on a
+	 * interface and only a couple of hosts on the ethernet) we would
+	 * generate a storm of ICMP host unreachable messages.
+	 */
+		if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0)
+			goto freeit;
 	/*
 	 * First, formulate icmp message
 	 */
@@ -489,7 +525,7 @@
 		icp->icmp_type = ICMP_TSTAMPREPLY;
 		icp->icmp_rtime = iptime();
 		icp->icmp_ttime = icp->icmp_rtime;	/* bogus, do later! */
-		if (badport_bandlim(BANDLIM_ICMP_TSTAMP) < 0)
+		if (badport_bandlim(BANDLIM_ICMP_ECHO) < 0)
 			goto freeit;
 		else
 			goto reflect;
@@ -887,37 +923,55 @@
 int
 badport_bandlim(int which)
 {
-#define	N(a)	(sizeof (a) / sizeof (a[0]))
 	static struct rate {
 		const char	*type;
 		struct timeval	lasttime;
 		int		curpps;
-	} rates[BANDLIM_MAX+1] = {
+	} rates[] = {
+		{ "icmp port unreach response" },
 		{ "icmp unreach response" },
-		{ "icmp ping response" },
-		{ "icmp tstamp response" },
+		{ "icmp echo/tstamp response" },
 		{ "closed port RST response" },
 		{ "open port RST response" }
 	};
-
-	/*
-	 * Return ok status if feature disabled or argument out of range.
-	 */
-	if (icmplim > 0 && (u_int) which < N(rates)) {
-		struct rate *r = &rates[which];
-		int opps = r->curpps;
-
-		if (!ppsratecheck(&r->lasttime, &r->curpps, icmplim))
-			return -1;	/* discard packet */
-		/*
-		 * If we've dropped below the threshold after having
-		 * rate-limited traffic print the message.  This preserves
-		 * the previous behaviour at the expense of added complexity.
-		 */
-		if (icmplim_output && opps > icmplim)
-			printf("Limiting %s from %d to %d packets/sec\n",
-				r->type, opps, icmplim);
+	struct rate	*r;
+	int		 opps;
+	int		 limit;
+
+	/* Return ok status if argument is out of range. */
+	switch (which) {
+	case BANDLIM_ICMP_UNREACH_PORT:
+		limit = icmplim_udp;
+		break;
+	case BANDLIM_ICMP_UNREACH:
+		limit = icmplim_unreach;
+		break;
+	case BANDLIM_ICMP_ECHO:
+		limit = icmplim_echo;
+		break;
+	case BANDLIM_RST_CLOSEDPORT:
+	case BANDLIM_RST_OPENPORT:
+		limit = icmplim_tcp;
+		break;
+	default:
+		return (0);
 	}
+	/* Return ok status if limit is <=0 (unlimited) */
+	if (limit <= 0)
+		return (0);
+
+	/* Do the actual rate check */
+	r = &rates[which];
+	opps = r->curpps;
+	if (!ppsratecheck(&r->lasttime, &r->curpps, limit))
+		return -1;	/* discard packet */
+	/*
+	 * If we've dropped below the threshold after having
+	 * rate-limited traffic print the message.  This preserves
+	 * the previous behaviour at the expense of added complexity.
+	 */
+	if (icmplim_output && opps > limit)
+		printf("Limiting %s from %d to %d packets/sec\n",
+			r->type, opps, limit);
 	return 0;			/* okay to send packet */
-#undef N
 }
Index: udp_usrreq.c
===================================================================
RCS file: /home/fcvs/cvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.170
diff -u -r1.170 udp_usrreq.c
--- udp_usrreq.c	8 Nov 2004 14:44:53 -0000	1.170
+++ udp_usrreq.c	10 Dec 2004 16:14:10 -0000
@@ -375,7 +375,7 @@
 		}
 		if (blackhole)
 			goto badheadlocked;
-		if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0)
+		if (badport_bandlim(BANDLIM_ICMP_UNREACH_PORT) < 0)
 			goto badheadlocked;
 		*ip = save_ip;
 		ip->ip_len += iphlen;

--------------090602040503050508090206--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?41BACF27.4080008>