diff --git a/dpd-api/src/lib.rs b/dpd-api/src/lib.rs index 7441178e..8d1c3e8b 100644 --- a/dpd-api/src/lib.rs +++ b/dpd-api/src/lib.rs @@ -9,7 +9,7 @@ use std::collections::BTreeMap; use std::net::{Ipv4Addr, Ipv6Addr}; -use dpd_types_versions::{latest, v1, v4, v7}; +use dpd_types_versions::{latest, v1, v4, v7, v8}; use dropshot::{ EmptyScanParams, HttpError, HttpResponseCreated, HttpResponseDeleted, HttpResponseOk, HttpResponseUpdatedNoContent, PaginationParams, Path, @@ -29,6 +29,7 @@ api_versions!([ // | example for the next person. // v // (next_int, IDENT), + (14, MCAST_EXTERNAL_SCOPE_DOCS), (13, ALLOW_DDM_TRAFFIC), (12, PRBS_ERROR_TRACKING), (11, WALLCLOCK_HISTORY), @@ -1971,14 +1972,17 @@ pub trait DpdApi { /** * Create an external-only multicast group configuration. * - * External-only groups are used for IPv4 and non-admin-local IPv6 multicast - * traffic that doesn't require replication infrastructure. These groups use - * simple forwarding tables and require a NAT target. + * External-only groups are used for IPv4 and IPv6 multicast traffic that + * does not require replication infrastructure. Any admitted IPv6 scope may + * be used, admin-local included, except within the reserved underlay + * subnet ff04::/64, which belongs to the internal multicast API. + * + * These groups carry no direct members and require a NAT target. */ #[endpoint { method = POST, path = "/multicast/external-groups", - versions = VERSION_MCAST_STRICT_UNDERLAY.., + versions = VERSION_MCAST_EXTERNAL_SCOPE_DOCS.., }] async fn multicast_group_create_external( rqctx: RequestContext, @@ -1988,6 +1992,29 @@ pub trait DpdApi { HttpError, >; + /** + * Create an external-only multicast group configuration. + * + * External-only groups are used for IPv4 and non-admin-local IPv6 multicast + * traffic that doesn't require replication infrastructure. These groups use + * simple forwarding tables and require a NAT target. + */ + #[endpoint { + method = POST, + path = "/multicast/external-groups", + versions = VERSION_MCAST_STRICT_UNDERLAY..VERSION_MCAST_EXTERNAL_SCOPE_DOCS, + operation_id = "multicast_group_create_external", + }] + async fn multicast_group_create_external_v8( + rqctx: RequestContext, + group: TypedBody, + ) -> Result< + HttpResponseCreated, + HttpError, + > { + Self::multicast_group_create_external(rqctx, group).await + } + /// Create an external-only multicast group configuration. #[endpoint { method = POST, @@ -2002,7 +2029,7 @@ pub trait DpdApi { HttpResponseCreated, HttpError, > { - Self::multicast_group_create_external(rqctx, group) + Self::multicast_group_create_external_v8(rqctx, group) .await .map(|resp| resp.map(Into::into)) } @@ -2278,15 +2305,17 @@ pub trait DpdApi { /** * Update an external-only multicast group configuration for a given group IP address. * - * External-only groups are used for IPv4 and non-admin-local IPv6 multicast - * traffic that doesn't require replication infrastructure. + * External-only groups are used for IPv4 and IPv6 multicast traffic that + * does not require replication infrastructure. Any admitted IPv6 scope may + * be used, admin-local included, except within the reserved underlay + * subnet ff04::/64, which belongs to the internal multicast API. * * The `tag` query parameter must match the group's existing tag. */ #[endpoint { method = PUT, path = "/multicast/external-groups/{group_ip}", - versions = VERSION_MCAST_STRICT_UNDERLAY.., + versions = VERSION_MCAST_EXTERNAL_SCOPE_DOCS.., }] async fn multicast_group_update_external( rqctx: RequestContext, @@ -2298,6 +2327,32 @@ pub trait DpdApi { HttpError, >; + /** + * Update an external-only multicast group configuration for a given group IP address. + * + * External-only groups are used for IPv4 and non-admin-local IPv6 multicast + * traffic that doesn't require replication infrastructure. + * + * The `tag` query parameter must match the group's existing tag. + */ + #[endpoint { + method = PUT, + path = "/multicast/external-groups/{group_ip}", + versions = VERSION_MCAST_STRICT_UNDERLAY..VERSION_MCAST_EXTERNAL_SCOPE_DOCS, + operation_id = "multicast_group_update_external", + }] + async fn multicast_group_update_external_v8( + rqctx: RequestContext, + path: Path, + query: Query, + group: TypedBody, + ) -> Result< + HttpResponseOk, + HttpError, + > { + Self::multicast_group_update_external(rqctx, path, query, group).await + } + /** * Update an external-only multicast group configuration. * diff --git a/dpd/src/mcast/mod.rs b/dpd/src/mcast/mod.rs index 3ac32330..7b961945 100644 --- a/dpd/src/mcast/mod.rs +++ b/dpd/src/mcast/mod.rs @@ -37,16 +37,18 @@ //! The multicast implementation uses a bifurcated design that separates //! external (customer) and (internal) underlay traffic: //! -//! 1. External-only groups (IPv4 and non-admin-local IPv6): +//! 1. External-only groups (IPv4 and IPv6 outside the reserved underlay +//! subnet ff04::/64): //! - Created from API control plane IPs for customer traffic //! - Handle customer traffic to/from outside the rack //! - Use the external multicast API (/multicast/external-groups) //! - Must have NAT targets pointing to internal groups for proper forwarding //! -//! 2. Internal groups (admin-local IPv6 multicast): -//! - Admin-local = scope 4 (ff04::/16) as defined in -//! [RFC 7346] and [RFC 4291] -//! - Geneve encapsulated multicast traffic (NAT targets of external-only groups) +//! 2. Internal groups (underlay IPv6 multicast): +//! - Reserved underlay subnet ff04::/64, within admin-local scope-4 +//! (ff04::/16) as defined in [RFC 7346] and [RFC 4291] +//! - Geneve encapsulated multicast traffic (NAT targets of +//! external-only groups) //! - Use the internal multicast API (/multicast/underlay-groups) //! - Can replicate to: //! a) External group members (customer traffic) @@ -559,7 +561,8 @@ pub(crate) fn del_group( let mut mcast = s.mcast.lock().unwrap(); // Check if this is an internal group referenced by an external group. - // Internal groups are identified by admin-scoped IPv6 addresses (ff04::/16). + // Internal groups are identified by addresses in the reserved underlay + // subnet (i.e., ff04::/64). if let IpAddr::V6(ipv6) = group_ip && let Ok(admin_scoped) = UnderlayMulticastIpv6::new(ipv6) && let Some(external_ip) = mcast.nat_target_refs.get(&admin_scoped) diff --git a/openapi/dpd/dpd-13.0.0-5db8bd.json.gitstub b/openapi/dpd/dpd-13.0.0-5db8bd.json.gitstub new file mode 100644 index 00000000..c886f19e --- /dev/null +++ b/openapi/dpd/dpd-13.0.0-5db8bd.json.gitstub @@ -0,0 +1 @@ +2df9101e95757037f2cc4e31e8bf00fd9e79efc7:openapi/dpd/dpd-13.0.0-5db8bd.json diff --git a/openapi/dpd/dpd-13.0.0-5db8bd.json b/openapi/dpd/dpd-14.0.0-0e5ee0.json similarity index 99% rename from openapi/dpd/dpd-13.0.0-5db8bd.json rename to openapi/dpd/dpd-14.0.0-0e5ee0.json index 22694237..1be26aea 100644 --- a/openapi/dpd/dpd-13.0.0-5db8bd.json +++ b/openapi/dpd/dpd-14.0.0-0e5ee0.json @@ -7,7 +7,7 @@ "url": "https://oxide.computer", "email": "api@oxide.computer" }, - "version": "13.0.0" + "version": "14.0.0" }, "paths": { "/all-settings": { @@ -1249,7 +1249,7 @@ "/multicast/external-groups": { "post": { "summary": "Create an external-only multicast group configuration.", - "description": "External-only groups are used for IPv4 and non-admin-local IPv6 multicast traffic that doesn't require replication infrastructure. These groups use simple forwarding tables and require a NAT target.", + "description": "External-only groups are used for IPv4 and IPv6 multicast traffic that does not require replication infrastructure. Any admitted IPv6 scope may be used, admin-local included, except within the reserved underlay subnet ff04::/64, which belongs to the internal multicast API.\n\nThese groups carry no direct members and require a NAT target.", "operationId": "multicast_group_create_external", "requestBody": { "content": { @@ -1284,7 +1284,7 @@ "/multicast/external-groups/{group_ip}": { "put": { "summary": "Update an external-only multicast group configuration for a given group IP address.", - "description": "External-only groups are used for IPv4 and non-admin-local IPv6 multicast traffic that doesn't require replication infrastructure.\n\nThe `tag` query parameter must match the group's existing tag.", + "description": "External-only groups are used for IPv4 and IPv6 multicast traffic that does not require replication infrastructure. Any admitted IPv6 scope may be used, admin-local included, except within the reserved underlay subnet ff04::/64, which belongs to the internal multicast API.\n\nThe `tag` query parameter must match the group's existing tag.", "operationId": "multicast_group_update_external", "parameters": [ { diff --git a/openapi/dpd/dpd-latest.json b/openapi/dpd/dpd-latest.json index d1a5660e..ebaa5053 120000 --- a/openapi/dpd/dpd-latest.json +++ b/openapi/dpd/dpd-latest.json @@ -1 +1 @@ -dpd-13.0.0-5db8bd.json \ No newline at end of file +dpd-14.0.0-0e5ee0.json \ No newline at end of file