Repository navigation
[multicast] Bitmap-based multicast replication for sidecar-lite - #152
zeeshanlakhani wants to merge 6 commits into
Conversation
09d1d59 to
88b2180
Compare
88b2180 to
d0a41f9
Compare
This wires up the softnpu ASIC backend to support multicast end-to-end by translating DPD's sidecar.p4 table operations into sidecar-lite.p4's simplified P4 pipeline. ## AsicMulticastOps We replace the stubbed AsicMulticastOps implementation (which returned "OperationUnsupported" for group creation and port addition with in-memory group tracking via McGroupData, following the tofino_stub pattern. Group membership is used by the table translation layer to build port bitmaps for sidecar-lite's Replicate extern. Ports >= 128 are rejected at add time to match sidecar-lite's 128-bit bitmap width. ## Table translation (asic/src/softnpu/table.rs) We map sidecar.p4 table names to sidecar-lite equivalents and translate action parameters where the designs differ for emulation. All multicast action arms are gated with #[cfg(feature = "multicast")]. ## References - [softnpu #183](oxidecomputer/softnpu#183) - [propolis #1093](oxidecomputer/propolis#1093) - [p4rs #240](oxidecomputer/p4#240) - [sidecar-lite #152](oxidecomputer/sidecar-lite#152) - tokio: 1.50 (due to softnpu) - oxide-tokio-rt: 0.1.3 (following-up from tokio's move to 1.50)
This wires up the softnpu ASIC backend to support multicast end-to-end by translating DPD's sidecar.p4 table operations into sidecar-lite.p4's simplified P4 pipeline. ## AsicMulticastOps We replace the stubbed AsicMulticastOps implementation (which returned "OperationUnsupported" for group creation and port addition with in-memory group tracking via McGroupData, following the tofino_stub pattern. Group membership is used by the table translation layer to build port bitmaps for sidecar-lite's Replicate extern. Ports >= 128 are rejected at add time to match sidecar-lite's 128-bit bitmap width. ## Table translation (asic/src/softnpu/table.rs) We map sidecar.p4 table names to sidecar-lite equivalents and translate action parameters where the designs differ for emulation. All multicast action arms are gated with #[cfg(feature = "multicast")]. ## References - [softnpu #183](oxidecomputer/softnpu#183) - [propolis #1093](oxidecomputer/propolis#1093) - [p4rs #240](oxidecomputer/p4#240) - [sidecar-lite #152](oxidecomputer/sidecar-lite#152) - tokio: 1.50 (due to softnpu) - oxide-tokio-rt: 0.1.3 (following-up from tokio's move to 1.50)
This wires up the softnpu ASIC backend to support multicast end-to-end by translating DPD's sidecar.p4 table operations into sidecar-lite.p4's simplified P4 pipeline. ## AsicMulticastOps We replace the stubbed AsicMulticastOps implementation (which returned "OperationUnsupported" for group creation and port addition with in-memory group tracking via McGroupData, following the tofino_stub pattern. Group membership is used by the table translation layer to build port bitmaps for sidecar-lite's Replicate extern. Ports >= 128 are rejected at add time to match sidecar-lite's 128-bit bitmap width. ## Table translation (asic/src/softnpu/table.rs) We map sidecar.p4 table names to sidecar-lite equivalents and translate action parameters where the designs differ for emulation. ## Fix update semantics We fix an issue with stale in-memory sources after update with absent source list(s). `modify_group_external` wrote back `new.sources.or(existing)` to the in-memory record while `update_external_tables` in the same call canonicalized `new.sources` directly to rewrite the P4 source-filter entries. A `None` update therefore cleared the P4 table but left a stale `Some([...])` in memory. All multicast action arms are gated with #[cfg(feature = "multicast")]. ## References and Deps - [softnpu #183](oxidecomputer/softnpu#183) - [propolis #1093](oxidecomputer/propolis#1093) - [p4rs #240](oxidecomputer/p4#240) - [sidecar-lite #152](oxidecomputer/sidecar-lite#152) - tokio: 1.50 (due to softnpu) - oxide-tokio-rt: 0.1.3 (following-up from tokio's move to 1.50) - bump oxide-tokio-rt to 0.1.4, tokio to 1.52.1 (matching Omicron)
8757f58 to
bcceb2d
Compare
This adds multicast replication to sidecar-lite, emulating DPD's
sidecar.p4 on dendrite @ zl/mcast-swadm, following RFD 488.
We manage 2 bitmaps per underlay group (external and underlay), for
which replication is driven by p4rs `replicate` (dep @ p4#240). Ingress
zeroes out at most one of these bitmaps determined by the geneve
multicast option tag (tag 0 zeroes out underlay; tag 1 zeroes out
external), and merges both.
Tag 0 (External) also decaps upon ingress. With no tag given, as on
external NAT ingress packets, both bitmaps remain in place.
Only geneve-encapsulated multicast commits to replication. Multicast
packets that miss ingress NAT drop on the empty bitmap, matching DPD's
`drop_mcastv{4,6}_no_group` semantics. Packet copies that
remain encapsulated lose a hop on the outer IPv6 header, as modelled in
`MulticastRouter6`.
External multicast is NAT-encapsulated into the underlay via
`nat_v{4,6}_mcast(_untagged)`, reusing `forward_to_sled`. Tagged and
untagged variants exist as separate tables since x4c cannot key
directly on header validity. The encap piece now zeroes out the outer
src, traffic class, flow label, and inner eth src, like DPD's
`add_encap_headers`.
Egress decap checks `ff04::/64` and `tag=2`, matching DPD's egress
`mcast_tag_check`, with per-port decap from a RID-keyed bitmap
(`tbl_decap_ports`) and optional VLAN. `tag=2` decapsulation also
rejects inner unicast. `tag=0` copies (already decapped in ingress)
use the table for the VLAN. Dst MAC derivation (RFC 1112, RFC 2464)
and per-port src MAC rewriting run on every packet copy.
Includes:
- The parser rejects `ff01` addresses outright, as well as `ff02` destined
packets with a hop limit of 0 (`ff02` *does* still go to the scrimlet).
- Multicast packets with a TTL/hop_limit <= 1 get dropped in ingress
after the `fwd_from_scrimlet` return, letting scrimlet transmissions
pass through.
- Both decapsulation paths drop inner TTL <= 1. IPv4 checksums use
RFC 1624 (eqn. 3): a checksum of 0xfeff turns into 0x0000 after
decrementing the TTL (XOR used here due to `~` not being parsable by x4c).
- scadm gets add/remove/dump for replication (v6), source filters, mcast
NAT, decap ports, src MAC, and asic-id-to-port (for logical mapping).
- Unicast modifications:
- The router drops TTL/hop_limit <= 1 and decrements remaining packets
on routes to nonzero ports, updating the IPv4 checksum.
- Port 0 (the scrimlet) skips both the TTL check and decrement.
- DPD's Router4/6 still decrement service-bound packets.
- The NAT encap zeroing described above is shared with unicast NAT.
- Packets with a parsed Geneve header skip ingress NAT, including
unicast.
### References
This work depends on the P4(rs) work in
[#240](oxidecomputer/p4#240) and softnpu
zl/multicast.
ff2aac0 to
8e55228
Compare
| // | ||
| // These are keyed on both the destination address and VLAN ID. | ||
| // | ||
| // Packets with a mismatched VLAN miss the table and are not NAT'd |
There was a problem hiding this comment.
Our disposition on VLANs to date is that we'll do tagging to participate in segmented upstream networks e.g., the vlan variants of the forwarding actions on routing tables. But we've not done anything in terms of ingress isolation/segmentation with VLANs. What is driving keying ingress NAT tables for mcast on vlan ids?
There was a problem hiding this comment.
Yep, this adds ingress VLAN filtering for multicast. It came from oxidecomputer/dendrite#195, which fixed oxidecomputer/dendrite#107 (a packet arriving on one VLAN got NAT-encapped into a group configured for another). In the thread, @FelixMcFelix suggested using a single exact VLAN entry per group for customer ingress, and that's what merged. Geneve packets bypass NatIngress and are decapsulated later without returning to it, which means the lookup sees the incoming (customer's) packet's original VLAN state.
In dpd, an external group's optional vlan_id feeds the NAT ingress key and egress route, and gets propagated to the underlay group's external decap bitmap. This linked setting controls ingress admission and VLAN tagging on decapsulated external packet copies.
I carried this over here for emulation parity with dendrite's sidecar (mcast_nat_v4_wrong_vlan_miss covers this miss).
There was a problem hiding this comment.
a packet arriving on one VLAN got NAT-encapped into a group configured for another
This implies we are attempting to support overlapping address spaces? Supporting overlapping address spaces is not something we support, independent of address type. I think would be a pretty big lift in control plane to get there. It seems to me that the destination group address alone needs to be sufficient to determine where a packet needs to go within the rack on ingress.
There was a problem hiding this comment.
I didn't mean to imply overlapping spaces actually. More so, the focus is on VLAN translation. Dpd rejects duplicate group IPs, and the VLAN match is about controlling admission. The destination group address already decides where it goes in the rack.
As mentioned, the check came from oxidecomputer/dendrite#195, fixing oxidecomputer/dendrite#107, which recorded a legit, failing test. The intent was to prevent cross-VLAN translation; #107 captured an encapsulated packet leaving an external port.
If ingress should accept the packet regardless of VLAN, I'm down to update dendrite off main and here as well. We'd need to enforce the external-to-external replication restriction separately, while also allowing delivery to internal receivers. Thoughts?
There was a problem hiding this comment.
There are a few alternative directions I take this too @rcgoodfellow, but I thought to just bring this up first as a question. @FelixMcFelix may have thoughts here too?
There was a problem hiding this comment.
If ingress should accept the packet regardless of VLAN, I'm down to update dendrite off main and here as well.
This is what we do for unicast. We are a router, and it's not generally expected that VLANs will be preserved across a routed boundary. There is no concept of VLAN isolation within the rack, so there are no VLAN-based boundaries we can meaningfully enforce on ingress.
I understand there was a failing test before, but it's not clear to me that we want multicast ingress tied to VLANs in the way that the test was specifying.
There was a problem hiding this comment.
I'll update here and with a PR off dendrite/main too.
Ry flagged this on oxidecomputer/sidecar-lite#152 (its handling of ff02 reject was emulating this sidecar). Ever since #14, the parser dropped ttl/hl (hop-limit) 0 before `services` was involved, which meant that a unicast packet headed to a switch address and `ff02::/16` headed to the scrimlet would both be dropped. RFC 1812 §4.2.2.9 and RFC 8200 §3 mention that the destination should still accept it, while the forwarding pass still has to drop (per RFC 1812 §5.3.1). This work updates the sidecar parser to record the reason via meta and then continue parsing. For unicast and IPv6 link-local multicast, the drop has been moved to the forwarding side. Note: some of the arrangement here is to make this change while operating at our 20-stage max. Includes: - mcast packets at ttl/hl 1 now get counted as `multicast_ttl_exceeded` - Geneve encapped packets headed to a switch address with an outer hop limit of 0 get decapped again, as it was before #14
Ry flagged this on oxidecomputer/sidecar-lite#152 (its handling of ff02 reject was emulating this sidecar). Ever since #14, the parser dropped ttl/hl (hop-limit) 0 before `services` was involved, which meant that a unicast packet headed to a switch address and `ff02::/16` headed to the scrimlet would both be dropped. RFC 1812 §4.2.2.9 and RFC 8200 §3 mention that the destination should still accept it, while the forwarding pass still has to drop (per RFC 1812 §5.3.1). This work updates the sidecar parser to record the reason via meta and then continue parsing. For unicast and IPv6 link-local multicast, the drop has been moved to the forwarding side. Note: some of the arrangement here is to make this change while operating at our 20-stage max. Includes: - mcast packets at ttl/hl 1 now get counted as `multicast_ttl_exceeded` - Geneve encapped packets headed to a switch address with an outer hop limit of 0 get decapped again, as it was before #14
Note: nonzero ports drop at <= 8w1, so an exempt ttl of 0 can't decrement and wrap around.
Per the discussion in #152 (comment), we're a router, and it's not expected that VLANs are preserved across a routed boundary. There's also no VLAN isolation in the rack to enforce on ingress.
|
@rcgoodfellow updated on this side. The dendrite fix will go in the mcast stack of PRs. |
This adds multicast replication to sidecar-lite, emulating DPD's sidecar.p4 on dendrite @ zl/mcast-swadm, following RFD 488.
We manage 2 bitmaps per underlay group (external and underlay), for which replication is driven by p4rs
replicate(dep @ p4#240). Ingress zeroes out at most one of these bitmaps determined by the geneve multicast option tag (tag 0 zeroes out underlay; tag 1 zeroes out external), and merges both.Tag 0 (External) also decaps upon ingress. With no tag given, as on external NAT ingress packets, both bitmaps remain in place.
Only geneve-encapsulated multicast commits to replication. Multicast packets that miss ingress NAT drop on the empty bitmap, matching DPD's
drop_mcastv{4,6}_no_groupsemantics. Packet copies that remain encapsulated lose a hop on the outer IPv6 header, as modelled inMulticastRouter6.External multicast is NAT-encapsulated into the underlay via
nat_v{4,6_mcast(_untagged), reusingforward_to_sled. Tagged and untagged variants exist as separate tables since x4c cannot key directly on header validity. The encap piece now zeroes out the outer src, traffic class, flow label, and inner eth src, like DPD'sadd_encap_headers.Egress decap checks
ff04::/64andtag=2, matching DPD's egressmcast_tag_check, with per-port decap from a RID-keyed bitmap (tbl_decap_ports) and optional VLAN.tag=2decapsulation also rejects inner unicast.tag=0copies (already decapped in ingress) use the table for the VLAN. Dst MAC derivation (RFC 1112, RFC 2464) and per-port src MAC rewriting run on every packet copy.Includes:
ff01addresses outright, as well asff02destined packets with a hop limit of 0 (ff02does still go to the scrimlet).fwd_from_scrimletreturn, letting scrimlet transmissions pass through.~not being parsable by x4c).References
This work depends on the P4(rs) work in #240 and softnpu's zl/multicast.