Skip to content

yanjiulab/packetforge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PacketForge

PacketForge is a programmable packet crafting and injection tool based on two DSLs:

  • PDL (Protocol Definition Language): define protocol headers and defaults.
  • PSL (Packet Stream Language): describe packet streams and flow control.

Installation

go install github.com/yanjiulab/packetforge/cmd/pf@latest

or:

git clone https://github.com/yanjiulab/packetforge.git
cd packetforge
go build -o pf ./cmd/pf

Command Capability Matrix

Command Primary goal Reads @fuzz Sends real packets Typical output
pf normal parse/build/send No (errors if present) Yes (--dry-run disables) send result or dry-run hexdump
pf fuzz run fuzz rules from PSL Yes Yes (--dry-run disables) per-case values + send/dry-run output
pf explain visualize packet layout No No layer offsets/length/hex (text or JSON)
pf builtin list built-in protocols N/A No protocol name list
pf gen generate Go/C/C++ struct definitions from PDL N/A No generated source/header files

Core CLI Usage

pf (normal mode)

pf -s examples/test.psl -i eth0
pf -s examples/test.psl -d

Common flags:

  • -s, --stream PSL file (required)
  • -p, --proto PDL directory (default proto)
  • -i, --iface interface (default lo)
  • -d, --dry-run parse/build only
  • --l3-udp send via UDP socket using packet ip/udp/payload; optional eth header is ignored when present
  • -r, --recv start receiving before sending and print received packet hex
  • --recv-match-only with -r, only print hex for packets matched by @expect
  • --recv-view recv output view: packetview|hex|both (default hex)
  • --recv-wait drain wait after all sends when --recv-count is 0 (default 1s). If --recv-count is set to a positive N, the default 1s is not used as a cap: wait until N drain-phase packets, unless you explicitly set --recv-wait or PF_RECV_WAIT to limit total wait time
  • --recv-count in the drain phase only, stop after N received packets (default 0, unlimited; send phase does not count toward the limit)
  • --recv-bpf tcpdump-style BPF filter for received packets (e.g. icmp, tcp port 80); on Linux, a filter requires a CGO build with libpcap (CGO_ENABLED=1 and e.g. libpcap-dev); without CGO, omit --recv-bpf or rebuild with CGO
  • --expect-match-mode expect matching mode: exact (bytes equality) or subset (subset fields for common layers)
  • -b, --builtin-proto load builtin protocols
  • --seed random seed for $rand* builtins

pf fuzz

pf fuzz -s examples/fuzz-basic.psl -i eth0
pf fuzz -s examples/fuzz-basic.psl -d --seed 42 --max-cases 20

Notes:

  • @fuzz rules are parsed only in pf fuzz.
  • In normal pf mode, scripts containing @fuzz return an error.

Supported rules:

  • @fuzz layer.field boundary
  • @fuzz layer.field pick(v1,v2,...)
  • @fuzz layer.field range(min,max[,step])
  • @fuzz count N

pf explain

pf explain -s examples/basic.psl
pf explain -s examples/random-builtins.psl --seed 42 --format json

Output contains, per packet:

  • layer name
  • offset
  • byte length
  • layer hex bytes

pf builtin

pf builtin

pf gen

pf gen -p proto -o generated --lang go
pf gen -p proto -o generated --lang all
pf gen -p proto -o generated --lang cpp --expand-builtin-heads

Notes:

  • Supported --lang: go, c, cpp, all.
  • Dynamic arrays are supported:
    • Go: []T
    • C: T* (for []T an extra <name>_len field is generated)
    • C++: std::vector<T>
  • C++ output is split into two files:
    • pdl_gen.hpp: type definitions
    • pdl_gen_codec.hpp: serialize/deserialize codec functions and usage comments
  • Field-length arrays ([field]Type) are generated as pointer + comment (length is referenced field).
  • By default builtin head fields (mac, ipv4, ipv6) are kept as named types (Mac/IPv4/IPv6); use --expand-builtin-heads to expand to raw byte arrays.
  • C/C++ generated struct names no longer use pf_ / Pf prefixes.

DSL Highlights

PDL

  • Built-in types: u8, u16, u32, u64, mac, ipv4, ipv6
  • Auto values: $len, $payload_len, $cksum
  • Struct arrays: []Type, [N]Type, [field]Type
  • Nested structures via struct + field references

PSL

  • Layer fields: proto(field=value,...)
  • Packet wrappers: [...] (single-line packet may omit brackets)
  • Payload: backticks with optional prefixes:
    • string (default): `hello`
    • hex: `x 48656c6c6f`
    • binary: `b 01000001`
    • base64: `64 SGVsbG8=`
  • Flow control:
    • @repeat N / @repeat forever
    • @interval 100ms|1s|...
    • @expect [ ... ] (packet stmt only; matching behavior depends on --expect-match-mode)
      • subset mode supports built-in locator layers (eth/ip/ipv6/udp/tcp) and one custom layer as the last layer.
      • For custom layer in subset, you must explicitly provide full locator chain before it (e.g. eth() ip() udp() myproto(...)).
      • Custom layer decode currently supports scalar fields and nested struct only (arrays are not supported).
      • If payload is specified in @expect, payload bytes are matched exactly.
    • @expect_timeout 2s|500ms|... (packet stmt only; default 1s)
    • @exit (for packet stmt: exit script after this packet statement finishes)
    • @ignore (stmt modifier, place after packet/block like @interval; skip executing that packet/block)
    • async { ... }
  • Constants: const NAME = ...
  • Builtins:
    • sequence: $inc(step), $seq(start[,step])
    • random: $rand(), $randn(max), $randrange(min,max), $randport(), $randmac(), $randipv4(), $randhex(n)

Quick Start Example

examples/test.psl:

const SOURCE = 192.168.1.1
const TARGET = 192.168.1.100

async {
  [eth() ip(src=SOURCE, dst=TARGET) icmp() `ping`]
  @repeat forever
  @interval 1s
}

[
  eth()
  ip(src=SOURCE, dst=TARGET, id=$seq(1, 1))
  tcp(sport=1234, dport=80)
  `GET / HTTP/1.1\r\n\r\n`
]
@repeat 10
@interval 100ms

Run:

pf -s examples/test.psl -i eth0

Directive demo:

pf -s examples/directives-ignore-exit.psl -d

Custom subset expect demo:

pf -s examples/expect-custom-subset.psl -p proto -r --expect-match-mode subset --recv-match-only --recv-view packetview

More examples are in examples/, and protocol definitions are in proto/.

About

PacketForge is a highly flexible, fully programmable network packet crafting and injection tool driven by two domain-specific languages (DSLs): PDL (Protocol Definition Language) and PSL (Packet Stream Language).

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors