Skip to content

Commit 1077080

Browse files
[multicast] Adapt multicast code to new dendrite DPD API (#9950)
This includes a few changes I was already working on in relation to #9898. This comes after testing with a feature-gated (for multicast) dpd binary. There's a minor follow-up (oxidecomputer/dendrite#232) that I'll work in after this. - Use actual group tags from DPD responses instead of hardcoded "nexus" - Rename IntoUnderlayMulticastIpv6 -> IntoUnderlayMulticast (shorter), fits trait definition / more accurate - Pass MulticastTag as parameter to dpd_update_external_or_create - Use existing_tag.try_into() / current_tag.try_into() for tag usage - Nit: dpd_state_matches_tag to use as_str() comparison - Fix: IpSrc::Subnet -> IpSrc::Any (new dendrite API) - Fix: SwitchLocation import (moved to sled_agent_types) - Remove unused multicast type imports
1 parent 4b6da86 commit 1077080

6 files changed

Lines changed: 142 additions & 92 deletions

File tree

nexus/db-queries/src/db/datastore/multicast/members.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ use crate::db::pagination::paginated;
3939
/// - **SSM (232.0.0.0/8, ff3x::/32)**: Always use `specific_sources` per RFC 4607.
4040
/// The `has_any_source_member` flag is ignored because API validation
4141
/// prevents SSM joins without sources.
42-
/// - **ASM**: Currently always passes `None` to DPD (Dendrite doesn't support
43-
/// ASM filtering yet). TODO: if `has_any_source_member` is true, skip
44-
/// switch-level filtering; otherwise use `specific_sources`.
42+
/// - **ASM**: If `has_any_source_member` is true, passes `None` to DPD
43+
/// (no switch-level filtering). Otherwise uses `specific_sources`.
4544
/// - **OPTE**: Always uses per-member source lists for fine-grained filtering,
4645
/// regardless of switch-level behavior.
4746
///
@@ -57,8 +56,8 @@ pub struct SourceFilterState {
5756

5857
/// True if any member has empty `source_ips` (wants any source).
5958
///
60-
/// For ASM groups: currently unused (Dendrite doesn't support ASM filtering).
61-
/// TODO: when true, switch-level filtering will be disabled.
59+
/// For ASM groups: when true, switch-level source filtering is disabled
60+
/// (sources passed as `None` to Dendrite).
6261
/// For SSM groups: ignored per RFC 4607 (API validation prevents SSM joins
6362
/// without sources).
6463
pub has_any_source_member: bool,

nexus/src/app/background/tasks/multicast/groups.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,19 @@ fn dpd_state_matches_tag(
109109
dpd_group: &dpd_client::types::MulticastGroupExternalResponse,
110110
db_group: &MulticastGroup,
111111
) -> bool {
112-
match (&dpd_group.tag, &db_group.tag) {
113-
(dpd_tag, Some(db_tag)) => dpd_tag == db_tag,
114-
_ => false,
112+
match &db_group.tag {
113+
Some(db_tag) => dpd_group.tag.as_str() == db_tag,
114+
None => false,
115115
}
116116
}
117117

118118
/// Check if DPD sources match the expected state based on source filter.
119119
///
120120
/// Source filtering logic per RFC 4607 (mirrors dataplane code):
121-
/// - SSM (232/8, ff3x::/32): MUST have specific sources. `has_any_source_member`
122-
/// is ignored because API validation prevents SSM joins without sources.
123-
/// - ASM: Currently expects `None` (Dendrite doesn't support ASM filtering yet).
124-
///
125-
/// TODO: Once Dendrite accepts ASM source filtering, enable it for ASM groups
126-
/// where `has_any_source_member=false`.
121+
/// - SSM (232/8, ff3x::/32): always expect specific sources. API validation
122+
/// prevents SSM joins without sources.
123+
/// - ASM: expect specific sources when all members specify sources,
124+
/// otherwise `None` to allow any source at the switch level.
127125
fn dpd_state_matches_sources(
128126
dpd_group: &dpd_client::types::MulticastGroupExternalResponse,
129127
source_filter: &SourceFilterState,
@@ -134,12 +132,11 @@ fn dpd_state_matches_sources(
134132

135133
// Expected DPD state based on source filter logic (RFC 4607)
136134
let expected_sources = if is_ssm_address(group_ip) {
137-
// SSM: always expect specific sources
138135
Some(&source_filter.specific_sources)
139-
} else {
140-
// ASM: Dendrite doesn't support ASM filtering yet
141-
// TODO: check `has_any_source_member` to enable/disable filtering
136+
} else if source_filter.has_any_source_member {
142137
None
138+
} else {
139+
Some(&source_filter.specific_sources)
143140
};
144141

145142
match (dpd_sources, expected_sources) {
@@ -152,7 +149,7 @@ fn dpd_state_matches_sources(
152149
.into_iter()
153150
.filter_map(|src| match src {
154151
dpd_client::types::IpSrc::Exact(ip) => Some(ip),
155-
_ => None, // Subnet matching removed in follow-up Dendrite TODO
152+
_ => None,
156153
})
157154
.collect();
158155
dpd_ips.sort();
@@ -1051,8 +1048,8 @@ mod tests {
10511048

10521049
#[test]
10531050
fn test_dpd_state_matches_sources_asm_address() {
1054-
// ASM address (not 232.x.x.x) - should always expect None from DPD
1055-
// regardless of specific_sources (Dendrite limitation, see TODO)
1051+
// ASM address with all members specifying sources: expect those
1052+
// sources in DPD.
10561053
let source_filter = SourceFilterState {
10571054
specific_sources: BTreeSet::from(["10.0.0.1"
10581055
.parse::<IpAddr>()
@@ -1062,15 +1059,15 @@ mod tests {
10621059

10631060
let group = create_group("224.1.1.1"); // ASM address (not 232.x.x.x)
10641061

1065-
// DPD has None (correct for ASM)
1066-
let dpd_group = create_dpd_group(None);
1067-
assert!(dpd_state_matches_sources(&dpd_group, &source_filter, &group));
1068-
1069-
// DPD has sources (mismatch: ASM should have none)
1062+
// DPD has matching sources (correct)
10701063
let dpd_group =
10711064
create_dpd_group(Some(vec![dpd_client::types::IpSrc::Exact(
10721065
"10.0.0.1".parse().unwrap(),
10731066
)]));
1067+
assert!(dpd_state_matches_sources(&dpd_group, &source_filter, &group));
1068+
1069+
// DPD has None (mismatch: ASM with all-specific should have sources)
1070+
let dpd_group = create_dpd_group(None);
10741071
assert!(!dpd_state_matches_sources(&dpd_group, &source_filter, &group));
10751072
}
10761073

0 commit comments

Comments
 (0)