Skip to content

Commit 51d2c58

Browse files
authored
Use Ipv4Addr instead of String in RackNetworkConfig IP address fields (#3399)
I believe based on current usage that all of these are required to be `Ipv4Addr`s specifically, and not `IpAddr`/`Ipv6Addr`: * `infra_ip_first` and `infra_ip_last` are stored in a variable named `ipv4_block`, and a later `ipv6_block` (that does not use those config values) is also created * `uplink_ip`'s doc comment says it should be in the `infra_ip_first..last` range, which means it would also have to be ipv4 * `gateway_ip` was being parsed into `nexthop: Option<Ipv4Addr>`. but I don't have a lot of context here so would appreciate confirmation. Fixes #3397.
1 parent e4d559c commit 51d2c58

File tree

9 files changed

+92
-82
lines changed

9 files changed

+92
-82
lines changed

common/src/api/internal/shared.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use crate::api::external;
88
use schemars::JsonSchema;
99
use serde::{Deserialize, Serialize};
10-
use std::net::IpAddr;
10+
use std::net::{IpAddr, Ipv4Addr};
1111
use uuid::Uuid;
1212

1313
/// The type of network interface
@@ -66,19 +66,19 @@ pub struct SourceNatConfig {
6666
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)]
6767
pub struct RackNetworkConfig {
6868
/// Gateway address
69-
pub gateway_ip: String,
69+
pub gateway_ip: Ipv4Addr,
7070
/// First ip address to be used for configuring network infrastructure
71-
pub infra_ip_first: String,
71+
pub infra_ip_first: Ipv4Addr,
7272
/// Last ip address to be used for configuring network infrastructure
73-
pub infra_ip_last: String,
73+
pub infra_ip_last: Ipv4Addr,
7474
/// Switchport to use for external connectivity
7575
pub uplink_port: String,
7676
/// Speed for the Switchport
7777
pub uplink_port_speed: PortSpeed,
7878
/// Forward Error Correction setting for the uplink port
7979
pub uplink_port_fec: PortFec,
8080
/// IP Address to apply to switchport (must be in infra_ip pool)
81-
pub uplink_ip: String,
81+
pub uplink_ip: Ipv4Addr,
8282
/// VLAN id to use for uplink
8383
pub uplink_vid: Option<u16>,
8484
}

nexus/src/app/rack.rs

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -263,24 +263,8 @@ impl super::Nexus {
263263

264264
let kind = AddressLotKind::Infra;
265265

266-
let first_address = IpAddr::from_str(
267-
&rack_network_config.infra_ip_first,
268-
)
269-
.map_err(|e| {
270-
Error::internal_error(&format!(
271-
"encountered error while parsing `infra_ip_first`: {e}"
272-
))
273-
})?;
274-
275-
let last_address = IpAddr::from_str(
276-
&rack_network_config.infra_ip_last,
277-
)
278-
.map_err(|e| {
279-
Error::internal_error(&format!(
280-
"encountered error while parsing `infra_ip_last`: {e}"
281-
))
282-
})?;
283-
266+
let first_address = IpAddr::V4(rack_network_config.infra_ip_first);
267+
let last_address = IpAddr::V4(rack_network_config.infra_ip_last);
284268
let ipv4_block =
285269
AddressLotBlockCreate { first_address, last_address };
286270

@@ -431,15 +415,8 @@ impl super::Nexus {
431415
))
432416
})?;
433417

434-
let gw = IpAddr::from_str(&rack_network_config.gateway_ip)
435-
.map_err(|e| {
436-
Error::internal_error(&format!(
437-
"failed to parse provided default gateway address: {e}"
438-
))
439-
})?;
440-
418+
let gw = IpAddr::V4(rack_network_config.gateway_ip);
441419
let vid = rack_network_config.uplink_vid;
442-
443420
let route = Route { dst, gw, vid };
444421

445422
port_settings_params.routes.insert(

openapi/bootstrap-agent.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -520,19 +520,23 @@
520520
"properties": {
521521
"gateway_ip": {
522522
"description": "Gateway address",
523-
"type": "string"
523+
"type": "string",
524+
"format": "ipv4"
524525
},
525526
"infra_ip_first": {
526527
"description": "First ip address to be used for configuring network infrastructure",
527-
"type": "string"
528+
"type": "string",
529+
"format": "ipv4"
528530
},
529531
"infra_ip_last": {
530532
"description": "Last ip address to be used for configuring network infrastructure",
531-
"type": "string"
533+
"type": "string",
534+
"format": "ipv4"
532535
},
533536
"uplink_ip": {
534537
"description": "IP Address to apply to switchport (must be in infra_ip pool)",
535-
"type": "string"
538+
"type": "string",
539+
"format": "ipv4"
536540
},
537541
"uplink_port": {
538542
"description": "Switchport to use for external connectivity",

openapi/nexus-internal.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,19 +2455,23 @@
24552455
"properties": {
24562456
"gateway_ip": {
24572457
"description": "Gateway address",
2458-
"type": "string"
2458+
"type": "string",
2459+
"format": "ipv4"
24592460
},
24602461
"infra_ip_first": {
24612462
"description": "First ip address to be used for configuring network infrastructure",
2462-
"type": "string"
2463+
"type": "string",
2464+
"format": "ipv4"
24632465
},
24642466
"infra_ip_last": {
24652467
"description": "Last ip address to be used for configuring network infrastructure",
2466-
"type": "string"
2468+
"type": "string",
2469+
"format": "ipv4"
24672470
},
24682471
"uplink_ip": {
24692472
"description": "IP Address to apply to switchport (must be in infra_ip pool)",
2470-
"type": "string"
2473+
"type": "string",
2474+
"format": "ipv4"
24712475
},
24722476
"uplink_port": {
24732477
"description": "Switchport to use for external connectivity",

openapi/wicketd.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,19 +1771,23 @@
17711771
"properties": {
17721772
"gateway_ip": {
17731773
"description": "Gateway address",
1774-
"type": "string"
1774+
"type": "string",
1775+
"format": "ipv4"
17751776
},
17761777
"infra_ip_first": {
17771778
"description": "First ip address to be used for configuring network infrastructure",
1778-
"type": "string"
1779+
"type": "string",
1780+
"format": "ipv4"
17791781
},
17801782
"infra_ip_last": {
17811783
"description": "Last ip address to be used for configuring network infrastructure",
1782-
"type": "string"
1784+
"type": "string",
1785+
"format": "ipv4"
17831786
},
17841787
"uplink_ip": {
17851788
"description": "IP Address to apply to switchport (must be in infra_ip pool)",
1786-
"type": "string"
1789+
"type": "string",
1790+
"format": "ipv4"
17871791
},
17881792
"uplink_port": {
17891793
"description": "Switchport to use for external connectivity",

sled-agent/src/rack_setup/service.rs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ use slog::Logger;
102102
use std::collections::{HashMap, HashSet};
103103
use std::iter;
104104
use std::net::IpAddr;
105-
use std::net::Ipv4Addr;
106105
use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6};
107106
use thiserror::Error;
108107

@@ -814,10 +813,10 @@ impl ServiceInner {
814813
let rack_network_config = match &config.rack_network_config {
815814
Some(config) => {
816815
let value = NexusTypes::RackNetworkConfig {
817-
gateway_ip: config.gateway_ip.clone(),
818-
infra_ip_first: config.infra_ip_first.clone(),
819-
infra_ip_last: config.infra_ip_last.clone(),
820-
uplink_ip: config.uplink_ip.clone(),
816+
gateway_ip: config.gateway_ip,
817+
infra_ip_first: config.infra_ip_first,
818+
infra_ip_last: config.infra_ip_last,
819+
uplink_ip: config.uplink_ip,
821820
uplink_port: config.uplink_port.clone(),
822821
uplink_port_speed: config.uplink_port_speed.clone().into(),
823822
uplink_port_fec: config.uplink_port_fec.clone().into(),
@@ -1098,11 +1097,7 @@ impl ServiceInner {
10981097
// TODO handle breakouts
10991098
// https://github.com/oxidecomputer/omicron/issues/3062
11001099
let link_id = LinkId(0);
1101-
let addr: IpAddr = rack_network_config.uplink_ip.parse()
1102-
.map_err(|e| {
1103-
SetupServiceError::BadConfig(format!(
1104-
"unable to parse rack_network_config.uplink_up as IpAddr: {e}"))
1105-
})?;
1100+
let addr = IpAddr::V4(rack_network_config.uplink_ip);
11061101

11071102
let link_settings = LinkSettings {
11081103
// TODO Allow user to configure link properties
@@ -1125,10 +1120,7 @@ impl ServiceInner {
11251120
format!("could not use value provided to rack_network_config.uplink_port as PortID: {e}")
11261121
))?;
11271122

1128-
let nexthop: Option<Ipv4Addr> = Some(rack_network_config.gateway_ip.parse()
1129-
.map_err(|e| SetupServiceError::BadConfig(
1130-
format!("unable to parse rack_network_config.gateway_ip as Ipv4Addr: {e}")
1131-
))?);
1123+
let nexthop = Some(rack_network_config.gateway_ip);
11321124

11331125
dpd_port_settings.v4_routes.insert(
11341126
Ipv4Cidr { prefix: "0.0.0.0".parse().unwrap(), prefix_len: 0 }

wicket/src/rack_setup/config_toml.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,10 @@ fn populate_network_table(
169169
) {
170170
// Helper function to serialize enums into their appropriate string
171171
// representations.
172-
fn enum_to_toml_string<T: Serialize>(value: &T) -> Cow<'static, str> {
172+
fn enum_to_toml_string<T: Serialize>(value: &T) -> String {
173173
let value = toml::Value::try_from(value).unwrap();
174174
match value {
175-
toml::Value::String(s) => Cow::from(s),
175+
toml::Value::String(s) => s,
176176
other => {
177177
panic!("improper use of enum_to_toml_string: got {other:?}");
178178
}
@@ -184,23 +184,24 @@ fn populate_network_table(
184184
};
185185

186186
for (property, value) in [
187-
("gateway_ip", Cow::from(&config.gateway_ip)),
188-
("infra_ip_first", Cow::from(&config.infra_ip_first)),
189-
("infra_ip_last", Cow::from(&config.infra_ip_last)),
190-
("uplink_port", Cow::from(&config.uplink_port)),
187+
("gateway_ip", config.gateway_ip.to_string()),
188+
("infra_ip_first", config.infra_ip_first.to_string()),
189+
("infra_ip_last", config.infra_ip_last.to_string()),
190+
("uplink_port", config.uplink_port.to_string()),
191191
("uplink_port_speed", enum_to_toml_string(&config.uplink_port_speed)),
192192
("uplink_port_fec", enum_to_toml_string(&config.uplink_port_fec)),
193-
("uplink_ip", Cow::from(&config.uplink_ip)),
193+
("uplink_ip", config.uplink_ip.to_string()),
194194
] {
195195
*table.get_mut(property).unwrap().as_value_mut().unwrap() =
196-
Value::String(Formatted::new(value.into_owned()));
196+
Value::String(Formatted::new(value));
197197
}
198198
}
199199

200200
#[cfg(test)]
201201
mod tests {
202202
use super::*;
203203
use omicron_common::api::internal::shared::RackNetworkConfig as InternalRackNetworkConfig;
204+
use std::net::Ipv4Addr;
204205
use std::net::Ipv6Addr;
205206
use wicket_common::rack_setup::PutRssUserConfigInsensitive;
206207
use wicketd_client::types::Baseboard;
@@ -300,10 +301,10 @@ mod tests {
300301
)],
301302
ntp_servers: vec!["ntp1.com".into(), "ntp2.com".into()],
302303
rack_network_config: Some(RackNetworkConfig {
303-
gateway_ip: "1.2.3.4".into(),
304-
infra_ip_first: "2.3.4.5".into(),
305-
infra_ip_last: "3.4.5.6".into(),
306-
uplink_ip: "4.5.6.7".into(),
304+
gateway_ip: Ipv4Addr::new(1, 2, 3, 4),
305+
infra_ip_first: Ipv4Addr::new(2, 3, 4, 5),
306+
infra_ip_last: Ipv4Addr::new(3, 4, 5, 6),
307+
uplink_ip: Ipv4Addr::new(4, 5, 6, 7),
307308
uplink_port_speed: PortSpeed::Speed400G,
308309
uplink_port_fec: PortFec::Firecode,
309310
uplink_port: "port0".into(),

wicket/src/ui/panes/rack_setup.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,19 +709,27 @@ fn rss_config_text<'a>(
709709
"External DNS zone name: ",
710710
Cow::from(insensitive.external_dns_zone_name.as_str()),
711711
),
712-
("Gateway IP: ", net_config.map_or("", |c| &c.gateway_ip).into()),
712+
(
713+
"Gateway IP: ",
714+
net_config.map_or("".into(), |c| c.gateway_ip.to_string().into()),
715+
),
713716
(
714717
"Infrastructure first IP: ",
715-
net_config.map_or("", |c| &c.infra_ip_first).into(),
718+
net_config
719+
.map_or("".into(), |c| c.infra_ip_first.to_string().into()),
716720
),
717721
(
718722
"Infrastructure last IP: ",
719-
net_config.map_or("", |c| &c.infra_ip_last).into(),
723+
net_config
724+
.map_or("".into(), |c| c.infra_ip_last.to_string().into()),
720725
),
721726
("Uplink port: ", net_config.map_or("", |c| &c.uplink_port).into()),
722727
("Uplink port speed: ", uplink_port_speed),
723728
("Uplink port FEC: ", uplink_port_fec),
724-
("Uplink IP: ", net_config.map_or("", |c| &c.uplink_ip).into()),
729+
(
730+
"Uplink IP: ",
731+
net_config.map_or("".into(), |c| c.uplink_ip.to_string().into()),
732+
),
725733
] {
726734
spans.push(Spans::from(vec![
727735
Span::styled(label, label_style),

wicketd/src/rss_config.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::http_entrypoints::CurrentRssUserConfig;
1111
use crate::http_entrypoints::CurrentRssUserConfigInsensitive;
1212
use crate::http_entrypoints::CurrentRssUserConfigSensitive;
1313
use crate::RackV1Inventory;
14+
use anyhow::anyhow;
1415
use anyhow::bail;
1516
use anyhow::Result;
1617
use bootstrap_agent_client::types::BootstrapAddressDiscovery;
@@ -22,6 +23,7 @@ use bootstrap_agent_client::types::UserId;
2223
use gateway_client::types::SpType;
2324
use omicron_certificates::CertificateValidator;
2425
use omicron_common::address;
26+
use omicron_common::address::Ipv4Range;
2527
use omicron_common::api::internal::shared::RackNetworkConfig;
2628
use sled_hardware::Baseboard;
2729
use std::collections::BTreeSet;
@@ -128,7 +130,7 @@ impl CurrentRssConfig {
128130
bail!("rack network config not set (have you uploaded a config?)");
129131
};
130132
let rack_network_config =
131-
validate_rack_network_config(rack_network_config);
133+
validate_rack_network_config(rack_network_config)?;
132134

133135
let known_bootstrap_sleds = bootstrap_peers.sleds();
134136
let mut bootstrap_ips = Vec::new();
@@ -357,18 +359,36 @@ impl From<&'_ CurrentRssConfig> for CurrentRssUserConfig {
357359

358360
fn validate_rack_network_config(
359361
config: &RackNetworkConfig,
360-
) -> bootstrap_agent_client::types::RackNetworkConfig {
362+
) -> Result<bootstrap_agent_client::types::RackNetworkConfig> {
361363
use bootstrap_agent_client::types::PortFec as BaPortFec;
362364
use bootstrap_agent_client::types::PortSpeed as BaPortSpeed;
363365
use omicron_common::api::internal::shared::PortFec;
364366
use omicron_common::api::internal::shared::PortSpeed;
365367

366-
// TODO Add client side checks on `rack_network_config` contents.
368+
// Make sure `infra_ip_first`..`infra_ip_last` is a well-defined range...
369+
let infra_ip_range =
370+
Ipv4Range::new(config.infra_ip_first, config.infra_ip_last).map_err(
371+
|s: String| {
372+
anyhow!("invalid `infra_ip_first`, `infra_ip_last` range: {s}")
373+
},
374+
)?;
375+
376+
// ... and that it contains `uplink_ip`.
377+
if config.uplink_ip < infra_ip_range.first
378+
|| config.uplink_ip > infra_ip_range.last
379+
{
380+
bail!(
381+
"`uplink_ip` must be in the range defined by `infra_ip_first` \
382+
and `infra_ip_last`"
383+
);
384+
}
367385

368-
bootstrap_agent_client::types::RackNetworkConfig {
369-
gateway_ip: config.gateway_ip.clone(),
370-
infra_ip_first: config.infra_ip_first.clone(),
371-
infra_ip_last: config.infra_ip_last.clone(),
386+
// TODO Add more client side checks on `rack_network_config` contents?
387+
388+
Ok(bootstrap_agent_client::types::RackNetworkConfig {
389+
gateway_ip: config.gateway_ip,
390+
infra_ip_first: config.infra_ip_first,
391+
infra_ip_last: config.infra_ip_last,
372392
uplink_port: config.uplink_port.clone(),
373393
uplink_port_speed: match config.uplink_port_speed {
374394
PortSpeed::Speed0G => BaPortSpeed::Speed0G,
@@ -386,7 +406,7 @@ fn validate_rack_network_config(
386406
PortFec::None => BaPortFec::None,
387407
PortFec::Rs => BaPortFec::Rs,
388408
},
389-
uplink_ip: config.uplink_ip.clone(),
409+
uplink_ip: config.uplink_ip,
390410
uplink_vid: config.uplink_vid,
391-
}
411+
})
392412
}

0 commit comments

Comments
 (0)