Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ggvisor / xdplganger

A ptrace-based syscall interceptor that transparently redirects UDP socket I/O through AF_XDP sockets — no source changes required in the target binary.

How it works

ggvisor forks the target binary under ptrace and intercepts every socket-related syscall at the kernel boundary:

  1. Entry interception — ggvisor replaces the syscall with an invalid no-op so the kernel does nothing.
  2. Synthetic return — ggvisor performs the equivalent operation itself (via AF_XDP) and writes the return value back into the tracee's registers.
  3. Transparent I/O — the target binary receives a normal fd and return codes; it never knows its traffic was redirected.

Intercepted syscalls: socket, bind, connect, sendto, sendmsg, write, writev, sendfile, recvfrom, close.

TX path

target binary → SYS_SEND* → ptrace stop → ggvisor reads payload
  → builds Ethernet+IPv4+UDP frame → writes into UMEM → kicks AF_XDP TX ring

RX path (optional, requires pinned XSKMAP)

NIC → XDP program → bpf_redirect_map(xsks_map) → AF_XDP RX ring → ggvisor
  → strips headers → injects data into tracee via recvfrom handler

Prerequisites

  • Linux kernel ≥ 5.4 with CONFIG_XDP_SOCKETS=y
  • CAP_NET_ADMIN or root (required for AF_XDP socket creation and ptrace)
  • Go ≥ 1.26 at /home/esc/.go/bin/go (or update scripts/test.sh)
  • clang + libbpf headers (for BPF program compilation, if rebuilding)

Build

# Build ggvisor
go build -buildvcs=false -o bin/ggvisor ./cmd/ggvisor

# Build test helper binaries
go build -buildvcs=false -o bin/test-sender   ./cmd/test-sender
go build -buildvcs=false -o bin/test-sendmsg  ./cmd/test-sendmsg
go build -buildvcs=false -o bin/test-sendfile ./cmd/test-sendfile
go build -buildvcs=false -o bin/test-writev   ./cmd/test-writev
go build -buildvcs=false -o bin/test-receiver ./cmd/test-receiver

Usage

ggvisor [flags] <binary> [binary-args...]

Flags:
  -iface    string   Network interface for AF_XDP (default "eth0")
  -queue    int      NIC queue ID (default 0)
  -dst-mac  string   Destination MAC for TX frames — gateway or peer MAC
                     (default "ff:ff:ff:ff:ff:ff" = broadcast)
  -xsk-map  string   Path to pinned BPF XSKMAP; enables RX path
                     (e.g. /sys/fs/bpf/xsks_map)

Quick example

See examples/wrap-nc.sh for a complete walkthrough that wraps nc (netcat) under ggvisor using a veth pair, with no changes to netcat.

Short version:

# 1. Create a veth pair
ip link add ggv0 type veth peer name ggv1
ip addr add 10.99.0.1/24 dev ggv0
ip link set ggv0 up

# 2. Get peer MAC
DST_MAC=$(cat /sys/class/net/ggv1/address)

# 3. Run your binary under ggvisor
sudo ggvisor -iface ggv0 -dst-mac "$DST_MAC" \
    /usr/bin/nc -u 10.99.0.2 9000

All UDP traffic from nc is now sent via AF_XDP on ggv0.

RX path setup (optional)

To receive traffic back into the tracee, load the XDP program and pin the map:

# Compile XDP program
clang -O2 -g -target bpf \
    -I/usr/include -I/usr/include/x86_64-linux-gnu \
    -c bpf/xdp_prog.c -o bpf/xdp_prog.o

# Load with bpftool and pin the map
bpftool prog loadall bpf/xdp_prog.o /sys/fs/bpf/ggvisor \
    pinmaps /sys/fs/bpf/ggvisor
bpftool net attach xdp id $(bpftool prog show name xdp_sock_prog -j \
    | jq '.[0].id') dev ggv0

# Pass the pinned map to ggvisor
sudo ggvisor -iface ggv0 -dst-mac "$DST_MAC" \
    -xsk-map /sys/fs/bpf/ggvisor/xsks_map \
    /usr/bin/nc -u 10.99.0.2 9000

Running tests

./scripts/test.sh

This runs the full Go integration suite as root. Tests spin up a veth pair + network namespace, run each send variant through ggvisor, and verify packets arrive at the receiver. TestXDPFramesOnWire uses tcpdump to confirm frames appear on the XDP interface.

Individual test:

sudo /home/esc/.go/bin/go test -v -tags integration -timeout 60s \
    ./tests/ -run TestSendto

Architecture

cmd/ggvisor/          CLI entry point
pkg/interceptor/
  tracer.go           ptrace loop, per-pid state machine
  handlers.go         per-syscall entry/exit handlers
  packet.go           Ethernet+IPv4+UDP frame builder/parser
  socket_table.go     fd → (XDP socket, addresses) mapping
  mem.go              process_vm_readv/writev helpers
  bpf.go              raw BPF syscall wrappers (map update, obj get)
pkg/xdp/
  umem.go             UMEM allocation (mmap + registered frames)
  socket.go           AF_XDP socket lifecycle, TX/RX rings
  types.go            ring descriptor structs
bpf/xdp_prog.c        XDP kernel program (XSKMAP redirect)
tests/                Go integration test suite
examples/             Worked examples

Limitations

  • UDP only — TCP connection state is not tracked.
  • TX path is always copy-mode XDP (works on veth and most drivers without zero-copy support).
  • RX path requires a pinned XSKMAP; without it, recvfrom falls through to the kernel.
  • x86-64 only (register mapping is architecture-specific).
  • One XDP socket per interface/queue; multi-queue NICs need one ggvisor instance per queue.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages