From: Jamie Bainbridge jamie.bainbridge@gmail.com
commit d0941130c93515411c8d66fc22bdae407b509a6d upstream.
There are multiple ICMP rate limiting mechanisms:
* Global limits: net.ipv4.icmp_msgs_burst/icmp_msgs_per_sec * v4 per-host limits: net.ipv4.icmp_ratelimit/ratemask * v6 per-host limits: net.ipv6.icmp_ratelimit/ratemask
However, when ICMP output is limited, there is no way to tell which limit has been hit or even if the limits are responsible for the lack of ICMP output.
Add counters for each of the cases above. As we are within local_bh_disable(), use the __INC stats variant.
Example output:
# nstat -sz "*RateLimit*" IcmpOutRateLimitGlobal 134 0.0 IcmpOutRateLimitHost 770 0.0 Icmp6OutRateLimitHost 84 0.0
Signed-off-by: Jamie Bainbridge jamie.bainbridge@gmail.com Suggested-by: Abhishek Rawal rawal.abhishek92@gmail.com Link: https://lore.kernel.org/r/273b32241e6b7fdc5c609e6f5ebc68caf3994342.167460577... Signed-off-by: Paolo Abeni pabeni@redhat.com Signed-off-by: Eric Dumazet edumazet@google.com --- include/uapi/linux/snmp.h | 3 +++ net/ipv4/icmp.c | 3 +++ net/ipv4/proc.c | 8 +++++--- net/ipv6/icmp.c | 4 ++++ net/ipv6/proc.c | 1 + 5 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index 4d7470036a8b5be598bd21367f60854b023cceef..8ecb509a84d3b1eb8323180d25a655dd301b9a10 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -95,6 +95,8 @@ enum ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ ICMP_MIB_CSUMERRORS, /* InCsumErrors */ + ICMP_MIB_RATELIMITGLOBAL, /* OutRateLimitGlobal */ + ICMP_MIB_RATELIMITHOST, /* OutRateLimitHost */ __ICMP_MIB_MAX };
@@ -112,6 +114,7 @@ enum ICMP6_MIB_OUTMSGS, /* OutMsgs */ ICMP6_MIB_OUTERRORS, /* OutErrors */ ICMP6_MIB_CSUMERRORS, /* InCsumErrors */ + ICMP6_MIB_RATELIMITHOST, /* OutRateLimitHost */ __ICMP6_MIB_MAX };
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 31051b327e53c870eb91755662653a9c7ffc0b32..190988bfa3e293adaf1d5c2849592b64dcaf38da 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -297,6 +297,7 @@ static bool icmpv4_global_allow(struct net *net, int type, int code) if (icmp_global_allow()) return true;
+ __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL); return false; }
@@ -326,6 +327,8 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, if (peer) inet_putpeer(peer); out: + if (!rc) + __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST); return rc; }
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 5386f460bd208c8f30902ea8b6aa613449d59ee0..1f52c5f2d3475f9ed1be1001c6e83b40d734b259 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -352,7 +352,7 @@ static void icmp_put(struct seq_file *seq) seq_puts(seq, "\nIcmp: InMsgs InErrors InCsumErrors"); for (i = 0; icmpmibmap[i].name; i++) seq_printf(seq, " In%s", icmpmibmap[i].name); - seq_puts(seq, " OutMsgs OutErrors"); + seq_puts(seq, " OutMsgs OutErrors OutRateLimitGlobal OutRateLimitHost"); for (i = 0; icmpmibmap[i].name; i++) seq_printf(seq, " Out%s", icmpmibmap[i].name); seq_printf(seq, "\nIcmp: %lu %lu %lu", @@ -362,9 +362,11 @@ static void icmp_put(struct seq_file *seq) for (i = 0; icmpmibmap[i].name; i++) seq_printf(seq, " %lu", atomic_long_read(ptr + icmpmibmap[i].index)); - seq_printf(seq, " %lu %lu", + seq_printf(seq, " %lu %lu %lu %lu", snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTMSGS), - snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS)); + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS), + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_RATELIMITGLOBAL), + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_RATELIMITHOST)); for (i = 0; icmpmibmap[i].name; i++) seq_printf(seq, " %lu", atomic_long_read(ptr + (icmpmibmap[i].index | 0x100))); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index e2af7ab9928218185b7ae0ef50df3f39dc001b07..7bea1d82e1783c1956ea388ddc6ed842e2a6268e 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -183,6 +183,7 @@ static bool icmpv6_global_allow(struct net *net, int type) if (icmp_global_allow()) return true;
+ __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL); return false; }
@@ -224,6 +225,9 @@ static bool icmpv6_xrlim_allow(struct sock *sk, u8 type, if (peer) inet_putpeer(peer); } + if (!res) + __ICMP6_INC_STATS(net, ip6_dst_idev(dst), + ICMP6_MIB_RATELIMITHOST); dst_release(dst); return res; } diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index d6306aa46bb1eb768ab77aae6a494640ed462157..e20b3705c2d2accedad4aac75064c33f733a80be 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -94,6 +94,7 @@ static const struct snmp_mib snmp6_icmp6_list[] = { SNMP_MIB_ITEM("Icmp6OutMsgs", ICMP6_MIB_OUTMSGS), SNMP_MIB_ITEM("Icmp6OutErrors", ICMP6_MIB_OUTERRORS), SNMP_MIB_ITEM("Icmp6InCsumErrors", ICMP6_MIB_CSUMERRORS), + SNMP_MIB_ITEM("Icmp6OutRateLimitHost", ICMP6_MIB_RATELIMITHOST), SNMP_MIB_SENTINEL };