Skip to content
Open
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
38 changes: 38 additions & 0 deletions .github/buildomat/jobs/linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@
#: series = "linux"
#: name = "mgadm.sha256.txt"
#: from_output = "/work/release/mgadm.sha256.txt"
#:
#: [[publish]]
#: series = "linux"
#: name = "ddmd"
#: from_output = "/work/release/ddmd"
#:
#: [[publish]]
#: series = "linux"
#: name = "ddmd.sha256.txt"
#: from_output = "/work/release/ddmd.sha256.txt"
#:
#: [[publish]]
#: series = "linux"
#: name = "ddmadm"
#: from_output = "/work/release/ddmadm"
#:
#: [[publish]]
#: series = "linux"
#: name = "ddmadm.sha256.txt"
#: from_output = "/work/release/ddmadm.sha256.txt"

set -o errexit
set -o pipefail
Expand Down Expand Up @@ -64,3 +84,21 @@ popd
cp target/debug/mgadm /work/debug
cp target/release/mgadm /work/release
digest /work/release/mgadm > /work/release/mgadm.sha256.txt

banner "ddmd"
pushd ddmd
cargo build --bin ddmd --no-default-features
cargo build --bin ddmd --no-default-features --release
popd
cp target/debug/ddmd /work/debug
cp target/release/ddmd /work/release
digest /work/release/ddmd > /work/release/ddmd.sha256.txt

banner "ddmadm"
pushd ddmadm
cargo build --bin ddmadm
cargo build --bin ddmadm --release
popd
cp target/debug/ddmadm /work/debug
cp target/release/ddmadm /work/release
digest /work/release/ddmadm > /work/release/ddmadm.sha256.txt
6 changes: 6 additions & 0 deletions Cargo.lock

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

36 changes: 36 additions & 0 deletions ddm-admin-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,39 @@ impl std::hash::Hash for types::TunnelOrigin {
self.metric.hash(state);
}
}

impl std::cmp::PartialEq for types::Vni {
fn eq(&self, other: &Self) -> bool {
self.0.eq(&other.0)
}
}

impl std::cmp::Eq for types::Vni {}

impl std::hash::Hash for types::Vni {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}

impl std::cmp::PartialEq for types::MulticastOrigin {
fn eq(&self, other: &Self) -> bool {
self.overlay_group.eq(&other.overlay_group)
&& self.underlay_group.eq(&other.underlay_group)
&& self.vni.eq(&other.vni)
&& self.source.eq(&other.source)
}
}

impl std::cmp::Eq for types::MulticastOrigin {}

/// Metric is excluded from identity so that metric changes update
/// an existing entry rather than creating a duplicate.
impl std::hash::Hash for types::MulticastOrigin {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.overlay_group.hash(state);
self.underlay_group.hash(state);
self.vni.hash(state);
self.source.hash(state);
}
}
75 changes: 73 additions & 2 deletions ddm-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use ddm_types_versions::latest;
use ddm_types_versions::v1;
use dropshot::HttpError;
use dropshot::HttpResponseOk;
use dropshot::HttpResponseUpdatedNoContent;
use dropshot::Path;
use dropshot::RequestContext;
use dropshot::TypedBody;
use dropshot_api_manager_types::api_versions;
use mg_common::net::TunnelOrigin;
use mg_common::net::{MulticastOrigin, TunnelOrigin};
use oxnet::Ipv6Net;
use std::collections::{HashMap, HashSet};

Expand All @@ -26,6 +27,7 @@ api_versions!([
// | example for the next person.
// v
// (next_int, IDENT),
(2, MULTICAST_SUPPORT),
(1, INITIAL),
]);

Expand All @@ -45,17 +47,48 @@ api_versions!([
pub trait DdmAdminApi {
type Context;

#[endpoint { method = GET, path = "/peers" }]
#[endpoint {
method = GET,
path = "/peers",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn get_peers(
ctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<HashMap<u32, latest::db::PeerInfo>>, HttpError>;

/// Returns peers without interface name information.
#[endpoint {
method = GET,
path = "/peers",
versions = ..VERSION_MULTICAST_SUPPORT
}]
async fn get_peers_v1(
ctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<HashMap<u32, v1::db::PeerInfo>>, HttpError>;

#[endpoint { method = DELETE, path = "/peers/{addr}" }]
async fn expire_peer(
ctx: RequestContext<Self::Context>,
params: Path<latest::admin::ExpirePathParams>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

/// Set peer information for a given interface index, bypassing the state machine.
///
/// Intended for test fixtures that run `ddmd` with `--no-state-machine`.
/// In a normal run, discovery writes peer entries keyed by interface
/// index whenever it processes an advertisement, so any directly-injected
/// entry for an active interface will be overwritten the next time a
/// peer is observed there.
#[endpoint {
method = PUT,
path = "/peer",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn put_peer(
ctx: RequestContext<Self::Context>,
request: TypedBody<latest::admin::PutPeerRequest>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

#[endpoint { method = GET, path = "/originated" }]
async fn get_originated(
ctx: RequestContext<Self::Context>,
Expand Down Expand Up @@ -100,6 +133,44 @@ pub trait DdmAdminApi {
request: TypedBody<HashSet<TunnelOrigin>>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

#[endpoint {
method = GET,
path = "/originated_multicast_groups",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn get_originated_multicast_groups(
ctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<HashSet<MulticastOrigin>>, HttpError>;

#[endpoint {
method = GET,
path = "/multicast_groups",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn get_multicast_groups(
ctx: RequestContext<Self::Context>,
) -> Result<HttpResponseOk<HashSet<latest::db::MulticastRoute>>, HttpError>;

#[endpoint {
method = PUT,
path = "/multicast_group",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn advertise_multicast_groups(
ctx: RequestContext<Self::Context>,
request: TypedBody<HashSet<MulticastOrigin>>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

#[endpoint {
method = DELETE,
path = "/multicast_group",
versions = VERSION_MULTICAST_SUPPORT..
}]
async fn withdraw_multicast_groups(
ctx: RequestContext<Self::Context>,
request: TypedBody<HashSet<MulticastOrigin>>,
) -> Result<HttpResponseUpdatedNoContent, HttpError>;

#[endpoint { method = PUT, path = "/sync" }]
async fn sync(
ctx: RequestContext<Self::Context>,
Expand Down
6 changes: 5 additions & 1 deletion ddm-types/versions/src/latest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ pub mod admin {
pub use crate::v1::admin::EnableStatsRequest;
pub use crate::v1::admin::ExpirePathParams;
pub use crate::v1::admin::PrefixMap;
pub use crate::v2::admin::PutPeerRequest;
}

pub mod db {
pub use crate::v1::db::PeerInfo;
pub use crate::v1::db::PeerStatus;
pub use crate::v1::db::RouterKind;
pub use crate::v1::db::TunnelRoute;
pub use crate::v2::db::MulticastRoute;
pub use crate::v2::db::PeerInfo;
}

pub mod exchange {
pub use crate::v1::exchange::PathVector;
pub use crate::v1::exchange::PathVectorV2;
pub use crate::v2::exchange::MulticastPathHop;
pub use crate::v2::exchange::MulticastPathVector;
}
2 changes: 2 additions & 0 deletions ddm-types/versions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@
pub mod latest;
#[path = "initial/mod.rs"]
pub mod v1;
#[path = "multicast_support/mod.rs"]
pub mod v2;
16 changes: 16 additions & 0 deletions ddm-types/versions/src/multicast_support/admin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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/.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::db::PeerInfo;

/// Body for `PUT /peer`. Sets `info` at the slot keyed by `if_index`
/// (interface index) in the in-memory peer map.
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
pub struct PutPeerRequest {
pub if_index: u32,
pub info: PeerInfo,
}
89 changes: 89 additions & 0 deletions ddm-types/versions/src/multicast_support/db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// 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/.

use std::net::Ipv6Addr;

use mg_common::net::MulticastOrigin;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::v1::db::{PeerStatus, RouterKind};
use crate::v2::exchange::MulticastPathHop;

/// A multicast route learned via DDM.
///
/// Carries a MulticastOrigin (overlay group + ff04::/64 underlay
/// mapping) and the path vector from the originating subscriber
/// through intermediate transit routers.
// The path enables loop detection and (in multi-rack topologies)
// replication optimizations (RFD 488) in the future.
//
// Equality and hashing consider only `origin` and `nexthop` so that
// a route update with a longer path replaces the existing entry in
// hash-based collections.
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct MulticastRoute {
/// The multicast group origin information.
pub origin: MulticastOrigin,

/// Underlay nexthop address (DDM peer that advertised this route).
/// Used to associate the route with a peer for expiration.
pub nexthop: Ipv6Addr,

/// Path vector from the originating subscriber outward.
/// Each hop records the router that redistributed this
/// subscription announcement. Used for loop detection on pull
/// and for future replication optimization in multi-rack
/// topologies.
#[serde(default)]
pub path: Vec<MulticastPathHop>,
}

impl PartialEq for MulticastRoute {
fn eq(&self, other: &Self) -> bool {
self.origin == other.origin && self.nexthop == other.nexthop
}
}

impl Eq for MulticastRoute {}

impl std::hash::Hash for MulticastRoute {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.origin.hash(state);
self.nexthop.hash(state);
}
}

impl From<MulticastRoute> for MulticastOrigin {
fn from(x: MulticastRoute) -> Self {
x.origin
}
}

/// Peer information with an optional interface name.
///
// Adds the `if_name` field to identify which underlay interface the peer
// was discovered on.
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
pub struct PeerInfo {
pub status: PeerStatus,
pub addr: Ipv6Addr,
pub host: String,
pub kind: RouterKind,
/// Interface name the peer was discovered on (e.g., "tfportrear0_0").
#[serde(default)]
pub if_name: Option<String>,
}

/// Downconvert v2 PeerInfo to v1 PeerInfo by dropping `if_name`.
impl From<PeerInfo> for crate::v1::db::PeerInfo {
fn from(p: PeerInfo) -> Self {
Self {
status: p.status,
addr: p.addr,
host: p.host,
kind: p.kind,
}
}
}
Loading