Skip to content

nabeel99/ADVANCED-SVM-ASSIGNMENT-2

advanced-svm-assignment-2

img.png 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.

How it works

            ┌─────────────────────── 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.

The pshred wire format (§7.2)

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.

Layout

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

Prerequisites

  1. stable rust toolchains: rustup toolchain install stable
  2. nightly rust toolchains: rustup toolchain install nightly --component rust-src
  3. (if cross-compiling) rustup target: rustup target add ${ARCH}-unknown-linux-musl
  4. (if cross-compiling) LLVM: (e.g.) brew install llvm (on macOS)
  5. bpf-linker: cargo install bpf-linker (--no-default-features on macOS)

Build & Run

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 (default lo).
  • --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.

Finding routable ifindexes

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 device

For 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 down

Redirect onto veth_a/veth_b and sniff their peers (veth_a_peer, …) to confirm packets actually egress.

Testing

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.

Malformed-packet handling

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 a veth pair (see setup_veths.sh) or a real NIC.

Cross-compiling on macOS

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.

About

XDP Forwarder , that uses proposer index to forward a shred to another IFACE.

Resources

License

Apache-2.0 and 2 other licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
GPL-2.0
LICENSE-GPL2
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors