Date: Thu, 8 Nov 2012 16:50:38 GMT From: Ingo Flaschberger <if@FreeBSD.org> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/173478: icmp forward bandwithlimit Message-ID: <201211081650.qA8Goc8M099479@red.freebsd.org> Resent-Message-ID: <201211081700.qA8H02Gb052170@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 173478 >Category: kern >Synopsis: icmp forward bandwithlimit >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Thu Nov 08 17:00:02 UTC 2012 >Closed-Date: >Last-Modified: >Originator: Ingo Flaschberger >Release: 9.1 Stable >Organization: crossip communications gmbh >Environment: 9.1-PRERELEASE >Description: Updated icmp forward bandwithlimit patch. *) added configurable sysctl option net.inet.icmp.icmplim_forward similar to net.inet.icmp.icmplim_output DEFAULT: OFF *) added icmp forward bandwithlimit also to fastforward >How-To-Repeat: >Fix: Patch attached with submission follows: diff -u -r sys_org/netinet/icmp_var.h /router/usr/src/sys/netinet/icmp_var.h --- sys_org/netinet/icmp_var.h 2012-11-08 15:15:11.000000000 +0100 +++ /router/usr/src/sys/netinet/icmp_var.h 2012-11-08 15:31:32.000000000 +0100 @@ -95,6 +95,7 @@ #define V_icmpstat VNET(icmpstat) extern int badport_bandlim(int); +extern int badport_bandlim_forward(int); #define BANDLIM_UNLIMITED -1 #define BANDLIM_ICMP_UNREACH 0 #define BANDLIM_ICMP_ECHO 1 @@ -103,7 +104,12 @@ #define BANDLIM_RST_OPENPORT 4 /* No connection, listener */ #define BANDLIM_ICMP6_UNREACH 5 #define BANDLIM_SCTP_OOTB 6 -#define BANDLIM_MAX 6 +#define BANDLIM_ICMP_FWD_UNREACH 7 /* forwarding: limit unreachable */ +#define BANDLIM_ICMP_FWD_TIMXCEED 8 /* forwarding: limit time-exceeded */ +#define BANDLIM_ICMP_FWD_NEEDFRAG 9 /* forwarding: limit need-frag */ +#define BANDLIM_ICMP_FWD_FILTER 10 /* forwarding: limit admin-prohib */ +#define BANDLIM_MAX 10 + #endif #endif diff -u -r sys_org/netinet/ip_fastfwd.c /router/usr/src/sys/netinet/ip_fastfwd.c --- sys_org/netinet/ip_fastfwd.c 2012-11-08 15:15:11.000000000 +0100 +++ /router/usr/src/sys/netinet/ip_fastfwd.c 2012-11-08 15:32:49.000000000 +0100 @@ -102,6 +103,7 @@ #include <netinet/ip.h> #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> #include <netinet/ip_options.h> #include <machine/in_cksum.h> @@ -142,7 +152,10 @@ IPSTAT_INC(ips_cantforward); if (rt) RTFREE(rt); - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_UNREACH) < 0) + m_freem(m); + else + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); return NULL; } return dst; @@ -299,8 +312,11 @@ if (ip_doopts == 1) return m; else if (ip_doopts == 2) { - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_FILTER_PROHIB, - 0, 0); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_FILTER) < 0) + m_freem(m); + else + icmp_error(m, ICMP_UNREACH, + ICMP_UNREACH_FILTER_PROHIB, 0, 0); return NULL; /* mbuf already free'd */ } /* else ignore IP options and continue */ @@ -399,7 +415,11 @@ if (!V_ipstealth) { #endif if (ip->ip_ttl <= IPTTLDEC) { - icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, 0); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_TIMXCEED) < 0) + m_freem(m); + else + icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, + 0, 0); return NULL; /* mbuf already free'd */ } @@ -507,6 +538,8 @@ if ((ro.ro_rt->rt_flags & RTF_REJECT) && (ro.ro_rt->rt_rmx.rmx_expire == 0 || time_uptime < ro.ro_rt->rt_rmx.rmx_expire)) { + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_UNREACH) < 0) + goto drop; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); goto consumed; } @@ -527,6 +560,8 @@ * Check if media link state of interface is not down */ if (ifp->if_link_state == LINK_STATE_DOWN) { + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_UNREACH) < 0) + goto drop; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); goto consumed; } @@ -557,6 +592,8 @@ */ if (ip->ip_off & IP_DF) { IPSTAT_INC(ips_cantfrag); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_NEEDFRAG) < 0) + goto drop; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, 0, mtu); goto consumed; diff -u -r sys_org/netinet/ip_icmp.c /router/usr/src/sys/netinet/ip_icmp.c --- sys_org/netinet/ip_icmp.c 2012-11-08 15:15:11.000000000 +0100 +++ /router/usr/src/sys/netinet/ip_icmp.c 2012-11-08 15:35:27.000000000 +0100 @@ -91,6 +91,12 @@ &VNET_NAME(icmplim_output), 0, "Enable rate limiting of ICMP responses"); +static VNET_DEFINE(int, icmplim_forward) = 0; +#define V_icmplim_forward VNET(icmplim_forward) +SYSCTL_VNET_INT(_net_inet_icmp, OID_AUTO, icmplim_forward, CTLFLAG_RW, + &VNET_NAME(icmplim_forward), 0, + "Enable rate limiting of forwarded ICMP responses"); + #ifdef INET VNET_DEFINE(struct icmpstat, icmpstat); SYSCTL_VNET_STRUCT(_net_inet_icmp, ICMPCTL_STATS, stats, CTLFLAG_RW, @@ -966,7 +972,11 @@ { "closed port RST response" }, { "open port RST response" }, { "icmp6 unreach response" }, - { "sctp ootb response" } + { "sctp ootb response" }, + { "forwarding: limit unreachable" }, + { "forwarding: limit time-exceeded" }, + { "forwarding: limit need-frag" }, + { "forwarding: limit admin-prohib" } }; /* @@ -990,3 +1000,67 @@ return 0; /* okay to send packet */ #undef N } + +/* + * badport_bandlim_fw() - check for ICMP bandwidth limit + * + * Return 0 if it is ok to send an ICMP error response, -1 if we have + * hit our bandwidth limit and it is not ok. + * + * If icmplim is <= 0, the feature is disabled and 0 is returned. + * + * For now we separate the TCP and UDP subsystems w/ different 'which' + * values. We may eventually remove this separation (and simplify the + * code further). + * + * Note that the printing of the error message is delayed so we can + * properly print the icmp error rate that the system was trying to do + * (i.e. 22000/100 pps, etc...). This can cause long delays in printing + * the 'final' error, but it doesn't make sense to solve the printing + * delay with more complex code. + */ + +int +badport_bandlim_forward(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] = { + { "icmp unreach response" }, + { "icmp ping response" }, + { "icmp tstamp response" }, + { "closed port RST response" }, + { "open port RST response" }, + { "icmp6 unreach response" }, + { "sctp ootb response" }, + { "forwarding: limit unreachable" }, + { "forwarding: limit time-exceeded" }, + { "forwarding: limit need-frag" }, + { "forwarding: limit admin-prohib" } + }; + + /* + * Return ok status if feature disabled or argument out of range. + */ + if (V_icmplim > 0 && (u_int) which < N(rates)) { + struct rate *r = &rates[which]; + int opps = r->curpps; + + if (!ppsratecheck(&r->lasttime, &r->curpps, V_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 (V_icmplim_forward && opps > V_icmplim) + log(LOG_NOTICE, "Limiting %s from %d to %d packets/sec\n", + r->type, opps, V_icmplim); + } + return 0; /* okay to send packet */ +#undef N +} diff -u -r sys_org/netinet/ip_input.c /router/usr/src/sys/netinet/ip_input.c --- sys_org/netinet/ip_input.c 2012-11-08 15:15:11.000000000 +0100 +++ /router/usr/src/sys/netinet/ip_input.c 2012-11-08 15:33:31.000000000 +0100 @@ -70,6 +70,7 @@ #include <netinet/ip_var.h> #include <netinet/ip_fw.h> #include <netinet/ip_icmp.h> +#include <netinet/icmp_var.h> #include <netinet/ip_options.h> #include <machine/in_cksum.h> #include <netinet/ip_carp.h> @@ -1370,6 +1371,7 @@ struct in_addr dest; struct route ro; int error, type = 0, code = 0, mtu = 0; + int icmp_send = 0; if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) { IPSTAT_INC(ips_cantforward); @@ -1380,8 +1382,11 @@ if (!V_ipstealth) { #endif if (ip->ip_ttl <= IPTTLDEC) { - icmp_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, - 0, 0); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_TIMXCEED) < 0) + m_freem(m); + else + icmp_error(m, ICMP_TIMXCEED, + ICMP_TIMXCEED_INTRANS, 0, 0); return; } #ifdef IPSTEALTH @@ -1396,7 +1401,10 @@ * ip_output in case of outgoing IPsec policy. */ if (!srcrt && ia == NULL) { - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); + if (badport_bandlim_forward(BANDLIM_ICMP_FWD_UNREACH) < 0) + m_freem(m); + else + icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, 0); return; } #endif @@ -1530,11 +1538,13 @@ default: type = ICMP_UNREACH; code = ICMP_UNREACH_HOST; + icmp_send = badport_bandlim_forward( BANDLIM_ICMP_FWD_UNREACH); break; case EMSGSIZE: type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG; + icmp_send = badport_bandlim_forward( BANDLIM_ICMP_FWD_NEEDFRAG); #ifdef IPSEC /* @@ -1590,7 +1600,10 @@ } if (ia != NULL) ifa_free(&ia->ia_ifa); - icmp_error(mcopy, type, code, dest.s_addr, mtu); + if (icmp_send < 0) + m_freem(m); + else + icmp_error(mcopy, type, code, dest.s_addr, mtu); } void >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201211081650.qA8Goc8M099479>