Skip to content

A packet flood

This page answers what traffic the router’s CPU has to handle looks like from inside a container that cannot see the router’s interfaces. The flood was provoked on purpose.

Measured on RB5009UG+S+ · 4 × 1.4 GHz Cortex-A72 · RouterOS 7.24.2 · Linux 5.6.3 · · agent at 10 Hz in an ephemeral privileged container

ICMP at the router’s own LAN address, from a LAN host, bounded to 10 s. This is traffic the router’s CPU must handle, unlike LAN-to-LAN traffic, which the switch chip forwards without the CPU ever seeing it:

Terminal window
ping -f -w 10 192.168.0.1 # 67 793 packets, ~6.8 kpps, 0 % loss

192.168.0.1 is the reference router’s LAN address; use your own router’s.

In 5-second buckets, before and during:

t busy% per-core busy% sirq squeeze top IRQ
2 4.8 3.8 8.0 3.0 4.2 51 50 switch0=5337
3 5.5 3.9 10.4 3.9 3.9 50 45 switch0=5567
4 9.6 23.3 2.8 3.3 8.8 149 138 switch0=34922
5 14.3 24.9 19.6 3.5 9.2 153 136 switch0=33832
  • switch0 interrupts went 5.5 k → 34 k per bucket, a 6× rise. This is the closest thing to a per-interface counter that exists inside the container: the network namespace hides /proc/net/dev, but the interrupt the NIC raises is global. It will not give you bytes; it will give you onset, offset and which core paid.
  • The cost landed on one core (core 0 at 23–25 %) because that IRQ is pinned. A flood that saturates a single core while three idle is a common and confusing shape — the total says 14 %, the router feels wedged.
  • softirq counts tripled (50 → 150), and time_squeeze rose with them. time_squeeze is the one to watch: it counts times the softirq handler ran out of budget with work still queued. Rising squeeze with flat throughput is the signature of a router at its packet-rate ceiling.
  • Hard-IRQ time does not appear in /proc/stat’s irq column on this kernel — it is always 0 (no IRQ_TIME_ACCOUNTING). That work is inside system. Do not read the irq field and conclude the router has no interrupt load.
  • The interrupt lines are mikroscope_irq_total{irq,name,cpu} on /metrics, per core, for the sources that appeared in any sample’s top-K; mikroscope_irq_delivered_total is every source summed, the denominator for what share the top-K accounts for. On InfluxDB the per-core distribution is mikroscope_irq_cpu, with a cpu tag. Which line a NIC raises and what it is called belong to the board and its driver: match on the name label or on the rate, not on switch0 written into a query.
  • The squeeze is mikroscope_softnet_total{cpu,kind="time_squeeze"}, beside kind="processed" and kind="dropped"; the softirqs are mikroscope_softirq_total{cpu,kind}.
  • The squeeze has no budget to be divided by: /proc/sys/net/core/* (netdev_budget, netdev_max_backlog) is absent in the container’s namespace.

The collector flags a sample as burst when a softnet queue dropped a packet or squeezed more than that CPU usually does (above its trailing 90th percentile and at least 3), in a sample whose packet count was at or below its trailing median — the kernel’s evidence of a burst shorter than the sample interval. Read the idle shape for why “any squeeze” is not that rule.

An aside that demonstrates it: running /system/routerboard/settings/print over SSH produced kernel events of its own —

[4] rb_ioctl, cmd: 0x5212, arg: 0x0
[4] rb: RB_GET_CF_INFO

Reading RouterOS configuration leaves a trace in the kernel log. When you are correlating events with your own actions, remember your instrument is part of the system. On this device each SSH connect also costs 20–27 % CPU for its duration.

Provoked on purpose ·

  • One interrupt line rising several-fold with a sharp onset and offset.
  • The cost on the one core that line is pinned to, while the device total stays modest.
  • softirq counts and time_squeeze rising together, against a squeeze that is never zero at idle.