Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

icmp: Add counters for rate limits

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.1674605770.git.jamie.bainbridge@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Jamie Bainbridge and committed by
Paolo Abeni
d0941130 9f927527

+16 -3
+3
include/uapi/linux/snmp.h
··· 95 95 ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */ 96 96 ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */ 97 97 ICMP_MIB_CSUMERRORS, /* InCsumErrors */ 98 + ICMP_MIB_RATELIMITGLOBAL, /* OutRateLimitGlobal */ 99 + ICMP_MIB_RATELIMITHOST, /* OutRateLimitHost */ 98 100 __ICMP_MIB_MAX 99 101 }; 100 102 ··· 114 112 ICMP6_MIB_OUTMSGS, /* OutMsgs */ 115 113 ICMP6_MIB_OUTERRORS, /* OutErrors */ 116 114 ICMP6_MIB_CSUMERRORS, /* InCsumErrors */ 115 + ICMP6_MIB_RATELIMITHOST, /* OutRateLimitHost */ 117 116 __ICMP6_MIB_MAX 118 117 }; 119 118
+3
net/ipv4/icmp.c
··· 296 296 if (icmp_global_allow()) 297 297 return true; 298 298 299 + __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL); 299 300 return false; 300 301 } 301 302 ··· 326 325 if (peer) 327 326 inet_putpeer(peer); 328 327 out: 328 + if (!rc) 329 + __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST); 329 330 return rc; 330 331 } 331 332
+5 -3
net/ipv4/proc.c
··· 353 353 seq_puts(seq, "\nIcmp: InMsgs InErrors InCsumErrors"); 354 354 for (i = 0; icmpmibmap[i].name; i++) 355 355 seq_printf(seq, " In%s", icmpmibmap[i].name); 356 - seq_puts(seq, " OutMsgs OutErrors"); 356 + seq_puts(seq, " OutMsgs OutErrors OutRateLimitGlobal OutRateLimitHost"); 357 357 for (i = 0; icmpmibmap[i].name; i++) 358 358 seq_printf(seq, " Out%s", icmpmibmap[i].name); 359 359 seq_printf(seq, "\nIcmp: %lu %lu %lu", ··· 363 363 for (i = 0; icmpmibmap[i].name; i++) 364 364 seq_printf(seq, " %lu", 365 365 atomic_long_read(ptr + icmpmibmap[i].index)); 366 - seq_printf(seq, " %lu %lu", 366 + seq_printf(seq, " %lu %lu %lu %lu", 367 367 snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTMSGS), 368 - snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS)); 368 + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_OUTERRORS), 369 + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_RATELIMITGLOBAL), 370 + snmp_fold_field(net->mib.icmp_statistics, ICMP_MIB_RATELIMITHOST)); 369 371 for (i = 0; icmpmibmap[i].name; i++) 370 372 seq_printf(seq, " %lu", 371 373 atomic_long_read(ptr + (icmpmibmap[i].index | 0x100)));
+4
net/ipv6/icmp.c
··· 183 183 if (icmp_global_allow()) 184 184 return true; 185 185 186 + __ICMP_INC_STATS(net, ICMP_MIB_RATELIMITGLOBAL); 186 187 return false; 187 188 } 188 189 ··· 225 224 if (peer) 226 225 inet_putpeer(peer); 227 226 } 227 + if (!res) 228 + __ICMP6_INC_STATS(net, ip6_dst_idev(dst), 229 + ICMP6_MIB_RATELIMITHOST); 228 230 dst_release(dst); 229 231 return res; 230 232 }
+1
net/ipv6/proc.c
··· 94 94 SNMP_MIB_ITEM("Icmp6OutMsgs", ICMP6_MIB_OUTMSGS), 95 95 SNMP_MIB_ITEM("Icmp6OutErrors", ICMP6_MIB_OUTERRORS), 96 96 SNMP_MIB_ITEM("Icmp6InCsumErrors", ICMP6_MIB_CSUMERRORS), 97 + SNMP_MIB_ITEM("Icmp6OutRateLimitHost", ICMP6_MIB_RATELIMITHOST), 97 98 SNMP_MIB_SENTINEL 98 99 }; 99 100