New ICMP limits
Michal Mertl
mime at traveller.cz
Wed Dec 8 06:51:11 PST 2004
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
-------------- next part --------------
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
}
More information about the freebsd-net
mailing list