Commit 0bc19985 authored by Stephen Suryaputra's avatar Stephen Suryaputra Committed by David S. Miller

ipv6: Add rate limit mask for ICMPv6 messages

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.

v2: Add dummy function proc_do_large_bitmap() if CONFIG_PROC_SYSCTL
    isn't defined, expand the description in ip-sysctl.txt and remove
    unnecessary conditional before kfree().
v3: Inline the bitmap instead of dynamically allocated. Still is a
    pointer to it is needed because of the way proc_do_large_bitmap work.
Signed-off-by: default avatarStephen Suryaputra <ssuryaextr@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4cf2d206
...@@ -1913,11 +1913,26 @@ enhanced_dad - BOOLEAN ...@@ -1913,11 +1913,26 @@ enhanced_dad - BOOLEAN
icmp/*: icmp/*:
ratelimit - INTEGER ratelimit - INTEGER
Limit the maximal rates for sending ICMPv6 packets. Limit the maximal rates for sending ICMPv6 messages.
0 to disable any limiting, 0 to disable any limiting,
otherwise the minimal space between responses in milliseconds. otherwise the minimal space between responses in milliseconds.
Default: 1000 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.
Refer to: https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml
for numerical values of ICMPv6 message types, e.g. echo request is 128
and echo reply is 129.
Default: 0-1,3-127 (rate limit ICMPv6 errors except Packet Too Big)
echo_ignore_all - BOOLEAN echo_ignore_all - BOOLEAN
If set non-zero, then the kernel will ignore all ICMP ECHO If set non-zero, then the kernel will ignore all ICMP ECHO
requests sent to it over the IPv6 protocol. requests sent to it over the IPv6 protocol.
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#ifndef __NETNS_IPV6_H__ #ifndef __NETNS_IPV6_H__
#define __NETNS_IPV6_H__ #define __NETNS_IPV6_H__
#include <net/dst_ops.h> #include <net/dst_ops.h>
#include <uapi/linux/icmpv6.h>
struct ctl_table_header; struct ctl_table_header;
...@@ -35,6 +36,8 @@ struct netns_sysctl_ipv6 { ...@@ -35,6 +36,8 @@ struct netns_sysctl_ipv6 {
int icmpv6_echo_ignore_all; int icmpv6_echo_ignore_all;
int icmpv6_echo_ignore_multicast; int icmpv6_echo_ignore_multicast;
int icmpv6_echo_ignore_anycast; int icmpv6_echo_ignore_anycast;
DECLARE_BITMAP(icmpv6_ratemask, ICMPV6_MSG_MAX + 1);
unsigned long *icmpv6_ratemask_ptr;
int anycast_src_echo_reply; int anycast_src_echo_reply;
int ip_nonlocal_bind; int ip_nonlocal_bind;
int fwmark_reflect; int fwmark_reflect;
......
...@@ -90,6 +90,8 @@ struct icmp6hdr { ...@@ -90,6 +90,8 @@ struct icmp6hdr {
#define ICMPV6_TIME_EXCEED 3 #define ICMPV6_TIME_EXCEED 3
#define ICMPV6_PARAMPROB 4 #define ICMPV6_PARAMPROB 4
#define ICMPV6_ERRMSG_MAX 127
#define ICMPV6_INFOMSG_MASK 0x80 #define ICMPV6_INFOMSG_MASK 0x80
#define ICMPV6_ECHO_REQUEST 128 #define ICMPV6_ECHO_REQUEST 128
...@@ -110,6 +112,8 @@ struct icmp6hdr { ...@@ -110,6 +112,8 @@ struct icmp6hdr {
#define ICMPV6_MRDISC_ADV 151 #define ICMPV6_MRDISC_ADV 151
#define ICMPV6_MSG_MAX 255
/* /*
* Codes for Destination Unreachable * Codes for Destination Unreachable
*/ */
......
...@@ -3326,6 +3326,11 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, ...@@ -3326,6 +3326,11 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
return -ENOSYS; return -ENOSYS;
} }
int proc_do_large_bitmap(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
#endif /* CONFIG_PROC_SYSCTL */ #endif /* CONFIG_PROC_SYSCTL */
...@@ -3366,3 +3371,4 @@ EXPORT_SYMBOL(proc_dointvec_ms_jiffies); ...@@ -3366,3 +3371,4 @@ EXPORT_SYMBOL(proc_dointvec_ms_jiffies);
EXPORT_SYMBOL(proc_dostring); EXPORT_SYMBOL(proc_dostring);
EXPORT_SYMBOL(proc_doulongvec_minmax); EXPORT_SYMBOL(proc_doulongvec_minmax);
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax); EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
EXPORT_SYMBOL(proc_do_large_bitmap);
...@@ -850,6 +850,15 @@ static int __net_init inet6_net_init(struct net *net) ...@@ -850,6 +850,15 @@ static int __net_init inet6_net_init(struct net *net)
net->ipv6.sysctl.icmpv6_echo_ignore_all = 0; net->ipv6.sysctl.icmpv6_echo_ignore_all = 0;
net->ipv6.sysctl.icmpv6_echo_ignore_multicast = 0; net->ipv6.sysctl.icmpv6_echo_ignore_multicast = 0;
net->ipv6.sysctl.icmpv6_echo_ignore_anycast = 0; net->ipv6.sysctl.icmpv6_echo_ignore_anycast = 0;
/* By default, rate limit error messages.
* Except for pmtu discovery, it would break it.
* proc_do_large_bitmap needs pointer to the bitmap.
*/
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.icmpv6_ratemask_ptr = net->ipv6.sysctl.icmpv6_ratemask;
net->ipv6.sysctl.flowlabel_consistency = 1; net->ipv6.sysctl.flowlabel_consistency = 1;
net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS; net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS;
net->ipv6.sysctl.idgen_retries = 3; net->ipv6.sysctl.idgen_retries = 3;
......
...@@ -168,22 +168,21 @@ static bool is_ineligible(const struct sk_buff *skb) ...@@ -168,22 +168,21 @@ static bool is_ineligible(const struct sk_buff *skb)
return false; 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_MSG_MAX)
if (type & ICMPV6_INFOMSG_MASK)
return true; return true;
/* Do not limit pmtu discovery, it would break it. */ /* Limit if icmp type is set in ratemask. */
if (type == ICMPV6_PKT_TOOBIG) if (!test_bit(type, net->ipv6.sysctl.icmpv6_ratemask))
return true; return true;
return false; 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; return true;
if (icmp_global_allow()) if (icmp_global_allow())
...@@ -202,7 +201,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, ...@@ -202,7 +201,7 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
struct dst_entry *dst; struct dst_entry *dst;
bool res = false; bool res = false;
if (icmpv6_mask_allow(type)) if (icmpv6_mask_allow(net, type))
return true; return true;
/* /*
...@@ -511,7 +510,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, ...@@ -511,7 +510,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
local_bh_disable(); local_bh_disable();
/* Check global sysctl_icmp_msgs_per_sec ratelimit */ /* 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; goto out_bh_enable;
mip6_addr_swap(skb); mip6_addr_swap(skb);
...@@ -731,6 +730,11 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -731,6 +730,11 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (IS_ERR(dst)) if (IS_ERR(dst))
goto out; 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); idev = __in6_dev_get(skb->dev);
msg.skb = skb; msg.skb = skb;
...@@ -751,6 +755,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -751,6 +755,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr,
skb->len + sizeof(struct icmp6hdr)); skb->len + sizeof(struct icmp6hdr));
} }
out_dst_release:
dst_release(dst); dst_release(dst);
out: out:
icmpv6_xmit_unlock(sk); icmpv6_xmit_unlock(sk);
...@@ -1137,6 +1142,13 @@ static struct ctl_table ipv6_icmp_table_template[] = { ...@@ -1137,6 +1142,13 @@ static struct ctl_table ipv6_icmp_table_template[] = {
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec, .proc_handler = proc_dointvec,
}, },
{
.procname = "ratemask",
.data = &init_net.ipv6.sysctl.icmpv6_ratemask_ptr,
.maxlen = ICMPV6_MSG_MAX + 1,
.mode = 0644,
.proc_handler = proc_do_large_bitmap,
},
{ }, { },
}; };
...@@ -1153,6 +1165,7 @@ struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net) ...@@ -1153,6 +1165,7 @@ struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net)
table[1].data = &net->ipv6.sysctl.icmpv6_echo_ignore_all; table[1].data = &net->ipv6.sysctl.icmpv6_echo_ignore_all;
table[2].data = &net->ipv6.sysctl.icmpv6_echo_ignore_multicast; table[2].data = &net->ipv6.sysctl.icmpv6_echo_ignore_multicast;
table[3].data = &net->ipv6.sysctl.icmpv6_echo_ignore_anycast; table[3].data = &net->ipv6.sysctl.icmpv6_echo_ignore_anycast;
table[4].data = &net->ipv6.sysctl.icmpv6_ratemask_ptr;
} }
return table; return table;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment