`AF_PACKET` XDP Driver Bypass: BPF maps remain empty (`ipv4_maps_count: 0`) despite `stream.bypass: true` (Suricata 8.0.6, Kernel 6.16)

Hi everyone,

INFRASTRUCTURE

OS: Debian Linux (Kernel 6.16) as a virsh guest on SUSE host with PCI pass-thru for

Suricata Version: 8.0.6 (Built from source, installed under /opt)

Mode: AF_PACKET IPS inline copy-mode

NICs: 03:00.0 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)
03:00.1 Ethernet controller: Intel Corporation 82576 Gigabit Network Connection (rev 01)

I’m setting up Suricata 8.0.6 in IPS mode using AF_PACKET with xdp-mode: driver on a 2-port inline bridge (enp9s0 <==> enp10s0). The eBPF driver program compiles, loads into the kernel without verifier errors, and links successfully. However, the BPF flow tables (flow_table_v4) remain completely empty ([]), and suricatasc -c ebpf-bypassed-stat reports 0 bypassed flows across both interfaces. Post suricata start my Internet in Debian VM and SUSE host goes down.

I’m trying to understand why Suricata’s engine is not populating the eBPF maps when a flow reaches stream.reassembly.depth or when stream.bypass: true is set.

The BPF object loads clean into kernel 6.16, and bpftool shows the XDP filter running:

# bpftool prog list name xdp_hashfilter
55: xdp  name xdp_hashfilter  tag baa10127dca180f2  gpl
        loaded_at 2026-08-12T18:08:10+0530  uid 0
        xlated 3040B  jited 1642B  memlock 4096B  map_ids 75,71,77,76,72,74,73
        btf_id 118

However, inspecting Map ID 71 (flow_table_v4) shows it is completely empty:

# bpftool map dump id 71
[]

And suricatasc confirms 0 bypassed flows:

# suricatasc -c ebpf-bypassed-stat
{
  "message": {
    "enp9s0": { "ipv4_maps_count": 0, "ipv4_success": 0, "ipv4_fail": 0 },
    "enp10s0": { "ipv4_maps_count": 0, "ipv4_success": 0, "ipv4_fail": 0 }
  },
  "return": "OK"
}


Configuration Snippets

ips-xdp.yaml:

af-packet:
  - interface: enp9s0
    threads: auto
    cluster-id: 99
    cluster-type: cluster_qm
    defrag: yes
    copy-mode: ips
    copy-iface: enp10s0
    xdp-mode: driver
    xdp-filter-file: /opt/libexec/suricata/ebpf/xdp_filter.bpf
    bypass: yes
    use-mmap: yes
    ring-size: 200000
    buffer-size: 64535

  - interface: enp10s0
    threads: auto
    cluster-id: 100
    cluster-type: cluster_qm
    defrag: yes
    copy-mode: ips
    copy-iface: enp9s0
    xdp-mode: driver
    xdp-filter-file: /opt/libexec/suricata/ebpf/xdp_filter.bpf
    bypass: yes
    use-mmap: yes
    ring-size: 200000
    buffer-size: 64535

suricata-ips.yaml (Stream section):

stream:
  memcap: 64 MiB
  bypass: true
  checksum-validation: yes
  midstream: false
  midstream-policy: bypass
  inline: auto
  reassembly:
    memcap: 256 MiB
    depth: 1 MiB

Additional information :stdout

Is stream.reassembly.depth + stream.bypass: true expected to automatically call EBPFBypassSetFlow() and insert flow tuples into flow_table_v4 in modern Suricata releases, or does kernel eBPF map insertion strictly require an explicit signature with a pass / bypass action?

Are there specific capture: or flow: configuration directives required in Suricata 8 to bridge user-space stream bypasses with AF_PACKET eBPF map population?

Any insight on what triggers the eBPF map insertion callback in standard AF_PACKET IPS mode would be greatly appreciated!

Hi @coldboot , welcome,

is your traffic encapsulated in something other than VLANs? Do you see any bypass counters in the stats? There should be a section flow_bypassed in the eve.json – if Suricata determines capture bypass is not available/cannot be used, it can still bypass packets locally so you should definitely see at least those counters in the stats. Otherwise there is nothing being bypassed.

Also, can you perhaps add a simple bypass rule to force bypass on every flow? Something like:

alert ip any any -> any any (msg: "bypass"; bypass; noalert; sid:1;)

I would first test it without noalert to make sure the alert fires because I just hand-written it.

1 Like

I don’t make networking mistakes. I create undocumented traffic-generation architectures. 180 seconds, 2 million packets, Suricata happily mirroring itself into oblivion. Eventually I discovered the revolutionary concept of “don’t plug the loop back into itself".

It is working fine now:

╭─[Sat Aug 29 17:27:06] [root@torq] [/etc/suricata]
╰─# suricata -c /etc/suricata/suricata-af-packet.yaml --af-packet -v
Info: conf-yaml-loader: Including configuration file /etc/suricata/af-packet.yaml.
Info: conf-yaml-loader: Including configuration file /etc/suricata/threading.yaml.
Info: conf-yaml-loader: Including configuration file /etc/suricata/outputs.yaml.
Info: conf-yaml-loader: Configuration node 'outputs' redefined.
Info: conf-yaml-loader: Configuration node 'logging' redefined.
Info: conf-yaml-loader: Including configuration file /etc/suricata/rules.yaml.
Info: conf-yaml-loader: Configuration node 'default-rule-path' redefined.
Info: conf-yaml-loader: Configuration node 'rule-files' redefined.
Info: conf-yaml-loader: Configuration node 'classification-file' redefined.
Info: conf-yaml-loader: Configuration node 'reference-config-file' redefined.
Info: conf-yaml-loader: Configuration node 'threshold-file' redefined.
Info: conf-yaml-loader: Configuration node 'detect' redefined.
Info: conf-yaml-loader: Configuration node 'app-layer' redefined.
Info: conf-yaml-loader: Configuration node 'flow' redefined.
Info: conf-yaml-loader: Configuration node 'defrag' redefined.
Info: conf-yaml-loader: Configuration node 'host' redefined.
Notice: suricata: This is Suricata version 8.0.6 RELEASE running in SYSTEM mode
Info: cpu: CPUs/cores online: 8
Info: af-packet: Setting IPS mode
Info: suricata: Preparing unexpected signal handling
Info: logopenfile: eve-log output device (regular) initialized: /var/log/suricata/eve.json
Info: logopenfile: stats output device (regular) initialized: /var/log/suricata/stats.log
Info: detect: 2 rule files processed. 52647 rules successfully loaded, 0 rules failed, 0 rules skipped
Info: threshold-config: Threshold config parsed: 0 rule(s) found
Info: detect: 52652 signatures processed. 1303 are IP-only rules, 4513 are inspecting packet payload, 46600 inspect application layer, 110 are decoder event only
Info: unix-manager: unix socket '/var/run/suricata/suricata-command.socket'
Info: af-packet: enp3s0f0: AF_PACKET IPS mode activated enp3s0f0->enp3s0f1
Info: ebpf: Successfully loaded eBPF file '/usr/local/libexec/ebpf/xdp_filter.bpf' on 'enp3s0f0'
Info: runmodes: enp3s0f0: creating 4 threads
Info: af-packet: enp3s0f1: AF_PACKET IPS mode activated enp3s0f1->enp3s0f0
Info: ebpf: Successfully loaded eBPF file '/usr/local/libexec/ebpf/xdp_filter.bpf' on 'enp3s0f1'
Info: runmodes: enp3s0f1: creating 4 threads
Notice: threads: Threads created -> W: 8 FM: 1 FR: 1   Engine started.

I apologies for the inconvenience.

I’m glad you were able to resolve that!