# Packet flood

A 10 s ICMP flood at a MikroTik RB5009, read from inside its container: the switch interrupt standing in for a port counter, one core paying, time_squeeze.

Source: https://docs.jmrp.io/mikroscope/playbooks/packet-flood/

**In short:** from inside the container, a flood aimed at the router shows as
one interrupt line rising several-fold, here `switch0` from 5.5 k to 34 k per
5 s bucket, with its cost on the one core that interrupt is pinned to, and with
softirq counts and `time_squeeze` rising together. The container cannot see the
router's interface counters, so the interrupt stands in for them: it gives onset,
offset and the core that paid, not bytes. The flood was provoked on purpose.

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

## Setup

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:

```sh
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.

## Recording

In 5-second buckets, before and during. There is no chart: the flood predates
the history of the reference InfluxDB store, which begins on 2026-09-19.

```text
   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
```

## What to look at

- **`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.

## Signal sources

- 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.
- A drop, unlike a squeeze, is an event a healthy router does not produce:
  `mikroscope doctor` reports it as `softnet-drops` when the running agent's ring
  holds one, and the `mikroscope-softnet-drops` alert fires on the first one.
  Neither counts squeezes.

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](https://docs.jmrp.io/mikroscope/playbooks/idle/) for why "any
squeeze" is not that rule.

## Observer effect

Running `/system/routerboard/settings/print` over SSH produced kernel events of
its own:

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

Reading RouterOS configuration leaves a trace in the kernel log, so set your own
actions apart before you correlate events with a cause. On this device each SSH
connect also costs 20–27 % CPU for its duration.

## Signature

**Provoked on purpose** · 2026-09-12

- 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.

> **Deliberately not provoked**
>
> A flood that reached the router's packet-rate ceiling: this one delivered ~6.8 kpps with 0 % loss.
> "Rising squeeze with flat throughput" is how the ceiling is expected to read, and it was not
> observed here. Nor was traffic the switch chip forwards between LAN ports, which the CPU never
> sees.

## See also

- [Idle baseline](https://docs.jmrp.io/mikroscope/playbooks/idle/): the squeeze and interrupt baseline this flood rose
  from.
- [Container visibility](https://docs.jmrp.io/mikroscope/limits/namespaces/): why the interrupt is visible and the
  interface counters are not.
- [Derived values](https://docs.jmrp.io/mikroscope/sinks/derive/): the `burst` flag and the per-packet costs.
