go-freebsd-pf

Manage the FreeBSD PF firewall from Go through /dev/pf ioctls — rules, tables, states, anchors and atomic transactions, without pfctl.

Gofirewall · pf · ioctl · transactionsfreebsd from gosource on github

Why

Programmatic control of firewall rules, tables and state from Go applications, without shelling out to pfctl.

How

  • Three layers: a public API in Go types (net.IP, net.IPNet, no C types leak out), a conversion layer, and one small C wrapper per ioctl so errno is captured.
  • cgo is required rather than optional: pfvar.h structs are full of nested unions, bitfields and version-specific layouts, and hand-computed layouts with raw syscall.Ioctl would be fragile. C helpers read and write union members that cgo only sees as byte arrays.
  • Atomic changes use DIOCXBEGIN/DIOCXCOMMIT/DIOCXROLLBACK with per-ruleset tickets: Begin, AddRule, then Commit or Rollback.
  • Every ioctl buffer is allocated with C.malloc — the fix for cgo’s “Go pointer to unpinned Go pointer” panics.
  • A mutex guards the client; rules are validated before they reach the kernel.

Limits

Struct layouts come from the build host’s headers, so binaries must be built for the FreeBSD release they run on. State listing needs 14.x. It uses the legacy struct ioctls; the nvlist variants are where FreeBSD is heading.

tx, err := client.Begin(anchor, pf.RuleSetFilter)
if err := tx.AddRule(rule); err != nil { tx.Rollback(); log.Fatal(err) }
if err := tx.Commit(); err != nil { log.Fatal(err) }
$ git clone https://github.com/zombocoder/go-freebsd-pf
← all projects