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

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
 }
_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-net
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to