A high-performance firewall built with DPDK graph nodes and a runtime CLI policy engine.
It is currently set up for AF_PACKET virtual devices inside a VM or container-friendly Linux environment using DPDK EAL arguments such as --no-pci --vdev=net_af_packet0,iface=eth0.
- Graph-based packet pipeline
- Runtime firewall policy updates from a CLI thread
- Stateful connection tracking
- Per-flow accounting in the conntrack table
- Drop-reason logging
- Trace mode for firewall decisions
+---------------------+
| Runtime CLI Thread |
| allow/reject/trace |
+----------+----------+
|
v
+----------------------+
| Runtime Policy |
| + Trace State |
+----------+-----------+
|
v
+-----------+ +----------------+ +-----------------+ +-------------------+ +------------------+ +----------------+ +---------+
| AF_PACKET | --> | rx_node | --> | eth_parse_node | --> | ipv4_parse_node | --> | firewall_acl_ | --> | conntrack_node | --> | tx_node |
| / NIC port | | burst receive | | IPv4 gate | | tuple parsing | | node | | flow updates | | |
+-----------+ +----------------+ +-----------------+ +-------------------+ +--------+---------+ +--------+-------+ +----+----+
| | |
v v v
+-----------------------------------------------+
| drop_node |
| malformed, denied, unsupported, tx-failed |
+-----------------------------------------------+
rx_nodepulls a burst of packets from DPDK port0.eth_parse_nodeaccepts only IPv4 Ethernet frames.ipv4_parse_nodeparses the IPv4 packet and 5-tuple.firewall_acl_nodeevaluates runtime policy and trace state.- Allowed packets go to
conntrack_nodefor flow accounting. tx_nodetransmits accepted packets.- Any parse, policy, or transmit failure is sent to
drop_node.
main.c: initializes DPDK EAL, mempool, port, graph, ACL loader, and CLI threadfirewall/runtime_policy.c: runtime policy engine forallow,reject,trace, andshowconntrack/flow_table.c: stateful flow table and byte/packet countersfirewall/acl_rules.c: ACL file loading support used during startup and reload
main.c: app bootstrap and CLI threadgraph.c: graph creation from the registered DPDK nodesnodes/: packet-processing graph nodesfirewall/: runtime policy engine and ACL loadingconntrack/: flow table implementationinclude/: shared packet and firewall structuresDockerfile: container image for building the projectMakefile: build, clean, and package commands
Build the binary from the repository root:
makeThis creates:
./graphshieldExample with AF_PACKET on eth0:
sudo ./graphshield -l 0-1 -n 4 --no-pci --vdev=net_af_packet0,iface=eth0 -- rules.txtNotes:
rules.txtis optional for the current runtime-policy path.- If the ACL file cannot be loaded, the app still starts in runtime CLI policy mode.
- In AF_PACKET mode, the Linux kernel network stack can still see traffic on the same interface.
Build the image:
docker build -t graphshield .Start a shell inside the built image:
docker run --rm -it graphshieldIf you want to run the firewall from the container, pass the needed network and privilege options for your environment and then execute ./graphshield with the required DPDK arguments inside the container.
Create a distributable source archive of the repository:
make packageThis creates:
graphshield-src.tar.gzTo extract it somewhere else:
tar -xzf graphshield-src.tar.gzDefault behavior on startup is deny-all.
A flow is allowed only when the runtime policy permits it.
For runtime allow, both of these dimensions must match:
- Protocol dimension:
allow tcp,allow udp,allow icmp, orallow any - Destination IP dimension:
allow ip 1.2.3.4orallow ip any
If either dimension does not match, the packet is dropped.
Explicit rejects are checked first:
reject anyreject tcpreject udpreject icmpreject ip <addr>reject ip any
- Conntrack is used for stateful flow tracking and the fast path.
- When policy changes through the CLI, the conntrack table is cleared.
tracealso clears conntrack first so the observed decision path is fresh.
helpshowshow tracetrace
allow tcpallow udpallow icmpallow anyallow ip <IPv4>allow ip anyaccept ...is treated the same asallow ...
reject tcpreject udpreject icmpreject anyreject ip <IPv4>reject ip anydeny ...anddrop ...are aliases forreject ...
clear any
This clears the broad allow any and reject any style toggles, but does not remove specific protocol or IP entries.
Command:
trace
Behavior:
- Starts a blocking trace session in the CLI.
- Ends when either:
10allowed packets are observed, or20rejected packets are observed first.
- The CLI prompt returns after the trace session completes.
Example trace outputs:
TRACE allowed path=runtime proto=6 src=... dst=... len=... progress=...
TRACE done: observed 20 rejected packets before 10 allowed. Firewall rules blocked traffic.
No command is required. Startup policy is already deny-all.
clear any
allow ip any
allow tcp
show
clear any
reject tcp
reject icmp
allow ip any
allow udp
show
clear any
allow ip 104.30.136.45
allow tcp
show
Firewall terminal:
trace
Traffic generator terminal:
curl -4 https://example.com >/dev/null 2>&1drop_node logs aggregated reasons over time:
non-ipv4malformedunsupported-l4acl-denytx-failed
Example:
drop reason=acl-deny count=4097
firewall/acl_rules.c still exists and is initialized from main.c, but the active packet decision path is centered on the runtime policy engine. If ACL file loading fails, the application continues in runtime CLI policy mode.
This means no packet matched your current allow rules before the reject threshold.
Check:
showoutput- Whether both protocol and destination IP dimensions are allowed
- Whether you generated matching traffic immediately after starting
trace
Explicitly reject protocols you do not want:
reject tcp
reject udp
reject icmp
Then re-allow only the protocol you want.
You must also allow ICMP:
allow ip 8.8.8.8
allow icmp
The current CLI is line-based and does not provide shell-like history support.
- DPDK graph nodes keep the data plane modular and fast.
- Burst RX/TX reduces per-packet overhead.
- Conntrack uses a DPDK hash table for efficient flow lookup and updates.