Packets are being dropped before your firewall sees them, and bpftool net will tell you which XDP program is responsible. You will inspect BPF programs attached to network interfaces, read the result as text or JSON, and remove or replace an XDP attachment without guessing which mode is active. The examples follow bpftool-net(8) as shipped by linux-tools-common version 6.8.0-139.139 on this system.
Allow 15 minutes for inspection, or longer if you are preparing a change. You need a shell, the bpftool executable, and an interface name such as enp6s0. Listing is read-only. Attaching, replacing and detaching can change packet processing immediately and normally need elevated privileges.
Check that the kernel-specific tool is available and identify the interface you intend to inspect:
$ command -v bpftool
$ ip -br link
$ bpftool version
On this host, /usr/sbin/bpftool is a wrapper, but the matching linux-tools-6.8.0-139-generic executable is not installed. The wrapper reports the missing tool instead of printing a version. Install the matching package through your normal system change process before you copy the remaining commands. Do not infer the bpftool version from the manpage package version.
Checkpoint: you should have a real interface name, and bpftool version should print a bpftool and libbpf version. If it does not, stop here and resolve the package mismatch.
Use show, or its alias list, to inspect all supported networking attachments:
$ sudo bpftool net show
The output begins with XDP entries, followed by tcx, netkit and older tc attachments, then flow dissector and netfilter entries when present. An XDP line has the interface, ifindex, mode and program ID, for example:
xdp:
enp6s0(4) driver id 16
The mode matters. driver is native XDP, while generic and offload identify different execution paths. The manpage also says multiple tc attachments have a defined display order, so do not assume the first network-related line is the program you want.
To narrow the check to one interface, add dev and its name:
$ sudo bpftool net show dev enp6s0
Checkpoint: record the displayed XDP mode and program ID before you change anything. If the interface has no XDP entry, there is nothing for an XDP detach to remove.
Scripts should consume JSON, not parse spacing in the human output. The short form -jp requests pretty JSON, because -p implies -j:
$ sudo bpftool -jp net show dev enp6s0
The JSON is an array containing objects such as xdp and tc. XDP records include the fields devname, ifindex, mode and id. A field such as tc may be absent when there are no entries, so your code should handle missing sections and not treat them as an error.
For a quick manual check, omit -p when compact JSON is easier to pass to another tool:
$ sudo bpftool -j net show dev enp6s0
Tip: do not enable --debug by default. It prints libbpf and verifier logs, which can bury a simple inventory result. Save it for diagnosing a failed load or attach.
The attach command accepts a program by numeric ID, pinned path or tag. Its network attachment types are xdp, xdpgeneric, xdpdrv and xdpoffload:
xdp tries native XDP and falls back to generic XDP if the driver does not support native attachment.xdpgeneric runs at the generic XDP hook, after packets have entered the receive path as sk_buffs.xdpdrv requests native XDP at the driver receive path.xdpoffload requests execution on a NIC that supports XDP offload.Pick the mode that matches your performance and hardware requirement. If you need native XDP, do not substitute xdp and assume the result is native. Inspect the reported mode afterwards.
Warning: attaching alters packet handling, can break connectivity and can replace an existing program. Confirm the program ID, interface, maintenance window and recovery access first. Keep an out-of-band console available if the interface carries your management connection.
$ sudo bpftool net attach xdpdrv id <PROGRAM_ID> dev <INTERFACE>
$ sudo bpftool net show dev <INTERFACE>
Replace both placeholders with values you have verified. A successful second command shows the interface under xdp: with the expected ID and driver mode. If the driver does not support native XDP, the attach fails. It does not turn xdpdrv into generic mode.
To use a pinned program instead, the documented form is:
$ sudo bpftool net attach xdp pinned <PINNED_PROGRAM_PATH> dev <INTERFACE>
Only use a path you have checked and that holds the intended BPF program. A tag can also identify a program, but an ID or pinned path is easier to audit in a change record.
Warning: never add overwrite until you have recorded the current attachment and accepted the interruption risk. The option replaces the previously attached program for that attachment type.
$ sudo bpftool net attach xdpdrv id <NEW_PROGRAM_ID> dev <INTERFACE> overwrite
$ sudo bpftool net show dev <INTERFACE>
The verification result should contain the new program ID. If the attach fails, read the error and the previous listing instead of retrying with different modes. A failed replacement is not proof that the old program was removed.
To undo an XDP attachment, use the same attachment type you attached with:
$ sudo bpftool net detach xdpdrv dev <INTERFACE>
$ sudo bpftool net show dev <INTERFACE>
Recovery: the XDP section should no longer list that attachment. If the listing showed generic, detach with xdpgeneric; if it showed offload, use xdpoffload. The detach command does not accept a program ID, so the interface and correct mode are your recovery details.
bpftool cgroup, while socket and lightweight tunnel attachments may need other tools such as iproute2.id field or the JSON id value for program identity.show and attach separate in scripts. Save a JSON snapshot, make the change, take a second snapshot, then compare interface, mode and program ID.bpftool version runs with the kernel-specific executable installed.