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
125 changes: 93 additions & 32 deletions common/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ use std::{
sync::LazyLock,
};

pub const BOOTSTRAP_SUBNET_PREFIX: u8 = 40;
pub const AZ_PREFIX: u8 = 48;
pub const RACK_PREFIX: u8 = 56;
pub const SLED_PREFIX: u8 = 64;
pub const BOOTSTRAP_SUBNET_PREFIX_LENGTH: u8 = 40;
pub const AZ_PREFIX_LENGTH: u8 = 48;
pub const RACK_PREFIX_LENGTH: u8 = 56;
pub const SLED_PREFIX_LENGTH: u8 = 64;

/// Effective MTU for external-facing OPTE ports when jumbo frames have been
/// opted into. 500 bytes of headroom under the 9000 byte underlay MTU leaves
Expand Down Expand Up @@ -109,8 +109,10 @@ pub const IPV4_LINK_LOCAL_MULTICAST_SUBNET: Ipv4Net =
/// See [RFC 4291] for IPv6 addressing architecture.
///
/// [RFC 4291]: https://www.rfc-editor.org/rfc/rfc4291
pub const IPV6_MULTICAST_RANGE: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 8);
pub const IPV6_MULTICAST_RANGE: Ipv6Net = Ipv6Net::new_unchecked(
Ipv6Addr::new(IPV6_MULTICAST_PREFIX, 0, 0, 0, 0, 0, 0, 0),
8,
);

/// IPv6 multicast prefix (ff00::/8) mask/value for scope checking.
///
Expand Down Expand Up @@ -276,7 +278,7 @@ pub const VPC_SUBNET_IPV6_PREFIX_LENGTH: u8 = 64;
/// Minimum prefix size supported in IPv4 VPC Subnets.
///
/// NOTE: This is the minimum _prefix_, which sets the maximum subnet size.
pub const MIN_VPC_IPV4_SUBNET_PREFIX: u8 = 8;
pub const MIN_VPC_IPV4_SUBNET_PREFIX_LENGTH: u8 = 8;

/// The number of reserved addresses at the beginning of a subnet range.
pub const NUM_INITIAL_RESERVED_IP_ADDRESSES: usize = 5;
Expand All @@ -287,7 +289,7 @@ pub const NUM_INITIAL_RESERVED_IP_ADDRESSES: usize = 5;
/// like, and the broadcast address at the end of the subnet. This size provides
/// room for 2 ** 6 - 6 = 58 IP addresses, which seems like a reasonable size
/// for the smallest subnet that's still useful in many contexts.
pub const MAX_VPC_IPV4_SUBNET_PREFIX: u8 = 26;
pub const MAX_VPC_IPV4_SUBNET_PREFIX_LENGTH: u8 = 26;

// The number of ports available to an SNAT IP.
// Note that for static NAT, this value isn't used, and all ports are available.
Expand Down Expand Up @@ -315,14 +317,41 @@ pub const NUM_SOURCE_NAT_PORTS: u16 = 1 << 14;
// The specific values aren't deployment-specific as they are virtualized
// within OPTE.

// VPC IPv6 prefixes are three octet-pairs, so we need three `u16` constants to
// represent the service VPC subnet prefix of `fd77:e9d2:9cd9::/48`.
//
// These are used to construct the `SERVICE_VPC_IPV6_SUBNET` constant below,
// which represents the entire /48, and the various constants for the individual
// service /64s within this subnet.
//
// XXX(eliza): it would be nice to have a way to construct these subnets from
// the shared prefix octet-pairs that is a bit less gross than having to scream
// `SERVICE_VPC_IPV6_PREFIX_n` three times for each one, such as by taking the
// `SERVICE_VPC_IPV6_SUBNET` value and just setting the value of the 4th
// octet-pair for each service subnet, but I couldn't figure out a way to do
// that with the current `oxnet` API. At least this way, we aren't hard-coding
// the `u16` literals for each subnet...
const SERVICE_VPC_IPV6_PREFIX_1: u16 = 0xfd77;
const SERVICE_VPC_IPV6_PREFIX_2: u16 = 0xe9d2;
const SERVICE_VPC_IPV6_PREFIX_3: u16 = 0x9cd9;

/// The IPv6 prefix assigned to the built-in services VPC.
// The specific prefix here was randomly chosen from the expected VPC
// prefix range (`fd00::/48`). See `random_vpc_ipv6_prefix`.
// Furthermore, all the below *_OPTE_IPV6_SUBNET constants are
// /64's within this prefix.
pub static SERVICE_VPC_IPV6_PREFIX: LazyLock<Ipv6Net> = LazyLock::new(|| {
pub static SERVICE_VPC_IPV6_SUBNET: LazyLock<Ipv6Net> = LazyLock::new(|| {
Ipv6Net::new(
Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 0, 0, 0, 0, 0),
Ipv6Addr::new(
SERVICE_VPC_IPV6_PREFIX_1,
SERVICE_VPC_IPV6_PREFIX_2,
SERVICE_VPC_IPV6_PREFIX_3,
0,
0,
0,
0,
0,
),
VPC_IPV6_PREFIX_LENGTH,
)
.unwrap()
Expand All @@ -335,7 +364,16 @@ pub static DNS_OPTE_IPV4_SUBNET: LazyLock<Ipv4Net> =
/// The IPv6 subnet for External DNS OPTE ports.
pub static DNS_OPTE_IPV6_SUBNET: LazyLock<Ipv6Net> = LazyLock::new(|| {
Ipv6Net::new(
Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 1, 0, 0, 0, 0),
Ipv6Addr::new(
SERVICE_VPC_IPV6_PREFIX_1,
SERVICE_VPC_IPV6_PREFIX_2,
SERVICE_VPC_IPV6_PREFIX_3,
1,
0,
0,
0,
0,
),
VPC_SUBNET_IPV6_PREFIX_LENGTH,
)
.unwrap()
Expand All @@ -348,7 +386,16 @@ pub static NEXUS_OPTE_IPV4_SUBNET: LazyLock<Ipv4Net> =
/// The IPv6 subnet for Nexus OPTE ports.
pub static NEXUS_OPTE_IPV6_SUBNET: LazyLock<Ipv6Net> = LazyLock::new(|| {
Ipv6Net::new(
Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 2, 0, 0, 0, 0),
Ipv6Addr::new(
SERVICE_VPC_IPV6_PREFIX_1,
SERVICE_VPC_IPV6_PREFIX_2,
SERVICE_VPC_IPV6_PREFIX_3,
2,
0,
0,
0,
0,
),
VPC_SUBNET_IPV6_PREFIX_LENGTH,
)
.unwrap()
Expand All @@ -361,7 +408,16 @@ pub static NTP_OPTE_IPV4_SUBNET: LazyLock<Ipv4Net> =
/// The IPv6 subnet for Boundary NTP OPTE ports.
pub static NTP_OPTE_IPV6_SUBNET: LazyLock<Ipv6Net> = LazyLock::new(|| {
Ipv6Net::new(
Ipv6Addr::new(0xfd77, 0xe9d2, 0x9cd9, 3, 0, 0, 0, 0),
Ipv6Addr::new(
SERVICE_VPC_IPV6_PREFIX_1,
SERVICE_VPC_IPV6_PREFIX_2,
SERVICE_VPC_IPV6_PREFIX_3,
3,
0,
0,
0,
0,
),
VPC_SUBNET_IPV6_PREFIX_LENGTH,
)
.unwrap()
Expand Down Expand Up @@ -478,11 +534,11 @@ impl<'de, const N: u8> Deserialize<'de> for Ipv6Subnet<N> {
Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord,
)]
pub struct DnsSubnet {
subnet: Ipv6Subnet<SLED_PREFIX>,
subnet: Ipv6Subnet<SLED_PREFIX_LENGTH>,
}

impl DnsSubnet {
pub fn new(subnet: Ipv6Subnet<SLED_PREFIX>) -> Self {
pub fn new(subnet: Ipv6Subnet<SLED_PREFIX_LENGTH>) -> Self {
Self { subnet }
}

Expand All @@ -492,7 +548,7 @@ impl DnsSubnet {
}

/// Returns the DNS subnet.
pub fn subnet(&self) -> Ipv6Subnet<SLED_PREFIX> {
pub fn subnet(&self) -> Ipv6Subnet<SLED_PREFIX_LENGTH> {
self.subnet
}

Expand Down Expand Up @@ -520,17 +576,19 @@ impl DnsSubnet {
/// A wrapper around an IPv6 network, indicating it is a "reserved" rack
/// subnet which can be used for AZ-wide services.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ReservedRackSubnet(pub Ipv6Subnet<RACK_PREFIX>);
pub struct ReservedRackSubnet(pub Ipv6Subnet<RACK_PREFIX_LENGTH>);

impl ReservedRackSubnet {
/// Returns the subnet for the reserved rack subnet.
pub fn new(subnet: Ipv6Subnet<AZ_PREFIX>) -> Self {
ReservedRackSubnet(Ipv6Subnet::<RACK_PREFIX>::new(subnet.net().addr()))
pub fn new(subnet: Ipv6Subnet<AZ_PREFIX_LENGTH>) -> Self {
ReservedRackSubnet(Ipv6Subnet::<RACK_PREFIX_LENGTH>::new(
subnet.net().addr(),
))
}

/// Infer the reserved rack subnet from a sled/AZ/DNS subnet.
pub fn from_subnet<const N: u8>(subnet: Ipv6Subnet<N>) -> Self {
Self::new(Ipv6Subnet::<AZ_PREFIX>::new(subnet.net().addr()))
Self::new(Ipv6Subnet::<AZ_PREFIX_LENGTH>::new(subnet.net().addr()))
}

/// Returns the `index`th DNS subnet from this reserved rack subnet.
Expand All @@ -541,7 +599,7 @@ impl ReservedRackSubnet {
/// Returns the DNS addresses from this reserved rack subnet.
///
/// These addresses will come from the first [`INTERNAL_DNS_REDUNDANCY`]
/// `/64s` of the [`RACK_PREFIX`] subnet.
/// `/64s` of this reserved rack subnet.
pub fn get_dns_subnets(&self) -> Vec<DnsSubnet> {
(0..INTERNAL_DNS_REDUNDANCY)
.map(|idx| self.get_dns_subnet(u8::try_from(idx + 1).unwrap()))
Expand All @@ -552,7 +610,7 @@ impl ReservedRackSubnet {
/// Return the list of DNS servers for the rack, given any address in the AZ
/// subnet
pub fn get_internal_dns_server_addresses(addr: Ipv6Addr) -> Vec<IpAddr> {
let az_subnet = Ipv6Subnet::<AZ_PREFIX>::new(addr);
let az_subnet = Ipv6Subnet::<AZ_PREFIX_LENGTH>::new(addr);
ReservedRackSubnet::new(az_subnet)
.get_dns_subnets()
.iter()
Expand All @@ -565,18 +623,21 @@ const SWITCH_ZONE_ADDRESS_INDEX: usize = 2;

/// Return the sled agent address for a subnet.
///
/// This address will come from the first address of the [`SLED_PREFIX`] subnet.
pub fn get_sled_address(sled_subnet: Ipv6Subnet<SLED_PREFIX>) -> SocketAddrV6 {
/// This address will come from the first address of the provided `sled_subnet`.
pub fn get_sled_address(
sled_subnet: Ipv6Subnet<SLED_PREFIX_LENGTH>,
) -> SocketAddrV6 {
let sled_agent_ip =
sled_subnet.net().nth(SLED_AGENT_ADDRESS_INDEX as u128).unwrap();
SocketAddrV6::new(sled_agent_ip, SLED_AGENT_PORT, 0, 0)
}

/// Return the switch zone address for a subnet.
///
/// This address will come from the second address of the [`SLED_PREFIX`] subnet.
/// This address will come from the second address of the provided
/// `sled_subnet`.
pub fn get_switch_zone_address(
sled_subnet: Ipv6Subnet<SLED_PREFIX>,
sled_subnet: Ipv6Subnet<SLED_PREFIX_LENGTH>,
) -> Ipv6Addr {
sled_subnet.net().nth(SWITCH_ZONE_ADDRESS_INDEX as u128).unwrap()
}
Expand All @@ -585,14 +646,14 @@ pub fn get_switch_zone_address(
///
/// The subnet at index == 0 is used for rack-local services.
pub fn get_64_subnet(
rack_subnet: Ipv6Subnet<RACK_PREFIX>,
rack_subnet: Ipv6Subnet<RACK_PREFIX_LENGTH>,
index: u8,
) -> Ipv6Subnet<SLED_PREFIX> {
) -> Ipv6Subnet<SLED_PREFIX_LENGTH> {
let mut rack_network = rack_subnet.net().addr().octets();

// To set bits distinguishing the /64 from the /56, we modify the 7th octet.
rack_network[7] = index;
Ipv6Subnet::<SLED_PREFIX>::new(Ipv6Addr::from(rack_network))
Ipv6Subnet::<SLED_PREFIX_LENGTH>::new(Ipv6Addr::from(rack_network))
}

/// The IP address version.
Expand Down Expand Up @@ -1046,7 +1107,7 @@ mod test {

#[test]
fn test_dns_subnets() {
let subnet = Ipv6Subnet::<AZ_PREFIX>::new(
let subnet = Ipv6Subnet::<AZ_PREFIX_LENGTH>::new(
"fd00:1122:3344:0100::".parse::<Ipv6Addr>().unwrap(),
);
let rack_subnet = ReservedRackSubnet::new(subnet);
Expand Down Expand Up @@ -1075,15 +1136,15 @@ mod test {

#[test]
fn test_sled_address() {
let subnet = Ipv6Subnet::<SLED_PREFIX>::new(
let subnet = Ipv6Subnet::<SLED_PREFIX_LENGTH>::new(
"fd00:1122:3344:0101::".parse::<Ipv6Addr>().unwrap(),
);
assert_eq!(
"[fd00:1122:3344:0101::1]:12345".parse::<SocketAddrV6>().unwrap(),
get_sled_address(subnet)
);

let subnet = Ipv6Subnet::<SLED_PREFIX>::new(
let subnet = Ipv6Subnet::<SLED_PREFIX_LENGTH>::new(
"fd00:1122:3344:0308::".parse::<Ipv6Addr>().unwrap(),
);
assert_eq!(
Expand Down
11 changes: 6 additions & 5 deletions common/src/api/internal/shared/private_ip_config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

//! Shared private IP config types.

use crate::address::MAX_VPC_IPV4_SUBNET_PREFIX;
use crate::address::MIN_VPC_IPV4_SUBNET_PREFIX;
use crate::address::MAX_VPC_IPV4_SUBNET_PREFIX_LENGTH;
use crate::address::MIN_VPC_IPV4_SUBNET_PREFIX_LENGTH;
use crate::address::VPC_SUBNET_IPV6_PREFIX_LENGTH;
use crate::api::external;
use daft::Diffable;
Expand All @@ -29,7 +29,8 @@ pub enum PrivateIpConfigError {
IpNotInSubnet { subnet: IpNet, ip: IpAddr },
#[error(
"IPv4 subnet prefix /{prefix} must be between \
/{MIN_VPC_IPV4_SUBNET_PREFIX} and /{MAX_VPC_IPV4_SUBNET_PREFIX}"
/{MIN_VPC_IPV4_SUBNET_PREFIX_LENGTH} and \
/{MAX_VPC_IPV4_SUBNET_PREFIX_LENGTH}"
)]
InvalidIpv4NetworkPrefix { prefix: u8 },
#[error(
Expand Down Expand Up @@ -263,8 +264,8 @@ impl PrivateIpv4Config {
subnet: Ipv4Net,
transit_ips: Vec<Ipv4Net>,
) -> Result<Self, PrivateIpConfigError> {
if subnet.width() < MIN_VPC_IPV4_SUBNET_PREFIX
|| subnet.width() > MAX_VPC_IPV4_SUBNET_PREFIX
if subnet.width() < MIN_VPC_IPV4_SUBNET_PREFIX_LENGTH
|| subnet.width() > MAX_VPC_IPV4_SUBNET_PREFIX_LENGTH
{
return Err(PrivateIpConfigError::InvalidIpv4NetworkPrefix {
prefix: subnet.width(),
Expand Down
9 changes: 6 additions & 3 deletions illumos-utils/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use crate::{
output_to_exec_error,
};
use libc::ESRCH;
use omicron_common::address::{AZ_PREFIX, BOOTSTRAP_SUBNET_PREFIX, Ipv6Subnet};
use omicron_common::address::{
AZ_PREFIX_LENGTH, BOOTSTRAP_SUBNET_PREFIX_LENGTH, Ipv6Subnet,
};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use tokio::process::Command;

Expand All @@ -35,7 +37,8 @@ impl Route {
datalink: &str,
) -> Result<(), ExecutionError> {
// Route to the underlay AZ's /48 by deriving it from the gateway IP.
let underlay_az: Ipv6Subnet<AZ_PREFIX> = Ipv6Subnet::new(gateway);
let underlay_az: Ipv6Subnet<AZ_PREFIX_LENGTH> =
Ipv6Subnet::new(gateway);
let gateway = Gateway::Ipv6(gateway);
Self::ensure_route_with_gateway(
&underlay_az.to_string(),
Expand Down Expand Up @@ -149,7 +152,7 @@ impl Route {
}

pub async fn add_bootstrap_route(
bootstrap_prefix: Ipv6Subnet<BOOTSTRAP_SUBNET_PREFIX>,
bootstrap_prefix: Ipv6Subnet<BOOTSTRAP_SUBNET_PREFIX_LENGTH>,
gz_bootstrap_addr: Ipv6Addr,
zone_vnic_name: &str,
) -> Result<(), ExecutionError> {
Expand Down
5 changes: 3 additions & 2 deletions illumos-utils/src/running_zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use camino::{Utf8Path, Utf8PathBuf};
use camino_tempfile::Utf8TempDir;
use debug_ignore::DebugIgnore;
use ipnetwork::IpNetwork;
use omicron_common::address::{AZ_PREFIX, Ipv6Subnet};
use omicron_common::address::{AZ_PREFIX_LENGTH, Ipv6Subnet};
use omicron_common::backoff;
use omicron_common::resolvable_files::ResolvableFileSource;
use omicron_uuid_kinds::OmicronZoneUuid;
Expand Down Expand Up @@ -476,7 +476,8 @@ impl RunningZone {
gateway: Ipv6Addr,
) -> Result<(), RunCommandError> {
// Route to the underlay AZ's /48 by deriving it from the gateway IP.
let underlay_az: Ipv6Subnet<AZ_PREFIX> = Ipv6Subnet::new(gateway);
let underlay_az: Ipv6Subnet<AZ_PREFIX_LENGTH> =
Ipv6Subnet::new(gateway);
self.run_cmd([
ROUTE,
"add",
Expand Down
6 changes: 3 additions & 3 deletions illumos-utils/src/zone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::addrobj::AddrObject;
use crate::dladm::{EtherstubVnic, VNIC_PREFIX_BOOTSTRAP, VNIC_PREFIX_CONTROL};
use crate::zpool::PathInPool;
use crate::{PFEXEC, execute_async};
use omicron_common::address::SLED_PREFIX;
use omicron_common::address::SLED_PREFIX_LENGTH;
use omicron_uuid_kinds::OmicronZoneUuid;

const DLADM: &str = "/usr/sbin/dladm";
Expand Down Expand Up @@ -230,7 +230,7 @@ impl AddressRequest {
pub fn new_static(ip: IpAddr, prefix: Option<u8>) -> Self {
let prefix = prefix.unwrap_or_else(|| match ip {
IpAddr::V4(_) => 24,
IpAddr::V6(_) => SLED_PREFIX,
IpAddr::V6(_) => SLED_PREFIX_LENGTH,
});
let addr = IpNetwork::new(ip, prefix).unwrap();
AddressRequest::Static(addr)
Expand Down Expand Up @@ -894,7 +894,7 @@ impl Zones {
.map_err(|err| anyhow!(err))?,
AddressRequest::new_static(
IpAddr::V6(address),
Some(omicron_common::address::SLED_PREFIX),
Some(omicron_common::address::SLED_PREFIX_LENGTH),
),
)
.await
Expand Down
Loading
Loading