Git Product home page Git Product logo

bpfilter's Introduction

bpfilter


An eBPF-based packet filtering framework.

bpfilter transforms how you control network traffic by leveraging the power of eBPF technology. This framework elegantly translates filtering rules into optimized BPF programs, bringing unparalleled performance and flexibility to your packet filtering needs.

Key featuresQuick startDocumentation

bpfilter

Key features

  • High performance: utilizes eBPF's near-native performance capabilities
  • Flexible integration: use the custom iptables integration or bpfilter's bfcli command line for extended functionalities
  • Low overhead: minimal resource consumption with maximized efficiency
  • Developer-friendly: clean architecture with clear separation of components

bpfilter combines three components: a CLI that allows users to define filtering rules in human-readable text, a daemon that converts these rules into efficient BPF programs, and a library that facilitates seamless communication between applications and the filtering subsystem.

Want to know more about bpfilter? Check the user's guide, the developer documentation, or watch our latest public talk!

Quick start

Install

bpfilter is packaged for Fedora 40+, EPEL 9+ and supports Fedora 40+, CentOS Stream 9+, and Ubuntu 24.04+. The examples below uses Fedora 41.

# Fedora 40+ or CentOS Stream 9+ (with EPEL)
sudo dnf install -y bpfilter bpfilter-devel

Build from sources

# Essential build requirements
sudo dnf install -y cmake gcc libbpf-devel libnl3-devel bison flex

# Configure the project and build bpfilter
cmake -S $SOURCES_DIR -B $BUILD_DIR -DNO_DOCS=ON -DNO_TESTS=ON -DNO_CHECKS=ON -DNO_BENCHMARKS=ON
make -C $BUILD_DIR install

Usage

# Start the daemon
sudo $BUILD_DIR/output/sbin/bpfilter

# Count the number of ping coming to interface #2
sudo $BUILD_DIR/output/sbin/bfcli ruleset set --str "chain BF_HOOK_XDP{ifindex=2} policy ACCEPT rule ip4.proto icmp counter ACCEPT"

The complete documentation is available on bpfilter.io.

License

bpfilter is licensed under GPLv2. You can find the licensing details in the COPYING file.

Acknowledgements

bpfilter was initially designed by Alexei Starovoitov with help from David S. Miller and Daniel Borkmann as a Linux kernel usermode helper, and later improved by Dmitrii Banshchikov.

bpfilter's People

Contributors

qdeslandes avatar ryanbsull avatar rphibel avatar daandemeyer avatar amscanne avatar adelavegaf avatar corentinmre avatar glitched-w0rld avatar ikruglov avatar ryantimwilson avatar shaikhyaser avatar tommy-u avatar orangepanda83 avatar

Stargazers

xhxhxh avatar valsha.github.io avatar Hanlin He avatar  avatar Saman avatar  avatar Nicholas Sielicki avatar Elisey Shemyakin avatar  avatar Spade_S avatar Youmu avatar Albin Kerouanton avatar Arcie Ren avatar peter wang avatar btw i use arch avatar Kate Scarlet avatar ryo-yamaoka avatar Maksim Zayakin avatar Eishun Kondoh avatar Sebastian Rolon avatar Mohamed Bassam avatar Sergey avatar  avatar Alexey Volkov avatar Boussebha Wassim avatar Rusty avatar Jared Baur avatar Raku Saito avatar  avatar Jordan ERNST avatar Daniele Cruciani avatar fulanawa avatar  avatar Isaac Young avatar Seungwon Heo avatar λ avatar F avatar Mitt avatar Jesse Brandeburg avatar QJ avatar  avatar Martin C avatar Srinidhi Kaushik avatar  avatar Kathleen Chad avatar Tobias Böhm avatar Sahand Akbarzadeh avatar  avatar  avatar  avatar  avatar Matthieu Barthelemy avatar rsavalos avatar Lucas BEE avatar staylightblow8 avatar  avatar Shivanshu Raj Shrivastava avatar  avatar Tony Norlin avatar s3rj1k avatar  avatar Jose Roman avatar Luigi De Matteis avatar Ian Lai avatar Francesco Benini avatar Gabriel Corona avatar Patrick Paechnatz avatar luobuda avatar 云微 avatar  avatar Gwon Seonggwang avatar Ivan Kapranov avatar  avatar Umut Özdemir avatar  avatar Charles Phillip avatar  avatar Alex  avatar Bora avatar  avatar Dimas Shidqi Parikesit avatar  avatar Sane avatar Ruslan Iusupov avatar MAD CITY MULTIMEDIA avatar Kyle Isom avatar Denis Denisov avatar Mrinal Wadhwa avatar Ryan Carter avatar Saber avatar RichardoMu avatar Bo Shao avatar smoky avatar zhq avatar smallnest avatar Moryyi avatar Manuel Rüger avatar Aaron U'Ren avatar bryan avatar Shivam Verma avatar

Watchers

Michel Lind avatar Alexandre Fiori avatar Kyle McMartin avatar  avatar Jevin Sweval avatar Davide Cavalca avatar  avatar dominix avatar  avatar Jose Roman avatar anitazha avatar  avatar  avatar  avatar Suraj avatar  avatar  avatar Kevin Worth avatar Andrew J Brush avatar  avatar  avatar  avatar  avatar

bpfilter's Issues

Filter IP addresses (v4 and v6) using longest prefix match

Sets are used to filter a multiple IP addresses in constant time. Each IP address to filter it inserted into a BPF map, and the program performs a lookup of the packet's IP into the map. This behaviour doesn't work well with subnets filtering: all the IP addresses of the subnet should be inserted into the set for the program to be able to filter the subnet.

# IPs to filter
192.168.1.1
192.168.1.2
192.168.2.0/24

# IPs in the set
192.168.1.1
192.168.1.2
192.168.2.0
192.168.2.1
[...]
192.168.2.254
192.168.2.255

An alternative is to implement subnets filtering using the Longest Prefix Match algorithm. Thankfully, BPF supports BPF_MAP_TYPE_LPM_TRIE maps.

End-to-end testing of matchers

More and more matchers are added to bpfilter (15 and counting!), each of them supporting different operators and interacting differently which each BPF flavour (XDP, TC, BPF_NETFILTER, CGroup).

All the matchers are manually tested, but as the number of matchers grows, it's becoming more and more difficult to avoid issues when the code generation logic is modified. Thus, we need an easy-to-use, deterministic way of testing all the matchers.

A solution would be to reuse the logic implemented in the benchmark, minus the numerous iterations of BPF_PROG_TEST_RUN:

  • Start the daemon
  • Use bfcli to create the filtering chain, using a specific matcher
  • Use BPF_PROG_TEST_RUN once and validate the result of the program

Use all hooks and matchers in `rules.bpfilter`

tests/rules.bpfilter contains a ruleset to test bfcli and bpfilter. This ruleset only contains a subset of the available hooks and matchers.

Complete the ruleset by defining chains for each hook and using every existing matcher.

Support traffic rate limiting

This issue is defined to keep track of the investigation, feasibility, and progress on rate limit traffic using bpfilter. Design work is required before any implementation.

Allow users to rate limit specific packets, such as ip6tables -A INPUT -p icmpv6 -m limit --limit 20/m --limit-burst 3 -j ACCEPT with iptables.

See iptables and nftables documentation.

Allow fetching rules and counters via bfcli

bpfilter daemon keeps a list of active rules and it also writes counters to BPF maps. Sure, we can access the rules/counters via bpftool - counter maps are named bf_cmap_XXX - but it is more convenient to use an API.

$ sudo bpftool map dump name bf_cmap_000201
[{
        "key": 0,
        "value": {
            "packets": 71220,
            "bytes": 112981553
        }
    },{
        "key": 1,
        "value": {
            "packets": 71288,
            "bytes": 112993982
        }
    }

To do this, we need to implement the BF_REQ_GET_RULES and BF_REQ_GET_RULES APIs in bpfilter backend and fetch the results via bfcli.

bfcli API could look like (where chain/rule are optional):

$ bfcli get-rules <chain> <rule>
$ bfcli get-counters <chain> <rule>

Note bfcli would need to introduce subcommands as bfcli is only used for setting rules now.

Support sets of `(IPv6, port)`

Add support for sets containing (IPv6, port) keys to filter on.

This change is straightforward: add a new key type to set.h and the corresponding key size in set.c.

error: ‘BPF_NETFILTER’ - build bpfilter in Ubuntu 22 and enable the hadware enablement (HWE) stack.

Hi everyone,

I am trying to build bpfilter in my current infrastructure which uses the Ubuntu 22.04 OS.
I enabled the HWE and upgraded the kernel to version 6.5, but there is an error when I hit make -C $BUILD_DIR for building the bpfilter. Here are some information:

## Upgrade kernal
sudo apt-get install --install-recommends linux-generic-hwe-22.04
root@com-glb-168:~/bpfilter# uname -r
6.5.0-45-generic
## Error log from make output
root@com-glb-168:~/bpfilter# make -C $BUILD_DIR
make: Entering directory '/root/bpfilter'
make[1]: Entering directory '/root/bpfilter'
make[2]: Entering directory '/root/bpfilter'
Consolidate compiler generated dependencies of target bpfilter
make[2]: Leaving directory '/root/bpfilter'
make[2]: Entering directory '/root/bpfilter'
[  1%] Building C object src/CMakeFiles/bpfilter.dir/core/bpf.c.o
/root/bpfilter/src/core/bpf.c: In function ‘bf_bpf_nf_link_create’:
/root/bpfilter/src/core/bpf.c:169:36: error: ‘BPF_NETFILTER’ undeclared (first use in this function); did you mean ‘IP_MSFILTER’?
  169 |     attr.link_create.attach_type = BPF_NETFILTER;
      |                                    ^~~~~~~~~~~~~
      |                                    IP_MSFILTER
/root/bpfilter/src/core/bpf.c:169:36: note: each undeclared identifier is reported only once for each function it appears in
/root/bpfilter/src/core/bpf.c:170:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  170 |     attr.link_create.netfilter.pf = NFPROTO_IPV4;
      |                     ^
/root/bpfilter/src/core/bpf.c:171:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  171 |     attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
      |                     ^
/root/bpfilter/src/core/bpf.c:172:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  172 |     attr.link_create.netfilter.priority = priority;
      |                     ^
make[2]: *** [src/CMakeFiles/bpfilter.dir/build.make:104: src/CMakeFiles/bpfilter.dir/core/bpf.c.o] Error 1
make[2]: Leaving directory '/root/bpfilter'
make[1]: *** [CMakeFiles/Makefile2:237: src/CMakeFiles/bpfilter.dir/all] Error 2
make[1]: Leaving directory '/root/bpfilter'
make: *** [Makefile:136: all] Error 2
make: Leaving directory '/root/bpfilter'

I think the error is related to BPF_NETFILTER kernel module, so do I need to do something to enable it?

does bpfilter support redirect?

I’m currently developing an open-source project, apfree-wifidog, which relies on nftables to handle HTTP and HTTPS redirection for network control purposes. I'm interested in using bpfilter to potentially accelerate performance. However, a key requirement is that bpfilter needs to support HTTP and HTTPS redirection functionality similar to what nftables offers. Does bpfilter currently support such redirection features? And if not, are there recommended workarounds or upcoming features that might facilitate this?

the nft rule like this:

nft add rule inet fw4 dstnat_wifidogx_unknown tcp dport 80 redirect to  2060

compiling error in Ubuntu 22.04.2 LTS

/home/liudf/work/bpfilter/src/core/bpf.c: In function ‘bf_bpf_nf_link_create’:
/home/liudf/work/bpfilter/src/core/bpf.c:187:36: error: ‘BPF_NETFILTER’ undeclared (first use in this function); did you mean ‘IP_MSFILTER’?
  187 |     attr.link_create.attach_type = BPF_NETFILTER;
      |                                    ^~~~~~~~~~~~~
      |                                    IP_MSFILTER
/home/liudf/work/bpfilter/src/core/bpf.c:187:36: note: each undeclared identifier is reported only once for each function it appears in
/home/liudf/work/bpfilter/src/core/bpf.c:188:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  188 |     attr.link_create.netfilter.pf = NFPROTO_IPV4;
      |                     ^
/home/liudf/work/bpfilter/src/core/bpf.c:189:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  189 |     attr.link_create.netfilter.hooknum = bf_hook_to_nf_hook(hook);
      |                     ^
/home/liudf/work/bpfilter/src/core/bpf.c:190:21: error: ‘struct <anonymous>’ has no member named ‘netfilter’
  190 |     attr.link_create.netfilter.priority = priority;
      |                     ^
make[2]: *** [src/core/CMakeFiles/core.dir/build.make:76: src/core/CMakeFiles/core.dir/bpf.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:291: src/core/CMakeFiles/core.dir/all] Error 2
make: *** [Makefile:136: all] Error 2

does bpfilter only support compiling on >= ubuntu 24.04?

Allow filtering unsupported protocols

r7 and r8 are used to store the L3 protocol ID (e.g. ETH_P_IP) and the L4 protocol ID (e.g. IPPROTO_TCP). During the L3 and L4 parsing logic, if the protocol is not supported, the register is set to 0 to prevent falsely relying on this header later on:

# Parsing L3 header
if (r7 == ETH_P_IP)
    l3_size = sizeof(struct iphdr);
else if (r7 == ETH_P_IPV6)
    l3_size = sizeof(struct ipv6hdr);
else
    r7 = 0

# Parse the L3 header
...

However, such behaviour prevents filtering on non-supported protocol: r7 does not contain the ID of unsupported protocols, so meta.l3_proto only works for IPv4 and IPv6.

Prevent the header parsing functions in stub.c from resetting the protocol ID to 0.

Make test coverage reporting optional for packaging

For packaging, it's useful to run tests, but coverage reporting is not important (we just care that tests provided by developers pass). It would be useful to be able to disable coverage reporting, and not have a dependency on lcov.

Support IPv6 next header filtering

Filter packets based on the "next header" content of the IPv6 header:

rule
    ipv6.nexthdr eq frag
    DROP
rule
    ipv6.nexthdr in {route, prot}
    counter
    DROP

Create a CONTINUE verdict

With the existing verdicts, it's not possible to forward a packet to the next rule: it is either accepted or dropped. In both cases, the chain won't process the remaining rules.

A CONTINUE verdict would allow packets to continue going through the filtering rules. Currently, the main interest of such a target would be to count the packets matching specific criteria. For example, this is not currently possible

# Counter the number of IPv6 packets and TCP packets going through the hook
rule
    meta.l3_proto ip6
    counter
    ACCEPT
rule
    meta.l4_proto tcp
    counter
    ACCEPT

IPv6 TCP packets would be counted towards rule #1 counter, as every IPv6 packet would be matched by rule #1 and accepted, stopping the processing. However, with a CONTINUE verdict:

# Counter the number of IPv6 packets and TCP packets going through the hook
rule
    meta.l3_proto ip6
    counter
    CONTINUE
rule
    meta.l4_proto tcp
    counter
    CONTINUE

IPv6 TCP packet would be processed by rule #1 and counted, then processing would continue with rule #2 and they would be counted again.

Replace self-hosted CI runners with GitHub-hosted ones

The CI uses self-hosted runners because when the CI workflow was created, GitHub-hosted runner didn't comply with the following requirements:

  • Run on Linux 6.4+: only Ubuntu 22.04 was available, running on an old Linux kernel.
  • Support arm64 runners: while GitHub has provided arm64 runners for some time now, they run on Apple Silicon, which doesn't allow for nested virtualization, thus preventing Docker from being used.

Lately, GitHub fixed both of those blockers as it provides arm64 runners on Ubuntu 24.04 for Team and Enterprise Cloud plans, which makes those available to bpfilter through the Facebook org.

Self-hosted runners come with some maintenance that we should avoid if we can, and bpfilter's CI should be modified to use the GitHub runners instead of the self-hosted ones.

Tests have been conducted to validate the viability of the self-hosted runners, especially when it comes to the usage of BPF_PROG_TEST_RUN (see better_ci branch). To benefit from the arm64 Ubuntu runners, the development, and tests must be performed on the main repository, meaning people without writing rights can't contribute to this task.

"shift/reduce conflicts" warning when building `parser.y`

When Bison builds parser.y for bfcli, the following warning appears:

[ 57%] [BISON][bfcli_parser] Building parser with bison 3.8.2
/home/quentin/Projects/bpfilter/src/bfcli/parser.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
/home/quentin/Projects/bpfilter/src/bfcli/parser.y: note: rerun with option '-Wcounterexamples' to generate conflict counterexamples

This warning is due to conflicting rules, which could generate unexpected output.

Support IPv6 "next header" field

An IPv6 "next header" field can contain one of the following information:

In the first case, the IPv6 header is followed by the transport layer header. This is properly supported by bpfilter.

In the second case, the IPv6 header is followed by an extension header of variable length. This extension header contains a "next header" field to identify the next header, which can be a transport layer header or another extension header. Extensions header are not supported by bpfilter: the BPF program will read the "next header" field of the IPv6 header and use it as the transport layer protocol.

If the packet contains an IPv6 extension header, the BPF program should read it to access the next header, until the transport layer header is found.

Log packets going through a rule

Allow users to log the packet matched by a rule.

Overview

A new keyword must be introduced to allow users to specify if a rule should log matched packets:

chain [...]
  rule
    meta.l3 ipv4
    log l3 l4
    DROP

The log keyword can have the following arguments:

  • No argument, or l3 l4: logs L3 and L4 headers
  • l3 or l4: log the specified header

The L2 (Ethernet) header is only available for some hooks, it can be skipped for now and supported later.

The logging flags will be carried as part of the chain definition. The generated BPF program will then write the requested headers into a BPF ring buffer. bpf_printk() is not a viable solution as it would be slower than a ring buffer, and bpfilter would have to log each packet according to its protocol and fields. Write the headers to the ring buffer allow the user to read them and log them as they want to. The headers will be logged to the ring buffer as logged_packet structures (see below) of variable size (depending on the logged layers).

At runtime, the packet will be logged to the ring buffer using a custom function, similarly to the counters update.

Eventually, bfcli could introduce chain log or rule log command to print the logs of a chain or rule. This is out of the scope of this feature.

Implementation

  • [core] Store the logging flag(s) in struct bf_rule, update the bf_rule_...() functions accordingly.
  • [bfcli] Add support for log [l3] [l4] to the parser and lexer. Use the counter keyword as an example. Update the documentation.
  • [cgen] Add support for the ring buffer map. No need to worry about the map's BTF data for now.
  • [cgen] bf_program should contain a reference to the new logging map. It should only be allocated if the chain requires logging.
  • [cgen] Store L3 and L4 header size into the program's runtime context. The header sizes are calculated in bf_stub_parse_l3_hdr() and bf_stub_parse_l4_hdr(), it should then be stored in the runtime context (e.g. BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_x, BF_PROG_CTX_OFF(lx_size))). If the protocol is not supported, store the size of its header as 0.
  • [cgen] Define the new logging function as a fixup (also here).
  • [cgen] Similarly to update_counters function, implement the log function to:
    • Fill a struct logged_packet on the stack (see below) to write to the ring buffer, such that for the protocol at layer X:
      • lX_proto is the protocol ID, or 0 if this layer should not be logged.
      • lX_size is the size of the header for layer X, or 0 if the protocol is not supported, or if the layer should not be logged.
    • Call bpf_ringbuf_output() kfunc
    • Handle errors properly (update the error counter if needed)
struct logged_packet
{
	uint32_t rule_index;
	uint8_t l3_proto;
	uint16_t l3_size;
	uint16_t l4_proto;
	uint16_t l4_size;
	uint8_t l3_data[l3_size];
	uint8_t l4_data[l4_size];
};

Filter prefixed IPv6 addresses

ip6.saddr and ip6.daddr should allow users to specific a prefix length to filter on a network address:

[...]
    rule
        ip6.saddr 2001:db8:abcd:0012::0/64
        DROP
    rule
        ip6.daddr 2001:db8:abcd:0012::0/112
        ACCEPT
[...]

Some logic is already implemented, but it should be validated with end-to-end tests and fixed if necessary.

Document minimum-known linux kernel version requirement

Potentially pairs with the new README.md note about libbpf 1.0 requirement.
This could also simply be a listing of the the earliest known-working/developer-tested version(s) of the kernel.

As of PR #12 I can't build on my kernel 5.15.x-based system. It seems the bpf_dynptr was introduced to bpf-next in May 2022. Guessing that puts a minimum kernel for dynptr somewhere around 5.18 or 5.19, which is past that of stock Ubuntu, RHEL, and SLE/SUSE releases according to wikipedia. libbpf 1.0.0 was August 22, 2022, so if libbpf and kernel need to move in-step, perhaps the required kernel is 6.0 or newer?

Totally understand if maintainers want to focus on current kernel releases rather than optional configs and work-around for old kernels, but might help avoid further "issue" reports to document a known minimum.

LAB testing

Hi , I install it but when i use iptables with option i have error iptables v1.8.9 (nf_tables): unknown option "--bpf".
How to install it correctly and using it?
i already start it @bpfilter/build/src# ./bpfilter
info : restored new codegen at 0x8006f0
info : restored new codegen at 0x800bb0
info : restored new codegen at 0x801070
info : cache already initialised, skipping initialisation
info : waiting for requests...

Integrate user-defined BPF (sub)programs

Allow users to provide pre-compiled BPF programs to perform filtering in-place of a rule in the generated BPF program.

For example:

chain ...
    rule
        ip6.addr ::1
        ACCEPT
    rule
        meta.program ~/bpf/test.o
        counter
        DROP

The custom BPF program would be called when the rule is processed and returned whether it matches the packet's data, the program would apply (or not) the verdict.

extract eBPF from bpfilter

Hi there,

Is it possible to get the eBPF code from a bpfilter ruleset? I am would like to use Klee to do symbolic execution on the firewall to extract a formula that defines its semantics. To do this, I make the input packet "symbolic" in Klee, and then collect the execution paths from Klee, and conjunct them together to get a single SMT formula that describes the firewall. Then I can compare two rulesets and give a formal proof that they are (or are not) equivalent. I am imagining this could be useful for checking that a refactor does not change the semantics (but might improve performance or code organization for example).

We (with @Jeffery2008) were working with this idea using iptables but it looks like bpfilters might be better. We were trying to verify migrations of iptables code to eBPF, but it seems like rather than manually migrating, it is better to just use the iptables support here. A user could just take their iptables-save and use it with the iptables system provided by this tool and get the benefits of running eBPF rather than iptables.

Also, if this doesn't make sense, would love some feedback. I am more of a program verification person than a networking person so apologies if we are missing some key point here.

Thank you!

Generic UDP and TCP ports filtering

bpfilter can filter on TCP or UDP source and destination port with tcp.sport, tcp.dport, udp.sport and udp.dport. However, matching a given port for both TCP and UDP requires two rules:

rule
    tcp.dport 22
    DROP
rule
    udp.dport 22
    DROP

On the other hand, nftables can filter on port TCP and UDP ports using a single rule:

nft add rule filter input meta l4proto { tcp, udp }  th dport 53 accept

A new matcher should be implemented to allow users to match on both TCP and UDP port (source or destination) using a single matcher, e.g.:

rule
    meta.sport 22
    DROP

feasible to use bpfilter with uBPF to evaluate/test nftables rulesets in userspace?

i have a dream, where all nftables rules are tested statically before insertion into the kernel.

reading the bpfilter documentation, it seems that the daemon can be used (transparently?) as a backend for nftables userspace tools. did i understand that correctly?

if that's possible, i'd like to pull the generated BPF bytecode from bpfilter and pass it through uBPF in order to perform analysis against simulated packets.

does this seem like something that would work well with bpfilter as currently implemented?

Suggestion: update README with what is currently working for iptables/nftables & examples with iptables/nftables

Hello,

Thank you for this project ! I Saw your presentations and I'm interested in this project as this seems to bridge the gap between sysadmins/netadmins (I am) and Software engineers who brings more performance for Linux filtering !

I'm currently trying to reproduce an example with some rules and it's not working and I'm not sure if this is because that is not (yet) implemented or if my build setup is wrong.
Would it be possible to add a section on the README file with what is working and not working for iptables/nftables ?

Also an example of an iptables/nftables rule add/suppression would be greatly appreciated for starters :)

Thank you.

Compiled bpfilter doesn't load: No such file or directory

Hello @qdeslandes,

I have just compiled the bpfilter module on both the linux-6.1.14 branch and the bpf-next branch, both times I get the following output in dmesg:

[    4.619942] bpfilter: Loaded bpfilter_umh pid 971
[    4.622811] bpfilter: generate forward packet assessment
[    4.622823] bpfilter: generate forward packet assessment
[    4.625250] bpfilter: failed to create TC hook: No such file or directory
[    4.625348] bpfilter: failed to load chain INPUT in table filter: processed 73 insns (limit 1000000) max_states_per_insn 0 total_states 4 peak_states 4 mark_read 3
[    4.625455] bpfilter: failed to install new table 'filter': No such file or directory
[    4.625578] bpfilter: failed to created filter table: No such file or directory
[    4.625807] bpfilter: read fail 0

Would you happen to know if I did something wrong?

Thanks a lot,
Mr. Hax

SIGSEGV on starting bpfilter

Hello,

I was starting with bpfilter, and I built it from source with v0.1.0 tag.

Here is the debug log I got (both release and debug mode it crashed immediately on startup):

info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_NF_LOCAL_IN
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_NF_FORWARD
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_NF_LOCAL_OUT

==596886==ERROR: AddressSanitizer: heap-use-after-free on address 0x603000000040 at pc 0x7fa6f2c4814b bp 0x7ffd66cb3e90 sp 0x7ffd66cb3640
READ of size 4 at 0x603000000040 thread T0
    #0 0x7fa6f2c4814a in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827
    #1 0x56413c29824d in bf_memcpy /home/zy/src/bpfilter/src/core/helper.h:176
    #2 0x56413c2985b6 in bf_marsh_new /home/zy/src/bpfilter/src/core/marsh.c:26
    #3 0x56413c2992e5 in bf_marsh_add_child_raw /home/zy/src/bpfilter/src/core/marsh.c:77
    #4 0x56413c21d6f9 in bf_cgen_marsh /home/zy/src/bpfilter/src/bpfilter/cgen/cgen.c:113
    #5 0x56413c296222 in bf_list_marsh /home/zy/src/bpfilter/src/core/list.c:127
    #6 0x56413c25987a in _bf_ctx_marsh /home/zy/src/bpfilter/src/bpfilter/ctx.c:275
    #7 0x56413c25aa5d in bf_ctx_save /home/zy/src/bpfilter/src/bpfilter/ctx.c:349
    #8 0x56413c218c96 in _bf_save /home/zy/src/bpfilter/src/bpfilter/main.c:210
    #9 0x56413c219f3e in _bf_init /home/zy/src/bpfilter/src/bpfilter/main.c:313
    #10 0x56413c21bca6 in main /home/zy/src/bpfilter/src/bpfilter/main.c:476
    #11 0x7fa6f20461c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #12 0x7fa6f2046284 in __libc_start_main_impl ../csu/libc-start.c:360
    #13 0x56413c216b80 in _start (/home/zy/src/bpfilter/build/output/sbin/bpfilter+0xc0b80)

0x603000000040 is located 0 bytes inside of 24-byte region [0x603000000040,0x603000000058)
freed by thread T0 here:
    #0 0x7fa6f2cb76a8 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x56413c21d4df in bf_cgen_free /home/zy/src/bpfilter/src/bpfilter/cgen/cgen.c:97
    #2 0x56413c2675fb in _bf_ipt_set_rules_handler /home/zy/src/bpfilter/src/bpfilter/xlate/ipt/ipt.c:474
    #3 0x56413c269c05 in _bf_ipt_setup /home/zy/src/bpfilter/src/bpfilter/xlate/ipt/ipt.c:660
    #4 0x56413c219cf1 in _bf_init /home/zy/src/bpfilter/src/bpfilter/main.c:303
    #5 0x56413c21bca6 in main /home/zy/src/bpfilter/src/bpfilter/main.c:476
    #6 0x7fa6f20461c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

previously allocated by thread T0 here:
    #0 0x7fa6f2cb89cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x56413c21c5be in bf_cgen_new /home/zy/src/bpfilter/src/bpfilter/cgen/cgen.c:32
    #2 0x56413c26629d in _bf_ipt_xlate_set_rules /home/zy/src/bpfilter/src/bpfilter/xlate/ipt/ipt.c:430
    #3 0x56413c266856 in _bf_ipt_set_rules_handler /home/zy/src/bpfilter/src/bpfilter/xlate/ipt/ipt.c:461
    #4 0x56413c269c05 in _bf_ipt_setup /home/zy/src/bpfilter/src/bpfilter/xlate/ipt/ipt.c:660
    #5 0x56413c219cf1 in _bf_init /home/zy/src/bpfilter/src/bpfilter/main.c:303
    #6 0x56413c21bca6 in main /home/zy/src/bpfilter/src/bpfilter/main.c:476
    #7 0x7fa6f20461c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-use-after-free ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c067fff8000: fa fa 00 00 00 fa fa fa[fd]fd fd fa fa fa fd fd
  0x0c067fff8010: fd fa fa fa fd fd fd fa fa fa fd fd fd fa fa fa
  0x0c067fff8020: fd fd fd fa fa fa fd fd fd fa fa fa fd fd fd fa
  0x0c067fff8030: fa fa fd fd fd fa fa fa fd fd fd fa fa fa fd fd
  0x0c067fff8040: fd fa fa fa fd fd fd fa fa fa fd fd fd fa fa fa
  0x0c067fff8050: fd fd fd fa fa fa fd fd fd fa fa fa fd fd fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==596886==ABORTING

In addition, if I run with "bpfilter -t" then it does not immediately. The segfault happened after I ctrl-C to send a SIGINT to the daemon. With very similar debug log. The difference is: when I run without -t, the BPF programs and maps were still pinned under /sys/fs/bpf/, with these names:

bf_cmap_030000 bf_cmap_040000 bf_cmap_070000 bf_pmap_030000 bf_pmap_040000 bf_pmap_070000 bf_prog_030000 bf_prog_040000 bf_prog_070000

/run/bpfilter/daemon.sock is not there. If I run with -t and SIGINT, then BPF objects were not pinned, /run/bpfilter/daemon.sock will remain undeleted.

For compiling from source we were using:
gcc 12.2.0
clang 18.1.5
libbpf 1.5.0

The platform we run were debian bookworm. The daemon segfault the same with both 6.12.12 kernel with Intel(R) Xeon(R) CPU E5-2650 v3, and 6.6.62 kernel with AMD EPYC 9684X. libc6 version is 2.36-9+deb12u9.

Appreciate if someone could help look into this issue. I can provide additional information or instrument the source for more information.

thanks
Yan

On-the-fly sets update

Sets are currently created and filled once: when the corresponding BPF program is loaded into the kernel. If a set is modified in the ruleset, the whole BPF program is regenerated and the set is re-created, even if none of the rules changed.

bpfilter should be able to update a set without re-creating the corresponding BPF program.

Cannot restart bpfilter daemon after a first stop

Hello,
I'm working on an Ubuntu 24.04 server, compilation is OK, the first launch of bpfilter is successful:

user@ubuntu-2404-32go:~$ sudo bpfilter_build/src/bpfilter
[sudo] password for user: 
info   : failed test access to context file: /run/bpfilter/data.bin: No such file or directory
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_LOCAL_IN::ens18
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_FORWARD::ens18
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_LOCAL_OUT::ens18
info   : waiting for requests...

but when exiting with Ctrl+C and relaunching bpfilter it does not work

user@ubuntu-2404-32go:~$ sudo bpfilter_build/src/bpfilter
[sudo] password for user: 
info   : failed test access to context file: /run/bpfilter/data.bin: No such file or directory
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_LOCAL_IN::ens18
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_FORWARD::ens18
info   : generating BF_FLAVOR_NF program for BF_FRONT_IPT::BF_HOOK_IPT_LOCAL_OUT::ens18
info   : waiting for requests...


^Cinfo   : received stop signal, exiting...
user@ubuntu-2404-32go:~$ sudo bpfilter_build/src/bpfilter
error  : failed to pin strings map: File exists
error  : failed to initialise messages map: File exists
error  : failed to initialize bpfilter: File exists
user@ubuntu-2404-32go:~$ 

I can make it work by rebooting my VM so I think there should be some files which are not cleaned with a ctrl+c ?

Thank you for your help.

Run the CI in a custom container

The CI jobs run in Docker containers, depending on the distribution the project needs to be built/tested on. Installing the dependencies and configuring the build container is long and account for most of the CI duration.

The CI duration could be reduced by using a container that already provides the required dependencies. We could also run each major step (build, test…) in a separate container to provide more granular check status on PRs.

Using a custom container, the workflow could be the following:

  • Build the containers (Ubuntu, Fedora, x64, arm64…)
  • Push the container to the repository's register(GHCR)
  • Run the build step in a container
  • Run the test step in a container
  • More check…

See better_ci for a PoC.

Avoid conflicting XDP programs

bpfilter will attach XDP programs using a BPF link tied to a specific interface index. If a BPF program is already attached to the interface, the link creation will fail and no packet filtering will be performed.

There is no solution at the moment to attach multiple XDP BPF programs to a single interface, except using a loader from user-space (e.g. xdp-loader). This solution works well if a user-space process is used as a proxy to manage XDP programs, but not so much with XDP programs loaded from different processes.

@ryanbsull has submitted a patch to support multi-prog for XDP BPF program. This appears to be the correct approach and should be supported by bpfilter.

Refactor `nft` front-end to properly parse bytecode

Plenty of new features have been introduced during H2 2024 which are supported by bfcli. The nft front-end hasn't progressed at the same speed due to many limitations with its current implementation, making bpfilter unusable with nftables.

The front-end's logic is the following:

  • nftables CLI binary parse the user-define rules and converts them into nft bytecode
  • libbpfilter (linked to nftables) send the bytecode to the bpfilter daemon using Netlink messages
  • The nft front-end in the daemon parses the Netlink messages to extract the nft bytecode, and converts the bytecode into its internal format (shared by all the front-ends)

The current implementation of the bytecode parsing logic can't be scalable to understand more complex filtering rules and only works with simple "filter IP xxx.xxx.xxx.xxx" rules. It must be refactored to provide a generic framework to properly parse the bytecode and allow for more matchers and rules to be supported in the future.

Some exploration is required to understand nft bytecode format, investigate if a similar tool or library already exists, and suggest possible solutions for this issue.

The purpose of this task is to define, document, and implement a scalable framework to parse nft bytecode, not to reach feature parity between nftables and bfcli.

Collect error metrics

Generated BPF programs call kfuncs and BPF helpers for various purposes, and those call might fail. In this situation, the program will return with a default verdict. There is currently no way to know if such an error occurred during the program's lifetime, except for reading /sys/kernel/debug/tracing/trace_pipe if bpfilter is running in debug mode (--debug).

An error/failure counter would bring more visibility to this situation and help to understand the program's behavior.

Support output interface filtering

bpfilter supports meta.ifindex to filter on a network interface, which is effectively filtering on the input network interface. Similarly, add support for meta.oifindex to filter on an output network interface.

Clarify the documentation related to meta.ifindex to specific it filters on the input interface.

Refactor the `nftables` patch and validate usability

Refactor the custom nftables patch to support the latest nft tool release. Validate the modified version of nftables for supported matchers and common operation (create a ruleset, fetch rules and counters, backup/restore...).

Once the patch is validated, it would be worth gathering feedback from the Netfilter mailing list.

Support generic sets

Sets keys are defined statically: set.h contains an enumeration of valid keys, and each set type has its logic supported in the source code.

This behaviour makes sets very rigid and even useless for some users: either your key type is supported, or you need to rely on slower rules processed sequentially.

Users should be able to define set keys themselves using one or more matcher keys, for example:

chain BF_HOOK_XDP{ifindex=2} policy ACCEPT
    set
        name test_set
        key ip4.saddr ip4.dport
        values { 192.168.1.1, 22 }, { 192.168.1.1, 53 }
  rule
      [...]

Failed to create a `BF_HOOK_NF_PRE_ROUTING` chain

Using the following chain:

chain BF_HOOK_NF_PRE_ROUTING policy ACCEPT
    rule
        meta.l4_proto eq icmpv6
        counter
        DROP

bfcli returns an error: error : failed to set chain for 'BF_HOOK_NF_PRE_ROUTING', skipping remaining chains with the following logs from bpfilter:

info   : generating BF_FLAVOR_NF program for BF_FRONT_CLI::BF_HOOK_NF_PRE_ROUTING
error  : failed to load BPF program (143 bytes):
�.
: Invalid argument
error  : failed to load new bf_program: Invalid argument
error  : failed to generate and load new program: Invalid argument

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.