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