icmp: Add counters for rate limits
authorJamie Bainbridge <jamie.bainbridge@gmail.com>
Wed, 25 Jan 2023 00:16:52 +0000 (11:16 +1100)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 26 Jan 2023 09:52:18 +0000 (10:52 +0100)
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>
include/uapi/linux/snmp.h
net/ipv4/icmp.c
net/ipv4/proc.c
net/ipv6/icmp.c
net/ipv6/proc.c

index 6600cb0..26f33a4 100644 (file)
@@ -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
 };
 
index 46aa2d6..8cebb47 100644 (file)
@@ -296,6 +296,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;
 }
 
@@ -325,6 +326,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;
 }
 
index f88daac..eaf1d31 100644 (file)
@@ -353,7 +353,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",
@@ -363,9 +363,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)));
index 9d92d51..79c769c 100644 (file)
@@ -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;
 }
index d6306aa..e20b370 100644 (file)
@@ -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
 };