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
16 changes: 12 additions & 4 deletions ddm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ hyper.workspace = true
hyper-util.workspace = true
http-body-util.workspace = true
serde_json.workspace = true
libnet.workspace = true
dpd-client.workspace = true
opte-ioctl.workspace = true
oxide-vpc.workspace = true
sled.workspace = true
mg-common.workspace = true
chrono.workspace = true
Expand All @@ -35,3 +31,15 @@ oxnet.workspace = true
uuid.workspace = true
ddm-api.workspace = true
ddm-types.workspace = true

# illumos-only deps used by the routing state machine and platform sys layer.
# Gated by the `state-machine` feature so stub builds (e.g. Linux test
# fixtures running ddmd with `--no-state-machine`) link cleanly.
libnet = { workspace = true, optional = true }
dpd-client = { workspace = true, optional = true }
opte-ioctl = { workspace = true, optional = true }
oxide-vpc = { workspace = true, optional = true }

[features]
default = ["state-machine"]
state-machine = ["dep:libnet", "dep:dpd-client", "dep:opte-ioctl", "dep:oxide-vpc"]
16 changes: 8 additions & 8 deletions ddm/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ use ddm_types::exchange::PathVector;
use dropshot::ApiDescription;
use dropshot::ApiDescriptionBuildErrors;
use dropshot::ConfigDropshot;
use dropshot::ConfigLogging;
use dropshot::ConfigLoggingLevel;
use dropshot::HttpError;
use dropshot::HttpResponseOk;
use dropshot::HttpResponseUpdatedNoContent;
Expand All @@ -23,7 +21,7 @@ use dropshot::TypedBody;
use mg_common::lock;
use mg_common::net::TunnelOrigin;
use oxnet::Ipv6Net;
use slog::{Logger, error, info};
use slog::{Logger, error, info, o};
use std::collections::{HashMap, HashSet};
use std::net::{IpAddr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::sync::Arc;
Expand All @@ -35,6 +33,8 @@ use tokio::task::JoinHandle;

pub const DDM_STATS_PORT: u16 = 8001;

const UNIT_API_SERVER: &str = "api_server";

#[derive(Default)]
pub struct RouterStats {
pub originated_underlay_prefixes: AtomicU64,
Expand Down Expand Up @@ -68,11 +68,11 @@ pub fn handler(
..Default::default()
};

let ds_log = ConfigLogging::StderrTerminal {
level: ConfigLoggingLevel::Error,
}
.to_logger("admin")
.map_err(|e| e.to_string())?;
let ds_log = log.new(o!(
"component" => crate::COMPONENT_DDM,
"module" => crate::MOD_ADMIN,
"unit" => UNIT_API_SERVER,
));

let api = api_description().map_err(|e| e.to_string())?;

Expand Down
117 changes: 117 additions & 0 deletions ddm/src/discovery/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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/.

//! This module implements the ddm router discovery mechanisms. These
//! mechanisms are responsible for three primary things
//!
//! 1. Soliciting other routers through UDP/IPv6 link local multicast.
//! 2. Sending out router advertisements in response to solicitations.
//! 3. Continuously soliciting link-local at a configurable rate to keep
//! sessions alive and sending out notifications when peering arrangements
//! expire due to not getting a solicitation response within a configurable
//! time threshold.
//!
//! [`Version`] and [`DiscoveryError`] are platform-agnostic and stay in this
//! module so the state machine type definitions in [`crate::sm`] continue to
//! compile when the routing runtime is gated out (e.g. Linux test fixtures
//! running ddmd with `--no-state-machine`). The runtime helpers that drive
//! the protocol over UDPv6 sockets live in the [`runtime`] submodule and
//! are illumos-only.
//!
//! ## Protocol
//!
//! The general sequence of events is depicted in the following diagram.
//!
//! *==========* *==========*
//! | violin | | piano |
//! *==========* *==========*
//! | |
//! | solicit(ff02::dd) |
//! |-------------------------->|
//! | advertise(fe80::47) |
//! |<--------------------------|
//! | |
//! | ... |
//! | |
//! | |
//! | solicit(ff02::dd) |
//! |-------------------------->|
//! | advertise(fe80::47) |
//! |<--------------------------|
//! | |
//! | solicit(ff02::dd) |
//! |-------------------------->|
//! | solicit(ff02::dd) |
//! |-------------------------->|
//! | solicit(ff02::dd) |
//! |-------------------------->|
//! | |
//! +----| |
//! expire | | |
//! piano | | |
//! +--->| |
//!
//! This shows violin sending a link-local multicast solicitation over the wire.
//! That solicitation is received by piano and piano respons with an
//! advertisement to violin's link-local unicast address. From this point
//! forward solicitations and responses continue. Each time violin gets a
//! response from piano, it updates the last seen timestamp for piano. If at
//! some point piano stops responding to solicitations and the last seen
//! timestamp is older than the expiration threshold, violin will expire the
//! session and send out a notification to the ddm state machine that started
//! it. Violin will continue to send out solicitations in case piano comes back.
//!
//! In the event that piano undergoes renumbering e.g. it's link-local unicast
//! address changes, this will be detected by violin and an advertisement update
//! will be sent to the ddm state machine through the notification channel
//! provided to the discovery subsystem.
//!
//! The DDM discovery multicast address is ff02::dd. Discovery packets are sent
//! over UDP using port number 0xddd.
//!
//! ## Packets
//!
//! Discovery packets follow a very simple format
//!
//! 1 2 3
//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | version |S A r r r r r r| router kind | hostname len |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | hostname :
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! : .... :
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//!
//! The first byte indicates the version. The only valid version at present is
//! version 1. The second byte is a flags bitfield. The first position `S`
//! indicates a solicitation. The second position `A` indicates and
//! advertisement. All other positions are reserved for future use. The third
//! byte indicates the kind of router. Current values are 0 for a server router
//! and 1 for a transit routers. The fourth byte is a hostname length followed
//! directly by a hostname of up to 255 bytes in length.

use thiserror::Error;

#[cfg(all(feature = "state-machine", target_os = "illumos"))]
mod runtime;

#[cfg(all(feature = "state-machine", target_os = "illumos"))]
pub(crate) use runtime::handler;

#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum Version {
V2 = 2,
V3 = 3,
}

#[derive(Error, Debug)]
pub enum DiscoveryError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),

#[error("serialization error: {0}")]
Serialization(#[from] ispf::Error),
}
Loading