Usage

bpfilter is composed of two main parts that work together: the clients are used by the users to define the filtering rules and send them to the daemon that performs the heavy lifting of generating the BPF bytecode.

Before anything, you will have to run the daemon on your system, see The daemon for more details.

Once the daemon is running, you need to choose which front-end’s CLI to use:

  • bfcli: bpfilter-specific CLI, developed as part of the project. bfcli supports new bpfilter features before other CLIs as it’s used for development. It allows for a more flexible rule definition: you can use a combination of filters and hooks that might not be possible with other CLIs. However, it doesn’t support nftables or iptables rules format.

  • nftables: requires a custom version of the nft binary with bpfilter support (see below), and support for new bpfilter features is usually a bit delayed.

  • iptables: similar to nftables, however iptables has been deprecated globally in favor of nftables.

Install

bpfilter is packaged for Fedora 40+, EPEL 9+ and supports Fedora 40+, CentOS Stream 9+, and Ubuntu 24.04+.

> sudo dnf install -y bpfilter

If you use a different distribution, you can still build and use bpfilter if you satisfy the requirements, see the developer documentation.

Example usage

From here on, we assume bpfilter has been installed on your system. If you build it locally, you will need to substitute the bpfilter command with $BUILD_DIR/output/sbin/bpfilter, same for bfcli. The example below is meant to familiarize you with bpfilter, more in-depth information can be found throughout the documentation.

This example will block ping requests sent going out of the local host to a remote server.

Can we ping now?

Let’s check if we can ping facebook.com before we do anything:

$ ping -n -c 4 facebook.com
PING facebook.com (157.240.253.35) 56(84) bytes of data.
64 bytes from 157.240.253.35: icmp_seq=1 ttl=128 time=24.9 ms
64 bytes from 157.240.253.35: icmp_seq=2 ttl=128 time=23.6 ms
64 bytes from 157.240.253.35: icmp_seq=3 ttl=128 time=28.6 ms
64 bytes from 157.240.253.35: icmp_seq=4 ttl=128 time=24.8 ms

--- facebook.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3001ms
rtt min/avg/max/mdev = 23.596/25.493/28.622/1.880 ms

Start the daemon

The daemon is responsible for receiving the user-defined filtering rules, and translating them into BPF programs. We will start it in --transient mode, so all the filtering programs defined will be discarded when we stop it, preventing any mistake on our side!

$ sudo bpfilter --transient
info   : waiting for requests...

Create a new filtering rule

Now that the daemon is up and running, we will use bfcli to send a filtering chain. A chain is a set of rules to filter packets on:

$ sudo bfcli ruleset set --str "
chain BF_HOOK_NF_LOCAL_OUT policy ACCEPT
    rule
        ip4.proto icmp
        DROP
"

We split the chain over multiple lines, to it’s easier to read. Alternatively, you can write the chain in a file and call bfcli ruleset set --file $MYFILE. We choose to create a chain attached to BF_HOOK_NF_LOCAL_OUT which is called for every packet leaving this host with ACCEPT as the default policy: if a packet doesn’t match any of the rules defined, it will be accepted by default.

Our chain contains a single rule matching against the IPv4’s protocol field. Packets matching this rule will be DROP ed.

Can we still ping?

Now that our filtering rule is in place, pings to facebook.com should be blocked, let’s check this out:

$ ping -n -c 4 facebook.com
PING facebook.com (157.240.253.35) 56(84) bytes of data.

--- facebook.com ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3010ms

100% packet loss? That’s great news! If you are eager to learn more ways to filter traffic using bfcli, check its documentation.