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