From nobody Thu Feb 13 16:46:17 2025 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Yv1KQ0v8hz5mycw; Thu, 13 Feb 2025 16:46:18 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Yv1KP20r7z3NFs; Thu, 13 Feb 2025 16:46:17 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1739465177; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=n34Q1Q79RDO9OJq4c2HIRHmUMji9Du9ALjoYZweDyYA=; b=D6m4WzlGdhng822JwdDe7NYQG+wYU6Q7GUAkqdn5GAQYsnPUbgxvTJLEGmiwbKvtRqjgQT yj5vHIdhmUaodqo2K5sVucfY6Yk3Gd0fgu9gLhqWkYKiIlbalnQVSStjfPjhwWtLuNXCNI 5r1wXE8yJtrDz6OLiq481qHK3jLoOvm+n2AOF5EvuU6wmn5ztvqqxROIVCpoAMirhW8GyJ vrdihPWPUjJmoZ69sXhzO5wp4JbYevvKNmEPc2EK5a0gRFzh347PpkT3ASStIMJX47WaMN KZpgNPXbnww+jI3yK7Wg5KiZo7DRFUj5vs6v+d43Dbd02DGU9KVReO6ewMRcYQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1739465177; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=n34Q1Q79RDO9OJq4c2HIRHmUMji9Du9ALjoYZweDyYA=; b=C2Je/lDsbsUjArgvkGsMpEQdkB5yeSQGsNRZlXl8d85FdO0AXdhn4tG7cUPHDUugSTfHzL kQ5F8KlUYisnigZScMpSCtQBa7ZVBf9ITl/yGAzs6ABCq8AvJOPtGOGGoYrZ5yIyaW8qO9 1hZ1JB4/lzuvchIp9L67xyewLrkN57I/fLyvjj3GZOmo4/o1qcREnnwmQqJYVOyZ11Vy91 3NgABzOxqoGvJSJSF0EIEwo2UpzIDQbm0ME+7h+O9RF00FguvDLkp26jNvuZmuNSdcT35D gKBEGNs21HI5SK5Vh7z3RcBu3UVis0ewo8Q37Gv0R/jEMobAtrZwqXEg/CUOBA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1739465177; a=rsa-sha256; cv=none; b=kKE2ChIUn9qIf82j1ZpAvF/eHV8Kv+OpK1N3xLu+z42hw3vd/9klSQVNvkpOAiuwhf+n5x daQUZNBFBQgeoTThjcVskpJ0QMdaYgbVxTu7teZr9DBmFbAPjB1Aaib89okBeTcokp9pr6 a+Bxy1Vjw2JWQTZd+ODBSFjdJRmhz0aUDey2Pox9rsBR8P10UPDQiEKYvyzL0dA4iajYuS BXR2bDazem9GU2sdm055lCz9VV6RxtAwXamtmko6LnMmlIccCW4UAOJrgnty6vG/xKZIJw FflLFTqjw+VVzSzhu92ia0Tbg97195S0wwagW3sZMjoCdO+qp9vZ1AcPVaYeUQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Yv1KP1SfyztCh; Thu, 13 Feb 2025 16:46:17 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 51DGkH5t041676; Thu, 13 Feb 2025 16:46:17 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 51DGkHjT041673; Thu, 13 Feb 2025 16:46:17 GMT (envelope-from git) Date: Thu, 13 Feb 2025 16:46:17 GMT Message-Id: <202502131646.51DGkHjT041673@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Michael Tuexen Subject: git: ebda0be762bf - releng/13.5 - icmp6: bring rate limiting on a par with IPv4 List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: tuexen X-Git-Repository: src X-Git-Refname: refs/heads/releng/13.5 X-Git-Reftype: branch X-Git-Commit: ebda0be762bfeba92717ed642bb4f51e02b5793a Auto-Submitted: auto-generated The branch releng/13.5 has been updated by tuexen: URL: https://cgit.FreeBSD.org/src/commit/?id=ebda0be762bfeba92717ed642bb4f51e02b5793a commit ebda0be762bfeba92717ed642bb4f51e02b5793a Author: Gleb Smirnoff AuthorDate: 2024-03-24 16:13:23 +0000 Commit: Michael Tuexen CommitDate: 2025-02-13 14:01:36 +0000 icmp6: bring rate limiting on a par with IPv4 Use counter_ratecheck() instead of racy and slow ppsratecheck. Use a separate counter for every currently known type of ICMPv6. Provide logging of ratelimit events. Provide jitter to counter open UDP port detection. With minor tweaks from tuexen@ to fix merge problems. Reviewed by: tuexen, zlei Differential Revision: https://reviews.freebsd.org/D44482 Approved by: re (cperciva) (cherry picked from commit a03aff88a14448c3084a0384082ec996d7213897) (cherry picked from commit 1cc0ac5ef37e134c26d93d4544633b458e46aa78) --- sys/netinet6/icmp6.c | 181 ++++++++++++++++++++++++++++++++++++++++++----- sys/netinet6/in6_proto.c | 4 -- 2 files changed, 164 insertions(+), 21 deletions(-) diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 09cb893b57fe..e1b0196fc62b 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -123,16 +123,10 @@ VNET_PCPUSTAT_SYSUNINIT(icmp6stat); VNET_DECLARE(struct inpcbinfo, ripcbinfo); VNET_DECLARE(struct inpcbhead, ripcb); -VNET_DECLARE(int, icmp6errppslim); -VNET_DEFINE_STATIC(int, icmp6errpps_count) = 0; -VNET_DEFINE_STATIC(struct timeval, icmp6errppslim_last); 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_icmp6_nodeinfo VNET(icmp6_nodeinfo) static void icmp6_errcount(int, int); @@ -2774,6 +2768,128 @@ icmp6_ctloutput(struct socket *so, struct sockopt *sopt) return (error); } +SYSCTL_DECL(_net_inet6_icmp6); + +static int sysctl_icmp6lim_and_jitter(SYSCTL_HANDLER_ARGS); +VNET_DEFINE_STATIC(u_int, icmp6errppslim) = 100; +#define V_icmp6errppslim VNET(icmp6errppslim) +SYSCTL_PROC(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, errppslimit, + CTLTYPE_UINT | CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6errppslim), 0, + &sysctl_icmp6lim_and_jitter, "IU", + "Maximum number of ICMPv6 error/reply messages per second"); + +VNET_DEFINE_STATIC(int, icmp6lim_curr_jitter) = 0; +#define V_icmp6lim_curr_jitter VNET(icmp6lim_curr_jitter) + +VNET_DEFINE_STATIC(u_int, icmp6lim_jitter) = 8; +#define V_icmp6lim_jitter VNET(icmp6lim_jitter) +SYSCTL_PROC(_net_inet6_icmp6, OID_AUTO, icmp6lim_jitter, CTLTYPE_UINT | + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6lim_jitter), 0, + &sysctl_icmp6lim_and_jitter, "IU", + "Random errppslimit jitter adjustment limit"); + +VNET_DEFINE_STATIC(int, icmp6lim_output) = 1; +#define V_icmp6lim_output VNET(icmp6lim_output) +SYSCTL_INT(_net_inet6_icmp6, OID_AUTO, icmp6lim_output, + CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6lim_output), 0, + "Enable logging of ICMPv6 response rate limiting"); + +typedef enum { + RATELIM_PARAM_PROB = 0, + RATELIM_TOO_BIG, + RATELIM_UNREACH, + RATELIM_TEXCEED, + RATELIM_REDIR, + RATELIM_REPLY, + RATELIM_OTHER, + RATELIM_MAX +} ratelim_which; + +static const char *icmp6_rate_descrs[RATELIM_MAX] = { + [RATELIM_PARAM_PROB] = "bad IPv6 header", + [RATELIM_TOO_BIG] = "packet too big", + [RATELIM_UNREACH] = "destination unreachable", + [RATELIM_TEXCEED] = "time exceeded", + [RATELIM_REPLY] = "echo reply", + [RATELIM_REDIR] = "neighbor discovery redirect", + [RATELIM_OTHER] = "(other)", +}; + +static void +icmp6lim_new_jitter(void) +{ + /* + * Adjust limit +/- to jitter the measurement to deny a side-channel + * port scan as in https://dl.acm.org/doi/10.1145/3372297.3417280 + */ + if (V_icmp6lim_jitter > 0) + V_icmp6lim_curr_jitter = + arc4random_uniform(V_icmp6lim_jitter * 2 + 1) - + V_icmp6lim_jitter; +} + +static int +sysctl_icmp6lim_and_jitter(SYSCTL_HANDLER_ARGS) +{ + uint32_t new; + int error; + bool lim; + + MPASS(oidp->oid_arg1 == &VNET_NAME(icmp6errppslim) || + oidp->oid_arg1 == &VNET_NAME(icmp6lim_jitter)); + + lim = (oidp->oid_arg1 == &VNET_NAME(icmp6errppslim)); + new = lim ? V_icmp6errppslim : V_icmp6lim_jitter; + error = sysctl_handle_int(oidp, &new, 0, req); + if (error == 0 && req->newptr) { + if (lim) { + if (new <= V_icmp6lim_jitter) + error = EINVAL; + else + V_icmp6errppslim = new; + } else { + if (new >= V_icmp6errppslim) + error = EINVAL; + else { + V_icmp6lim_jitter = new; + icmp6lim_new_jitter(); + } + } + } + MPASS(V_icmp6errppslim + V_icmp6lim_curr_jitter > 0); + + return (error); +} + + +VNET_DEFINE_STATIC(struct counter_rate, icmp6_rates[RATELIM_MAX]); +#define V_icmp6_rates VNET(icmp6_rates) + +static void +icmp6_ratelimit_init(void) +{ + + for (int i = 0; i < RATELIM_MAX; i++) { + V_icmp6_rates[i].cr_rate = counter_u64_alloc(M_WAITOK); + V_icmp6_rates[i].cr_ticks = ticks; + } + icmp6lim_new_jitter(); +} +VNET_SYSINIT(icmp6_ratelimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, + icmp6_ratelimit_init, NULL); + +#ifdef VIMAGE +static void +icmp6_ratelimit_uninit(void) +{ + + for (int i = 0; i < RATELIM_MAX; i++) + counter_u64_free(V_icmp6_rates[i].cr_rate); +} +VNET_SYSUNINIT(icmp6_ratelimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, + icmp6_ratelimit_uninit, NULL); +#endif + /* * Perform rate limit check. * Returns 0 if it is okay to send the icmp6 packet. @@ -2783,24 +2899,55 @@ icmp6_ctloutput(struct socket *so, struct sockopt *sopt) * 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) +icmp6_ratelimit(const struct in6_addr *dst, const int type, const int code) { - int ret; + ratelim_which which; + int64_t pps; - ret = 0; /* okay to send */ + if (V_icmp6errppslim == 0) + return (0); - /* PPS limit */ - if (!ppsratecheck(&V_icmp6errppslim_last, &V_icmp6errpps_count, - V_icmp6errppslim)) { - /* The packet is subject to rate limit */ - ret++; + switch (type) { + case ICMP6_PARAM_PROB: + which = RATELIM_PARAM_PROB; + break; + case ICMP6_PACKET_TOO_BIG: + which = RATELIM_TOO_BIG; + break; + case ICMP6_DST_UNREACH: + which = RATELIM_UNREACH; + break; + case ICMP6_TIME_EXCEEDED: + which = RATELIM_TEXCEED; + break; + case ND_REDIRECT: + which = RATELIM_REDIR; + break; + case ICMP6_ECHO_REPLY: + which = RATELIM_REPLY; + break; + default: + which = RATELIM_OTHER; + break; + }; + + pps = counter_ratecheck(&V_icmp6_rates[which], V_icmp6errppslim + + V_icmp6lim_curr_jitter); + if (pps > 0) { + if (V_icmp6lim_output) + log(LOG_NOTICE, "Limiting ICMPv6 %s output from %jd " + "to %d packets/sec\n", icmp6_rate_descrs[which], + (intmax_t )pps, V_icmp6errppslim + + V_icmp6lim_curr_jitter); + icmp6lim_new_jitter(); + } + if (pps == -1) { ICMP6STAT_INC(icp6s_toofreq); + return (-1); } - return ret; + return (0); } diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c index cfa368e7f7b0..0416d938995f 100644 --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -409,7 +409,6 @@ VNET_DEFINE(int, pmtu_probe) = 60*2; /* ICMPV6 parameters */ 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 */ /* control how to respond to NI queries */ VNET_DEFINE(int, icmp6_nodeinfo) = 0; VNET_DEFINE(int, icmp6_nodeinfo_oldmcprefix) = 1; @@ -592,9 +591,6 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_NODEINFO_OLDMCPREFIX, &VNET_NAME(icmp6_nodeinfo_oldmcprefix), 0, "Join old IPv6 NI group address in draft-ietf-ipngwg-icmp-name-lookup " "for compatibility with KAME implementation"); -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_ND6_MAXNUDHINT, nd6_maxnudhint, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nd6_maxnudhint), 0, ""); /* XXX unused */