New ICMP limits
Michal Mertl
mime at traveller.cz
Sat Dec 11 02:42:53 PST 2004
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
-------------- 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 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;
More information about the freebsd-net
mailing list