From patchwork Sun Apr 14 23:24:31 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Suryaputra X-Patchwork-Id: 1085405 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="la86336v"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 44j7yn34TRz9s3q for ; Mon, 15 Apr 2019 10:04:17 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725975AbfDNX65 (ORCPT ); Sun, 14 Apr 2019 19:58:57 -0400 Received: from mail-qk1-f194.google.com ([209.85.222.194]:33029 "EHLO mail-qk1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725372AbfDNX64 (ORCPT ); Sun, 14 Apr 2019 19:58:56 -0400 Received: by mail-qk1-f194.google.com with SMTP id k189so8858664qkc.0 for ; Sun, 14 Apr 2019 16:58:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=CZLfhKpzLaLTZhEuK3f/K4usTN78TZ80g+hjtw33evw=; b=la86336vlAU6p9qgC3O4vSDQmySxoCNqCbKWzSCWePY0katMQ6MF6A2dQwhrTsrrTs +yh/2xJmgA12JDxHCYizh1w4bmBRtm/2wx0HEThjgJgCfRbHi6qNq9NHAU8qNsQmxXUM peZVnZ3NhSgs524Bq1ZKBGVIQIKWX5xirD8tW3DJSz1L/tgHwk/nLyNTChOYT26Pghag FAEq2Pr0ShSY0ypHPHHiGtlAovLWDMaXq4R+O14+Qy6UqUS6I1miJfCEiopv82n00Mrr nsVxvL/jYUSW8eQhLpt9tgqGgtjxZqt75BqM607ak9B42yfdCxczR9Uwv3ORZTtGIbHZ wwYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=CZLfhKpzLaLTZhEuK3f/K4usTN78TZ80g+hjtw33evw=; b=Tua3BkS7Ja78AjjOhpDGOcDMAoFotHibpx5ST3Sq8VPdm8OsT3Sd7TdUJVZq0FJwu0 chVkHkRy0MmPA9VL4jKHdArp2tw+QJi89xZZjsabAeBeQH659Xr84cZ2pZSF6KhGlr+x 2uoVKTl/xfG311OFCFYJcbDEGFmR02bERX5hHaDsmwNCjEtJbXfehqJWQom38ngI8gmd wbh351e+1gkXAVhs3WJFhRhJf6ziJVGOrrksfnPbqNpN7mFK98ZTrfzOMNLEM2tn5/Ql tkK9bxXs/KWpoZyAYlnxhO8YZFhnPnHT67x1opOUiJ+x3S+9mofVfBnOenvc5eCWTx7C O3vA== X-Gm-Message-State: APjAAAUZwybiLBQ+LysMvhf30dRZVHYOqjlHYA5H7uy7tozlwkz8n5Dc yfTsG5PMX4EC7Eqihd81gsXRbgE= X-Google-Smtp-Source: APXvYqwBm6soxqFxq0djgCAV+dFJXyl5moa2AymRJn9OB4scLm4V3HTMWnOgsqd9WsUusQLMlg8h/g== X-Received: by 2002:ae9:c017:: with SMTP id u23mr57563284qkk.143.1555286335071; Sun, 14 Apr 2019 16:58:55 -0700 (PDT) Received: from localhost.localdomain (99-149-127-125.lightspeed.rlghnc.sbcglobal.net. [99.149.127.125]) by smtp.gmail.com with ESMTPSA id c84sm27650503qke.0.2019.04.14.16.58.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 14 Apr 2019 16:58:54 -0700 (PDT) From: Stephen Suryaputra To: netdev@vger.kernel.org Cc: brouer@redhat.com, Stephen Suryaputra Subject: [PATCH net-next] ipv6: Add rate limit mask for ICMPv6 messages Date: Sun, 14 Apr 2019 19:24:31 -0400 Message-Id: <20190414232431.21091-1-ssuryaextr@gmail.com> X-Mailer: git-send-email 2.17.1 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org To make ICMPv6 closer to ICMPv4, add ratemask parameter. Since the ICMP message types use larger numeric values, a simple bitmask doesn't fit. I use large bitmap. The input and output are the in form of list of ranges. Set the default to rate limit all error messages but Packet Too Big. For Packet Too Big, use ratemask instead of hard-coded. There are functions where icmpv6_xrlim_allow() and icmpv6_global_allow() aren't called. This patch only adds them to icmpv6_echo_reply(). Rate limiting error messages is mandated by RFC 4443 but RFC 4890 says that it is also acceptable to rate limit informational messages. Thus, I removed the current hard-coded behavior of icmpv6_mask_allow() that doesn't rate limit informational messages. Signed-off-by: Stephen Suryaputra --- Documentation/networking/ip-sysctl.txt | 13 +++++++++- include/net/netns/ipv6.h | 2 ++ include/uapi/linux/icmpv6.h | 4 ++++ kernel/sysctl.c | 1 + net/ipv6/af_inet6.c | 12 ++++++++++ net/ipv6/icmp.c | 33 +++++++++++++++++++------- 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 5eedc6941ce5..b03aa46b2039 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1913,11 +1913,22 @@ enhanced_dad - BOOLEAN icmp/*: ratelimit - INTEGER - Limit the maximal rates for sending ICMPv6 packets. + Limit the maximal rates for sending ICMPv6 messages. 0 to disable any limiting, otherwise the minimal space between responses in milliseconds. Default: 1000 +ratemask - list of comma separated ranges + For ICMPv6 message types matching the ranges in the ratemask, limit + the sending of the message according to ratelimit parameter. + + The format used for both input and output is a comma separated + list of ranges (e.g. "0-127,129" for ICMPv6 message type 0 to 127 and + 129). Writing to the file will clear all previous ranges of ICMPv6 + message types and update the current list with the input. + + Default: 0-1,3-127 (rate limit ICMPv6 errors except Packet Too Big) + echo_ignore_all - BOOLEAN If set non-zero, then the kernel will ignore all ICMP ECHO requests sent to it over the IPv6 protocol. diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 64e29b58bb5e..de438b67dcb7 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -8,6 +8,7 @@ #ifndef __NETNS_IPV6_H__ #define __NETNS_IPV6_H__ #include +#include struct ctl_table_header; @@ -35,6 +36,7 @@ struct netns_sysctl_ipv6 { int icmpv6_echo_ignore_all; int icmpv6_echo_ignore_multicast; int icmpv6_echo_ignore_anycast; + unsigned long *icmpv6_ratemask; int anycast_src_echo_reply; int ip_nonlocal_bind; int fwmark_reflect; diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h index 325395f56bfa..2622b5a3e616 100644 --- a/include/uapi/linux/icmpv6.h +++ b/include/uapi/linux/icmpv6.h @@ -90,6 +90,8 @@ struct icmp6hdr { #define ICMPV6_TIME_EXCEED 3 #define ICMPV6_PARAMPROB 4 +#define ICMPV6_ERRMSG_MAX 127 + #define ICMPV6_INFOMSG_MASK 0x80 #define ICMPV6_ECHO_REQUEST 128 @@ -110,6 +112,8 @@ struct icmp6hdr { #define ICMPV6_MRDISC_ADV 151 +#define ICMPV6_MSG_MAX 255 + /* * Codes for Destination Unreachable */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c9ec050bcf46..85a967b7bd2f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -3366,3 +3366,4 @@ EXPORT_SYMBOL(proc_dointvec_ms_jiffies); EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_doulongvec_minmax); EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); +EXPORT_SYMBOL(proc_do_large_bitmap); diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 1dac6ea6666a..037416071e08 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -850,6 +850,14 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.icmpv6_echo_ignore_all = 0; net->ipv6.sysctl.icmpv6_echo_ignore_multicast = 0; net->ipv6.sysctl.icmpv6_echo_ignore_anycast = 0; + net->ipv6.sysctl.icmpv6_ratemask = kzalloc(ICMPV6_MSG_MAX + 1, GFP_KERNEL); + if (net->ipv6.sysctl.icmpv6_ratemask) { + /* By default, rate limit error messages. + * Except for pmtu discovery, it would break it. + */ + bitmap_set(net->ipv6.sysctl.icmpv6_ratemask, 0, ICMPV6_ERRMSG_MAX + 1); + bitmap_clear(net->ipv6.sysctl.icmpv6_ratemask, ICMPV6_PKT_TOOBIG, 1); + } net->ipv6.sysctl.flowlabel_consistency = 1; net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS; net->ipv6.sysctl.idgen_retries = 3; @@ -890,6 +898,10 @@ static int __net_init inet6_net_init(struct net *net) static void __net_exit inet6_net_exit(struct net *net) { + if (net->ipv6.sysctl.icmpv6_ratemask) { + kfree(net->ipv6.sysctl.icmpv6_ratemask); + net->ipv6.sysctl.icmpv6_ratemask = NULL; + } #ifdef CONFIG_PROC_FS udp6_proc_exit(net); tcp6_proc_exit(net); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index cc14b9998941..b51cd45cd65b 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -168,22 +168,23 @@ static bool is_ineligible(const struct sk_buff *skb) return false; } -static bool icmpv6_mask_allow(int type) +static bool icmpv6_mask_allow(struct net *net, int type) { - /* Informational messages are not limited. */ - if (type & ICMPV6_INFOMSG_MASK) + if (type > ICMPV6_MSG_MAX) return true; - /* Do not limit pmtu discovery, it would break it. */ - if (type == ICMPV6_PKT_TOOBIG) + /* Limit if icmp type is set in ratemask. */ + if (!net->ipv6.sysctl.icmpv6_ratemask) + return true; + if (!test_bit(type, net->ipv6.sysctl.icmpv6_ratemask)) return true; return false; } -static bool icmpv6_global_allow(int type) +static bool icmpv6_global_allow(struct net *net, int type) { - if (icmpv6_mask_allow(type)) + if (icmpv6_mask_allow(net, type)) return true; if (icmp_global_allow()) @@ -202,7 +203,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, struct dst_entry *dst; bool res = false; - if (icmpv6_mask_allow(type)) + if (icmpv6_mask_allow(net, type)) return true; /* @@ -511,7 +512,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, local_bh_disable(); /* Check global sysctl_icmp_msgs_per_sec ratelimit */ - if (!(skb->dev->flags&IFF_LOOPBACK) && !icmpv6_global_allow(type)) + if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type)) goto out_bh_enable; mip6_addr_swap(skb); @@ -731,6 +732,11 @@ static void icmpv6_echo_reply(struct sk_buff *skb) if (IS_ERR(dst)) goto out; + /* Check the ratelimit */ + if ((!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, ICMPV6_ECHO_REPLY)) || + !icmpv6_xrlim_allow(sk, ICMPV6_ECHO_REPLY, &fl6)) + goto out_dst_release; + idev = __in6_dev_get(skb->dev); msg.skb = skb; @@ -751,6 +757,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, skb->len + sizeof(struct icmp6hdr)); } +out_dst_release: dst_release(dst); out: icmpv6_xmit_unlock(sk); @@ -1137,6 +1144,13 @@ static struct ctl_table ipv6_icmp_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "ratemask", + .data = &init_net.ipv6.sysctl.icmpv6_ratemask, + .maxlen = ICMPV6_MSG_MAX + 1, + .mode = 0644, + .proc_handler = proc_do_large_bitmap, + }, { }, }; @@ -1153,6 +1167,7 @@ struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net) table[1].data = &net->ipv6.sysctl.icmpv6_echo_ignore_all; table[2].data = &net->ipv6.sysctl.icmpv6_echo_ignore_multicast; table[3].data = &net->ipv6.sysctl.icmpv6_echo_ignore_anycast; + table[4].data = &net->ipv6.sysctl.icmpv6_ratemask; } return table; }