From nobody Wed Jun 26 04:50:27 2024 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 4W88QW6vY2z5P0Bh; Wed, 26 Jun 2024 04:50:27 +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 "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4W88QW573jz57X1; Wed, 26 Jun 2024 04:50:27 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1719377427; 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=zeKkbbKQMu1eDPVPjnykIeUjSuTvfkrsc7cmEyi7nxo=; b=xAxt9DiN6fX7VALv4nfNUDG0ecfu19uLgtnYOgpkSOYCmwrjfEajozy7kSG7VYhXf/EDeO CA4ACv3k9Vh9797oz70i9Wc8FTm/hfMWzwHIEJZwheTZFWCJc3EkiIENcpDwrjZ3dh+Y4f 9PdcSGMk5ML1H595DFVwpeaX58gcpR/ei9DFtqRG8x3wl0ZHcRdnD5ToneNvCND1qjtnLI vojduiVSYvWKkOph1yB+jsH6NmjU3KBxDWGcLmIOscTF8aAJKwHF1dU5ldQ3qc48YVv0G7 U/aW9Yc/mSAxYpesM4xMpAYmSJd855bjeGt0JTTsQwZXWrAc7xYmeUBBXWqxjQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1719377427; a=rsa-sha256; cv=none; b=N0RoEVuhRZETwUsc4OuOwBnHs3z0GdPlNQLO02J7Dbl+18Ny1MGdRpOEKue3qkYEqX4FF6 Gm79um0BsIyeZxsbBF8+m1O6aqZ37JMEcV3eZcCrl1pwS/WLXfb1HbMMo/oooos6dquE/n aNUIv7p4U01ZmKzeJYzcEkoZ+QrEUtib9g6vq27YvmMzJpqnRk4i4l6NA3enTcxISKPaIt A1KjAJxV/sfeslBsB40xGvBhDU9qMfpTsGMdj+B5LYiOCQSObGwdM6n7lzh8dDwPitu91Q eFkOMon4BZmbqN7sLMHuZQf1fbTsl86P5d3hrNBP5S6O8NHYlhHQdHsroeX6mA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1719377427; 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=zeKkbbKQMu1eDPVPjnykIeUjSuTvfkrsc7cmEyi7nxo=; b=qCGpQbogJurLc2nxClcrODA+e8/ZlLN6/+ZzGgtJBVotmjE0aj3wCX7W2wzQhSYuGzLU2+ KB1h0VNpyBRlh28yhGakZgLBGh9dcp0cV52tHrwjNouvdNGuEZ8GnKObGiGkZv0sFI8UxZ hxpQ2VcT7qcZa0BHWgTRa2pUl7YF5Ree6WmkFNBeD3H5Z5gEE1SIXul/2hwVQWloQnQSyt Pb1oZNL2lVUmx0i0bjU2lEf5iSZkbiGrrtAMMTTk/QSavj7JLz3LH/hu0IuMmbZprpEOCl Z5czNwL8V3bNJZgEQFZLcJKEzL8zCbVvH7wlAypdi4/xc+6SvIl8gqRYYpmKPA== 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 4W88QW4kGfzX6B; Wed, 26 Jun 2024 04:50:27 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 45Q4oRCa063941; Wed, 26 Jun 2024 04:50:27 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 45Q4oRAJ063938; Wed, 26 Jun 2024 04:50:27 GMT (envelope-from git) Date: Wed, 26 Jun 2024 04:50:27 GMT Message-Id: <202406260450.45Q4oRAJ063938@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Zhenlei Huang Subject: git: b544e62dd00f - stable/14 - 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: zlei X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: b544e62dd00f167a3d8b4df3dd31fcd7bf1873a8 Auto-Submitted: auto-generated The branch stable/14 has been updated by zlei: URL: https://cgit.FreeBSD.org/src/commit/?id=b544e62dd00f167a3d8b4df3dd31fcd7bf1873a8 commit b544e62dd00f167a3d8b4df3dd31fcd7bf1873a8 Author: Gleb Smirnoff AuthorDate: 2024-03-24 16:13:23 +0000 Commit: Zhenlei Huang CommitDate: 2024-06-26 04:48:44 +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. Reviewed by: tuexen, zlei Differential Revision: https://reviews.freebsd.org/D44482 (cherry picked from commit a03aff88a14448c3084a0384082ec996d7213897) --- sys/netinet6/icmp6.c | 185 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 162 insertions(+), 23 deletions(-) diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 996416856b20..4bce366c7ed4 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -145,18 +145,6 @@ SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_NODEINFO, nodeinfo, VNET_DECLARE(struct inpcbinfo, ripcbinfo); #define V_ripcbinfo VNET(ripcbinfo) -VNET_DEFINE_STATIC(int, icmp6errppslim) = 100; -#define V_icmp6errppslim VNET(icmp6errppslim) -SYSCTL_INT(_net_inet6_icmp6, ICMPV6CTL_ERRPPSLIMIT, errppslimit, - CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(icmp6errppslim), 0, - "Maximum number of ICMPv6 error messages per second"); - -VNET_DEFINE_STATIC(int, icmp6errpps_count) = 0; -VNET_DEFINE_STATIC(struct timeval, icmp6errppslim_last); - -#define V_icmp6errpps_count VNET(icmp6errpps_count) -#define V_icmp6errppslim_last VNET(icmp6errppslim_last) - static void icmp6_errcount(int, int); static int icmp6_rip6_input(struct mbuf **, int); static void icmp6_reflect(struct mbuf *, size_t); @@ -2745,6 +2733,126 @@ icmp6_ctloutput(struct socket *so, struct sockopt *sopt) return (error); } +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. @@ -2754,24 +2862,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 */ 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); }