Skip to content

Commit c44dd25

Browse files
[feature] ASIC-focused multicast replication and dendrite API (#14)
This brings multicast group management and hardware-accelerated forwarding to Dendrite, providing the foundation for our multicast networking stack. The PR includes: **API Layer:** - Multicast group operations and lifecycle management - Integrated Source-Specific Multicast (SSM) support - Bulk reset functionality for clearing multicast state - Endpoints designed for integration with higher-level orchestration **Hardware Integration:** - Updated sidecar.p4 for native Tofino ASIC multicast replication - ASIC table management for efficient multicast forwarding - Geneve option parsing for multicast tagging - Egress table handling for decapped packets and MAC rewriting - Range-based port bitmap matching for multicast egress (for decap) **Network Processing:** - Leverages the Tofino packet replication engine for replication - Handles proper encapsulation/decapsulation flows - *Bifurcated multicast handling*: Separate processing paths for internal admin-scoped IPv6 groups vs external IPv4/IPv6 groups - *Multiple replication scenarios*: - Unencapped external multicast → encapsulated for underlay transport - Encapped IPv6 multicast → underlay-only replication (infrastructure traffic) - Encapped IPv6 multicast → external-only replication with decapsulation - Bifurcated replication: Both external members (decapped) and underlay members (encapped) from a group - *Admin-scoped NAT target validation*: External groups properly reference internal admin-scoped groups **Validation:** - Extensive iperf3 testing shows no performance regression between baseline and multicast-enhanced versions - *Comprehensive integration test suite* covering: - All replication scenarios (external-only, underlay-only, bifurcated) - Encapsulation/decapsulation flows with Geneve multicast tagging - Source-Specific Multicast (SSM) filtering with exact and prefix matching - Admin-scoped NAT target validation and lifecycle management - Performance validation with concurrent packet replication - Edge cases including TTL handling, MAC derivation, and VLAN propagation **Context:** - Implements the dendrite side of the bifurcated multicast design, handling both **internal admin-scoped IPv6 groups** (admin-local, site-local, and organization-local scopes) and external IPv4/IPv6 groups with NAT encapsulation - External multicast traffic gets encapsulated for underlay transport, then properly decapped and MAC-rewritten for local delivery - Internal groups stay within the admin scope for rack-local multicast communication - Supports complex replication patterns where single groups can serve both customer traffic (external, decapped) and infrastructure traffic (underlay, encapsulated) **Associated PRs** - [X] oxidecomputer/omicron#8576
1 parent 196b6b1 commit c44dd25

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+12875
-717
lines changed

.github/buildomat/jobs/image.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ pfexec chown "$UID" /out
101101

102102
banner "P4 Codegen"
103103
# Add gcc-12 so the p4 compiler can find cpp
104-
# The tofino2 has 20 stages, but the current sidecar.p4 will fit into 14. We
105-
# add the "--stages 14" here to detect if/when the program grows beyond that
104+
# The tofino2 has 20 stages, but the current sidecar.p4 will fit into 18. We
105+
# add the "--stages 18" here to detect if/when the program grows beyond that
106106
# limit. It's not necessarily a problem if we grow, but given the limited space
107107
# on the ASIC, we want to grow deliberatately and thoughtfully.
108-
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages 14
108+
PATH=/opt/gcc-12/bin:$PATH cargo xtask codegen --stages 18
109109

110110
# Preserve all the diagnostics spit out by the compiler
111111
mkdir -p /out/p4c-diags

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ p4_artifacts*
1717
# Editor config
1818
.vscode
1919
.dir-locals.el
20+
bacon.toml
2021

2122
# OS artifacts
2223
.DS_Store

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ omicron-common = { git = "https://github.com/oxidecomputer/omicron", branch= "ma
4444
oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
4545
oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
4646
oximeter-instruments = { git = "https://github.com/oxidecomputer/omicron", branch = "main", default-features = false, features = ["kstat"] }
47-
oxnet = { version = "0.1.1", default-features = false, features = ["schemars", "serde"] }
47+
oxnet = { version = "0.1.2", default-features = false, features = ["schemars", "serde"] }
4848
propolis = { git = "https://github.com/oxidecomputer/propolis" }
4949
sled-agent-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
5050
smf = { git = "https://github.com/illumos/smf-rs" }

aal/src/lib.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,15 @@ pub trait AsicOps {
202202
/// For a given multicast group, return the number of ports assigned to it.
203203
fn mc_port_count(&self, group_id: u16) -> AsicResult<usize>;
204204

205-
/// Add a port to a multicast group. The port is identified using its ASIC
205+
/// Add a port to a multicast group. The port is identified using its ASIC
206206
/// identifier.
207-
fn mc_port_add(&self, group_id: u16, port: AsicId) -> AsicResult<()>;
207+
fn mc_port_add(
208+
&self,
209+
group_id: u16,
210+
port: AsicId,
211+
rid: u16,
212+
level_1_excl_id: u16,
213+
) -> AsicResult<()>;
208214

209215
/// Remove a port from a multicast group. The port is identified using its ASIC
210216
/// identifier.
@@ -216,6 +222,21 @@ pub trait AsicOps {
216222
/// Destroy a multicast group.
217223
fn mc_group_destroy(&self, group_id: u16) -> AsicResult<()>;
218224

225+
/// Check if a multicast group exists.
226+
fn mc_group_exists(&self, group_id: u16) -> bool {
227+
self.mc_domains().contains(&group_id)
228+
}
229+
230+
/// Get the total number of multicast groups.
231+
fn mc_groups_count(&self) -> AsicResult<usize>;
232+
233+
/// Set the maximum number of multicast nodes.
234+
fn mc_set_max_nodes(
235+
&self,
236+
max_nodes: u32,
237+
max_link_aggregated_nodes: u32,
238+
) -> AsicResult<()>;
239+
219240
/// Get sidecar identifiers of the device being managed.
220241
fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers>;
221242

aal/src/match_action.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ impl MatchData {
7272
/// The MatchParse trait defines the behavior needed to convert a high-level
7373
/// Match field into our intermediate representation.
7474
pub trait MatchParse {
75-
/// Return all the name sand values of the key fields as strings
75+
/// Return all the names and values of the key fields as strings
7676
fn key_values(&self) -> BTreeMap<String, String>;
7777
/// Convert the key Struct to a MatchData struct
7878
fn key_to_ir(&self) -> AsicResult<MatchData>;
@@ -452,6 +452,27 @@ impl From<bool> for ValueTypes {
452452
}
453453
}
454454

455+
impl TryFrom<&ValueTypes> for bool {
456+
type Error = &'static str;
457+
458+
fn try_from(v: &ValueTypes) -> Result<Self, Self::Error> {
459+
match v {
460+
ValueTypes::U64(v) => {
461+
if *v == 0 {
462+
Ok(false)
463+
} else if *v == 1 {
464+
Ok(true)
465+
} else {
466+
Err("value not a boolean")
467+
}
468+
}
469+
_ => Err("value not a boolean"),
470+
}
471+
}
472+
}
473+
474+
unwrap_value_entry!(bool);
475+
455476
#[derive(Debug, Hash, Clone)]
456477
pub enum ValueTypes {
457478
U64(u64),

asic/src/chaos/mod.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,18 @@ impl TableChaos {
9494
(table::SWITCH_IPV4_ADDR, v),
9595
(table::SWITCH_IPV6_ADDR, v),
9696
(table::NAT_INGRESS_IPV4, v),
97-
(table::NAT_INGRESS_IPV6, v)
97+
(table::NAT_INGRESS_IPV6, v),
98+
(table::MCAST_NAT_INGRESS_IPV4, v),
99+
(table::MCAST_NAT_INGRESS_IPV6, v),
100+
(table::MCAST_REPLICATION_IPV4, v),
101+
(table::MCAST_REPLICATION_IPV6, v),
102+
(table::MCAST_SRC_FILTER_IPV4, v),
103+
(table::MCAST_SRC_FILTER_IPV6, v),
104+
(table::MCAST_ROUTE_IPV4, v),
105+
(table::MCAST_ROUTE_IPV6, v),
106+
(table::MCAST_MAC_REWRITE, v),
107+
(table::MCAST_DECAP_PORTS, v),
108+
(table::MCAST_PORT_ID_MAPPING, v)
98109
)
99110
}
100111

@@ -141,6 +152,8 @@ pub struct AsicConfig {
141152
pub mc_port_remove: Chaos,
142153
pub mc_group_create: Chaos,
143154
pub mc_group_destroy: Chaos,
155+
pub mc_groups_count: Chaos,
156+
pub mc_set_max_nodes: Chaos,
144157
pub get_sidecar_identifiers: Chaos,
145158
pub table_new: TableChaos,
146159
pub table_clear: TableChaos,
@@ -177,6 +190,8 @@ impl AsicConfig {
177190
mc_port_remove: Chaos::new(v),
178191
mc_group_create: Chaos::new(v),
179192
mc_group_destroy: Chaos::new(v),
193+
mc_groups_count: Chaos::new(v),
194+
mc_set_max_nodes: Chaos::new(v),
180195
get_sidecar_identifiers: Chaos::new(v),
181196
table_new: TableChaos::uniform(v),
182197
table_clear: TableChaos::uniform(v),
@@ -203,6 +218,7 @@ impl AsicConfig {
203218
port_enable_get: Chaos::new(v),
204219
connector_avail_channels: Chaos::new(v),
205220
mc_port_count: Chaos::new(v),
221+
mc_groups_count: Chaos::new(v),
206222
get_sidecar_identifiers: Chaos::new(v),
207223
..Default::default()
208224
}
@@ -224,6 +240,7 @@ impl AsicConfig {
224240
mc_port_remove: Chaos::new(v),
225241
mc_group_create: Chaos::new(v),
226242
mc_group_destroy: Chaos::new(v),
243+
mc_set_max_nodes: Chaos::new(v),
227244
// TODO this can cause dpd to fail to start
228245
//table_clear: TableChaos::uniform(v),
229246
table_default_set: TableChaos::uniform(v),
@@ -476,7 +493,13 @@ impl AsicOps for Handle {
476493
Ok(self.ports.lock().unwrap().len())
477494
}
478495

479-
fn mc_port_add(&self, _group_id: u16, _port: u16) -> AsicResult<()> {
496+
fn mc_port_add(
497+
&self,
498+
_group_id: u16,
499+
_port: u16,
500+
_rid: u16,
501+
_level1_excl_id: u16,
502+
) -> AsicResult<()> {
480503
unfurl!(self, mc_port_add);
481504
Err(AsicError::OperationUnsupported)
482505
}
@@ -496,6 +519,20 @@ impl AsicOps for Handle {
496519
Ok(())
497520
}
498521

522+
fn mc_groups_count(&self) -> AsicResult<usize> {
523+
unfurl!(self, mc_groups_count);
524+
Ok(self.ports.lock().unwrap().len())
525+
}
526+
527+
fn mc_set_max_nodes(
528+
&self,
529+
_max_nodes: u32,
530+
_max_link_aggregated_nodes: u32,
531+
) -> AsicResult<()> {
532+
unfurl!(self, mc_set_max_nodes);
533+
Ok(())
534+
}
535+
499536
fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers> {
500537
unfurl!(self, get_sidecar_identifiers);
501538
Ok(Identifiers::default())

asic/src/chaos/table.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ pub const SWITCH_IPV4_ADDR: &str = "pipe.Ingress.filter.switch_ipv4_addr";
2525
pub const SWITCH_IPV6_ADDR: &str = "pipe.Ingress.filter.switch_ipv6_addr";
2626
pub const NAT_INGRESS_IPV4: &str = "pipe.Ingress.nat_ingress.ingress_ipv4";
2727
pub const NAT_INGRESS_IPV6: &str = "pipe.Ingress.nat_ingress.ingress_ipv6";
28+
pub(crate) const MCAST_NAT_INGRESS_IPV4: &str =
29+
"pipe.Ingress.nat_ingress.ingress_ipv4_mcast";
30+
pub(crate) const MCAST_NAT_INGRESS_IPV6: &str =
31+
"pipe.Ingress.nat_ingress.ingress_ipv6_mcast";
32+
pub(crate) const MCAST_REPLICATION_IPV4: &str =
33+
"pipe.Ingress.mcast_ingress.mcast_replication_ipv4";
34+
pub(crate) const MCAST_REPLICATION_IPV6: &str =
35+
"pipe.Ingress.mcast_ingress.mcast_replication_ipv6";
36+
pub(crate) const MCAST_SRC_FILTER_IPV4: &str =
37+
"pipe.Ingress.mcast_ingress.mcast_source_filter_ipv4";
38+
pub(crate) const MCAST_SRC_FILTER_IPV6: &str =
39+
"pipe.Ingress.mcast_ingress.mcast_source_filter_ipv6";
40+
pub(crate) const MCAST_ROUTE_IPV4: &str =
41+
"pipe.Ingress.l3_router.MulticastRouter4.tbl";
42+
pub(crate) const MCAST_ROUTE_IPV6: &str =
43+
"pipe.Ingress.l3_router.MulticastRouter6.tbl";
44+
pub(crate) const MCAST_MAC_REWRITE: &str =
45+
"pipe.Egress.mac_rewrite.mac_rewrite";
46+
pub(crate) const MCAST_DECAP_PORTS: &str =
47+
"pipe.Egress.mcast_egress.tbl_decap_ports";
48+
pub(crate) const MCAST_PORT_ID_MAPPING: &str =
49+
"pipe.Egress.mcast_egress.asic_id_to_port";
2850

2951
pub struct Table {
3052
name: String,

asic/src/softnpu/mod.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,13 @@ impl AsicOps for Handle {
349349
Ok(self.ports.lock().unwrap().len())
350350
}
351351

352-
fn mc_port_add(&self, _group_id: u16, _port: u16) -> AsicResult<()> {
352+
fn mc_port_add(
353+
&self,
354+
_group_id: u16,
355+
_port: u16,
356+
_rid: u16,
357+
_level1_excl_id: u16,
358+
) -> AsicResult<()> {
353359
Err(AsicError::OperationUnsupported)
354360
}
355361

@@ -365,6 +371,18 @@ impl AsicOps for Handle {
365371
Ok(())
366372
}
367373

374+
fn mc_groups_count(&self) -> AsicResult<usize> {
375+
Ok(self.ports.lock().unwrap().len())
376+
}
377+
378+
fn mc_set_max_nodes(
379+
&self,
380+
_max_nodes: u32,
381+
_max_link_aggregated_nodes: u32,
382+
) -> AsicResult<()> {
383+
Ok(())
384+
}
385+
368386
fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers> {
369387
Ok(Identifiers {
370388
id: Uuid::new_v4(),

asic/src/tofino_asic/imported_bf_functions

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ bf_mc_create_session
2222
bf_mc_destroy_session
2323
bf_mc_mgrp_create
2424
bf_mc_mgrp_destroy
25+
bf_mc_mgrp_get_count
2526
bf_mc_node_create
2627
bf_mc_node_destroy
2728
bf_mc_node_update
2829
bf_mc_associate_node
2930
bf_mc_dissociate_node
31+
bf_mc_set_max_node_threshold
3032

3133
# bf_rt calls
3234
bf_rt_table_from_name_get

0 commit comments

Comments
 (0)