Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 08 Dec 2004 15:51:06 +0100
From:      Michal Mertl <mime@traveller.cz>
To:        freebsd-net@freebsd.org
Cc:        Robert Watson <rwatson@freebsd.org>
Subject:   New ICMP limits
Message-ID:  <41B714DA.6090505@traveller.cz>

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

Hello,

I think some network administrators may want to set different maximum rate 
for different types of ICMP replies. Currently the limit 
net.inet.icmp.icmplim is enforced independently for the following cases - 
ICMP echo-reply, ICMP timestamp reply, ICMP port unreachable (generated as a 
response to a packet received on a UDP port with no listening application). 
It's in addition a bit misused (or at least misnamed) for limiting sending 
of TCP reset packets on closed and open ports.

Andre Oppermann wrote a patch which adds support for limiting the sending of 
ICMP host unreachable messages. These are generated by a router when it 
can't send the packet to the destination, such as when it's about to send to 
an unused IP address on a directly connected network.

I think we should look at what other similar packets we generate and if it 
makes any sense to limit their rate too. I'm aware of about only ICMP mask 
reply but there may be others. Special case are ICMP packets which could be 
returned by firewalls - filter prohibited and others but these may be better 
handled inside the firewall packages. I checked only ipfw2 and it uses 
icmp_error to send the response, co we could limit the rate there too.

I wrote a patch which extends on net.inet.icmp.icmplim sysctl. It adds new 
sysctl branch net.inet.icmp.limits and all different types of ICMP (and TCP 
RST) replies have separate entries. It also changes the meaning of the value 
<=0. If the limit is set to 0, no limit is enforces, and if the limit is set 
to <0 packet is never sent. Setting the limit to <0 has rather interesting 
effects similar to blackhole(4) for tcp and udp and special with ICMP replies.

The attached patch integrates Andre's patch for limiting the rate of ICMP 
host-unreachables.

What do you think?

-- 
Michal Mertl


--------------030904050009000006050607
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	5 Dec 2004 17:35:39 -0000
@@ -57,32 +57,18 @@
 	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_HOST 1
+#define BANDLIM_ICMP_ECHO 2
+#define BANDLIM_ICMP_TSTAMP 3
+#define BANDLIM_RST_CLOSEDPORT 4	/* No connection, and no listeners */
+#define BANDLIM_RST_OPENPORT 5		/* No connection, listener */
+#define BANDLIM_MAX 5
 #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	5 Dec 2004 18:20:21 -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,37 @@
 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, limits, CTLFLAG_RW, 0,
+    "ICMP replies limits");
+
+static int      icmplim_unreach_port = 200;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_ICMP_UNREACH, unreach_port,
+	CTLFLAG_RW, &icmplim_unreach_port, 0,
+	"Maximum rate of ICMP port unreachables");
+
+static int      icmplim_unreach_host = 10;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_ICMP_UNREACH_HOST,
+	unreach_host, CTLFLAG_RW, &icmplim_unreach_host, 0,
+	"Maximum rate of ICMP host unreachables");
+
+static int      icmplim_echo = 200;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_ICMP_ECHO, echo,
+	CTLFLAG_RW, &icmplim_echo, 0,"Maximum rate of ICMP echo replies");
+
+static int      icmplim_tstamp = 200;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_ICMP_TSTAMP, tstamp,
+	CTLFLAG_RW, &icmplim_tstamp, 0,
+	"Maximum rate of ICMP tstamp replies");
+
+static int      icmplim_rst_closed = 200;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_RST_CLOSEDPORT, rst_closed,
+	CTLFLAG_RW, &icmplim_rst_closed, 0,
+	"Maximum rate of RSTs of closed ports");
+
+static int      icmplim_rst_open = 200;
+SYSCTL_INT(_net_inet_icmp_limits, BANDLIM_RST_OPENPORT, rst_open,
+	CTLFLAG_RW, &icmplim_rst_open, 0,
+	"Maximum rate of RSTs of open ports");
 
 static int	icmplim_output = 1;
 SYSCTL_INT(_net_inet_icmp, OID_AUTO, icmplim_output, CTLFLAG_RW,
@@ -172,6 +200,18 @@
 	if (n->m_flags & (M_BCAST|M_MCAST))
 		goto freeit;
 	/*
+	 * Limit sending of ICMP host unreachable messages.
+	 * 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 (type == ICMP_UNREACH && code == ICMP_UNREACH_HOST) {
+		if (badport_bandlim(BANDLIM_ICMP_UNREACH_HOST) < 0)
+			goto freeit;
+	}
+	/*
 	 * First, formulate icmp message
 	 */
 	m = m_gethdr(M_DONTWAIT, MT_HEADER);
@@ -893,31 +933,60 @@
 		struct timeval	lasttime;
 		int		curpps;
 	} rates[BANDLIM_MAX+1] = {
-		{ "icmp unreach response" },
+		{ "icmp unreach port response" },
+		{ "icmp unreach host response" },
 		{ "icmp ping response" },
 		{ "icmp 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:
+		limit = icmplim_unreach_port;
+		break;
+	case BANDLIM_ICMP_UNREACH_HOST:
+		limit = icmplim_unreach_host;
+		break;
+	case BANDLIM_ICMP_ECHO:
+		limit = icmplim_echo;
+		break;
+	case BANDLIM_ICMP_TSTAMP:
+		limit = icmplim_tstamp;
+		break;
+	case BANDLIM_RST_CLOSEDPORT:
+		limit = icmplim_rst_closed;
+		break;
+	case BANDLIM_RST_OPENPORT:
+		limit = icmplim_rst_open;
+		break;
+	default:
+		return (0);
 	}
+	/* Return ok status if limit is 0 (unlimited) */
+	if (limit == 0)
+		return (0);
+	/* Return fail status if limit is <0 (never send) */
+	if (limit < 0)
+		return (-1);
+
+	/* 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
 }

--------------030904050009000006050607--



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