Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ dropshot-api-manager = "0.7.2"
dropshot-api-manager-types = "0.7.2"
expectorate = "1.2.0"
schemars = { version = "0.8.22", features = [ "uuid1", "chrono" ] }
tokio = { version = "1.49", features = ["full"] }
tokio = { version = "1.52.1", features = ["full"] }
serde_repr = "0.1"
anyhow = "1.0.103"
port-file = "0.1.0"
Expand Down Expand Up @@ -132,6 +132,7 @@ oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main"}
oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", branch = "main"}
oxnet = { version = "0.1.6", default-features = false, features = ["schemars", "serde"] }
omicron-common = { git = "https://github.com/oxidecomputer/omicron", branch = "main"}
poptrie = { git = "https://github.com/oxidecomputer/poptrie", branch = "main" }
gateway-client = { git = "https://github.com/oxidecomputer/omicron", branch = "main" }
uuid = { version = "1.21", features = ["serde", "v4"] }
smf = { git = "https://github.com/illumos/smf-rs", branch = "main" }
Expand Down
6 changes: 6 additions & 0 deletions client-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,9 @@
name = "client-common"
version = "0.1.0"
edition = "2024"

[dependencies]
oxnet.workspace = true

[dev-dependencies]
omicron-common.workspace = true
143 changes: 143 additions & 0 deletions client-common/src/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

// Copyright 2026 Oxide Computer Company

//! Multicast addressing constants shared across the routing suite.
//!
//! These mirror the canonical definitions in `omicron_common::address`.
//! They are duplicated here so the client and API-types crates consumed by
//! Omicron remain free of an Omicron dependency, which would otherwise form a
//! dependency cycle. These constants must be reachable at compile time from the
//! Omicron-free API-types crate because its newtypes validate addresses at
//! deserialization via `#[serde(try_from)]`.
//!
//! References: [RFC 4291] (IPv6 addressing), [RFC 4607] (SSM),
//! [RFC 5771] (IPv4 multicast), [RFC 7346] (IPv6 multicast scopes).
//!
//! [RFC 4291]: https://www.rfc-editor.org/rfc/rfc4291
//! [RFC 4607]: https://www.rfc-editor.org/rfc/rfc4607
//! [RFC 5771]: https://www.rfc-editor.org/rfc/rfc5771
//! [RFC 7346]: https://www.rfc-editor.org/rfc/rfc7346

use oxnet::{Ipv4Net, Ipv6Net};
use std::net::{Ipv4Addr, Ipv6Addr};

// TODO: Consolidate these constants and the `omicron_common::address`
// originals into `oxnet`, the cycle-free leaf crate that maghemite, dendrite,
// and omicron already share, so the duplication can be removed.

/// IPv4 Source-Specific Multicast (SSM) subnet (232.0.0.0/8) per RFC 4607 §3.
pub const IPV4_SSM_SUBNET: Ipv4Net =
Ipv4Net::new_unchecked(Ipv4Addr::new(232, 0, 0, 0), 8);

/// IPv6 Source-Specific Multicast (SSM) subnet.
///
/// RFC 4607 §3 specifies ff3x::/32, where the `x` nibble is the multicast
/// scope. We use /12 as an implementation convenience matching all per-scope
/// blocks (ff30:: through ff3f:ffff:..:ffff) with a single subnet, since all
/// SSM addresses share the first 12 bits (0xff prefix plus flag field 3).
/// This superset is used only for contains-based classification, not as an
/// allocation boundary.
pub const IPV6_SSM_SUBNET: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff30, 0, 0, 0, 0, 0, 0, 0), 12);

/// IPv4 multicast address range (224.0.0.0/4) per RFC 5771.
pub const IPV4_MULTICAST_RANGE: Ipv4Net =
Ipv4Net::new_unchecked(Ipv4Addr::new(224, 0, 0, 0), 4);

/// IPv4 link-local multicast subnet (224.0.0.0/24) per RFC 5771 §4.
///
/// Reserved for local network control protocols and not routed beyond the
/// local link.
pub const IPV4_LINK_LOCAL_MULTICAST_SUBNET: Ipv4Net =
Ipv4Net::new_unchecked(Ipv4Addr::new(224, 0, 0, 0), 24);

/// IPv6 multicast address range (ff00::/8) per RFC 4291.
pub const IPV6_MULTICAST_RANGE: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 8);

/// IPv6 multicast prefix (ff00::/8) value for scope checking per RFC 4291 §2.7.
pub const IPV6_MULTICAST_PREFIX: u16 = 0xff00;

/// Admin-local IPv6 multicast prefix (ff04::/16) as a u16 for address
/// construction and normalization of underlay multicast addresses.
///
/// See RFC 4291 §2.7 and RFC 7346 for the multicast address format and scope
/// definitions.
pub const IPV6_ADMIN_SCOPED_MULTICAST_PREFIX: u16 = 0xff04;

/// Fixed underlay admin-local IPv6 multicast subnet (ff04::/64).
///
/// Admin-local scope (4) is the smallest scope that must be administratively
/// configured per RFC 7346. The Oxide rack maps overlay multicast groups 1:1
/// into this /64.
pub const UNDERLAY_MULTICAST_SUBNET: Ipv6Net = Ipv6Net::new_unchecked(
Ipv6Addr::new(IPV6_ADMIN_SCOPED_MULTICAST_PREFIX, 0, 0, 0, 0, 0, 0, 0),
64,
);

/// IPv6 interface-local multicast subnet (ff01::/16) per RFC 4291 §2.7.
///
/// Not routable.
pub const IPV6_INTERFACE_LOCAL_MULTICAST_SUBNET: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff01, 0, 0, 0, 0, 0, 0, 0), 16);

/// IPv6 link-local multicast subnet (ff02::/16) per RFC 4291 §2.7.
///
/// Not routable beyond the local link.
pub const IPV6_LINK_LOCAL_MULTICAST_SUBNET: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 0), 16);

/// IPv6 reserved-scope multicast subnet (ff00::/16) per RFC 4291 §2.7.
///
/// Scope 0 is reserved. Packets with this scope must not be originated and
/// must be silently dropped if received.
pub const IPV6_RESERVED_SCOPE_MULTICAST_SUBNET: Ipv6Net =
Ipv6Net::new_unchecked(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 16);

#[cfg(test)]
mod tests {
use omicron_common::address as canonical;

use super::*;

/// Assert each local constant equals its `omicron_common::address`
/// original so the copies cannot drift from the source of truth.
///
/// `omicron_common` is a dev-dependency only, so it does not appear in the
/// normal dependency tree the no-omicron CI check inspects.
#[test]
fn constants_match_canonical_values() {
assert_eq!(IPV4_SSM_SUBNET, canonical::IPV4_SSM_SUBNET);
assert_eq!(IPV6_SSM_SUBNET, canonical::IPV6_SSM_SUBNET);
assert_eq!(IPV4_MULTICAST_RANGE, canonical::IPV4_MULTICAST_RANGE);
assert_eq!(
IPV4_LINK_LOCAL_MULTICAST_SUBNET,
canonical::IPV4_LINK_LOCAL_MULTICAST_SUBNET
);
assert_eq!(IPV6_MULTICAST_RANGE, canonical::IPV6_MULTICAST_RANGE);
assert_eq!(IPV6_MULTICAST_PREFIX, canonical::IPV6_MULTICAST_PREFIX);
assert_eq!(
IPV6_ADMIN_SCOPED_MULTICAST_PREFIX,
canonical::IPV6_ADMIN_SCOPED_MULTICAST_PREFIX
);
assert_eq!(
UNDERLAY_MULTICAST_SUBNET,
canonical::UNDERLAY_MULTICAST_SUBNET
);
assert_eq!(
IPV6_INTERFACE_LOCAL_MULTICAST_SUBNET,
canonical::IPV6_INTERFACE_LOCAL_MULTICAST_SUBNET
);
assert_eq!(
IPV6_LINK_LOCAL_MULTICAST_SUBNET,
canonical::IPV6_LINK_LOCAL_MULTICAST_SUBNET
);
assert_eq!(
IPV6_RESERVED_SCOPE_MULTICAST_SUBNET,
canonical::IPV6_RESERVED_SCOPE_MULTICAST_SUBNET
);
}
}
2 changes: 2 additions & 0 deletions client-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

pub mod address;

/// Like `println!`, but silently exits on broken pipe (EPIPE) instead of
/// panicking. Other I/O errors still panic.
#[macro_export]
Expand Down
1 change: 1 addition & 0 deletions mg-api-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ mg-api-types-versions.workspace = true

[features]
clap = ["mg-api-types-versions/clap"]
proptest = ["mg-api-types-versions/proptest"]
1 change: 1 addition & 0 deletions mg-api-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

pub mod bfd;
pub mod bgp;
pub mod mrib;
pub mod ndp;
pub mod rdb;
pub mod rib;
Expand Down
8 changes: 8 additions & 0 deletions mg-api-types/src/mrib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

pub use mg_api_types_versions::latest::mrib::*;

#[cfg(feature = "proptest")]
pub use mg_api_types_versions::proptest::mrib::*;
5 changes: 5 additions & 0 deletions mg-api-types/versions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@ edition = "2024"
[dependencies]
chrono.workspace = true
clap = { workspace = true, optional = true }
client-common.workspace = true
nom.workspace = true
num_enum.workspace = true
oxnet.workspace = true
proptest = { workspace = true, optional = true }
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
slog.workspace = true
thiserror.workspace = true
uuid.workspace = true

[dev-dependencies]
omicron-common.workspace = true
serde_json.workspace = true

[features]
clap = ["dep:clap"]
proptest = ["dep:proptest"]
2 changes: 2 additions & 0 deletions mg-api-types/versions/src/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
//! Functional code for the latest versions of types.

pub(crate) mod bgp;
#[cfg(feature = "proptest")]
pub mod mrib;
pub(crate) mod rdb;
Loading