From nobody Sun Mar 24 16:19:34 2024 X-Original-To: dev-commits-src-main@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 4V2h931N7Kz5G0C0; Sun, 24 Mar 2024 16:19:35 +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 4V2h92661Gz4GM2; Sun, 24 Mar 2024 16:19:34 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1711297174; 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=k3yRA9e4dlt/XXZ1AFH96pIIOhDpGbjixeY3p5IOEx4=; b=yz4ecQVfjtJGQadzIXFFcSJB1Kxbbht93jLTrioXDOvDXpL+J7vZYj25EWMEkcxN2dkUyf 4VfoYIWEQIiHc+FwYjexk/5VeFIVIQ+CFt5po+Qbgvb4IJPCdJciH0NyWlMw/JOhh7KrwA tyIdHJqifkIY63Aj0c13vLzh3AiZWN1JNSWNlVofbl+FZPLeY4KDra2TqVs/4WX4j4snLu 6dFzvKer24Lrh8EBxHLBeS4BowHafN+5pH15cjUXnsVHA5eIxbxOx0+b2gCUbYalgxO4ha Bj8Ya8954eXLqvLInnAk2tFFaq3h7WqTbd1cDAHjdkgBEtmM3GWVIJpL+JxNfQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1711297174; a=rsa-sha256; cv=none; b=pz3EDQbAynWmvmznwhH7iqrsfmSO295GzDpnpJxXBbyRT2+K/TKzlu/E4KX/dXSi0+wjPz zE65nBMwePj5dGdaUqGvLk2H7Z9KNWH07pMhHfr4pX+HiuGud3RHQ7FGAbU+N2C+r/Vl/n UcvTe/UcqDO4PtbIP1NRpExv6cYdXfhyD+QvVTfLQUCwcwCRd8fuB8ZktSmMpiPxSGhQfk QesnVIitRfjSm1FZ0ZM7RJR+U+BbvqK7idA8MXRSIoNY+JfshZT35ZjdxMLmaUiFVtXXPK rGJVOALy8oPFzpbK32s5wd1bk0B2FrnffnM5+rl7lkhic/tyAi2SaxnZSOA/ag== 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=1711297174; 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=k3yRA9e4dlt/XXZ1AFH96pIIOhDpGbjixeY3p5IOEx4=; b=pqy/ulXTMwadi8IwZcM22ORlXd7fpt1GlRHvb6QKgZZXF6VL8wDJMvYgne4RQOZ7MJQjim tp6gtfOkYqOc7Y6ci94HpOJmCq4lZdEEKVyuv/cIByrhKpqAu7xQZVxGCy9WdLFAGRf2ex wnykCDweTKNgUARXrAhMIC4087qKadknAGfp4kSM4CyqxlQIuzsyWlLwIYO4kaO9wshP1a j+jLTAEtOZLg9gF2x8+UvqFgHfH9/9QGVMe8rYMhwaGG8kbk6qpW8eEriNkPRiv53kHYgl 846zPUouh0r5z1JJ1yZD8Q8imJQcsp3CqRGSIwMzYad2ZSk44TNiL+uoHHZDqA== 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 4V2h925hKqzfST; Sun, 24 Mar 2024 16:19:34 +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 42OGJYiA079683; Sun, 24 Mar 2024 16:19:34 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 42OGJYU9079680; Sun, 24 Mar 2024 16:19:34 GMT (envelope-from git) Date: Sun, 24 Mar 2024 16:19:34 GMT Message-Id: <202403241619.42OGJYU9079680@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Gleb Smirnoff Subject: git: a03aff88a144 - main - icmp6: bring rate limiting on a par with IPv4 List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: glebius X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: a03aff88a14448c3084a0384082ec996d7213897 Auto-Submitted: auto-generated The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=a03aff88a14448c3084a0384082ec996d7213897 commit a03aff88a14448c3084a0384082ec996d7213897 Author: Gleb Smirnoff AuthorDate: 2024-03-24 16:13:23 +0000 Commit: Gleb Smirnoff CommitDate: 2024-03-24 16:13:23 +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 --- 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 8f039d6dd196..ef4216b844b5 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -143,18 +143,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); @@ -2743,6 +2731,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. @@ -2752,24 +2860,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); }