use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
time::SystemTime,
};
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use scion_proto::address::{EndhostAddr, IsdAsn};
pub(crate) fn system_time_from_unix_epoch_secs(secs: u64) -> std::time::SystemTime {
std::time::UNIX_EPOCH + std::time::Duration::from_secs(secs)
}
pub(crate) fn unix_epoch_from_system_time(time: SystemTime) -> u64 {
time.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SessionRenewalResponse {
#[prost(uint64, tag = "1")]
pub valid_until: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AddressRange {
#[prost(uint64, tag = "1")]
pub isd_as: u64,
#[prost(uint32, tag = "2")]
pub ip_version: u32,
#[prost(uint32, tag = "3")]
pub prefix_length: u32,
#[prost(bytes = "vec", tag = "4")]
pub address: Vec<u8>,
}
impl AddressRange {
pub(crate) fn ipnet(&self) -> Result<IpNet, AddrError> {
match self.ip_version {
4 => {
if self.prefix_length != 32 {
return Err(AddrError::InvalidPrefixLen {
actual: self.prefix_length as u8,
max: 32,
});
}
if self.address.len() != 4 {
return Err(AddrError::InvalidAddressLen {
actual: self.address.len() as u8,
expected: 4,
});
}
let mut bytes = [0u8; 4];
bytes[..].copy_from_slice(&self.address[..]);
Ok(Ipv4Net::new_assert(Ipv4Addr::from(bytes), self.prefix_length as u8).into())
}
6 => {
if self.prefix_length != 128 {
return Err(AddrError::InvalidPrefixLen {
actual: self.prefix_length as u8,
max: 128,
});
}
if self.address.len() != 16 {
return Err(AddrError::InvalidAddressLen {
actual: self.address.len() as u8,
expected: 16,
});
}
let mut bytes = [0u8; 16];
bytes[..].copy_from_slice(&self.address[..]);
Ok(Ipv6Net::new_assert(Ipv6Addr::from(bytes), self.prefix_length as u8).into())
}
v => Err(AddrError::InvalidIPVersion(v)),
}
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AddressAssignRequest {
#[prost(message, repeated, tag = "1")]
pub requested_addresses: Vec<AddressRange>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AddressAssignResponse {
#[prost(message, repeated, tag = "1")]
pub assigned_addresses: Vec<AddressRange>,
}
impl TryInto<EndhostAddr> for &AddressRange {
type Error = AddrError;
fn try_into(self) -> Result<EndhostAddr, Self::Error> {
let addr: IpNet = self.ipnet()?;
let isd_as = IsdAsn::from(self.isd_as);
if isd_as.is_wildcard() {
return Err(AddrError::InvalidIsdAs);
}
Ok(EndhostAddr::new(isd_as, addr.addr()))
}
}
impl TryInto<(IsdAsn, IpNet)> for &AddressRange {
type Error = AddrError;
fn try_into(self) -> Result<(IsdAsn, IpNet), Self::Error> {
let addr: IpNet = self.ipnet()?;
let isd_as = IsdAsn::from(self.isd_as);
if isd_as.is_wildcard() {
return Err(AddrError::InvalidIsdAs);
}
Ok((isd_as, addr))
}
}
impl From<&EndhostAddr> for AddressRange {
fn from(addr: &EndhostAddr) -> Self {
let isd_as = addr.isd_asn().to_u64();
let (ip_version, prefix_length, address) = match addr.local_address() {
IpAddr::V4(a) => (4, 32, a.octets().to_vec()),
IpAddr::V6(a) => (6, 128, a.octets().to_vec()),
};
AddressRange {
isd_as,
ip_version,
prefix_length,
address,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum AddrError {
#[error("unsupported IP version {0}")]
InvalidIPVersion(u32),
#[error("invalid address length")]
InvalidAddressLen {
actual: u8,
expected: u8,
},
#[error("invalid prefix length")]
InvalidPrefixLen {
actual: u8,
max: u8,
},
#[error("wildcard ISD-AS is not allowed")]
InvalidIsdAs,
}
#[cfg(test)]
mod tests {
use std::net::IpAddr;
use assert_matches::assert_matches;
use scion_proto::address::{Asn, Isd};
use super::*;
const TEST_ISD_AS: IsdAsn = IsdAsn::new(Isd(1), Asn::new(0xff00_0000_0110));
#[test]
fn try_into_endhost_addr_ipv4_success() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 4,
prefix_length: 32,
address: vec![192, 0, 2, 1],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
let endhost_addr = result.expect("conversion should succeed");
let expected_addr = EndhostAddr::new(TEST_ISD_AS, IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
assert_eq!(endhost_addr, expected_addr);
}
#[test]
fn try_into_endhost_addr_ipv6_success() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 6,
prefix_length: 128,
address: vec![
0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70,
0x73, 0x34,
],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
let endhost_addr = result.expect("conversion should succeed");
let expected_addr = EndhostAddr::new(
TEST_ISD_AS,
IpAddr::V6(Ipv6Addr::new(
0x2001, 0x0db8, 0x85a3, 0, 0, 0x8a2e, 0x0370, 0x7334,
)),
);
assert_eq!(endhost_addr, expected_addr);
}
#[test]
fn try_into_endhost_addr_invalid_ip_version() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 5, prefix_length: 32,
address: vec![192, 0, 2, 1],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(result, Err(AddrError::InvalidIPVersion(5)));
}
#[test]
fn try_into_endhost_addr_ipv4_invalid_prefix() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 4,
prefix_length: 24, address: vec![192, 0, 2, 1],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(
result,
Err(AddrError::InvalidPrefixLen {
actual: 24,
max: 32
})
);
}
#[test]
fn try_into_endhost_addr_ipv6_invalid_prefix() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 6,
prefix_length: 64, address: vec![0; 16],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(
result,
Err(AddrError::InvalidPrefixLen {
actual: 64,
max: 128
})
);
}
#[test]
fn try_into_endhost_addr_ipv4_invalid_addr_len() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 4,
prefix_length: 32,
address: vec![192, 0, 2], };
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(
result,
Err(AddrError::InvalidAddressLen {
actual: 3,
expected: 4
})
);
}
#[test]
fn try_into_endhost_addr_ipv6_invalid_addr_len() {
let address_range = AddressRange {
isd_as: TEST_ISD_AS.to_u64(),
ip_version: 6,
prefix_length: 128,
address: vec![0; 15], };
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(
result,
Err(AddrError::InvalidAddressLen {
actual: 15,
expected: 16
})
);
}
#[test]
fn try_into_endhost_addr_wildcard_isd_as() {
let address_range = AddressRange {
isd_as: IsdAsn::WILDCARD.to_u64(), ip_version: 4,
prefix_length: 32,
address: vec![192, 0, 2, 1],
};
let result: Result<EndhostAddr, _> = (&address_range).try_into();
assert_matches!(result, Err(AddrError::InvalidIsdAs));
}
}