Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,322 changes: 1,509 additions & 1,813 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ digest = "0.10.7"
dns-server = { path = "dns-server" }
dns-server-api = { path = "dns-server-api" }
dns-service-client = { path = "clients/dns-service-client" }
dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "37992295b5dc708d8f120cee805d67418741b556" }
dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "f20f786e67c86388dfaaf0ef3aa9d2693dfe1da4" }
dropshot = { version = "0.16.6", features = [ "usdt-probes" ] }
dropshot-api-manager = "0.4.0"
dropshot-api-manager-types = "0.4.0"
Expand Down
1 change: 1 addition & 0 deletions dev-tools/ls-apis/tests/api_dependencies.out
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Downstairs Controller (debugging only) (client: dsc-client)

Management Gateway Service (client: gateway-client)
consumed by: dpd (dendrite/dpd) via 1 path
consumed by: lldpd (lldp/lldpd) via 1 path
consumed by: mgd (maghemite/mgd) via 1 path
consumed by: omicron-nexus (omicron/nexus) via 4 paths
consumed by: omicron-sled-agent (omicron/sled-agent) via 1 path
Expand Down
2 changes: 1 addition & 1 deletion dev-tools/omdb/src/bin/omdb/mgs/sensors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ fn sp_info_csv<R: std::io::Read>(
"line {}: unrecognized value \
\"{}\" in field {}",
position.line(),
record[ndx + len].to_string(),
&record[ndx + len],
ndx + len
);
}
Expand Down
2 changes: 1 addition & 1 deletion dev-tools/omdb/tests/env.out
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ termination: Exited(2)
stdout:
---------------------------------------------
stderr:
error: invalid value 'junk' for '--db-url <DB_URL>': invalid connection string: unexpected EOF
error: invalid value 'junk' for '--db-url <DB_URL>': invalid connection string

For more information, try '--help'.
=============================================
Expand Down
8 changes: 6 additions & 2 deletions nexus-config/src/postgres_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,17 @@ mod test {
// tokio_postgres::config::Config parser to include in the error
// message.
let error = "foo".parse::<PostgresConfigWithUrl>().unwrap_err();
assert!(error.to_string().contains("unexpected EOF"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your change here seems totally reasonable to me; invalid connection string is describing the situation here accurately, and I don't think "unexpected EOF" even described the condition we were looking for before. It just happened to be the string emitted by the old postgres version.

assert!(
error.to_string().contains("invalid connection string"),
"'{error}' does not contain 'invalid connection string'"
);
"http://127.0.0.1:1234".parse::<PostgresConfigWithUrl>().unwrap_err();
let error = "postgresql://example.com?sslmode=not-a-real-ssl-mode"
.parse::<PostgresConfigWithUrl>()
.unwrap_err();
assert!(
error.to_string().contains("invalid value for option `sslmode`")
error.to_string().contains("invalid connection string"),
"'{error}' does not contain 'invalid connection string'"
);
}

Expand Down
9 changes: 4 additions & 5 deletions nexus/db-queries/src/db/datastore/multicast/members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ use crate::db::pagination::paginated;
/// - **SSM (232.0.0.0/8, ff3x::/32)**: Always use `specific_sources` per RFC 4607.
/// The `has_any_source_member` flag is ignored because API validation
/// prevents SSM joins without sources.
/// - **ASM**: Currently always passes `None` to DPD (Dendrite doesn't support
/// ASM filtering yet). TODO: if `has_any_source_member` is true, skip
/// switch-level filtering; otherwise use `specific_sources`.
/// - **ASM**: If `has_any_source_member` is true, passes `None` to DPD
/// (no switch-level filtering). Otherwise uses `specific_sources`.
/// - **OPTE**: Always uses per-member source lists for fine-grained filtering,
/// regardless of switch-level behavior.
///
Expand All @@ -57,8 +56,8 @@ pub struct SourceFilterState {

/// True if any member has empty `source_ips` (wants any source).
///
/// For ASM groups: currently unused (Dendrite doesn't support ASM filtering).
/// TODO: when true, switch-level filtering will be disabled.
/// For ASM groups: when true, switch-level source filtering is disabled
/// (sources passed as `None` to Dendrite).
/// For SSM groups: ignored per RFC 4607 (API validation prevents SSM joins
/// without sources).
pub has_any_source_member: bool,
Expand Down
41 changes: 19 additions & 22 deletions nexus/src/app/background/tasks/multicast/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,19 @@ fn dpd_state_matches_tag(
dpd_group: &dpd_client::types::MulticastGroupExternalResponse,
db_group: &MulticastGroup,
) -> bool {
match (&dpd_group.tag, &db_group.tag) {
(Some(dpd_tag), Some(db_tag)) => dpd_tag == db_tag,
_ => false,
match &db_group.tag {
Some(db_tag) => dpd_group.tag.as_str() == db_tag,
None => false,
}
}

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

// Expected DPD state based on source filter logic (RFC 4607)
let expected_sources = if is_ssm_address(group_ip) {
// SSM: always expect specific sources
Some(&source_filter.specific_sources)
} else {
// ASM: Dendrite doesn't support ASM filtering yet
// TODO: check `has_any_source_member` to enable/disable filtering
} else if source_filter.has_any_source_member {
None
} else {
Some(&source_filter.specific_sources)
};

match (dpd_sources, expected_sources) {
Expand All @@ -152,7 +149,7 @@ fn dpd_state_matches_sources(
.into_iter()
.filter_map(|src| match src {
dpd_client::types::IpSrc::Exact(ip) => Some(ip),
_ => None, // Subnet matching removed in follow-up Dendrite TODO
_ => None,
})
.collect();
dpd_ips.sort();
Expand Down Expand Up @@ -948,7 +945,7 @@ mod tests {
dpd_client::types::MulticastGroupExternalResponse {
group_ip: "232.1.1.1".parse().unwrap(),
sources,
tag: Some("test-tag".to_string()),
tag: "test-tag".to_string(),
external_group_id: 1,
external_forwarding: dpd_client::types::ExternalForwarding {
vlan_id: None,
Expand Down Expand Up @@ -1051,8 +1048,8 @@ mod tests {

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

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

// DPD has None (correct for ASM)
let dpd_group = create_dpd_group(None);
assert!(dpd_state_matches_sources(&dpd_group, &source_filter, &group));

// DPD has sources (mismatch: ASM should have none)
// DPD has matching sources (correct)
let dpd_group =
create_dpd_group(Some(vec![dpd_client::types::IpSrc::Exact(
"10.0.0.1".parse().unwrap(),
)]));
assert!(dpd_state_matches_sources(&dpd_group, &source_filter, &group));

// DPD has None (mismatch: ASM with all-specific should have sources)
let dpd_group = create_dpd_group(None);
assert!(!dpd_state_matches_sources(&dpd_group, &source_filter, &group));
}

Expand Down
Loading
Loading