Skip to content

Copies for a packet capture, measured on the XGS 136 and both XGS 3300s, 2026-10-25

Discoveries 12 and 17 of the discoveries, which turned out to be one thing, taken from the source to a product. A capture on a port sees nothing of a connection once the coprocessor forwards it. The fast path can send the host a copy of what it forwards: the configuration word's FP_PKT_DUMP bit, 0x010, has it send control message type 6 (MIRROR_PKT) for every frame - a byte of direction (0 arriving at the port, 1 leaving it), a reserved byte, the port's tag, then the frame as it crossed that port (the vendor's cmsg_types.h, struct cmsg_mrr_hdr). The vendor's host hands each copy to the packet capture of that port and nowhere else. Until now both drivers counted type 6 and let it go: an earlier page took the bit for a log that wrote nothing (struck there).

Now both drivers show each copy to that port's BPF - only while something listens there, an arriving copy with the port as its receiving interface and a leaving one with none, so tcpdump -Q in and -Q out read it as they read the host's own frames - and to nothing else: no copy enters the stack, so pf, the rules and the counters never see a frame twice (octep_dp_mirror, npugiu_mirror; dev.octep.0.dp.mirror, dev.npuep.0.giu.mirror). XGS: Settings Copies for packet capture, on the Acceleration tab and off by default, writes the bit with the offload gate, at a save and at every boot (scripts/xgs/offload.sh). octep.ko 7b22d527 on both XGS 3300s and npuep.ko 68c476f1 on the XGS 136, built from this change against the appliances' own kernel.

How it was measured

On each board, a TCP stream it forwards in hardware between two of its ports, in three phases (images/deploy/lab-mirror-octep.sh, lab-mirror-npuep.sh):

  • A - the bit set, tcpdump on both ports, each direction apart, a stream held to 200 Mbit/s for 8 s;
  • B - the bit set, no tcpdump, the stream at full rate for 10 s;
  • C - the bit clear, the stream at full rate for 10 s.

Read each time: the driver's copy counts, the type-6 messages, and the stream's own rate. The word went back to what it was at the end of every run. OPNsense's hostwatch has a BPF listener on every interface, so on these boards a copy always finds one; "nothing listening" is never reached.

board its coprocessor's software ports frames
the XGS 136 SFOS 21 Port13 in (npup13), Port8 out (npup8), the PC to the second XGS 3300 1,500 bytes
the first XGS 3300 SFOS 23 F1 (oxp0) to F2 (oxp1), both ends on the second XGS 3300 as in the flow runs 1,500 bytes
the second XGS 3300 SFOS 22 F1 (oxp0) from the PC to A4 (oxp12), the first XGS 3300 at the far end, the lab tunnel down for the run 9,000 bytes

The XGS 136, SFOS 21

The XGS 136, npuep.ko 68c476f1: tcpdump on Port13 and Port8 counted 137,365 data frames and 137,369 replies; the host itself received 9,968 and 9,753 of them, the coprocessor sent 255,193 copies a direction; at full rate 914 Mbit/s with the copies, 809 without

Every frame, both ways, on both ports. In the run of the picture tcpdump counted 137,365 data frames arriving at Port13 and 137,369 replies arriving at Port8. The host itself received 9,968 and 9,753 of them - the frames it forwarded before the connection was in hardware, which a capture always saw (giu.rx_port) - and the coprocessor sent 255,193 copies a direction: 274,914 frames in all, where the two captures counted 274,734. At the link's full rate the copies kept pace by count.

run (UTC) at 200 Mbit/s, tcpdump: data / replies the host's own copies a direction full rate with the copies, copies a direction in 10 s without
00:23 136,764 / 137,450 not read 248,262 919 Mbit/s, 1,531,528 911 Mbit/s
00:40 137,305 / 137,309 not read 250,752 897 Mbit/s, 1,532,663 813 Mbit/s
00:47 137,489 / 137,493 not read 182,733 858 Mbit/s, 1,354,862 845 Mbit/s
00:49, the picture 137,365 / 137,369 9,968 + 9,753 255,193 914 Mbit/s, 1,529,489 809 Mbit/s

The run at 00:47 has 92,249 frames the copies do not account for and no count of the host's own to set against them; the read was added to the run after it.

The first XGS 3300, SFOS 23

The first XGS 3300 over SFOS 23's software, octep.ko 7b22d527: tcpdump on F1 and F2 counted 138,349 data frames arriving and 138,939 replies, the coprocessor sent 277,790 copies a direction; 1.03 Gbit/s at full rate with the copies, 1.02 without

Every frame, both ways, on both ports.

run (UTC) at 200 Mbit/s, tcpdump: data / replies copies a direction full rate with the copies, copies a direction in 10 s without
00:19 138,948 / 138,918 277,719 1.27 Gbit/s, 2,184,391 1.02 Gbit/s
00:39 138,970 / 138,957 277,846 1.00 Gbit/s, 1,719,804 1.00 Gbit/s
00:46, the picture 138,349 / 138,939 277,790 1.03 Gbit/s, 1,769,479 1.02 Gbit/s

In the first two runs the copies came to the two captures' count less 147 and 81, the frames before the connection was in hardware. In the picture's run tcpdump counted 57 fewer data frames arriving at F1 than leaving F2, and the copies were 502 more than the captures counted: tcpdump itself fell behind there. Both ends are the second board's own processor, which sets the full rate.

The second XGS 3300, SFOS 22

The second XGS 3300 over SFOS 22's software, after a boot with the setting on: tcpdump counted 22,928 replies but only 1,560 of the stream's data frames; 1,254,606 copies a direction at 8.96 Gbit/s, 8.94 Gbit/s without

Part of them, and not the same part from one boot to the next. Before this board's next boot:

run (UTC) at 200 Mbit/s, tcpdump: data / replies, each port copies a direction full rate with the copies, copies a direction in 10 s without
00:18 22,896 / 22,900 45,596 3.64 Gbit/s, 108,783 3.34 Gbit/s

and after it, the setting on through the boot and then saved off and on:

run (UTC) at 200 Mbit/s, tcpdump: data / replies, each port copies a direction full rate with the copies, copies a direction in 10 s without
00:38 1,556 / 22,893 24,388 9.00 Gbit/s, 1,260,661 8.91 Gbit/s
00:41 1,558 / 22,929 24,427 8.46 Gbit/s, 1,179,482 8.57 Gbit/s
00:42 1,556 / 22,913 24,408 7.79 Gbit/s, 1,084,313 8.89 Gbit/s
00:46, the picture 1,560 / 22,928 24,427 8.96 Gbit/s, 1,254,606 8.94 Gbit/s

Before the boot, every frame at 200 Mbit/s, and at full rate 108,783 copies a direction in ten seconds where the stream alone carried some 474,000 data frames; after it, every reply but only the stream's first 1,556 to 1,560 data frames - about its first half second - at 200 Mbit/s, and far more at full rate. Read by hand before the boot, not photographed: 83,005 copies a direction in five seconds at 5.42 Gbit/s, while the host's receive path counted nothing lost (oq_lose, rx_span_nobuf, rx_long unmoved) - the coprocessor sent fewer, the host did not drop them; and with the first board's F1 set to 1,500 bytes for a moment, 204,126 a direction at 1.73 Gbit/s and 152,022 at 1 Gbit/s, five seconds each. What decides which frames this board copies was not found. The second XGS 3300 is also the only one of the three whose stream leaves by a port of its expansion module.

Two runs before these are not in the tables: in them the stream was in the lab's IPsec tunnel, which carries the PC's network to the first XGS 3300, and the copies were of its encrypted frames.

The setting, saved and booted

The second XGS 3300 and the XGS 136 after a boot with the setting on: the word 23 written by the boot on both, and 23,328 copies a direction over a 3-second stream through the XGS 136; then the setting saved off, on and off on the three boards, the word 7, 23, 7

The XGS 136's saves, after two of its connections did not answer in the run above: off, on, off - the word 7, 23, 7 - and the save's report

The setting set as the page's save sets it - the model's field, config.xml, then apply.php (images/deploy/lab-copies-setting.php). Saved on, every board's word became 23 (0x17: the gate, the sequence check, "Cut in hardware" and the copies); saved off, 7. The XGS 136 cannot be asked for its word, so what is shown there is the word its last write recorded, and the copies that a stream then brought. Rebooted with the setting on, the second XGS 3300 came up with the word 23, and the XGS 136's boot hook wrote 23 and the copies came. On that family the word goes out as a byte in hex, and offload.sh wrote it as 0${new} - right only while the word stayed below 10; with the copies it is 23, so it is written with two hex digits now.

What it does not say

  • Whether OPNsense's Intrusion Detection in IDS mode sees the copies was not tried.
  • Why the second XGS 3300 copies only part of its frames, and why which part changed at a boot.
  • The cost to the host's processor with the copies on was not read; forwarding was, and stayed within the spread of the runs without them.

Discussion

The discussion of this page is held in the project's GitHub Discussions. Showing it here loads giscus from giscus.app and GitHub, and writing in it needs a GitHub account - what that means.