-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add path type with metadata and basic daemon requests (#24)
* feat(daemon): add basic types and requests * refactor: address review comments * refactor: opaque PathParseError * refactor: reorganize path module * feat: use StandardPath within Path * feat: check lengths of path-metadata vectors * test(paths): extend and improve tests * refactor(path): minor reorganization and some tests * fix: address review comments * refactor: use Paths iterator type
- Loading branch information
Showing
16 changed files
with
950 additions
and
58 deletions.
There are no files selected for viewing
This file contains 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 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 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 |
---|---|---|
@@ -1,2 +1,5 @@ | ||
mod types; | ||
pub use types::{AsInfo, PathRequestFlags}; | ||
mod messages; | ||
pub use messages::{AsInfo, PathRequest}; | ||
|
||
mod client; | ||
pub use client::{DaemonClient, DaemonClientError}; |
This file contains 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,112 @@ | ||
use scion_grpc::daemon::{v1 as daemon_grpc, v1::daemon_service_client::DaemonServiceClient}; | ||
use thiserror::Error; | ||
use tonic::transport::Channel; | ||
use tracing::warn; | ||
|
||
use super::{messages::PathRequest, AsInfo}; | ||
use crate::{address::IsdAsn, packet::ByEndpoint, path::Path}; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum DaemonClientError { | ||
#[error("A communication error occurred: {0}")] | ||
ConnectionError(#[from] tonic::transport::Error), | ||
#[error("A gRPC error occurred: {0}")] | ||
GrpcError(#[from] tonic::Status), | ||
#[error("Response contained invalid data")] | ||
InvalidData, | ||
} | ||
|
||
/// A service to communicate with the local SCION daemon | ||
#[derive(Clone, Debug)] | ||
pub struct DaemonClient { | ||
connection: Channel, | ||
local_isd_asn: IsdAsn, | ||
} | ||
|
||
impl DaemonClient { | ||
/// Create a new client to communicate with the SCION daemon located at `address` | ||
/// | ||
/// This attempts to connect to the daemon directly and fetch the local ISD-ASN. | ||
/// | ||
/// Errors: | ||
/// | ||
/// This returns an error, if any error occurs during the connection setup or during the request | ||
/// for the AS info. | ||
pub async fn connect(address: &str) -> Result<Self, DaemonClientError> { | ||
let mut client = Self { | ||
connection: tonic::transport::Endpoint::new(address.to_string())? | ||
.connect() | ||
.await?, | ||
local_isd_asn: IsdAsn::WILDCARD, | ||
}; | ||
client.local_isd_asn = client.as_info(IsdAsn::WILDCARD).await?.isd_asn; | ||
|
||
Ok(client) | ||
} | ||
|
||
/// Request information about an AS; [`IsdAsn::WILDCARD`] can be used to obtain information | ||
/// about the local AS. | ||
pub async fn as_info(&self, isd_asn: IsdAsn) -> Result<AsInfo, DaemonClientError> { | ||
self.client() | ||
.r#as(daemon_grpc::AsRequest::from(isd_asn)) | ||
.await? | ||
.into_inner() | ||
.try_into() | ||
.map_err(|_| DaemonClientError::InvalidData) | ||
} | ||
|
||
/// Request a set of end-to-end paths between the source and destination AS | ||
pub async fn paths(&self, request: &PathRequest) -> Result<Paths, DaemonClientError> { | ||
let src_isd_asn = if request.source.is_wildcard() { | ||
self.local_isd_asn | ||
} else { | ||
request.source | ||
}; | ||
let isd_asn = ByEndpoint { | ||
source: src_isd_asn, | ||
destination: request.destination, | ||
}; | ||
Ok(Paths { | ||
isd_asn, | ||
grpc_paths: self | ||
.client() | ||
.paths(daemon_grpc::PathsRequest::from(request)) | ||
.await? | ||
.into_inner() | ||
.paths | ||
.into_iter(), | ||
}) | ||
} | ||
|
||
#[inline] | ||
pub async fn paths_to( | ||
&self, | ||
destination: IsdAsn, | ||
) -> Result<impl Iterator<Item = Path>, DaemonClientError> { | ||
self.paths(&PathRequest::new(destination)).await | ||
} | ||
|
||
fn client(&self) -> DaemonServiceClient<Channel> { | ||
DaemonServiceClient::new(self.connection.clone()) | ||
} | ||
} | ||
|
||
/// Iterator for SCION [Path]s obtained from the SCION Daemon via gRPC | ||
pub struct Paths { | ||
isd_asn: ByEndpoint<IsdAsn>, | ||
grpc_paths: std::vec::IntoIter<daemon_grpc::Path>, | ||
} | ||
|
||
impl Iterator for Paths { | ||
type Item = Path; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
for grpc_path in self.grpc_paths.by_ref() { | ||
match Path::try_from_grpc_with_endpoints(grpc_path, self.isd_asn) { | ||
Ok(path) => return Some(path), | ||
Err(e) => warn!(?e, "a parse error occurred for a path"), | ||
} | ||
} | ||
None | ||
} | ||
} |
This file contains 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,170 @@ | ||
use std::num::TryFromIntError; | ||
|
||
use scion_grpc::daemon::v1::{self as daemon_grpc}; | ||
|
||
use crate::address::IsdAsn; | ||
|
||
impl From<IsdAsn> for daemon_grpc::AsRequest { | ||
fn from(value: IsdAsn) -> Self { | ||
Self { | ||
isd_as: value.as_u64(), | ||
} | ||
} | ||
} | ||
|
||
/// Information about an AS | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct AsInfo { | ||
/// The AS's ISD-ASN | ||
pub isd_asn: IsdAsn, | ||
/// Is the AS a core AS? | ||
pub core: bool, | ||
/// The maximum transmission unit (MTU) in the AS | ||
pub mtu: u16, | ||
} | ||
|
||
impl TryFrom<daemon_grpc::AsResponse> for AsInfo { | ||
type Error = TryFromIntError; | ||
fn try_from(value: daemon_grpc::AsResponse) -> Result<Self, Self::Error> { | ||
Ok(Self { | ||
isd_asn: value.isd_as.into(), | ||
core: value.core, | ||
mtu: value.mtu.try_into()?, | ||
}) | ||
} | ||
} | ||
|
||
/// Path requests specifying source and destination ISD-ASN with some flags | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct PathRequest { | ||
pub source: IsdAsn, | ||
pub destination: IsdAsn, | ||
pub flags: PathRequestFlags, | ||
} | ||
|
||
/// Flags for path requests | ||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] | ||
pub struct PathRequestFlags { | ||
pub refresh: bool, | ||
pub hidden: bool, | ||
} | ||
|
||
impl From<&PathRequest> for daemon_grpc::PathsRequest { | ||
fn from(value: &PathRequest) -> Self { | ||
Self { | ||
source_isd_as: value.source.as_u64(), | ||
destination_isd_as: value.destination.as_u64(), | ||
refresh: value.flags.refresh, | ||
hidden: value.flags.hidden, | ||
} | ||
} | ||
} | ||
|
||
impl PathRequest { | ||
pub fn new(dst_isd_asn: IsdAsn) -> Self { | ||
Self { | ||
source: IsdAsn::WILDCARD, | ||
destination: dst_isd_asn, | ||
flags: PathRequestFlags::default(), | ||
} | ||
} | ||
|
||
pub fn with_src_isd_asn(mut self, src_isd_asn: IsdAsn) -> Self { | ||
self.source = src_isd_asn; | ||
self | ||
} | ||
|
||
pub fn with_refresh(mut self) -> Self { | ||
self.flags.refresh = true; | ||
self | ||
} | ||
|
||
pub fn with_hidden(mut self) -> Self { | ||
self.flags.hidden = true; | ||
self | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
mod as_info { | ||
use super::*; | ||
|
||
#[test] | ||
fn request_conversion() { | ||
assert_eq!( | ||
daemon_grpc::AsRequest::from(IsdAsn::from(42)), | ||
daemon_grpc::AsRequest { isd_as: 42 } | ||
) | ||
} | ||
|
||
#[test] | ||
fn response_conversion() { | ||
assert_eq!( | ||
AsInfo::try_from(daemon_grpc::AsResponse { | ||
isd_as: 42, | ||
core: true, | ||
mtu: 1500 | ||
}), | ||
Ok(AsInfo { | ||
isd_asn: IsdAsn::from(42), | ||
core: true, | ||
mtu: 1500 | ||
}) | ||
) | ||
} | ||
} | ||
|
||
mod path { | ||
use super::*; | ||
|
||
#[test] | ||
fn grpc_conversion() { | ||
assert_eq!( | ||
daemon_grpc::PathsRequest::from(&PathRequest::new(IsdAsn::from(1))), | ||
daemon_grpc::PathsRequest { | ||
source_isd_as: 0, | ||
destination_isd_as: 1, | ||
refresh: false, | ||
hidden: false, | ||
} | ||
) | ||
} | ||
|
||
#[test] | ||
fn full_construction() { | ||
let source = IsdAsn::from(42); | ||
let destination = IsdAsn::from(314); | ||
let request = PathRequest::new(destination); | ||
assert_eq!( | ||
request, | ||
PathRequest { | ||
source: IsdAsn::WILDCARD, | ||
destination, | ||
flags: PathRequestFlags::default() | ||
} | ||
); | ||
assert_eq!( | ||
request.with_src_isd_asn(source), | ||
PathRequest { | ||
source, | ||
destination, | ||
flags: PathRequestFlags::default() | ||
} | ||
); | ||
assert_eq!( | ||
request.with_hidden().with_refresh(), | ||
PathRequest { | ||
source: IsdAsn::WILDCARD, | ||
destination, | ||
flags: PathRequestFlags { | ||
refresh: true, | ||
hidden: true | ||
} | ||
} | ||
); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains 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
Oops, something went wrong.