From owner-freebsd-net@FreeBSD.ORG Wed Dec 8 14:51:11 2004 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 24AE116A4CE; Wed, 8 Dec 2004 14:51:11 +0000 (GMT) Received: from ss.eunet.cz (ss.eunet.cz [193.85.228.13]) by mx1.FreeBSD.org (Postfix) with ESMTP id 3848C43D46; Wed, 8 Dec 2004 14:51:10 +0000 (GMT) (envelope-from mime@traveller.cz) Received: from [127.0.0.1] (ss.eunet.cz. [193.85.228.13]) by ss.eunet.cz (8.13.1/8.13.1) with ESMTP id iB8EoqJq003092; Wed, 8 Dec 2004 15:50:52 +0100 (CET) (envelope-from mime@traveller.cz) Message-ID: <41B714DA.6090505@traveller.cz> Date: Wed, 08 Dec 2004 15:51:06 +0100 From: Michal Mertl User-Agent: Mozilla/5.0 (X11; U; FreeBSD i386; cs-CZ; rv:1.7.3) Gecko/20041117 X-Accept-Language: cs, en-us, en MIME-Version: 1.0 To: freebsd-net@freebsd.org Content-Type: multipart/mixed; boundary="------------030904050009000006050607" cc: Andre Oppermann cc: Robert Watson Subject: New ICMP limits X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Dec 2004 14:51:11 -0000 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--