cloudflare/filterforge
Python
Captured source
source ↗cloudflare/filterforge
Description: Tool for solving BPF filters and crafting packets based on these.
Language: Python
License: NOASSERTION
Stars: 60
Forks: 4
Open issues: 1
Created: 2026-03-26T16:27:53Z
Pushed: 2026-04-23T21:03:38Z
Default branch: main
Fork: no
Archived: no
README:
filterforge
Python toolkit for reverse-engineering Berkeley Packet Filter (BPF) bytecode. Given raw BPF instructions — the kind you might extract from a malware sample, a packet capture filter, or a network appliance — filterforge disassembles them into human-readable form, uses Z3 to solve for the set of packet constraints that satisfy an accepting path, and then leverages Scapy to forge a concrete network packet that would pass the filter.
In short: hand it a BPF program and it will give you back a fully-formed packet (Ethernet, IPv4/IPv6, TCP/UDP/SCTP and beyond) that the filter accepts — ready to inspect, save to PCAP, or send on the wire.
It is recommended to install uv for running this tool.
Usage
Please refer to the examples below for the usage of the tool, it's likely that more features will be added over time.
CLI
To quickly get results for a given BPF it's easiest to use the command-line. Due to how some reverse engineering tools are giving back the data copied you can either give a raw hex-string, or an array of hex values, using the -d flag to simply show the disassembly of the given BPF;
# hex-string uv run ff -d -b "280000000c00000015002900060800001500000bdd860000300000001400000015000027110000002800000038000000150024003500000015002300e91400001500220043000000150021009c690000150020006c07000015001f0089000000280000003600000015001d0fe91400001500001d0008000030000000170000001500000f11000000280000001400000045001900ff1f0000b10000000e0000004800000010000000150015003500000015001400e91400001500130043000000150012009c690000150011006c0700001500100089000000480000000e00000015000e00e914000015000d009c69000015000c006c07000015000b0c890000001500000b06000000280000001400000045000900ff1f0000b10000000e000000500000001a000000740000000400000024000000040000000c000000000000000700000000000000400000000e000000150000012054454706000000000004000600000000000000" # array of hex values uv run ff -d -b "[0x28,0x0,0x0,0xc,0x15,0x0,0x1b,0x800,0x30,0x0,0x0,0x17,0x15,0x0,0x5,0x11,0x28,0x0,0x0,0x14,0x45,0x17,0x0,0x1fff,0xb1,0x0,0x0,0xe,0x48,0x0,0x0,0x16,0x15,0x13,0x14,0x7255,0x15,0x0,0x7,0x1,0x28,0x0,0x0,0x14,0x45,0x11,0x0,0x1fff,0xb1,0x0,0x0,0xe,0x48,0x0,0x0,0x16,0x15,0x0,0xe,0x7255,0x50,0x0,0x0,0xe,0x15,0xb,0xc,0x8,0x15,0x0,0xb,0x6,0x28,0x0,0x0,0x14,0x45,0x9,0x0,0x1fff,0xb1,0x0,0x0,0xe,0x50,0x0,0x0,0x1a,0x54,0x0,0x0,0xf0,0x74,0x0,0x0,0x2,0xc,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x48,0x0,0x0,0xe,0x15,0x0,0x1,0x5293,0x6,0x0,0x0,0xffff,0x6,0x0,0x0,0x0]"
To get an overview of what a network packet would look like if it were to be crafted from the given BPF bytecode you can use the -c flag:
uv run ff -c -b "280000000c0000001500000fdd8600003000000014000000150002008400000015000100060000001500002611000000280000003600000015002300fad5000015002200f6e500001500210012e90000150020005fd6000015001f0083e2000015001e0042fb000015001d006db2000015001c00fbf60000280000003800000015001a13fad500001500001a000800003000000017000000150002008400000015000100060000001500001611000000280000001400000045001400ff1f0000b10000000e000000480000000e00000015001000fad5000015000f00f6e5000015000e0012e9000015000d005fd6000015000c0083e2000015000b0042fb000015000a006db2000015000900fbf60000480000001000000015000700fad5000015000600f6e500001500050012e90000150004005fd600001500030083e200001500020042fb0000150001006db2000015000001fbf6000006000000ffff00000600000000000000" 2026-02-09 12:06:23,342 - INFO - Loaded 45 instructions 2026-02-09 12:06:23,342 - DEBUG - Found 50 accepting path(s) 2026-02-09 12:06:23,346 - DEBUG - Path 1 is satisfiable 2026-02-09 12:06:23,347 - INFO - Hexdump: 0000 00 00 00 00 00 00 00 00 00 00 00 00 86 DD 60 00 ..............`. 0010 00 00 00 00 84 00 00 00 00 00 00 00 00 00 00 00 ................ 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0030 00 00 00 00 00 00 D5 FA 00 00 00 00 00 00 00 00 ................ 0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 2026-02-09 12:06:23,347 - INFO - Packet summary: ###[ Ethernet ]### dst = 00:00:00:00:00:00 src = 00:00:00:00:00:00 type = IPv6 ###[ IPv6 ]### version = 6 tc = 0 fl = 0 plen = 0 nh = SCTP hlim = 0 src = :: dst = :: ###[ SCTP ]### sport = 54778 dport = 0 tag = 0x0 chksum = 0x0 ###[ Raw ]### load = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
Some BPF might be using the raw IP layer, you can specify the link-type -l raw for this purpose (defaults to ethernet);
uv run ff -c -b…
Excerpt shown — open the source for the full document.
Notability
notability 3.0/10Low traction new repo, routine