-
Notifications
You must be signed in to change notification settings - Fork 6
[multicast] DDM multicast exchange: V4 protocol, MRIB sync #696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
6d959de
[multicast] DDM multicast exchange: V4 protocol, MRIB sync, M2P hooks
zeeshanlakhani 86ec97f
[review] address PR review: doc comments, lock! cleanup
zeeshanlakhani b5e96c8
[review+arch-change] address PR review + revisit M2P/OPTE handling
zeeshanlakhani bf2a703
[api] add if_name to PeerInfo for sled-to-port mapping
zeeshanlakhani 6bd0e0e
merge upstream branch
zeeshanlakhani a3df7a0
[multicast|deps] remove mg-common dep from ddm-admin-client
zeeshanlakhani b92bbba
merge upstream branch
zeeshanlakhani 1002d01
merge zl/mrib
zeeshanlakhani bc74980
[mrib] sync rpf revalidation on unicast route removal
zeeshanlakhani 8c961bc
merge upstream branch
zeeshanlakhani 66f87ea
merge upstream branch
zeeshanlakhani 8949e82
[ddmd] add --no-state-machine flag for test fixtures and Linux build
zeeshanlakhani fab9beb
[ddm-api] PUT /peer for state-machine-bypassing peer injection
zeeshanlakhani f369dd7
[merge] merge upstream
zeeshanlakhani ea12d44
[merge + update] merge upstream branch
zeeshanlakhani 4d77f62
[merge] mrib into ddm-mcast
zeeshanlakhani 62c627c
merge upstream branch
zeeshanlakhani 60a5a24
mg-common: shared tfport name parser for qsfp and rear ports
zeeshanlakhani acb26b9
[refactor] mg-lower: extract `port_link_from_ifname` helper from dend…
zeeshanlakhani e506027
[design-change] ddm: program DPD multicast members in ddmd from impor…
zeeshanlakhani 5aaa7c9
[ddm] negotiate and exchange underlay multicast routes (post-merge)
zeeshanlakhani c49f755
[ddm] clarify --api-only peer-set docs, bind admin API synchronously
zeeshanlakhani 4cc5698
[merge] Merge upstream: includes MRIB/v12 update
zeeshanlakhani 398b4a7
[merge] merge main: dpd-client to latest multicast-e2e, Db error Conf…
zeeshanlakhani 37a0e2e
[merge] merge in parent
zeeshanlakhani 77ff902
[ddm] run the multicast sweep as a runtime task, add periodic exchang…
zeeshanlakhani 0969755
[ci] helios target
zeeshanlakhani c7dadcb
[merge] merge in parent
zeeshanlakhani faec135
[merge] merge in parent
zeeshanlakhani e4d2432
[deps] bump dendrite mcast pin to 72b200e5
zeeshanlakhani f74e746
[deps] update dendrite mcast pin to 0d48b5a3
zeeshanlakhani 4507e3a
[merge] merge main w/ mcast pins
zeeshanlakhani 5fedb5d
[ddm] bound V4 multicast exchange bodies
zeeshanlakhani e9fb532
[merge] merge upstream
zeeshanlakhani 96a2f15
[merge] merge upstream
zeeshanlakhani f0e5ad9
[client-common] record oxnet consolidation intent for vni and multica…
zeeshanlakhani 6b571a9
[merge] merge zl/mrib
zeeshanlakhani 36e4365
[merge] merge zl/mrib
zeeshanlakhani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,237 @@ | ||
| // 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/. | ||
|
|
||
| //! Validated underlay multicast address shared across the routing suite. | ||
| //! | ||
| //! Lives in the cycle-free leaf crate so the API-types crates consumed by | ||
| //! Omicron can share a single definition without depending on | ||
| //! `omicron_common`, which would form a dependency cycle. | ||
|
|
||
| // TODO: Consolidate these types into `oxnet`, the cycle-free leaf crate that | ||
| // maghemite, dendrite, and omicron already share, so the duplication can be | ||
| // removed. `dpd_types::mcast::UnderlayMulticastIpv6` in dendrite carries an | ||
| // independent copy today. | ||
|
|
||
| use crate::address::UNDERLAY_MULTICAST_SUBNET; | ||
| use schemars::JsonSchema; | ||
| use serde::{Deserialize, Serialize}; | ||
| use std::fmt; | ||
| use std::net::{IpAddr, Ipv6Addr}; | ||
| use std::str::FromStr; | ||
| use thiserror::Error; | ||
|
|
||
| /// Error constructing an [`UnderlayMulticastIpv6`] address. | ||
| #[derive(Debug, Clone, Error)] | ||
| pub enum UnderlayMulticastError { | ||
| /// The address is not within the underlay multicast subnet (ff04::/64). | ||
| #[error( | ||
| "underlay address {addr} is not within {UNDERLAY_MULTICAST_SUBNET}" | ||
| )] | ||
| NotInSubnet { addr: Ipv6Addr }, | ||
|
|
||
| /// The string could not be parsed as an IPv6 address. | ||
| #[error("invalid IPv6 address: {0}")] | ||
| InvalidIpv6(#[from] std::net::AddrParseError), | ||
| } | ||
|
|
||
| /// Error constructing an [`OverlayMulticast`] address. | ||
| #[derive(Debug, Clone, Error)] | ||
| pub enum OverlayMulticastError { | ||
| /// The address is not a multicast address. | ||
| #[error("overlay address {addr} is not a multicast address")] | ||
| NotMulticast { addr: IpAddr }, | ||
|
|
||
| /// The string could not be parsed as an IP address. | ||
| #[error("invalid IP address: {0}")] | ||
| InvalidIp(#[from] std::net::AddrParseError), | ||
| } | ||
|
|
||
| /// A validated overlay multicast group address (IPv4 or IPv6). | ||
| /// | ||
| /// The application-visible group an operator announces via DDM, e.g. | ||
| /// `233.252.0.1` or `ff0e::1`. The overlay group spans both address | ||
| /// families, so this type wraps [`IpAddr`] and enforces only that the | ||
| /// address is multicast: IPv4 `224.0.0.0/4` per [RFC 1112 §4] or IPv6 | ||
| /// `ff00::/8` per [RFC 4291 §2.7]. Its admin-local underlay mapping is the | ||
| /// separately validated [`UnderlayMulticastIpv6`]. The mapping is defined by | ||
| /// [RFD 488]. | ||
| /// | ||
| /// [RFC 1112 §4]: https://www.rfc-editor.org/rfc/rfc1112#section-4 | ||
| /// [RFC 4291 §2.7]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7 | ||
| /// [RFD 488]: https://rfd.shared.oxide.computer/rfd/488 | ||
| #[derive( | ||
| Debug, | ||
| Copy, | ||
| Clone, | ||
| Eq, | ||
| PartialEq, | ||
| PartialOrd, | ||
| Ord, | ||
| Hash, | ||
| Serialize, | ||
| Deserialize, | ||
| JsonSchema, | ||
| )] | ||
| #[serde(try_from = "IpAddr", into = "IpAddr")] | ||
| #[schemars(transparent)] | ||
| pub struct OverlayMulticast(IpAddr); | ||
|
|
||
| impl OverlayMulticast { | ||
| /// Create a new validated overlay multicast address. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns [`OverlayMulticastError::NotMulticast`] if the address is not | ||
| /// a multicast address. | ||
| pub fn new(value: IpAddr) -> Result<Self, OverlayMulticastError> { | ||
| if !value.is_multicast() { | ||
| return Err(OverlayMulticastError::NotMulticast { addr: value }); | ||
| } | ||
| Ok(Self(value)) | ||
| } | ||
|
|
||
| /// Return the underlying IP address. | ||
| #[inline] | ||
| pub const fn ip(&self) -> IpAddr { | ||
| self.0 | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for OverlayMulticast { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "{}", self.0) | ||
| } | ||
| } | ||
|
|
||
| impl TryFrom<IpAddr> for OverlayMulticast { | ||
| type Error = OverlayMulticastError; | ||
|
|
||
| fn try_from(value: IpAddr) -> Result<Self, Self::Error> { | ||
| Self::new(value) | ||
| } | ||
| } | ||
|
|
||
| impl From<OverlayMulticast> for IpAddr { | ||
| fn from(addr: OverlayMulticast) -> Self { | ||
| addr.0 | ||
| } | ||
| } | ||
|
|
||
| impl FromStr for OverlayMulticast { | ||
| type Err = OverlayMulticastError; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| let addr: IpAddr = s.parse()?; | ||
| Self::new(addr) | ||
| } | ||
| } | ||
|
|
||
| /// A validated underlay multicast IPv6 address within `ff04::/64`. | ||
| /// | ||
| /// The Oxide rack maps overlay multicast groups 1:1 to admin-local scoped | ||
| /// IPv6 multicast addresses in `UNDERLAY_MULTICAST_SUBNET` (`ff04::/64`, | ||
| /// admin-local scope per [RFC 4291 §2.7]). The mapping is defined by | ||
| /// [RFD 488]. This type enforces the subnet invariant at construction | ||
| /// time. | ||
| /// | ||
| /// [RFC 4291 §2.7]: https://www.rfc-editor.org/rfc/rfc4291#section-2.7 | ||
| /// [RFD 488]: https://rfd.shared.oxide.computer/rfd/488 | ||
| #[derive( | ||
| Debug, | ||
| Copy, | ||
| Clone, | ||
| Eq, | ||
| PartialEq, | ||
| PartialOrd, | ||
| Ord, | ||
| Hash, | ||
| Serialize, | ||
| Deserialize, | ||
| JsonSchema, | ||
| )] | ||
| #[serde(try_from = "Ipv6Addr", into = "Ipv6Addr")] | ||
| #[schemars(transparent)] | ||
| pub struct UnderlayMulticastIpv6(Ipv6Addr); | ||
|
|
||
| impl UnderlayMulticastIpv6 { | ||
| /// Create a new validated underlay multicast address. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns [`UnderlayMulticastError::NotInSubnet`] if the address is | ||
| /// not within `UNDERLAY_MULTICAST_SUBNET` (ff04::/64). | ||
| pub fn new(value: Ipv6Addr) -> Result<Self, UnderlayMulticastError> { | ||
| if !UNDERLAY_MULTICAST_SUBNET.contains(value) { | ||
| return Err(UnderlayMulticastError::NotInSubnet { addr: value }); | ||
| } | ||
| Ok(Self(value)) | ||
| } | ||
|
|
||
| /// Return the underlying IPv6 address. | ||
| #[inline] | ||
| pub const fn ip(&self) -> Ipv6Addr { | ||
| self.0 | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for UnderlayMulticastIpv6 { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| write!(f, "{}", self.0) | ||
| } | ||
| } | ||
|
|
||
| impl TryFrom<Ipv6Addr> for UnderlayMulticastIpv6 { | ||
| type Error = UnderlayMulticastError; | ||
|
|
||
| fn try_from(value: Ipv6Addr) -> Result<Self, Self::Error> { | ||
| Self::new(value) | ||
| } | ||
| } | ||
|
|
||
| impl From<UnderlayMulticastIpv6> for Ipv6Addr { | ||
| fn from(addr: UnderlayMulticastIpv6) -> Self { | ||
| addr.0 | ||
| } | ||
| } | ||
|
|
||
| impl From<UnderlayMulticastIpv6> for IpAddr { | ||
| fn from(addr: UnderlayMulticastIpv6) -> Self { | ||
| IpAddr::V6(addr.0) | ||
| } | ||
| } | ||
|
|
||
| impl FromStr for UnderlayMulticastIpv6 { | ||
| type Err = UnderlayMulticastError; | ||
|
|
||
| fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
| let addr: Ipv6Addr = s.parse()?; | ||
| Self::new(addr) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn overlay_serde_rejects_unicast() { | ||
| let json = | ||
| serde_json::to_string(&"192.0.2.1".parse::<IpAddr>().unwrap()) | ||
| .unwrap(); | ||
| let result: Result<OverlayMulticast, _> = serde_json::from_str(&json); | ||
| assert!(result.is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn underlay_serde_rejects_invalid() { | ||
| // ff0e::1 serialized as an Ipv6Addr, then deserialized as | ||
| // UnderlayMulticastIpv6 should fail via try_from. | ||
| let json = | ||
| serde_json::to_string(&Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 1)) | ||
| .unwrap(); | ||
| let result: Result<UnderlayMulticastIpv6, _> = | ||
| serde_json::from_str(&json); | ||
| assert!(result.is_err()); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.