Skip to content
Draft
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
39 changes: 23 additions & 16 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ oximeter = { git = "https://github.com/oxidecomputer/omicron", branch = "main"}
oximeter-producer = { git = "https://github.com/oxidecomputer/omicron", branch = "main"}
oxnet = { version = "0.1.4", 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 All @@ -142,11 +143,11 @@ natord = "1.0"

[workspace.dependencies.opte-ioctl]
git = "https://github.com/oxidecomputer/opte"
rev = "04c3d5d37d7b919cbf01019d2a17b93ff2df2eb8"
rev = "c570ac2126dbbebbd8e98e73b580c5be6b7e460e"

[workspace.dependencies.oxide-vpc]
git = "https://github.com/oxidecomputer/opte"
rev = "04c3d5d37d7b919cbf01019d2a17b93ff2df2eb8"
rev = "c570ac2126dbbebbd8e98e73b580c5be6b7e460e"

[workspace.dependencies.dpd-client]
git = "https://github.com/oxidecomputer/dendrite"
Expand Down
56 changes: 56 additions & 0 deletions mg-api/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/.

// Copyright 2026 Oxide Computer Company

use std::collections::HashMap;
use std::net::IpAddr;

Expand All @@ -17,6 +19,7 @@ use dropshot::{
};
use dropshot_api_manager_types::api_versions;
use mg_types_versions::{latest, v1, v2, v5};
use rdb::types::MulticastRoute;
use rdb::{BfdPeerConfig, Prefix};

api_versions!([
Expand All @@ -31,6 +34,7 @@ api_versions!([
// | example for the next person.
// v
// (next_int, IDENT),
(8, MULTICAST_SUPPORT),
(7, OPERATION_ID_CLEANUP),
(6, RIB_EXPORTED_STRING_KEY),
(5, UNNUMBERED),
Expand Down Expand Up @@ -622,4 +626,56 @@ pub trait MgAdminApi {
rqctx: RequestContext<Self::Context>,
request: Query<latest::ndp::NdpInterfaceSelector>,
) -> Result<HttpResponseOk<latest::ndp::NdpInterface>, HttpError>;

// MRIB: Multicast ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// Get imported multicast routes from `mrib_in`.
#[endpoint { method = GET, path = "/mrib/status/imported", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn get_mrib_imported(
rqctx: RequestContext<Self::Context>,
query: Query<latest::mrib::MribQuery>,
) -> Result<HttpResponseOk<Vec<MulticastRoute>>, HttpError>;

/// Get selected multicast routes from `mrib_loc` (RPF-validated).
#[endpoint { method = GET, path = "/mrib/status/selected", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn get_mrib_selected(
rqctx: RequestContext<Self::Context>,
query: Query<latest::mrib::MribQuery>,
) -> Result<HttpResponseOk<Vec<MulticastRoute>>, HttpError>;

/// Add static multicast routes.
#[endpoint { method = PUT, path = "/static/mroute", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn static_add_mcast_route(
rqctx: RequestContext<Self::Context>,
request: TypedBody<latest::mrib::MribAddStaticRequest>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

/// Remove static multicast routes.
#[endpoint { method = DELETE, path = "/static/mroute", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn static_remove_mcast_route(
rqctx: RequestContext<Self::Context>,
request: TypedBody<latest::mrib::MribDeleteStaticRequest>,
) -> Result<HttpResponseDeleted, HttpError>;

/// List all static multicast routes from persistence.
#[endpoint { method = GET, path = "/static/mroute", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn static_list_mcast_routes(
rqctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<Vec<MulticastRoute>>, HttpError>;

/// Get the RPF rebuild rate-limit interval.
#[endpoint { method = GET, path = "/mrib/config/rpf/rebuild-interval", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn read_mrib_rpf_rebuild_interval(
rqctx: RequestContext<Self::Context>,
) -> Result<
HttpResponseOk<latest::mrib::MribRpfRebuildIntervalResponse>,
HttpError,
>;

/// Set the RPF rebuild rate-limit interval.
#[endpoint { method = POST, path = "/mrib/config/rpf/rebuild-interval", versions = VERSION_MULTICAST_SUPPORT.. }]
async fn update_mrib_rpf_rebuild_interval(
rqctx: RequestContext<Self::Context>,
request: TypedBody<latest::mrib::MribRpfRebuildIntervalRequest>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;
}
3 changes: 3 additions & 0 deletions mg-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// 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

pub mod bfd;
pub mod bgp;
pub mod mrib;
pub mod ndp;
pub mod rib;
pub mod static_routes;
Expand Down
7 changes: 7 additions & 0 deletions mg-types/src/mrib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 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

pub use mg_types_versions::latest::mrib::*;
2 changes: 2 additions & 0 deletions mg-types/versions/src/impls/mod.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/.

// Copyright 2026 Oxide Computer Company

//! Functional code for the latest versions of types.

mod bgp;
Expand Down
6 changes: 6 additions & 0 deletions mg-types/versions/src/latest.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/.

// Copyright 2026 Oxide Computer Company

//! Re-exports of the latest versions of types.

pub mod bfd {
Expand All @@ -27,6 +29,10 @@ pub mod bgp {
pub use crate::v5::bgp::UnnumberedNeighborSelector;
}

pub mod mrib {
pub use crate::v8::mrib::*;
}

pub mod ndp {
pub use crate::v5::ndp::NdpInterface;
pub use crate::v5::ndp::NdpInterfaceSelector;
Expand Down
6 changes: 6 additions & 0 deletions mg-types/versions/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/.

// Copyright 2026 Oxide Computer Company

//! Versioned types for the Maghemite Admin API.
//!
//! # Adding a new API version
Expand Down Expand Up @@ -41,3 +43,7 @@ pub mod v3;
pub mod v4;
#[path = "unnumbered/mod.rs"]
pub mod v5;
// v6 (RIB_EXPORTED_STRING_KEY) and v7 (OPERATION_ID_CLEANUP) introduced
// no new types.
#[path = "multicast_support/mod.rs"]
pub mod v8;
12 changes: 12 additions & 0 deletions mg-types/versions/src/multicast_support/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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

//! Version `MULTICAST_SUPPORT` of the Maghemite Admin API.
//!
//! Added MRIB (Multicast Routing Information Base) support with static
//! multicast route management, RPF verification, and query endpoints.

pub mod mrib;
98 changes: 98 additions & 0 deletions mg-types/versions/src/multicast_support/mrib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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

//! MRIB (Multicast Routing Information Base) API types.

use std::net::IpAddr;

use rdb::types::{
AddressFamily, MulticastRouteKey, UnderlayMulticastIpv6, Vni,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// Input for adding static multicast routes.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct StaticMulticastRouteInput {
/// The multicast route key (S,G) or (*,G).
pub key: MulticastRouteKey,
/// Underlay multicast group address (ff04::/64).
pub underlay_group: UnderlayMulticastIpv6,
}

/// Request body for adding static multicast routes to the MRIB.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MribAddStaticRequest {
/// List of static multicast routes to add.
pub routes: Vec<StaticMulticastRouteInput>,
}

/// Request body for deleting static multicast routes from the MRIB.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MribDeleteStaticRequest {
/// List of route keys to delete.
pub keys: Vec<MulticastRouteKey>,
}

/// Response containing the current RPF rebuild interval.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MribRpfRebuildIntervalResponse {
/// Minimum interval between RPF cache rebuilds in milliseconds.
/// A value of 0 means rate-limiting is disabled.
pub interval_ms: u64,
}

/// Request body for setting the RPF rebuild interval.
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MribRpfRebuildIntervalRequest {
/// Minimum interval between RPF cache rebuilds in milliseconds.
/// A value of 0 disables rate-limiting.
/// Every unicast RIB change triggers an immediate poptrie rebuild.
pub interval_ms: u64,
}

/// Filter for multicast route origin.
#[derive(
Debug, Clone, Copy, Deserialize, Serialize, JsonSchema, PartialEq, Eq,
)]
#[serde(rename_all = "snake_case")]
pub enum RouteOriginFilter {
/// Static routes only (operator configured).
Static,
/// Dynamic routes only (learned via IGMP, MLD, etc.).
Dynamic,
}

/// Query parameters for MRIB routes.
///
/// When `group` is provided, looks up a specific route.
/// When `group` is omitted, lists all routes (with optional filters).
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct MribQuery {
/// Multicast group address. If provided, returns a specific route.
/// If omitted, returns all routes matching the filters.
#[serde(default)]
pub group: Option<IpAddr>,
/// Source address (`None` for (*,G) routes). Only used when `group`
/// is set.
#[serde(default)]
pub source: Option<IpAddr>,
/// VNI (defaults to 77 for fleet-scoped multicast). Only used when
/// `group` is set.
#[serde(default = "default_multicast_vni")]
pub vni: Vni,
/// Filter by address family. Only used when listing all routes.
#[serde(default)]
pub address_family: Option<AddressFamily>,
/// Filter by route origin ("static" or "dynamic").
/// Only used when listing all routes.
#[serde(default)]
pub route_origin: Option<RouteOriginFilter>,
}

fn default_multicast_vni() -> Vni {
Vni::DEFAULT_MULTICAST_VNI
}
Loading