Skip to content

feat: network lab (stream.sh --netem) - #13

Open
pzanella wants to merge 1 commit into
feat/sgai-playerfrom
feat/network-lab
Open

pzanella wants to merge 1 commit into
feat/sgai-playerfrom
feat/network-lab

Conversation

@pzanella

Copy link
Copy Markdown
Owner

Depends on #12. Merge #8–#12 first; this PR then moves to main.

Why

A MoQ lab on a perfect localhost network shows little about MoQ itself. This PR makes it possible to watch, on the same stream, what happens to RTT, bitrate, jitter, frame rate and latency on a bad network.

What

  • stream.sh --netem PROFILE: mobile, lossy, congested, satellite, off. The profiles are defined only in lib/netem.mjs.
  • Dashboard "Network lab" card (when started with --netem): switches profile at runtime.
    • GET/POST /api/netem only accepts a profile name, never a tc command.
    • tc runs with execFile, without a shell.
    • The POST needs Content-Type: application/json, so a page from another site can't send it without a CORS preflight, which the server never answers.
  • Only relay → viewer traffic is shaped, so the delay is one way.
    • A prio qdisc with netem in band 3, and a u32 filter on the relay's source port.
    • Set up on eth0 (rootful Podman, real networks) and on lo: with rootless Podman the port forwarder hands the viewer traffic to the relay over lo. On lo there is an extra match on the container's own IP.
    • The ingest (moq import → relay on localhost) and the dashboard's HTTP API (its clock-offset measurement needs a symmetric path) are not shaped.
  • Least privilege: NET_ADMIN only with --netem. The image gets iproute2.
  • CI smoke-test-podman-netem: the profile is applied at startup, the relay is slowed (≥ 0.3 s) and the dashboard is not, switching to off works, and an invalid name gets a 400.
  • README (new "Network lab" section, flag table), CONTRIBUTING, CHANGELOG.

Found while testing

  • My first version shaped only eth0, and the rootless viewer traffic didn't go through it: 2 packets in the qdisc counters, RTT unchanged. Hence the setup on lo.
  • tc qdisc replace on an existing netem only changes the given parameters (rate stayed from the previous profile), so it is now delete, then add.
  • With satellite the browser can pick the WebSocket fallback: the WebTransport/WebSocket race goes to TCP. This is documented in the README.

Tests

Local, Podman rootless (podman machine, kernel 7.1):

  • satellite: RTT ~317 ms over WebTransport, ~413 ms (relay PROBE) over WebSocket. Latency ~630 ms over WebTransport, 776–873 ms over WebSocket. Dashboard HTTP stays at ~2–5 ms.
  • Switching lossy → satellite → off → congested gives clean qdiscs each time.
  • Without NET_ADMIN: --netem: RTNETLINK answers: Operation not permitted and the run stops.
  • The CI job's checks, run by hand against a local container: all pass.

stream.sh --netem PROFILE shapes what the relay sends to its viewers
with Linux netem: mobile, lossy, congested, satellite or off. The
profiles live in lib/netem.mjs. With --dashboard, a Network lab card
switches profiles at runtime through /api/netem, which only takes a
profile name.

Only relay -> viewer traffic is shaped. The ingest and the dashboard's
HTTP API are not: a prio qdisc and a u32 filter on the relay port send
only those packets to netem, on eth0 and on lo (rootless Podman hands
viewer traffic to the relay over lo). NET_ADMIN is added only with
--netem. New CI job smoke-test-podman-netem.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant