ifInOctets vs ifHCInOctets: when a 32-bit SNMP counter wraps
· Vladimir Chemeris, the developer of SNMP Inspector
Read ifInOctets and ifHCInOctets on the same 10G port and the two numbers differ by about 2.7 trillion. Both count the same octets: the 32-bit counter has rolled over 630 times, and the 64-bit one holds the full total.
The developer recorded the output in this post on 2026-11-11 with snmpget from net-snmp 5.9.4, against a MikroTik RB4011iGS+5HacQ2HnD on RouterOS 7.25 and a Linux host running net-snmp snmpd. In the commands, 192.0.2.x stands in for the real addresses and <community> for the community string.
ifInOctets vs ifHCInOctets
Both objects belong to IF-MIB (RFC 2863) and count “the total number of octets received on the interface, including framing characters”. ifInOctets belongs to ifTable and has the type Counter32. ifHCInOctets belongs to ifXTable, has the type Counter64, and RFC 2863 calls it “a 64-bit version of ifInOctets”. The transmit side has the same pair: ifOutOctets and ifHCOutOctets.
Interface index 11 on the RB4011 is its 10G SFP+ port, sfp-sfpplus1. One GET reads its name, speed and both counters:
$ snmpget -v2c -c <community> 192.0.2.1 ifName.11 ifHighSpeed.11 ifInOctets.11 ifHCInOctets.11
IF-MIB::ifName.11 = STRING: sfp-sfpplus1
IF-MIB::ifHighSpeed.11 = Gauge32: 10000
IF-MIB::ifInOctets.11 = Counter32: 3688345511
IF-MIB::ifHCInOctets.11 = Counter64: 2709517741991
The two values look unrelated until you divide the 64-bit one by 2^32 (4,294,967,296):
2,709,517,741,991 = 630 × 4,294,967,296 + 3,688,345,511
= 2,705,829,396,480 + 3,688,345,511
The remainder, 3,688,345,511, is the value of ifInOctets. The 32-bit counter holds the 64-bit total modulo 2^32, its low 32 bits. Since the counters started, it has rolled over from 4,294,967,295 to 0 a total of 630 times.
The agent had been up for about 16 days. The first command of the session read sysUpTime:
$ snmpget -v2c -c <community> 192.0.2.1 sysDescr.0 sysUpTime.0
SNMPv2-MIB::sysDescr.0 = STRING: RouterOS RB4011iGS+5HacQ2HnD 7.25
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (138807700) 16 days, 1:34:37.00
If the counters started with the agent, 1,388,077 seconds over 630 wraps makes one wrap every 2,203 seconds, about 37 minutes, at an average inbound load of 15.6 Mbit/s. At 0.16% of its capacity, that port overflowed a Counter32 about 39 times a day.
Two reads ten seconds apart
Ten seconds later, a second GET read both counters and sysUpTime:
$ (10 s later) snmpget -v2c -c <community> 192.0.2.1 ifInOctets.11 ifHCInOctets.11 sysUpTime.0
IF-MIB::ifInOctets.11 = Counter32: 3689487455
IF-MIB::ifHCInOctets.11 = Counter64: 2709518883935
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (138809900) 16 days, 1:34:59.00
ifHCInOctets 2,709,518,883,935 - 2,709,517,741,991 = 1,141,944
ifInOctets 3,689,487,455 - 3,688,345,511 = 1,141,944
Both counters moved by 1,141,944 octets, about 0.91 Mbit/s over ten seconds. The 32-bit counter stayed below 4,294,967,295 in that window, so plain subtraction gives the right delta. The low 32 bits match again: 2,709,518,883,935 = 630 × 2^32 + 3,689,487,455.
You can correct the 32-bit delta across one wrap. If the new value is lower than the old one, add 2^32: delta = new + 4,294,967,296 - old. The correction holds while fewer than 4,294,967,296 octets arrive between two reads, which allows one wrap at most. Past that, the counter can wrap twice, or come back above its old value after one wrap, and the delta comes out 4,294,967,296 octets short for each lost lap. The reply carries no sign of the extra wrap. Keep the poll interval under the wrap time at the port’s highest rate.
SNMP counter wrap at 1 Gbit/s and faster
A Counter32 octet counter wraps after 2^32 × 8 = 34,359,738,368 bits. Divide by the port speed and you get the shortest wrap time, with the port receiving at full line rate:
| Port speed | Counter32 (ifInOctets) |
Counter64 (ifHCInOctets) |
|---|---|---|
| 10 Mbit/s | 57.3 min | about 468,000 years |
| 100 Mbit/s | 5.7 min | about 46,800 years |
| 1 Gbit/s | 34.4 s | about 4,700 years |
| 10 Gbit/s | 3.4 s | about 468 years |
| 25 Gbit/s | 1.4 s | about 187 years |
| 100 Gbit/s | 0.34 s | about 47 years |
RFC 2863 gives the same figures for the first three rows: over 57 minutes at 10 Mbit/s, 5.7 minutes at 100 Mbit/s and 34 seconds at 1 Gbit/s. Poll ifInOctets every five minutes on a 1G port at line rate, and the counter wraps eight or nine times per interval while the poller reports a fraction of the traffic.
Agents that cache counters leave less room. An agent may refresh its counters on a timer of about 10 seconds instead of on each request. Polling faster than the cache then gives a zero delta followed by a double one. On a 1G port, a 10-second cache fits inside the 34-second wrap. On a 10G port at line rate, the 32-bit counter wraps about three times within one cache period, and no poll interval can recover the lost laps.
SNMP 64-bit counters in RFC 2863
RFC 2863 (section 3.1.6, Counter Size) sets the required counter width by interface speed:
| Interface speed | Octet counters | Packet counters |
|---|---|---|
| 20,000,000 bit/s or less | 32-bit | 32-bit |
| Faster than 20,000,000 and slower than 650,000,000 bit/s | 64-bit | 32-bit |
| 650,000,000 bit/s or faster | 64-bit | 64-bit |
The RFC keeps the 32-bit counters next to the 64-bit ones and doesn’t deprecate them. An agent that serves a 1G port without ifHCInOctets falls short of RFC 2863. The RB4011 serves both on its 10G port, and net-snmp on Linux serves both on the host’s eno1:
$ snmpget -v2c -c <community> 192.0.2.12 ifName.2 ifInOctets.2 ifHCInOctets.2 dot3StatsFCSErrors.2
IF-MIB::ifName.2 = STRING: eno1
IF-MIB::ifInOctets.2 = Counter32: 0
IF-MIB::ifHCInOctets.2 = Counter64: 0
EtherLike-MIB::dot3StatsFCSErrors.2 = No Such Instance currently exists at this OID
Both counters read 0: the port has received nothing since its counters started. The missing FCS counter on the last line is a separate case; switch port errors and FCS explains it. The compatibility page lists the devices and public SNMP recordings where SNMP Inspector read 64-bit counters.
64-bit counters need SNMPv2c or SNMPv3
RFC 3584 (section 4.2.2.1) names Counter64 as the one SMIv2 type that SMIv1 can’t carry. It says an agent that speaks several versions “MUST NOT ever return a variable binding containing a Counter64 value in a response to a request that was received using the SNMPv1 message version”. By that rule, a v1 GET for ifHCInOctets gets noSuchName, and a v1 GETNEXT skips the Counter64 columns.
RouterOS 7.25 answered a v1 GET with the 64-bit value anyway:
$ snmpget -v1 -c <community> 192.0.2.1 ifHCInOctets.11
IF-MIB::ifHCInOctets.11 = Counter64: 2709518883935
Treat that as the behavior of this agent version and don’t build on it. An agent that follows RFC 3584 returns an error for the same request, and a v1 walk shows only the 32-bit counters. Read 64-bit counters over SNMPv2c or SNMPv3. If v2c gets no reply, SNMP timeout with no response lists the causes, and SNMPv3 authentication failure covers a rejected v3 user.
ifSpeed and ifHighSpeed on 10G ports
ifSpeed is a Gauge32 in bit/s, so it tops out at 4,294,967,295, about 4.3 Gbit/s. RFC 2863 tells agents to report that maximum for faster interfaces and to give the speed in ifHighSpeed, in units of 1,000,000 bit/s. The RB4011 reports ifHighSpeed.11 = Gauge32: 10000 for its 10G port. If an agent reports 4,294,967,295 in ifSpeed on a 10G port, a utilization figure based on ifSpeed comes out 2.33 times too high.
Traffic graph spike after a reboot
After an agent restart the counters start over, and RFC 2863 names re-initialization of the management system as a point where the counters in ifTable and ifXTable can have a discontinuity. The next read comes back far below the last one. A poller that subtracts gets a negative rate. A poller that treats the drop as a wrap adds 2^32, up to 4.3 GB of traffic that never crossed the port, or 2^64, about 18 exabytes, and the graph shows a spike that flattens the rest of the chart.
You can detect the restart with two objects:
sysUpTime.0went back compared with the last read. The whole agent restarted.sysUpTimecounts hundredths of a second in 32 bits and wraps on its own after about 497 days, so on a device up for more than 497 days, a drop can be that wrap.ifCounterDiscontinuityTime.Nchanged. RFC 2863 defines it as thesysUpTimeof the most recent discontinuity in the counters of interface N, and 0 if none happened since the agent started.
A rate calculation that handles resets:
- Read the counters,
sysUpTime.0andifCounterDiscontinuityTimein one GET. - If
sysUpTimewent back orifCounterDiscontinuityTimechanged, drop the pair, keep the new read as a baseline and leave a gap in the graph. - If a 64-bit counter went down without either sign, treat it as a reset too. At 100 Gbit/s it needs 47 years to wrap.
- If a 32-bit counter went down and neither check shows a restart, add 2^32 once, and trust the result only while the interval stays under the wrap time at the port speed.
What SNMP Inspector shows
SNMP Inspector prefers 64-bit counters and falls back to 32-bit ones when the agent serves none; its Prefer 64-bit counters setting notes “SNMPv1 has none. The app falls back to 32-bit and marks it.” On 32-bit counters it shows “32-bit: may miss samples at ≥1 Gbit/s” and “Counter32 needs an interval under 34s”, and its note on caching reads “Agents may cache counters for about 10s; the app then polls at the cache interval. A 1G Counter32 wraps in about 34s.” Above 4 Gbit/s it takes the speed from ifHighSpeed. It watches sysUpTime and ifCounterDiscontinuityTime, and after an agent restart it shows “Agent restarted. Counters reset.” with a gap in the chart in place of a spike; one of the demo devices is a router with such a restart.
Questions
What is the difference between ifInOctets and ifHCInOctets?
Both count the octets received on the interface, framing included (IF-MIB, RFC 2863). ifInOctets is a Counter32 in ifTable. ifHCInOctets is a Counter64 in ifXTable. On the 10G port of a MikroTik RB4011, the 32-bit value equals the 64-bit value modulo 2^32.
How often does a 32-bit SNMP counter wrap at 1 Gbit/s?
Every 34.4 seconds at full line rate, and every 3.4 seconds at 10 Gbit/s. A rate from ifInOctets stays correct while the poll interval is shorter than that; past it, the delta comes out short by 4,294,967,296 octets per lost wrap.
Why does my traffic graph spike after a switch reboot?
The restart resets the counters, and a poller that reads the drop as a wrap adds 2^32 or 2^64 octets to one interval. Compare sysUpTime.0 and ifCounterDiscontinuityTime with the last read, and start a new baseline when either one changed.
Can I read ifHCInOctets with SNMPv1?
Not by the standard. RFC 3584 forbids an agent to return a Counter64 value to an SNMPv1 request, and a v1 GET for it gets noSuchName. RouterOS 7.25 returned one anyway in the developer's test. Use SNMPv2c or SNMPv3.
Why does ifSpeed show 4294967295 on a 10G port?
ifSpeed is a Gauge32 in bit/s and can't go higher. RFC 2863 tells agents to report that maximum for faster interfaces and to give the speed in ifHighSpeed, in Mbit/s. On a 10G port, ifHighSpeed reads 10000.