Index: UPDATING =================================================================== --- UPDATING +++ UPDATING @@ -51,6 +51,18 @@ ****************************** SPECIAL WARNING: ****************************** +20170413: + A number of rate limits were previously controlled via the + net.inet.icmp.icmplim and net.inet.icmp.icmplim_output sysctls. + This functionality has now been split into separate sysctls + for the impacted protocols: + - net.inet.tcp.rstlim and net.inet.tcp.rstlim_output + - net.inet.sctp.badport_lim and net.inet.sctp.badport_lim_output + + Additionally, there is now a rate limit on ICMP6 informational + replies, which is controlled through the net.inet6.icmp6.infoppslimit + sysctl. + 20170407: arm64 builds now use the base system LLD 4.0.0 linker by default, instead of requiring that the aarch64-binutils port or package be Index: share/man/man4/tcp.4 =================================================================== --- share/man/man4/tcp.4 +++ share/man/man4/tcp.4 @@ -34,7 +34,7 @@ .\" From: @(#)tcp.4 8.1 (Berkeley) 6/5/93 .\" $FreeBSD$ .\" -.Dd February 6, 2017 +.Dd April 13, 2017 .Dt TCP 4 .Os .Sh NAME @@ -592,6 +592,19 @@ .It Va insecure_syn Use criteria defined in RFC793 instead of RFC5961 for accepting SYN segments. Default is false. +.It Va rstlim +The maximum number of RSTs per second that the TCP stack will send per second +for invalid segments. +The same limit is used for segments destined to closed ports and for invalid +segments destined for open ports. +However, the limit is tracked separately for those two classes of segments, and +the system will send up to +.Va rstlim +RSTs per second for each class. +.It Va rstlim_output +If non-zero, the system will create a log message when the +.Va rstlim +limit is exceeded. .El .Sh ERRORS A socket operation may fail with one of the following errors returned: Index: share/man/man9/counter.9 =================================================================== --- share/man/man9/counter.9 +++ share/man/man9/counter.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 22, 2017 +.Dd April 13, 2017 .Dt COUNTER 9 .Os .Sh NAME @@ -52,7 +52,11 @@ .Ft void .Fn counter_u64_zero "counter_u64_t c" .Ft int64_t -.Fn counter_ratecheck "struct counter_rate *cr" "int64_t limit" +.Fn counter_ratecheck "int which" +.Ft void +.Fn counter_rate_register "int which" "const char *descr" "int *limitp" "int *logp" +.Ft void +.Fn counter_rate_unregister "int which" .In sys/sysctl.h .Fn SYSCTL_COUNTER_U64 parent nbr name access ptr descr .Fn SYSCTL_ADD_COUNTER_U64 ctx parent nbr name access ptr descr @@ -130,18 +134,71 @@ Clear the counter .Fa c and set it to zero. -.It Fn counter_ratecheck cr limit +.It Fn counter_ratecheck which The function is a multiprocessor-friendly version of .Fn ppsratecheck , which uses .Nm internally. +The +.Fa which +argument defines the appropriate rate-limiting configuration. +(See sys/counter.h for valid +.Fa which +values.) Returns non-negative value if the rate is not yet reached during the current second, and a negative value otherwise. If the limit was reached on previous second, but was just reset back to zero, then .Fn counter_ratecheck returns number of events since previous reset. +.It Fn counter_rate_register which descr limitp logp +The function initializes the rate limit configuration for +.Fa which . +The +.Fa descr +argument must point to a description for the rate limit. +The +.Fa limitp +argument points to a limit which will be used for this rate limit configuration. +If +.Fa limitp +is NULL or points to a integer which has a non-positive value, then there is no +rate limit. +The +.Fa logp +argument points to an integer which controls whether +.Fn counter_ratecheck +should generate log messages when the limit is exceeded. +If +.Fa logp +is NULL or points to an integer with a zero value, then log messages will be +suppressed. +.Fn counter_ratecheck +checks the value of the +.Fa limitp +and +.Fa logp +variables on each invocation. +Therefore, changes to the value of the variables to which +.Fa limitp +and +.Fa logp +point will take effect immediately. +.It Fn counter_rate_unregister which +The function removes the rate-limiting configuration for +.Fa which . +There should be one call to +.Fn counter_rate_unregister +for each call to +.Fn counter_rate_register . +The code should not call +.Fn counter_rate_unregister +at the same time other threads may be calling +.Fn counter_ratecheck +for the same +.Fa which +value. .It Fn SYSCTL_COUNTER_U64 parent nbr name access ptr descr Declare a static .Xr sysctl 9 Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -4084,7 +4084,7 @@ netinet/ip_ecn.c optional inet | inet6 netinet/ip_encap.c optional inet | inet6 netinet/ip_fastfwd.c optional inet -netinet/ip_icmp.c optional inet | inet6 +netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_mroute.c optional mrouting inet netinet/ip_options.c optional inet Index: sys/kern/subr_counter.c =================================================================== --- sys/kern/subr_counter.c +++ sys/kern/subr_counter.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #define IN_SUBR_COUNTER_C @@ -120,6 +122,9 @@ return (0); } +static VNET_DEFINE(struct counter_rate, counter_rates[BANDLIM_MAX]); +#define V_counter_rates VNET(counter_rates) + /* * MP-friendly version of ppsratecheck(). * @@ -130,11 +135,26 @@ * is number of events since last reset. */ int64_t -counter_ratecheck(struct counter_rate *cr, int64_t limit) +counter_ratecheck(int which) { + struct counter_rate *cr; + int64_t limit; int64_t val; int now; + if (which == BANDLIM_UNLIMITED) + return (0); + + KASSERT(which >= 0 && which < BANDLIM_MAX, + ("%s: which value %d is out of range (0-%d)", __func__, which, + BANDLIM_MAX)); + + cr = &V_counter_rates[which]; + + /* Missing, negative, or 0 limit implies there is no limit. */ + if (cr->cr_limitp == NULL || (limit = *cr->cr_limitp) <= 0) + return (0); + val = cr->cr_over; now = ticks; @@ -164,7 +184,7 @@ * be running counter_u64_zero(), so it is not safe * to do an update, we skip it. */ - return (val); + goto done; } counter_u64_add(cr->cr_rate, 1); @@ -173,5 +193,79 @@ if (counter_u64_fetch(cr->cr_rate) > limit) val = cr->cr_over = -1; +done: + if (val > 0 && cr->cr_logp != NULL && *cr->cr_logp) + log(LOG_NOTICE, "Limited %s from %jd to %jd packets/sec\n", + cr->cr_descr, (intmax_t)val, (intmax_t)limit); return (val); } + +/* + * Register a counter rate limit. + * + * Arguments: + * which: One of the BANDLIM_* values (required). + * descr: A pointer to a description for the rate (required). + * limitp: A pointer to the limit value (no limit if NULL. + * logp: A pointer to an integer to control logging (if logging if NULL; + * otherwise, log is *logp is non-0). + */ +void +counter_rate_register(int which, const char *descr, int *limitp, int *logp) +{ + struct counter_rate *cr; + + KASSERT(which >= 0 && which < BANDLIM_MAX, + ("%s: which value %d is out of range (0-%d)", __func__, which, + BANDLIM_MAX)); + KASSERT(descr != NULL, ("%s: NULL description", __func__)); + KASSERT(V_counter_rates[which].cr_descr == NULL, + ("%s: attempt to reinitialize rate %d", __func__, which)); + + cr = &V_counter_rates[which]; + cr->cr_rate = counter_u64_alloc(M_WAITOK); + cr->cr_ticks = ticks; + cr->cr_limitp = limitp; + cr->cr_descr = descr; + cr->cr_logp = logp; +} + +/* + * Unregister a counter rate limit. + * + * This must be called one time for each counter_rate_register() call. + */ +void +counter_rate_unregister(int which) +{ + struct counter_rate *cr; + + KASSERT(which >= 0 && which < BANDLIM_MAX, + ("%s: which value %d is out of range (0-%d)", __func__, which, + BANDLIM_MAX)); + KASSERT(V_counter_rates[which].cr_descr != NULL, + ("%s: attempt to unregister unintialized rate %d", __func__, + which)); + + cr = &V_counter_rates[which]; + counter_u64_free(cr->cr_rate); + cr->cr_limitp = NULL; + cr->cr_descr = NULL; + cr->cr_logp = NULL; +} + +#if defined(VIMAGE) && defined(INVARIANTS) +static void +counter_rate_uninit(void) +{ + int i; + + for (i = 0; i < BANDLIM_MAX; i++) + KASSERT(V_counter_rates[i].cr_descr == NULL, + ("%s: Attempt to deinitialize VNET without deregistering " + "rate %d", __func__, i)); +} + +VNET_SYSUNINIT(counter_ratelimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, + counter_rate_uninit, NULL); +#endif Index: sys/netinet/icmp6.h =================================================================== --- sys/netinet/icmp6.h +++ sys/netinet/icmp6.h @@ -680,6 +680,9 @@ #define ICMPV6CTL_ND6_MAXQLEN 24 #define ICMPV6CTL_NODEINFO_OLDMCPREFIX 25 #define ICMPV6CTL_MAXID 26 +#define ICMPV6CTL_INFOPPSLIMIT 27 +#define ICMPV6CTL_ERRPPSOUTPUT 28 +#define ICMPV6CTL_INFOPPSOUTPUT 29 #define RTF_PROBEMTU RTF_PROTO1 Index: sys/netinet/icmp_var.h =================================================================== --- sys/netinet/icmp_var.h +++ sys/netinet/icmp_var.h @@ -86,17 +86,6 @@ #ifdef _KERNEL SYSCTL_DECL(_net_inet_icmp); - -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_ICMP6_UNREACH 5 -#define BANDLIM_SCTP_OOTB 6 -#define BANDLIM_MAX 7 #endif #endif Index: sys/netinet/ip_icmp.c =================================================================== --- sys/netinet/ip_icmp.c +++ sys/netinet/ip_icmp.c @@ -540,11 +540,10 @@ ICMPSTAT_INC(icps_bmcastecho); break; } - icp->icmp_type = ICMP_ECHOREPLY; - if (badport_bandlim(BANDLIM_ICMP_ECHO) < 0) + if (counter_ratecheck(BANDLIM_ICMP_ECHO) < 0) goto freeit; - else - goto reflect; + icp->icmp_type = ICMP_ECHOREPLY; + goto reflect; case ICMP_TSTAMP: if (V_icmptstamprepl == 0) @@ -558,13 +557,12 @@ ICMPSTAT_INC(icps_badlen); break; } + if (counter_ratecheck(BANDLIM_ICMP_TSTAMP) < 0) + goto freeit; 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) - goto freeit; - else - goto reflect; + goto reflect; case ICMP_MASKREQ: if (V_icmpmaskrepl == 0) @@ -960,47 +958,16 @@ #endif /* INET */ -/* - * badport_bandlim() - 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. - */ -struct icmp_rate { - const char *descr; - struct counter_rate cr; -}; -static VNET_DEFINE(struct icmp_rate, icmp_rates[BANDLIM_MAX]) = { - { "icmp unreach response" }, - { "icmp ping response" }, - { "icmp tstamp response" }, - { "closed port RST response" }, - { "open port RST response" }, - { "icmp6 unreach response" }, - { "sctp ootb response" } -}; -#define V_icmp_rates VNET(icmp_rates) - static void icmp_bandlimit_init(void) { - for (int i = 0; i < BANDLIM_MAX; i++) { - V_icmp_rates[i].cr.cr_rate = counter_u64_alloc(M_WAITOK); - V_icmp_rates[i].cr.cr_ticks = ticks; - } + counter_rate_register(BANDLIM_ICMP_UNREACH, "icmp unreach response", + &V_icmplim, &V_icmplim_output); + counter_rate_register(BANDLIM_ICMP_ECHO, "icmp ping response", + &V_icmplim, &V_icmplim_output); + counter_rate_register(BANDLIM_ICMP_TSTAMP, "icmp tstamp response", + &V_icmplim, &V_icmplim_output); } VNET_SYSINIT(icmp_bandlimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, icmp_bandlimit_init, NULL); @@ -1009,28 +976,10 @@ icmp_bandlimit_uninit(void) { - for (int i = 0; i < BANDLIM_MAX; i++) - counter_u64_free(V_icmp_rates[i].cr.cr_rate); + counter_rate_unregister(BANDLIM_ICMP_UNREACH); + counter_rate_unregister(BANDLIM_ICMP_ECHO); + counter_rate_unregister(BANDLIM_ICMP_TSTAMP); } VNET_SYSUNINIT(icmp_bandlimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, icmp_bandlimit_uninit, NULL); -int -badport_bandlim(int which) -{ - int64_t pps; - - if (V_icmplim == 0 || which == BANDLIM_UNLIMITED) - return (0); - - KASSERT(which >= 0 && which < BANDLIM_MAX, - ("%s: which %d", __func__, which)); - - pps = counter_ratecheck(&V_icmp_rates[which].cr, V_icmplim); - if (pps == -1) - return (-1); - if (pps > 0 && V_icmplim_output) - log(LOG_NOTICE, "Limiting %s from %jd to %d packets/sec\n", - V_icmp_rates[which].descr, (intmax_t )pps, V_icmplim); - return (0); -} Index: sys/netinet/sctp_input.c =================================================================== --- sys/netinet/sctp_input.c +++ sys/netinet/sctp_input.c @@ -5762,7 +5762,7 @@ } if (inp == NULL) { SCTP_STAT_INCR(sctps_noport); - if (badport_bandlim(BANDLIM_SCTP_OOTB) < 0) { + if (counter_ratecheck(BANDLIM_SCTP_OOTB) < 0) { goto out; } if (ch->chunk_type == SCTP_SHUTDOWN_ACK) { Index: sys/netinet/sctp_os_bsd.h =================================================================== --- sys/netinet/sctp_os_bsd.h +++ sys/netinet/sctp_os_bsd.h @@ -79,7 +79,6 @@ #include #include #include -#include #ifdef INET6 #include Index: sys/netinet/sctp_sysctl.h =================================================================== --- sys/netinet/sctp_sysctl.h +++ sys/netinet/sctp_sysctl.h @@ -114,6 +114,8 @@ uint32_t sctp_buffer_splitting; uint32_t sctp_initial_cwnd; uint32_t sctp_blackhole; + int sctp_badport_lim; + int sctp_badport_lim_output; #if defined(SCTP_DEBUG) uint32_t sctp_debug_on; #endif @@ -536,6 +538,16 @@ #define SCTPCTL_BLACKHOLE_MAX 2 #define SCTPCTL_BLACKHOLE_DEFAULT SCTPCTL_BLACKHOLE_MIN +#define SCTPCTL_BADPORT_LIM_DESC "Number of responses per second for segments to closed ports" +#define SCTPCTL_BADPORT_LIM_MIN -1 +#define SCTPCTL_BADPORT_LIM_MAX INT_MAX +#define SCTPCTL_BADPORT_LIM_DEFAULT 200 + +#define SCTPCTL_BADPORT_LIM_OUTPUT_DESC "Enable logging of rate limiting for responses to for segments to closed ports" +#define SCTPCTL_BADPORT_LIM_OUTPUT_MIN INT_MIN +#define SCTPCTL_BADPORT_LIM_OUTPUT_MAX INT_MAX +#define SCTPCTL_BADPORT_LIM_OUTPUT_DEFAULT 1 + #define SCTPCTL_DIAG_INFO_CODE_DESC "Diagnostic information error cause code" #define SCTPCTL_DIAG_INFO_CODE_MIN 0 #define SCTPCTL_DIAG_INFO_CODE_MAX 65535 Index: sys/netinet/sctp_sysctl.c =================================================================== --- sys/netinet/sctp_sysctl.c +++ sys/netinet/sctp_sysctl.c @@ -117,6 +117,8 @@ SCTP_BASE_SYSCTL(sctp_steady_step) = SCTPCTL_RTTVAR_STEADYS_DEFAULT; SCTP_BASE_SYSCTL(sctp_use_dccc_ecn) = SCTPCTL_RTTVAR_DCCCECN_DEFAULT; SCTP_BASE_SYSCTL(sctp_blackhole) = SCTPCTL_BLACKHOLE_DEFAULT; + SCTP_BASE_SYSCTL(sctp_badport_lim) = SCTPCTL_BADPORT_LIM_DEFAULT; + SCTP_BASE_SYSCTL(sctp_badport_lim_output) = SCTPCTL_BADPORT_LIM_OUTPUT_DEFAULT; SCTP_BASE_SYSCTL(sctp_diag_info_code) = SCTPCTL_DIAG_INFO_CODE_DEFAULT; #if defined(SCTP_LOCAL_TRACE_BUF) memset(&SCTP_BASE_SYSCTL(sctp_log), 0, sizeof(struct sctp_log)); @@ -851,6 +853,29 @@ CTLFLAG_VNET|CTLTYPE_UINT|CTLFLAG_RW, NULL, 0, \ sctp_sysctl_handle_##mib_name, "UI", prefix##_DESC); +#define SCTP_INT_SYSCTL(mib_name, var_name, prefix) \ + static int \ + sctp_sysctl_handle_##mib_name(SYSCTL_HANDLER_ARGS) \ + { \ + int error; \ + int new; \ + \ + new = SCTP_BASE_SYSCTL(var_name); \ + error = sysctl_handle_int(oidp, &new, 0, req); \ + if ((error == 0) && (req->newptr != NULL)) { \ + if ((new < prefix##_MIN) || \ + (new > prefix##_MAX)) { \ + error = EINVAL; \ + } else { \ + SCTP_BASE_SYSCTL(var_name) = new; \ + } \ + } \ + return (error); \ + } \ + SYSCTL_PROC(_net_inet_sctp, OID_AUTO, mib_name, \ + CTLFLAG_VNET|CTLTYPE_INT|CTLFLAG_RW, NULL, 0, \ + sctp_sysctl_handle_##mib_name, "I", prefix##_DESC); + /* * sysctl definitions */ @@ -931,6 +956,8 @@ SCTP_UINT_SYSCTL(rttvar_steady_step, sctp_steady_step, SCTPCTL_RTTVAR_STEADYS) SCTP_UINT_SYSCTL(use_dcccecn, sctp_use_dccc_ecn, SCTPCTL_RTTVAR_DCCCECN) SCTP_UINT_SYSCTL(blackhole, sctp_blackhole, SCTPCTL_BLACKHOLE) +SCTP_INT_SYSCTL(badport_lim, sctp_badport_lim, SCTPCTL_BADPORT_LIM) +SCTP_INT_SYSCTL(badport_lim_output, sctp_badport_lim_output, SCTPCTL_BADPORT_LIM_OUTPUT) SCTP_UINT_SYSCTL(diag_info_code, sctp_diag_info_code, SCTPCTL_DIAG_INFO_CODE) #ifdef SCTP_DEBUG SCTP_UINT_SYSCTL(debug, sctp_debug_on, SCTPCTL_DEBUG) Index: sys/netinet/sctp_usrreq.c =================================================================== --- sys/netinet/sctp_usrreq.c +++ sys/netinet/sctp_usrreq.c @@ -81,6 +81,9 @@ SCTP_BASE_SYSCTL(sctp_recvspace) = SCTP_BASE_SYSCTL(sctp_sendspace); SCTP_BASE_VAR(first_time) = 0; SCTP_BASE_VAR(sctp_pcb_initialized) = 0; + counter_rate_register(BANDLIM_SCTP_OOTB, "sctp ootb response", + &SCTP_BASE_SYSCTL(sctp_badport_lim), + &SCTP_BASE_SYSCTL(sctp_badport_lim_output)); sctp_pcb_init(); #if defined(SCTP_PACKET_LOGGING) SCTP_BASE_VAR(packet_log_writers) = 0; @@ -94,6 +97,7 @@ sctp_finish(void *unused __unused) { sctp_pcb_finish(); + counter_rate_unregister(BANDLIM_SCTP_OOTB); } VNET_SYSUNINIT(sctp, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, sctp_finish, NULL); Index: sys/netinet/tcp_input.c =================================================================== --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -88,8 +88,6 @@ #include #include #include -#include /* required for icmp_var.h */ -#include /* for ICMP_BANDLIM */ #include #include #include @@ -3324,7 +3322,7 @@ #endif /* Perform bandwidth limiting. */ - if (badport_bandlim(rstreason) < 0) + if (counter_ratecheck(rstreason) < 0) goto drop; /* tcp_respond consumes the mbuf chain. */ Index: sys/netinet/tcp_subr.c =================================================================== --- sys/netinet/tcp_subr.c +++ sys/netinet/tcp_subr.c @@ -232,6 +232,18 @@ VNET_DEFINE(struct hhook_head *, tcp_hhh[HHOOK_TCP_LAST+1]); #endif +static VNET_DEFINE(int, tcprstlim) = 200; +#define V_tcprstlim VNET(tcprstlim) +SYSCTL_INT(_net_inet_tcp, OID_AUTO, rstlim, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(tcprstlim), 0, + "Number of RSTs per second for invalid segments to open and closed ports"); + +static VNET_DEFINE(int, tcprstlim_output) = 1; +#define V_tcprstlim_output VNET(tcprstlim_output) +SYSCTL_INT(_net_inet_tcp, OID_AUTO, rstlim_output, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(tcprstlim_output), 0, + "Enable logging of RST rate limiting"); + static struct inpcb *tcp_notify(struct inpcb *, int); static struct inpcb *tcp_mtudisc_notify(struct inpcb *, int); static void tcp_mtudisc(struct inpcb *, int); @@ -669,6 +681,11 @@ tcp_fastopen_init(); #endif + counter_rate_register(BANDLIM_RST_CLOSEDPORT, + "closed port RST response", &V_tcprstlim, &V_tcprstlim_output); + counter_rate_register(BANDLIM_RST_OPENPORT, + "open port RST response", &V_tcprstlim, &V_tcprstlim_output); + /* Skip initialization of globals for non-default instances. */ if (!IS_DEFAULT_VNET(curvnet)) return; @@ -776,6 +793,9 @@ HHOOK_TYPE_TCP, HHOOK_TCP_EST_OUT, error); } #endif + + counter_rate_unregister(BANDLIM_RST_CLOSEDPORT); + counter_rate_unregister(BANDLIM_RST_OPENPORT); } VNET_SYSUNINIT(tcp, SI_SUB_PROTO_DOMAIN, SI_ORDER_FOURTH, tcp_destroy, NULL); #endif Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c +++ sys/netinet/udp_usrreq.c @@ -81,7 +81,6 @@ #include #endif #include -#include #include #include #ifdef INET6 @@ -690,7 +689,7 @@ } if (V_udp_blackhole) goto badunlocked; - if (badport_bandlim(BANDLIM_ICMP_UNREACH) < 0) + if (counter_ratecheck(BANDLIM_ICMP_UNREACH) < 0) goto badunlocked; *ip = save_ip; icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); Index: sys/netinet6/icmp6.c =================================================================== --- sys/netinet6/icmp6.c +++ sys/netinet6/icmp6.c @@ -122,20 +122,21 @@ VNET_DECLARE(struct inpcbinfo, ripcbinfo); VNET_DECLARE(struct inpcbhead, ripcb); VNET_DECLARE(int, icmp6errppslim); -static VNET_DEFINE(int, icmp6errpps_count) = 0; -static VNET_DEFINE(struct timeval, icmp6errppslim_last); +VNET_DECLARE(int, icmp6infoppslim); +VNET_DECLARE(int, icmp6errppslim_output); +VNET_DECLARE(int, icmp6infoppslim_output); VNET_DECLARE(int, icmp6_nodeinfo); #define V_ripcbinfo VNET(ripcbinfo) #define V_ripcb VNET(ripcb) #define V_icmp6errppslim VNET(icmp6errppslim) -#define V_icmp6errpps_count VNET(icmp6errpps_count) -#define V_icmp6errppslim_last VNET(icmp6errppslim_last) +#define V_icmp6infoppslim VNET(icmp6infoppslim) +#define V_icmp6errppslim_output VNET(icmp6errppslim_output) +#define V_icmp6infoppslim_output VNET(icmp6infoppslim_output) #define V_icmp6_nodeinfo VNET(icmp6_nodeinfo) static void icmp6_errcount(int, int); static int icmp6_rip6_input(struct mbuf **, int); -static int icmp6_ratelimit(const struct in6_addr *, const int, const int); static const char *icmp6_redirect_diag(struct in6_addr *, struct in6_addr *, struct in6_addr *); static struct mbuf *ni6_input(struct mbuf *, int); @@ -350,7 +351,7 @@ oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */ /* Finally, do rate limitation check. */ - if (icmp6_ratelimit(&oip6->ip6_src, type, code)) { + if (counter_ratecheck(BANDLIM_ICMP6_ERRORS) < 0) { ICMP6STAT_INC(icp6s_toofreq); goto freeit; } @@ -554,6 +555,8 @@ icmp6_ifstat_inc(ifp, ifs6_in_echo); if (code != 0) goto badcode; + if (counter_ratecheck(BANDLIM_ICMP6_INFOREPLIES) < 0) + break; if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) { /* Give up remote */ break; @@ -638,6 +641,8 @@ if (!V_icmp6_nodeinfo) break; + if (counter_ratecheck(BANDLIM_ICMP6_INFOREPLIES) < 0) + break; if (icmp6len == sizeof(struct icmp6_hdr) + 4) mode = WRU; @@ -2463,7 +2468,7 @@ goto fail; /* what should we do here? */ /* rate limit */ - if (icmp6_ratelimit(&sip6->ip6_src, ND_REDIRECT, 0)) + if (counter_ratecheck(BANDLIM_ICMP6_ERRORS) < 0) goto fail; /* @@ -2782,32 +2787,26 @@ return (error); } -/* - * Perform rate limit check. - * Returns 0 if it is okay to send the icmp6 packet. - * Returns 1 if the router SHOULD NOT send this icmp6 packet due to rate - * limitation. - * - * XXX per-destination/type check necessary? - * - * dst - not used at this moment - * type - not used at this moment - * code - not used at this moment - */ -static int -icmp6_ratelimit(const struct in6_addr *dst, const int type, - const int code) +static void +icmp6_bandlimit_init(void) { - int ret; - ret = 0; /* okay to send */ + counter_rate_register(BANDLIM_ICMP6_INFOREPLIES, + "icmp6 informational responses", &V_icmp6infoppslim, + &V_icmp6errppslim_output); + counter_rate_register(BANDLIM_ICMP6_ERRORS, + "icmp6 error responses", &V_icmp6errppslim, + &V_icmp6errppslim_output); +} +VNET_SYSINIT(icmp6_bandlimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, + icmp6_bandlimit_init, NULL); - /* PPS limit */ - if (!ppsratecheck(&V_icmp6errppslim_last, &V_icmp6errpps_count, - V_icmp6errppslim)) { - /* The packet is subject to rate limit */ - ret++; - } +static void +icmp6_bandlimit_uninit(void) +{ - return ret; + counter_rate_unregister(BANDLIM_ICMP6_INFOREPLIES); + counter_rate_unregister(BANDLIM_ICMP6_ERRORS); } +VNET_SYSUNINIT(icmp6_bandlimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, + icmp6_bandlimit_uninit, NULL); Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c +++ sys/netinet6/in6_proto.c @@ -419,6 +419,9 @@ VNET_DEFINE(int, icmp6_rediraccept) = 1;/* accept and process redirects */ VNET_DEFINE(int, icmp6_redirtimeout) = 10 * 60; /* 10 minutes */ VNET_DEFINE(int, icmp6errppslim) = 100; /* 100pps */ +VNET_DEFINE(int, icmp6infoppslim) = 100; /* 100pps */ +VNET_DEFINE(int, icmp6errppslim_output) = 1; +VNET_DEFINE(int, icmp6infoppslim_output) = 1; /* control how to respond to NI queries */ VNET_DEFINE(int, icmp6_nodeinfo) = (ICMP6_NODEINFO_FQDNOK|ICMP6_NODEINFO_NODEADDROK); @@ -607,6 +610,15 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, errppslimit, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6errppslim), 0, "Maximum number of ICMPv6 error messages per second"); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSOUTPUT, errppslimit_output, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6errppslim_output), 0, + "Enable logging of ICMPv6 error message rate limiting"); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_INFOPPSLIMIT, infoppslimit, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6infoppslim), 0, + "Maximum number of ICMPv6 informational response messages per second"); +SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_INFOPPSOUTPUT, infoppslimit_output, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6infoppslim_output), 0, + "Enable logging of ICMPv6 informational response rate limiting"); SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ND6_MAXNUDHINT, nd6_maxnudhint, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_maxnudhint), 0, ""); /* XXX unused */ Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c +++ sys/netinet6/udp6_usrreq.c @@ -104,9 +104,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -481,8 +479,6 @@ } if (V_udp_blackhole) goto badunlocked; - if (badport_bandlim(BANDLIM_ICMP6_UNREACH) < 0) - goto badunlocked; icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); return (IPPROTO_DONE); } Index: sys/sys/counter.h =================================================================== --- sys/sys/counter.h +++ sys/sys/counter.h @@ -65,12 +65,33 @@ */ struct counter_rate { counter_u64_t cr_rate; /* Events since last second */ + int *cr_limitp; /* Pointer to limit */ + const char *cr_descr; /* Description */ + int *cr_logp; /* Whether to log failures */ volatile int cr_lock; /* Lock to clean the struct */ int cr_ticks; /* Ticks on last clean */ int cr_over; /* Over limit since cr_ticks? */ }; -int64_t counter_ratecheck(struct counter_rate *, int64_t); +int64_t counter_ratecheck(int which); +void counter_rate_register(int which, const char *descr, int *limitp, + int *logp); +void counter_rate_unregister(int which); + +#define BANDLIM_UNLIMITED -1 +enum counter_rate_specs { + BANDLIM_ICMP_UNREACH = 0, + BANDLIM_ICMP_ECHO, + BANDLIM_ICMP_TSTAMP, + BANDLIM_RST_CLOSEDPORT, + BANDLIM_RST_OPENPORT, + BANDLIM_SCTP_OOTB, + BANDLIM_ICMP6_INFOREPLIES, + BANDLIM_ICMP6_ERRORS, + + /* Add new entries before BANDLIM_MAX. */ + BANDLIM_MAX +}; #endif /* _KERNEL */ #endif /* ! __SYS_COUNTER_H__ */