#[cfg(feature = "async")]
pub use net_lattice_async::EventStream;
pub use net_lattice_core::{Error, Id, PlatformErrorCode, Result};
pub use net_lattice_ip::{
Ipv4Address, Ipv4Network, Ipv4PrefixLength, Ipv6Address, Ipv6Network, Ipv6PrefixLength,
};
pub use net_lattice_model::dns::{DnsConfig, NewDnsConfig};
pub use net_lattice_model::event::{ChangeKind, Event, EventDomain, EventFilter};
pub use net_lattice_model::ifaddr::{InterfaceAddress, InterfaceAddressId, NewInterfaceAddress};
pub use net_lattice_model::interface::{
AdminState, Interface, InterfaceId, InterfaceKind, OperationalState,
};
pub use net_lattice_model::mac::MacAddress;
pub use net_lattice_model::mutation::{
Mutation, MutationConfirmation, MutationIdempotency, MutationKind, MutationOutcome,
MutationPlan, MutationPlanReport, MutationPrecondition, MutationPrivilege,
MutationReversibility, MutationSemantics, RollbackStatus,
};
pub use net_lattice_model::neighbor::{NeighborEntry, NeighborId, NeighborState};
pub use net_lattice_model::route::{Route, RouteId};
pub use net_lattice_model::{IpAddress, Network};
#[cfg(feature = "async")]
pub use net_lattice_platform::TokioEventProvider;
pub use net_lattice_platform::{
AddressMutator, AddressProvider, Capability, CapabilityProvider, DnsMutator, DnsProvider,
EventProvider, EventReceiver, InterfaceProvider, NeighborProvider, RouteProvider,
};
#[cfg(test)]
use std::sync::atomic::{AtomicBool, Ordering};
pub mod backend {
pub use crate::LatticeBackend;
pub use net_lattice_platform::{
AddressMutator, AddressProvider, CapabilityProvider, DnsMutator, DnsProvider,
EventProvider, EventReceiver, EventSender, InterfaceProvider, NeighborProvider,
RouteProvider,
};
#[cfg(feature = "async")]
pub use net_lattice_platform::{TokioEventProvider, TokioEventReceiver, TokioEventSender};
}
pub trait LatticeBackend:
RouteProvider<Route = Route>
+ InterfaceProvider<Interface = Interface>
+ DnsMutator<NewDnsConfig = NewDnsConfig, DnsConfig = DnsConfig>
+ NeighborProvider<NeighborEntry = NeighborEntry>
+ AddressProvider<InterfaceAddress = InterfaceAddress>
+ AddressMutator<NewInterfaceAddress = NewInterfaceAddress, InterfaceAddress = InterfaceAddress>
+ EventProvider<Event = Event, EventFilter = EventFilter>
+ CapabilityProvider
{
}
impl<B> LatticeBackend for B where
B: RouteProvider<Route = Route>
+ InterfaceProvider<Interface = Interface>
+ DnsMutator<NewDnsConfig = NewDnsConfig, DnsConfig = DnsConfig>
+ NeighborProvider<NeighborEntry = NeighborEntry>
+ AddressProvider<InterfaceAddress = InterfaceAddress>
+ AddressMutator<
NewInterfaceAddress = NewInterfaceAddress,
InterfaceAddress = InterfaceAddress,
> + EventProvider<Event = Event, EventFilter = EventFilter>
+ CapabilityProvider
{
}
pub struct Lattice<B: LatticeBackend> {
backend: B,
}
#[cfg(test)]
static FORCE_CONNECT_FAILURE: AtomicBool = AtomicBool::new(false);
impl<B: LatticeBackend> Lattice<B> {
pub fn routes(&self) -> Result<Vec<Route>> {
self.backend.routes()
}
pub fn add_route(&self, route: Route) -> Result<()> {
self.backend.add_route(route)
}
pub fn remove_route(&self, route: Route) -> Result<()> {
self.backend.remove_route(route)
}
pub fn interfaces(&self) -> Result<Vec<Interface>> {
self.backend.interfaces()
}
pub fn dns_config(&self) -> Result<DnsConfig> {
self.backend.dns_config()
}
pub fn set_dns_config(&self, config: NewDnsConfig) -> Result<DnsConfig> {
self.backend.set_dns_config(config)
}
pub fn neighbors(&self) -> Result<Vec<NeighborEntry>> {
self.backend.neighbors()
}
pub fn addresses(&self) -> Result<Vec<InterfaceAddress>> {
self.backend.addresses()
}
pub fn add_address(&self, address: NewInterfaceAddress) -> Result<InterfaceAddress> {
self.backend.add_address(address)
}
pub fn remove_address(&self, address: InterfaceAddress) -> Result<()> {
self.backend.remove_address(address)
}
pub fn capabilities(&self) -> Capability {
self.backend.capabilities()
}
pub fn supports(&self, capability: Capability) -> bool {
self.backend.capabilities().contains(capability)
}
pub fn watch(&self) -> Result<EventReceiver<Event>> {
self.ensure_monitoring()?;
self.backend.watch()
}
#[cfg(feature = "async")]
pub fn watch_async(&self, filter: EventFilter) -> Result<EventStream<Event>>
where
B: TokioEventProvider<Event = Event, EventFilter = EventFilter>,
{
self.ensure_monitoring()?;
Ok(net_lattice_async::from_tokio_receiver(
self.backend.watch_tokio(filter)?,
))
}
pub fn watch_filtered(&self, filter: EventFilter) -> Result<EventReceiver<Event>> {
self.ensure_monitoring()?;
self.backend.watch_filtered(filter)
}
fn ensure_monitoring(&self) -> Result<()> {
if self.supports(Capability::MONITORING) {
Ok(())
} else {
Err(Error::Unsupported)
}
}
}
#[cfg(target_os = "linux")]
impl Lattice<net_lattice_backend_linux::LinuxBackend> {
pub fn connect() -> Result<Self> {
#[cfg(test)]
if FORCE_CONNECT_FAILURE.swap(false, Ordering::SeqCst) {
return Err(Error::Unsupported);
}
Ok(Self {
backend: net_lattice_backend_linux::LinuxBackend::new()?,
})
}
}
#[cfg(target_os = "windows")]
impl Lattice<net_lattice_backend_windows::WindowsBackend> {
pub fn connect() -> Result<Self> {
#[cfg(test)]
if FORCE_CONNECT_FAILURE.swap(false, Ordering::SeqCst) {
return Err(Error::Unsupported);
}
Ok(Self {
backend: net_lattice_backend_windows::WindowsBackend::new()?,
})
}
}
#[cfg(target_os = "macos")]
impl Lattice<net_lattice_backend_darwin::DarwinBackend> {
pub fn connect() -> Result<Self> {
#[cfg(test)]
if FORCE_CONNECT_FAILURE.swap(false, Ordering::SeqCst) {
return Err(Error::Unsupported);
}
Ok(Self {
backend: net_lattice_backend_darwin::DarwinBackend::new()?,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestBackend {
capabilities: Capability,
fail_events: bool,
}
fn network() -> Network {
Network::from(Ipv4Network::new(
Ipv4Address::new(192, 0, 2, 0),
Ipv4PrefixLength::new(24).expect("valid prefix"),
))
}
fn route() -> Route {
Route::new(RouteId::new(1), network()).with_interface_index(1)
}
impl RouteProvider for TestBackend {
type Route = Route;
fn routes(&self) -> Result<Vec<Self::Route>> {
Ok(vec![route()])
}
fn add_route(&self, _route: Self::Route) -> Result<()> {
Ok(())
}
fn remove_route(&self, _route: Self::Route) -> Result<()> {
Ok(())
}
}
impl InterfaceProvider for TestBackend {
type Interface = Interface;
fn interfaces(&self) -> Result<Vec<Self::Interface>> {
Ok(vec![Interface::new(
InterfaceId::new(1),
1,
"test0",
InterfaceKind::Ethernet,
)])
}
}
impl DnsProvider for TestBackend {
type DnsConfig = DnsConfig;
fn dns_config(&self) -> Result<Self::DnsConfig> {
Ok(DnsConfig::new())
}
}
impl DnsMutator for TestBackend {
type NewDnsConfig = NewDnsConfig;
fn set_dns_config(&self, _config: Self::NewDnsConfig) -> Result<Self::DnsConfig> {
Ok(DnsConfig::new())
}
}
impl NeighborProvider for TestBackend {
type NeighborEntry = NeighborEntry;
fn neighbors(&self) -> Result<Vec<Self::NeighborEntry>> {
Ok(vec![NeighborEntry::new(
NeighborId::new(1),
1,
IpAddress::from(Ipv4Address::new(192, 0, 2, 1)),
)])
}
}
impl AddressProvider for TestBackend {
type InterfaceAddress = InterfaceAddress;
fn addresses(&self) -> Result<Vec<Self::InterfaceAddress>> {
Ok(vec![InterfaceAddress::new(
InterfaceAddressId::new(1),
1,
network(),
)])
}
}
impl AddressMutator for TestBackend {
type NewInterfaceAddress = NewInterfaceAddress;
type InterfaceAddress = InterfaceAddress;
fn add_address(
&self,
address: Self::NewInterfaceAddress,
) -> Result<Self::InterfaceAddress> {
Ok(InterfaceAddress::new(
InterfaceAddressId::new(1),
address.interface_id.value() as u32,
address.address,
))
}
fn remove_address(&self, _address: Self::InterfaceAddress) -> Result<()> {
Ok(())
}
}
impl CapabilityProvider for TestBackend {
fn capabilities(&self) -> Capability {
self.capabilities
}
}
impl EventProvider for TestBackend {
type Event = Event;
type EventFilter = EventFilter;
fn watch(&self) -> Result<EventReceiver<Self::Event>> {
self.watch_filtered(EventFilter::ALL)
}
fn watch_filtered(&self, filter: Self::EventFilter) -> Result<EventReceiver<Self::Event>> {
if self.fail_events {
return Err(Error::InvalidState);
}
let (sender, receiver) = EventReceiver::bounded();
let event = Event::Route {
id: RouteId::new(1),
kind: ChangeKind::Added,
};
if filter.matches(event) {
assert!(sender.send(event, Event::resync_all()));
Ok(receiver)
} else {
Ok(receiver.with_subscription(sender))
}
}
}
#[cfg(feature = "async")]
impl TokioEventProvider for TestBackend {
type Event = Event;
type EventFilter = EventFilter;
fn watch_tokio(
&self,
filter: Self::EventFilter,
) -> Result<net_lattice_platform::TokioEventReceiver<Self::Event>> {
if self.fail_events {
return Err(Error::InvalidState);
}
let (sender, receiver) = net_lattice_platform::TokioEventReceiver::bounded();
let event = Event::Route {
id: RouteId::new(1),
kind: ChangeKind::Added,
};
if filter.matches(event) {
assert!(sender.send(event, Event::resync_all));
Ok(receiver)
} else {
Ok(receiver.with_subscription(sender))
}
}
}
fn lattice(capabilities: Capability) -> Lattice<TestBackend> {
Lattice {
backend: TestBackend {
capabilities,
fail_events: false,
},
}
}
#[test]
fn facade_forwards_all_read_and_mutation_operations() {
let lattice = lattice(Capability::MONITORING | Capability::DNS_MUTATION);
let route = route();
let address = NewInterfaceAddress::new(InterfaceId::new(1), network());
assert_eq!(lattice.routes().expect("routes").len(), 1);
lattice.add_route(route.clone()).expect("add route");
lattice.remove_route(route).expect("remove route");
assert_eq!(lattice.interfaces().expect("interfaces").len(), 1);
assert_eq!(lattice.dns_config().expect("dns").nameservers.len(), 0);
assert_eq!(lattice.neighbors().expect("neighbors").len(), 1);
assert_eq!(lattice.addresses().expect("addresses").len(), 1);
let observed = lattice.add_address(address).expect("add address");
lattice.remove_address(observed).expect("remove address");
assert_eq!(
lattice
.set_dns_config(NewDnsConfig::new())
.expect("set DNS")
.search_domains
.len(),
0
);
}
#[test]
fn facade_enforces_monitoring_capability_and_forwards_filters() {
let unsupported = lattice(Capability::empty());
assert!(unsupported.watch().is_err());
assert!(unsupported.watch_filtered(EventFilter::ALL).is_err());
let lattice = lattice(Capability::MONITORING);
assert!(lattice.supports(Capability::MONITORING));
assert!(!lattice.supports(Capability::DNS_MUTATION));
assert!(lattice.capabilities().contains(Capability::MONITORING));
assert!(lattice.watch().expect("watch").recv().is_ok());
assert!(
lattice
.watch_filtered(EventFilter::none().route(RouteId::new(1)))
.expect("filtered watch")
.recv()
.is_ok()
);
assert!(
lattice
.watch_filtered(EventFilter::none())
.expect("empty filtered watch")
.try_recv()
.unwrap()
.is_none()
);
}
#[test]
fn facade_propagates_backend_watcher_errors() {
let lattice = Lattice {
backend: TestBackend {
capabilities: Capability::MONITORING,
fail_events: true,
},
};
assert!(lattice.watch().is_err());
assert!(lattice.watch_filtered(EventFilter::ALL).is_err());
}
#[cfg(feature = "async")]
#[test]
fn async_facade_propagates_native_watcher_errors() {
let lattice = Lattice {
backend: TestBackend {
capabilities: Capability::MONITORING,
fail_events: true,
},
};
assert!(lattice.watch_async(EventFilter::ALL).is_err());
}
#[cfg(feature = "async")]
#[test]
fn async_facade_enforces_monitoring_capability() {
let lattice = lattice(Capability::empty());
assert!(lattice.watch_async(EventFilter::ALL).is_err());
}
#[cfg(feature = "async")]
#[test]
fn async_facade_uses_the_backend_native_watcher_contract() {
use futures::{FutureExt, StreamExt};
futures::executor::block_on(async {
let lattice = lattice(Capability::MONITORING);
let mut events = lattice
.watch_async(EventFilter::none().route(RouteId::new(1)))
.expect("async watch");
assert!(events.next().await.is_some());
assert!(events.next().await.is_none());
let mut events = lattice
.watch_async(EventFilter::none())
.expect("empty async watch");
assert!(events.next().now_or_never().is_none());
});
}
#[test]
fn connect_uses_the_current_platform_backend() {
let _ = Lattice::connect();
}
#[test]
fn connect_propagates_backend_construction_error() {
FORCE_CONNECT_FAILURE.store(true, Ordering::SeqCst);
assert!(Lattice::connect().is_err());
}
}