kern/173478: icmp forward bandwithlimit

Ingo Flaschberger if at FreeBSD.org
Thu Nov 8 17:00:02 UTC 2012


>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:


More information about the freebsd-bugs mailing list