diff --git a/Cargo.lock b/Cargo.lock index 5431c497935..93560fa58a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6429,6 +6429,7 @@ dependencies = [ "cfg-if", "cfg_aliases 0.2.1", "libc", + "memoffset", ] [[package]] @@ -7545,6 +7546,7 @@ dependencies = [ "memchr", "mio", "newtype-uuid", + "nix 0.29.0", "nom", "num-bigint-dig", "num-integer", @@ -11244,6 +11246,7 @@ dependencies = [ "gateway-messages", "gateway-types", "hex", + "nix 0.29.0", "omicron-common", "omicron-workspace-hack", "serde", diff --git a/Cargo.toml b/Cargo.toml index 34dca94a31a..025438b6ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -494,6 +494,7 @@ nexus-test-interface = { path = "nexus/test-interface" } nexus-test-utils-macros = { path = "nexus/test-utils-macros" } nexus-test-utils = { path = "nexus/test-utils" } nexus-types = { path = "nexus/types" } +nix = { version = "0.29", features = ["net"] } nom = "7.1.3" num-integer = "0.1.46" num = { version = "0.4.3", default-features = false, features = [ "libm" ] } diff --git a/dev-tools/omdb/src/bin/omdb/db.rs b/dev-tools/omdb/src/bin/omdb/db.rs index 287b9b8b553..6cf6a71b64a 100644 --- a/dev-tools/omdb/src/bin/omdb/db.rs +++ b/dev-tools/omdb/src/bin/omdb/db.rs @@ -1563,7 +1563,7 @@ async fn cmd_db_disk_info( struct DownstairsRow { host_serial: String, region: String, - zone: String, + dataset: String, physical_disk: String, } @@ -1688,7 +1688,7 @@ async fn cmd_db_disk_info( rows.push(DownstairsRow { host_serial: my_sled.serial_number().to_string(), region: region.id().to_string(), - zone: format!("oxz_crucible_{}", dataset.id()), + dataset: dataset.id().to_string(), physical_disk: my_zpool.physical_disk_id.to_string(), }); } @@ -1724,7 +1724,7 @@ async fn get_and_display_vcr( for v in volumes { match serde_json::from_str(&v.data()) { Ok(vcr) => { - println!("\nVCR from volume ID {volume_id}"); + println!("VCR from volume ID {volume_id}"); print_vcr(vcr, 0); } Err(e) => { @@ -2125,12 +2125,12 @@ async fn cmd_db_snapshot_info( struct DownstairsRow { host_serial: String, region: String, - zone: String, + dataset: String, physical_disk: String, } use db::schema::snapshot::dsl as snapshot_dsl; - let snapshots = snapshot_dsl::snapshot + let mut snapshots = snapshot_dsl::snapshot .filter(snapshot_dsl::id.eq(args.uuid)) .limit(1) .select(Snapshot::as_select()) @@ -2138,32 +2138,75 @@ async fn cmd_db_snapshot_info( .await .context("loading requested snapshot")?; - let mut dest_volume_ids = Vec::new(); - let mut source_volume_ids = Vec::new(); - let rows = snapshots.into_iter().map(|snapshot| { - dest_volume_ids.push(snapshot.destination_volume_id()); - source_volume_ids.push(snapshot.volume_id()); - SnapshotRow::from(snapshot) - }); - if rows.len() == 0 { - bail!("No snapshout with UUID: {} found", args.uuid); + if snapshots.is_empty() { + bail!("No snapshot with UUID: {} found", args.uuid); } + let snap = snapshots.pop().expect("Found more that one snapshot"); - let table = tabled::Table::new(rows) - .with(tabled::settings::Style::empty()) - .with(tabled::settings::Padding::new(0, 1, 0, 0)) - .to_string(); + let dest_vol_id = snap.destination_volume_id; + let source_vol_id = snap.volume_id; + let snap = SnapshotRow::from(snap); - println!("{}", table); + println!(" Name: {}", snap.snap_name); + println!(" id: {}", snap.id); + println!(" state: {}", snap.state); + println!(" size: {}", snap.size); + println!(" source_disk_id: {}", snap.source_disk_id); + println!(" source_volume_id: {}", snap.source_volume_id); + println!("destination_volume_id: {}", snap.destination_volume_id); - println!("SOURCE VOLUME VCR:"); - for vol in source_volume_ids { - get_and_display_vcr(vol, datastore).await?; + use db::schema::region_snapshot::dsl as region_snapshot_dsl; + let region_snapshots = region_snapshot_dsl::region_snapshot + .filter(region_snapshot_dsl::snapshot_id.eq(args.uuid)) + .select(RegionSnapshot::as_select()) + .load_async(&*datastore.pool_connection_for_tests().await?) + .await + .context("loading region snapshots")?; + + println!(); + if region_snapshots.is_empty() { + println!("No region snapshot info found"); + } else { + // The row describing the region_snapshot. + #[derive(Tabled)] + #[tabled(rename_all = "SCREAMING_SNAKE_CASE")] + struct RegionSnapshotRow { + dataset_id: String, + region_id: String, + snapshot_id: String, + snapshot_addr: String, + volume_references: String, + } + let mut rsnap = Vec::new(); + + // From each region snapshot: + // Collect the snapshot IDs for later use. + // Display the region snapshot rows. + let mut snapshot_ids = HashSet::new(); + for rs in region_snapshots { + snapshot_ids.insert(rs.snapshot_id); + let rs = RegionSnapshotRow { + dataset_id: rs.dataset_id.to_string(), + region_id: rs.region_id.to_string(), + snapshot_id: rs.snapshot_id.to_string(), + snapshot_addr: rs.snapshot_addr.to_string(), + volume_references: rs.volume_references.to_string(), + }; + rsnap.push(rs); + } + let table = tabled::Table::new(rsnap) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("REGION SNAPSHOT INFO:"); + println!("{}", table); } - for vol_id in dest_volume_ids { - // Get the dataset backing this volume. - let regions = datastore.get_allocated_regions(vol_id).await?; + let regions = datastore.get_allocated_regions(source_vol_id.into()).await?; + if regions.is_empty() { + println!("\nNo source region info found"); + } else { let mut rows = Vec::with_capacity(3); for (dataset, region) in regions { let my_pool_id = dataset.pool_id; @@ -2184,7 +2227,7 @@ async fn cmd_db_snapshot_info( rows.push(DownstairsRow { host_serial: my_sled.serial_number().to_string(), region: region.id().to_string(), - zone: format!("oxz_crucible_{}", dataset.id()), + dataset: dataset.id().to_string(), physical_disk: my_zpool.physical_disk_id.to_string(), }); } @@ -2194,12 +2237,51 @@ async fn cmd_db_snapshot_info( .with(tabled::settings::Padding::new(0, 1, 0, 0)) .to_string(); - println!("DESTINATION REGION INFO:"); + println!("\nSOURCE REGION INFO:"); println!("{}", table); - println!("DESTINATION VOLUME VCR:"); - get_and_display_vcr(vol_id, datastore).await?; } + println!("SOURCE VOLUME VCR:"); + get_and_display_vcr(source_vol_id.into(), datastore).await?; + + // Get the dataset backing this volume. + let regions = datastore.get_allocated_regions(dest_vol_id.into()).await?; + + let mut rows = Vec::with_capacity(3); + for (dataset, region) in regions { + let my_pool_id = dataset.pool_id; + let (_, my_zpool) = LookupPath::new(opctx, datastore) + .zpool_id(my_pool_id) + .fetch() + .await + .context("failed to look up zpool")?; + + let my_sled_id = my_zpool.sled_id; + + let (_, my_sled) = LookupPath::new(opctx, datastore) + .sled_id(my_sled_id) + .fetch() + .await + .context("failed to look up sled")?; + + rows.push(DownstairsRow { + host_serial: my_sled.serial_number().to_string(), + region: region.id().to_string(), + dataset: dataset.id().to_string(), + physical_disk: my_zpool.physical_disk_id.to_string(), + }); + } + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("DESTINATION REGION INFO:"); + println!("{}", table); + println!("DESTINATION VOLUME VCR:"); + get_and_display_vcr(dest_vol_id.into(), datastore).await?; + Ok(()) } diff --git a/gateway-test-utils/configs/sp_sim_config.test.toml b/gateway-test-utils/configs/sp_sim_config.test.toml index 543f7865bce..3f86214760b 100644 --- a/gateway-test-utils/configs/sp_sim_config.test.toml +++ b/gateway-test-utils/configs/sp_sim_config.test.toml @@ -8,12 +8,18 @@ # concurrently. # [[simulated_sps.sidecar]] -multicast_addr = "::1" -bind_addrs = ["[::1]:0", "[::1]:0"] serial_number = "SimSidecar0" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000000" +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:0" + +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:0" + [[simulated_sps.sidecar.components]] id = "dev-0" device = "fake-tmp-sensor" @@ -35,19 +41,31 @@ sensors = [ ] [[simulated_sps.sidecar]] -multicast_addr = "::1" -bind_addrs = ["[::1]:0", "[::1]:0"] serial_number = "SimSidecar1" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000001" +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:0" + +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:0" + [[simulated_sps.gimlet]] -multicast_addr = "::1" -bind_addrs = ["[::1]:0", "[::1]:0"] serial_number = "SimGimlet00" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000002" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:0" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:0" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" @@ -151,12 +169,18 @@ sensors = [ [[simulated_sps.gimlet]] -multicast_addr = "::1" -bind_addrs = ["[::1]:0", "[::1]:0"] serial_number = "SimGimlet01" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000003" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:0" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:0" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs index bc7544b0ba8..b461a0b635b 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs @@ -925,13 +925,23 @@ impl<'a> BlueprintBuilder<'a> { match zone.disposition { BlueprintZoneDisposition::Expunged => (), BlueprintZoneDisposition::InService - | BlueprintZoneDisposition::Quiesced => todo!("fixme-1"), + | BlueprintZoneDisposition::Quiesced => { + return Err(Error::Planner(anyhow!( + "expunged all disks but a zone \ + is still in service: {zone:?}" + ))); + } } } for dataset in editor.datasets(BlueprintDatasetFilter::All) { match dataset.disposition { BlueprintDatasetDisposition::Expunged => (), - BlueprintDatasetDisposition::InService => todo!("fixme-2"), + BlueprintDatasetDisposition::InService => { + return Err(Error::Planner(anyhow!( + "expunged all disks but a dataset \ + is still in service: {dataset:?}" + ))); + } } } diff --git a/smf/sp-sim/config.toml b/smf/sp-sim/config.toml index eda37959cd7..bde9d84e0ed 100644 --- a/smf/sp-sim/config.toml +++ b/smf/sp-sim/config.toml @@ -3,20 +3,31 @@ # [[simulated_sps.sidecar]] -multicast_addr = "ff15:0:1de::0" -bind_addrs = ["[::]:33300", "[::]:33301"] serial_number = "SimSidecar0" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000000" +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:33300" + +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:33301" [[simulated_sps.gimlet]] -multicast_addr = "ff15:0:1de::1" -bind_addrs = ["[::]:33310", "[::]:33311"] serial_number = "SimGimlet0" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000001" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33310" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33311" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" @@ -26,12 +37,18 @@ presence = "Present" serial_console = "[::1]:33312" [[simulated_sps.gimlet]] -multicast_addr = "ff15:0:1de::2" -bind_addrs = ["[::]:33320", "[::]:33321"] serial_number = "SimGimlet1" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000002" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33320" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33321" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" diff --git a/sp-sim/Cargo.toml b/sp-sim/Cargo.toml index 92969472a15..66819bf9c52 100644 --- a/sp-sim/Cargo.toml +++ b/sp-sim/Cargo.toml @@ -24,6 +24,7 @@ thiserror.workspace = true tokio = { workspace = true, features = [ "full" ] } toml.workspace = true omicron-workspace-hack.workspace = true +nix.workspace = true [[bin]] name = "sp-sim" diff --git a/sp-sim/examples/config.toml b/sp-sim/examples/config.toml index 2e3d62b258c..8c88ac6afe1 100644 --- a/sp-sim/examples/config.toml +++ b/sp-sim/examples/config.toml @@ -3,19 +3,31 @@ # [[simulated_sps.sidecar]] -multicast_addr = "ff15:0:1de::0" -bind_addrs = ["[::]:33300", "[::]:33301"] serial_number = "SimSidecar0" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000000" +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:33300" + +[[simulated_sps.sidecar.network_config]] +[simulated_sps.sidecar.network_config.simulated] +bind_addr = "[::1]:33301" + [[simulated_sps.gimlet]] -multicast_addr = "ff15:0:1de::1" -bind_addrs = ["[::]:33310", "[::]:33311"] serial_number = "SimGimlet0" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000001" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33310" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33311" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" @@ -45,12 +57,18 @@ sensors = [ ] [[simulated_sps.gimlet]] -multicast_addr = "ff15:0:1de::2" -bind_addrs = ["[::]:33320", "[::]:33321"] serial_number = "SimGimlet1" manufacturing_root_cert_seed = "01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de01de" device_id_cert_seed = "01de000000000000000000000000000000000000000000000000000000000002" +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33320" + +[[simulated_sps.gimlet.network_config]] +[simulated_sps.gimlet.network_config.simulated] +bind_addr = "[::1]:33321" + [[simulated_sps.gimlet.components]] id = "sp3-host-cpu" device = "sp3-host-cpu" diff --git a/sp-sim/src/config.rs b/sp-sim/src/config.rs index d45e956dee6..28d0f3760ad 100644 --- a/sp-sim/src/config.rs +++ b/sp-sim/src/config.rs @@ -17,15 +17,65 @@ use std::path::Path; use std::path::PathBuf; use thiserror::Error; +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum NetworkConfig { + /// Listen address for a fake KSZ8463 port + Simulated { bind_addr: SocketAddrV6 }, + + Real { + bind_addr: SocketAddrV6, + + /// IPv6 multicast address + multicast_addr: Ipv6Addr, + + /// IPv6 multicast interface to use. + multicast_interface: String, + }, +} + +impl slog::KV for NetworkConfig { + fn serialize( + &self, + _record: &slog::Record<'_>, + serializer: &mut dyn slog::Serializer, + ) -> slog::Result { + match &self { + NetworkConfig::Simulated { bind_addr } => { + serializer.emit_str("type".into(), "simulated")?; + serializer + .emit_str("bind_addr".into(), &bind_addr.to_string())?; + } + + NetworkConfig::Real { + bind_addr, + multicast_addr, + multicast_interface, + } => { + serializer.emit_str("type".into(), "real")?; + serializer + .emit_str("bind_addr".into(), &bind_addr.to_string())?; + serializer.emit_str( + "multicast_addr".into(), + &multicast_addr.to_string(), + )?; + serializer.emit_str( + "multicast_interface".into(), + &multicast_interface, + )?; + } + } + + Ok(()) + } +} + /// Common configuration for all flavors of SP #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SpCommonConfig { - /// IPv6 multicast address to join. - #[serde(skip_serializing_if = "Option::is_none", default)] - pub multicast_addr: Option, - /// UDP address of the two (fake) KSZ8463 ports + /// Network config for the two (fake) KSZ8463 ports. #[serde(skip_serializing_if = "Option::is_none", default)] - pub bind_addrs: Option<[SocketAddrV6; 2]>, + pub network_config: Option<[NetworkConfig; 2]>, /// Fake serial number pub serial_number: String, /// 32-byte seed to create a manufacturing root certificate. diff --git a/sp-sim/src/gimlet.rs b/sp-sim/src/gimlet.rs index de6c91b4d7f..4245e360eb0 100644 --- a/sp-sim/src/gimlet.rs +++ b/sp-sim/src/gimlet.rs @@ -194,10 +194,10 @@ impl Gimlet { let (commands, commands_rx) = mpsc::unbounded_channel(); let last_request_handled = Arc::default(); - // Weird case - if we don't have any bind addresses, we're only being + // Weird case - if we don't have any network config, we're only being // created to simulate an RoT, so go ahead and return without actually // starting a simulated SP. - let Some(bind_addrs) = gimlet.common.bind_addrs else { + let Some(network_config) = &gimlet.common.network_config else { return Ok(Self { local_addrs: None, handler: None, @@ -210,12 +210,14 @@ impl Gimlet { }; // bind to our two local "KSZ" ports - assert_eq!(bind_addrs.len(), 2); // gimlet SP always has 2 ports + assert_eq!(network_config.len(), 2); // gimlet SP always has 2 ports + let servers = future::try_join( - UdpServer::new(bind_addrs[0], gimlet.common.multicast_addr, &log), - UdpServer::new(bind_addrs[1], gimlet.common.multicast_addr, &log), + UdpServer::new(&network_config[0], &log), + UdpServer::new(&network_config[1], &log), ) .await?; + let servers = [servers.0, servers.1]; for component_config in &gimlet.common.components { diff --git a/sp-sim/src/server.rs b/sp-sim/src/server.rs index 8c2161c823a..d1324b605b8 100644 --- a/sp-sim/src/server.rs +++ b/sp-sim/src/server.rs @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::config::Config; +use crate::config::NetworkConfig; use crate::Responsiveness; use anyhow::bail; use anyhow::Context; @@ -10,11 +11,11 @@ use anyhow::Result; use gateway_messages::sp_impl; use gateway_messages::sp_impl::Sender; use gateway_messages::sp_impl::SpHandler; +use nix::net::if_::if_nametoindex; use slog::debug; use slog::error; use slog::info; use slog::Logger; -use std::net::Ipv6Addr; use std::net::SocketAddr; use std::net::SocketAddrV6; use std::sync::atomic::AtomicBool; @@ -32,30 +33,50 @@ pub(crate) struct UdpServer { impl UdpServer { pub(crate) async fn new( - bind_address: SocketAddrV6, - multicast_addr: Option, + network_config: &NetworkConfig, log: &Logger, ) -> Result { - let sock = - Arc::new(UdpSocket::bind(bind_address).await.with_context( - || format!("failed to bind to {}", bind_address), - )?); - - // If we don't have a multicast address, use a non-multicast address; - // this avoids some unslightly if/else blocks around log statements - // below without affecting logic (as we also have to handle - // non-multicast multicast addresses for CI below). - let multicast_addr = multicast_addr.unwrap_or(Ipv6Addr::LOCALHOST); - - // In some environments where sp-sim runs (e.g., some CI runners), we're - // not able to join ipv6 multicast groups. In those cases, we're - // configured with a "multicast_addr" that isn't actually multicast, so - // don't try to join the group if we have such an address. - if multicast_addr.is_multicast() { - sock.join_multicast_v6(&multicast_addr, 0).with_context(|| { - format!("failed to join multicast group {}", multicast_addr) - })?; - } + let sock = match network_config { + // In some environments where sp-sim runs (e.g., some CI runners), + // we're not able to join ipv6 multicast groups. In those cases, + // we're configured with an address that isn't actually multicast, + // so don't try to join the group if we have such an address. + NetworkConfig::Simulated { bind_addr } => { + UdpSocket::bind(bind_addr).await.with_context(|| { + format!("failed to bind to {}", bind_addr) + })? + } + + NetworkConfig::Real { + bind_addr, + multicast_addr, + multicast_interface, + } => { + if !multicast_addr.is_multicast() { + bail!("{multicast_addr} is not multicast!"); + } + + let interface_index = + if_nametoindex(multicast_interface.as_str()) + .context("if_nametoindex for {multicast_interface}")?; + + let sock = + UdpSocket::bind(bind_addr).await.with_context(|| { + format!("failed to bind to {}", bind_addr) + })?; + + sock.join_multicast_v6(&multicast_addr, interface_index) + .with_context(|| { + format!( + "failed to join multicast group {multicast_addr} \ + for interface {multicast_interface} index \ + {interface_index}", + ) + })?; + + sock + } + }; let local_addr = sock .local_addr() @@ -64,13 +85,14 @@ impl UdpServer { SocketAddr::V4(addr) => bail!("bound IPv4 address {}", addr), SocketAddr::V6(addr) => Ok(addr), })?; + info!(log, "simulated SP UDP socket bound"; "local_addr" => %local_addr, - "multicast_addr" => %multicast_addr, + network_config, ); Ok(Self { - sock, + sock: Arc::new(sock), local_addr, buf: [0; gateway_messages::MAX_SERIALIZED_SIZE], }) diff --git a/sp-sim/src/sidecar.rs b/sp-sim/src/sidecar.rs index 286dd506387..309d4b4489f 100644 --- a/sp-sim/src/sidecar.rs +++ b/sp-sim/src/sidecar.rs @@ -167,22 +167,14 @@ impl Sidecar { let (commands, commands_rx) = mpsc::unbounded_channel(); let (local_addrs, inner_task, handler, responses_sent_count) = - if let Some(bind_addrs) = sidecar.common.bind_addrs { + if let Some(network_config) = &sidecar.common.network_config { // bind to our two local "KSZ" ports - assert_eq!(bind_addrs.len(), 2); let servers = future::try_join( - UdpServer::new( - bind_addrs[0], - sidecar.common.multicast_addr, - &log, - ), - UdpServer::new( - bind_addrs[1], - sidecar.common.multicast_addr, - &log, - ), + UdpServer::new(&network_config[0], &log), + UdpServer::new(&network_config[1], &log), ) .await?; + let servers = [servers.0, servers.1]; let local_addrs = [servers[0].local_addr(), servers[1].local_addr()]; diff --git a/tools/opte_version b/tools/opte_version index a80da921ae5..90403514c69 100644 --- a/tools/opte_version +++ b/tools/opte_version @@ -1 +1 @@ -0.34.311 +0.34.334 diff --git a/tools/opte_version_override b/tools/opte_version_override index 6ababee9f14..8d57f7ae9f4 100644 --- a/tools/opte_version_override +++ b/tools/opte_version_override @@ -2,4 +2,4 @@ # only set this if you want to override the version of opte/xde installed by the # install_opte.sh script -OPTE_COMMIT="b56afeeb14e0042cbd7bda85b166ed86ee17820e" +OPTE_COMMIT="" diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 744c3a1f375..8f53470e056 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -79,6 +79,7 @@ log = { version = "0.4.22", default-features = false, features = ["kv_unstable", managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.7.4" } newtype-uuid = { version = "1.2.1" } +nix = { version = "0.29.0", features = ["feature", "net", "uio"] } nom = { version = "7.1.3" } num-bigint-dig = { version = "0.8.4", default-features = false, features = ["i128", "prime", "serde", "u64_digit", "zeroize"] } num-integer = { version = "0.1.46", features = ["i128"] } @@ -199,6 +200,7 @@ log = { version = "0.4.22", default-features = false, features = ["kv_unstable", managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.7.4" } newtype-uuid = { version = "1.2.1" } +nix = { version = "0.29.0", features = ["feature", "net", "uio"] } nom = { version = "7.1.3" } num-bigint-dig = { version = "0.8.4", default-features = false, features = ["i128", "prime", "serde", "u64_digit", "zeroize"] } num-integer = { version = "0.1.46", features = ["i128"] }