[multicast] Host-side plumbing for externally sourced multicast - #28
Open
zeeshanlakhani wants to merge 1 commit into
Open
[multicast] Host-side plumbing for externally sourced multicast#28zeeshanlakhani wants to merge 1 commit into
zeeshanlakhani wants to merge 1 commit into
Conversation
zeeshanlakhani
changed the base branch from
zl/omicron-era-fixes
to
zl/mcast-prereqs
August 11, 2026 02:54
zeeshanlakhani
force-pushed
the
zl/mcast-host-plumbing
branch
from
August 25, 2026 09:18
f30cd03 to
6c0c13e
Compare
This work gets externally sourced multicast into a running rack.
voxel can now provide plumbing for:
- a route for each group pointed at `ce`
- a mirror on the transit router carrying each group's flood toward
every scrimlet
- a membership so that the router's NIC accepts the group's frames to
begin with
This work adds all of these steps via `voxel network multicast
{up,down,check}` commands.
The mirror rests on two main properties:
1. external multicast ingresses at whichever switch holds the group's
external NAT entry, elected by the rack and not visible to the host
(so that every scrimlet has to receive a copy).
2. flower classification ends at the first matching filter, so the
targets are chained `action mirred` clauses inside one filter per
group rather than one filter per scrimlet.
The mirror also only sees frames the NIC accepts, and a NIC accepts a
multicast group only after a group join (subscription). Nothing on the
router joins these groups (voxel's FRR runs only unicast BGP) on its
own, so the `up` command pins a static link-layer membership for each
group's Ethernet address (the RFC 1112 section 6.4 mapping) on the
host-facing NIC.
This mapping folds 28 group bits into 23 (RFC 7042 section 2.1.1),
meaning that 32 groups alias each Ethernet address and teardown drops
a membership only when no remaining group still maps to it. Each
membership `up` adds is recorded under `/run` on the router, and `down`
removes only recorded ones, so a membership the router already held is
never voxel's to delete. The record dies with the router, exactly as the
memberships do.
Host routes are the one piece that outlives `voxel destroy`, and the
host's route table is shared with every other Falcon environment. So `up`
records what it installs in `.falcon/multicast-<hex environment name>.json`,
and `down` and `check` read that record instead of scanning. The record,
not the nexthop, is what makes a route owned by this specific
environment: in isolated mode `ce`'s address derives from `[network]`
rather than from the environment name, so two environments would share a
nexthop and it proves nothing. That record is written ahead of the route,
so a crash between the two leaves a record with no route rather than a
route nothing can prove is ours, and both directions fail closed: an
unrecorded route is neither swept by `up` nor removed by `down`.
Teardown also has to work against a rack that has already gone away.
`ssh_try_capture` separates an unreachable node (ssh exit 255) from a
command that ran and failed, so `down` clears the host routes and the
record when `cr1` is unreachable while still reporting a genuine failure
on a live router.
The `check` command also prints each group's control-plane mapping onto
the underlay, read from `swadm multicast list` in every switch zone,
naming the NAT target an external group replicates onto.
This work also repins sidecar-lite and omicron to the leaves of their
respective multicast branches, `zl/multicast` and `zl/mcast-build`.
Most importantly, we add `docs/multicast.md`, covering the API side
(pools, groups, members, probes), this plumbing, and the static
production equivalents of each piece.
Also here:
- `voxel commtest --traffic multicast` refuses to start when the setup is
missing, with `--setup-mcast` to plumb it in place. It also defaults
`--icmp-loss-tolerance` to 500, the value omicron's a4x2 CI passes, and
rejects an inverted `--ip-pool-begin`/`--ip-pool-end` pair up front.
- `[falcon].ssh_pubkey` stages a public key into every node, so
`ssh root@<node>` authenticates by key rather than by the images' empty
root password.
- `voxel status` reports each sled's rpool usage and `zpool status -x`
health.
- Format arguments are inlined across the workspace, and `commtest`'s
`mod test` is renamed to `mod tests`.
zeeshanlakhani
force-pushed
the
zl/mcast-host-plumbing
branch
from
August 25, 2026 09:40
6c0c13e to
d01ae00
Compare
zeeshanlakhani
marked this pull request as ready for review
August 25, 2026 09:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This work gets externally sourced multicast into a running rack (and relies on the latest st.louis branch of https://github.com/oxidecomputer/helios).
voxel can now provide plumbing for:
ceThis work adds all of these steps via
voxel network multicast {up,down,check}commands.The mirror rests on two main properties:
action mirredclauses inside one filter per group rather than one filter per scrimlet.The mirror also only sees frames the NIC accepts, and a NIC accepts a multicast group only after a group join (subscription). Nothing on the router joins these groups (voxel's FRR runs only unicast BGP) on its own, so the
upcommand pins a static link-layer membership for each group's Ethernet address (the RFC 1112 section 6.4 mapping) on the host-facing NIC.This mapping folds 28 group bits into 23 (RFC 7042 section 2.1.1), meaning that 32 groups alias each Ethernet address and teardown drops a membership only when no remaining group still maps to it. Each membership
upadds is recorded under/runon the router, anddownremoves only recorded ones, so a membership the router already held is never voxel's to delete. The record dies with the router, exactly as the memberships do.Host routes are the one piece that outlives
voxel destroy, and the host's route table is shared with every other Falcon environment. Souprecords what it installs in.falcon/multicast-<hex environment name>.json, anddownandcheckread that record instead of scanning. The record, not the nexthop, is what makes a route owned by this specific environment: in isolated modece's address derives from[network]rather than from the environment name, so two environments would share a nexthop and it proves nothing. That record is written ahead of the route, so a crash between the two leaves a record with no route rather than a route nothing can prove is ours, and both directions fail closed: an unrecorded route is neither swept byupnor removed bydown.Teardown also has to work against a rack that has already gone away.
ssh_try_captureseparates an unreachable node (ssh exit 255) from a command that ran and failed, sodownclears the host routes and the record whencr1is unreachable while still reporting a genuine failure on a live router.The
checkcommand also prints each group's control-plane mapping onto the underlay, read fromswadm multicast listin every switch zone, naming the NAT target an external group replicates onto.This work also repins sidecar-lite and omicron to the leaves of their respective multicast branches,
zl/multicastandzl/mcast-build.Most importantly, we add
docs/multicast.md, covering the API side (pools, groups, members, probes), this plumbing, and the static production equivalents of each piece.Also here:
voxel commtest --traffic multicastrefuses to start when the setup is missing, with--setup-mcastto plumb it in place. It also defaults--icmp-loss-toleranceto 500, the value omicron's a4x2 CI passes, and rejects an inverted--ip-pool-begin/--ip-pool-endpair up front.[falcon].ssh_pubkeystages a public key into every node, sossh root@<node>authenticates by key rather than by the images' empty root password.voxel statusreports each sled's rpool usage andzpool status -xhealth.commtest'smod testis renamed tomod tests.Dependencies
The multicast stack spans multiple repositories, but voxel itself pins only two of them: the Omicron commit handed to
voxel image create(OMICRON_REPOselects the clone source) and the sidecar-lite artifact rev the build fetches (SIDECAR_LITE_REV). Everything else rides the chosen Omicron commit's own pins, so pointing the image at the right Omicron commit pulls the whole set.zl/mcast-build,521e1b39rack-init-configpin, viavoxel image createmain, starting at omicron#9912multicast-e2e,3d49b131tools/dendrite_*pinsmainzl/ddm-mcast,96a2f153zl/mrib; related maghemite#402 (zl/mgd-ddm-meta)tools/maghemite_*pinsmainafterzl/mribmaster,0525f2f95(0.41.506)Cargo.toml/tools/opte_versionzl/multicast,3c07d60apackage-manifest.toml(guest propolis-server)master; host side also needs the viona V7 tables in docs/multicast.mdzl/multicast-joiner,486559bcpackage-manifest.toml(probe zone, prebuilt)mainzl/multicast,461cbe19voxel image create(SIDECAR_LITE_REV)mainzl/multicast,284c6830tools/softnpu_version(xtaskSOFTNPU_COMMIT)mainzl/multicast(p4rs)main, then softnpu#183/sidecar-lite#152 repointCommtest Run (example)
The Setup
After launching voxel, we ran:
voxel network multicast up --group 239.100.0.1 --group 239.100.0.2 \ --group 232.100.0.1 --group 239.100.0.9 voxel commtest --source /oxide/workspace/omicron --traffic multi -- run \ --test-duration 200s --warmup 10s --packet-rate 10 \ --mcast-group 239.100.0.1 \ --mcast-group 239.100.0.2@172.30.199.199 \ --mcast-group 232.100.0.1@172.30.199.199 \ --mcast-deny-group 239.100.0.9@172.30.199.198Group verification
239.100.0.1239.100.0.2172.30.199.199(host)232.100.0.1172.30.199.199(host)239.100.0.9172.30.199.198(excludes host)Per-member connectivity
239.100.0.1239.100.0.1239.100.0.1239.100.0.1239.100.0.2239.100.0.2239.100.0.2239.100.0.2232.100.0.1232.100.0.1232.100.0.1232.100.0.1239.100.0.9(deny)