An XDP packet router that demultiplexes pshreds by their source identifier.
A pshred is the Shred message from the Constellation / MCP protocol spec
(anza-xyz/mcp mcp-protocol-spec.md,
§7.2), carried directly as a UDP payload. The router runs at the XDP hook (before
the kernel network stack), reads the proposer_index field out of the payload,
and uses it as a key to redirect each source's packets to a configured egress
device. Unknown sources fall through to the stack untouched.
┌─────────────────────── XDP program (eBPF) ───────────────────────┐
NIC RX ───▶ │ parse Eth ▶ IPv4/IPv6 ▶ UDP ─(dst port == SHRED_PORT?) │
│ └▶ read proposer_index (u32 LE @ payload offset 8) │
│ └▶ SHRED_OUTPUTS.redirect(proposer_index) │
└───────┬───────────────────────────┬──────────────────────────────┘
│ hit │ miss
XDP_REDIRECT XDP_PASS
(out mapped ifindex) (up the kernel stack)
The demultiplexer is a single map: SHRED_OUTPUTS, a DevMapHash keyed by
proposer_index (u32) with an egress ifindex as the value. Userspace fills it
from --route arguments; the eBPF program looks it up per packet.
All integers are little-endian (unlike the big-endian internet headers in
front, which network-types decodes for us):
| offset | size | field | notes |
|---|---|---|---|
| 0 | 8 | slot |
u64 LE |
| 8 | 4 | proposer_index |
u32 LE — the source id |
| 12 | 4 | shred_index |
u32 LE |
| 16 | 32 | commitment |
|
| 48 | 1024 | shred_data |
SHRED_DATA_BYTES |
| 1072 | 1 | witness_len |
u8 |
| 1073 | 32 × witness_len |
witness |
variable-length Merkle witness |
| … | 64 | proposer_sig |
The layout is described once as a #[repr(C)] struct Shred in the -common
crate; the eBPF program derives the field offset it needs with
core::mem::offset_of! rather than hard-coded constants. Only the fixed head
(slot..commitment) is wire-accurate in that struct — see its docs for why
you must never ptr_at::<Shred>() the whole thing.
| crate | role |
|---|---|
advanced-svm-assignment-2-common |
shared Shred wire-format struct + constants (no_std) |
advanced-svm-assignment-2-ebpf |
the XDP program (parse → read proposer_index → redirect) |
advanced-svm-assignment-2 |
userspace loader: attaches the program, fills the route table |
- stable rust toolchains:
rustup toolchain install stable - nightly rust toolchains:
rustup toolchain install nightly --component rust-src - (if cross-compiling) rustup target:
rustup target add ${ARCH}-unknown-linux-musl - (if cross-compiling) LLVM: (e.g.)
brew install llvm(on macOS) - bpf-linker:
cargo install bpf-linker(--no-default-featureson macOS)
On Linux, use cargo build, cargo check, etc. as normal. Cargo build scripts
build the eBPF object and embed it in the loader automatically.
sudo RUST_LOG=info ./advanced-svm-assignment-2 \
--iface eth0 \
--route 7:5 \ # proposer_index 7 -> ifindex 5
--route 9:7 # proposer_index 9 -> ifindex 7--iface <name>— interface to attach the XDP program to (defaultlo).--route <PROPOSER_INDEX>:<IFINDEX>— repeatable; one entry per source you want to forward. Sources without a route fall through (XDP_PASS).
The pshred UDP port is SHRED_PORT (8591), defined in the eBPF program.
Routes redirect to a Linux ifindex whose driver supports ndo_xdp_xmit
(real NICs and veth pairs qualify; lo does not — a route to lo silently
drops). Enumerate them in the runtime:
ip -o link show # leading integer per line is the ifindex
cat /sys/class/net/eth0/ifindex # ifindex of a single deviceFor a self-contained demo, setup_veths.sh creates a couple of veth pairs,
prints their ifindexes, and emits a ready-to-run loader command:
sudo ./setup_veths.sh # creates veth_a, veth_b; prints ifindexes
sudo ./setup_veths.sh down # tear them downRedirect onto veth_a/veth_b and sniff their peers (veth_a_peer, …) to
confirm packets actually egress.
send_pshreds.sh builds byte-exact pshreds (valid and malformed) and sends them
as UDP:
./send_pshreds.sh [host] [port] [mode] # defaults: 127.0.0.1 8591 all| mode | what it sends |
|---|---|
valid |
well-formed shreds for proposer_index 7, 9, 42 (watch them demux) |
malformed |
truncated, head-only, bad witness_len, length mismatches, wrong port |
all |
both (default) |
With RUST_LOG=info, the eBPF program logs pshred proposer_index=N -> action=M
for each packet, so you can confirm 7/9 redirect, 42 passes (no route), malformed
shreds drop, and the wrong-port packet is ignored.
Once a packet is identified as pshred traffic (right UDP port, readable
proposer_index), the program validates the rest before redirecting: it reads
witness_len and requires the payload to be exactly
offset_of(witness) + 32*witness_len + 64 bytes, with witness_len ≤ MAX_WITNESS_LEN. Anything that disagrees — truncated body, oversized
witness_len, missing signature, length/witness_len mismatch — is dropped
(XDP_DROP). A packet too short to even contain the source id (< 16 payload
bytes) is indistinguishable from noise and falls through (XDP_PASS).
On
lo, attach in SKB/generic mode and note that redirect won't physically transmit — use avethpair (seesetup_veths.sh) or a real NIC.
The aya userspace crate is Linux-only, so on macOS cross-compile to
*-unknown-linux-musl and run the binary in a Linux VM (e.g. the included
lima-aya-dev.yaml). Works on both Intel and Apple Silicon.
cargo build --package advanced-svm-assignment-2 --release \
--target=${ARCH}-unknown-linux-musl \
--config=target.${ARCH}-unknown-linux-musl.linker=\"rust-lld\"The cross-compiled program target/${ARCH}-unknown-linux-musl/release/advanced-svm-assignment-2
can be copied to a Linux server or VM and run there.