1use std::{
17 net::{IpAddr, Ipv4Addr, Ipv6Addr},
18 time::SystemTime,
19};
20
21use ipnet::{IpNet, Ipv4Net, Ipv6Net};
22use scion_proto::address::{EndhostAddr, IsdAsn};
23
24pub(crate) fn system_time_from_unix_epoch_secs(secs: u64) -> std::time::SystemTime {
25 std::time::UNIX_EPOCH + std::time::Duration::from_secs(secs)
26}
27
28pub(crate) fn unix_epoch_from_system_time(time: SystemTime) -> u64 {
29 time.duration_since(SystemTime::UNIX_EPOCH)
30 .unwrap_or_default()
31 .as_secs()
32}
33
34#[derive(Clone, PartialEq, ::prost::Message)]
36pub struct SessionRenewalResponse {
37 #[prost(uint64, tag = "1")]
39 pub valid_until: u64,
40}
41
42#[derive(Clone, PartialEq, ::prost::Message)]
44pub struct AddressRange {
45 #[prost(uint64, tag = "1")]
47 pub isd_as: u64,
48 #[prost(uint32, tag = "2")]
50 pub ip_version: u32,
51 #[prost(uint32, tag = "3")]
55 pub prefix_length: u32,
56 #[prost(bytes = "vec", tag = "4")]
59 pub address: Vec<u8>,
60}
61
62impl AddressRange {
63 pub(crate) fn ipnet(&self) -> Result<IpNet, AddrError> {
64 match self.ip_version {
65 4 => {
66 if self.prefix_length != 32 {
67 return Err(AddrError::InvalidPrefixLen {
68 actual: self.prefix_length as u8,
69 max: 32,
70 });
71 }
72 if self.address.len() != 4 {
73 return Err(AddrError::InvalidAddressLen {
74 actual: self.address.len() as u8,
75 expected: 4,
76 });
77 }
78 let mut bytes = [0u8; 4];
79 bytes[..].copy_from_slice(&self.address[..]);
80 Ok(Ipv4Net::new_assert(Ipv4Addr::from(bytes), self.prefix_length as u8).into())
81 }
82 6 => {
83 if self.prefix_length != 128 {
84 return Err(AddrError::InvalidPrefixLen {
85 actual: self.prefix_length as u8,
86 max: 128,
87 });
88 }
89 if self.address.len() != 16 {
90 return Err(AddrError::InvalidAddressLen {
91 actual: self.address.len() as u8,
92 expected: 16,
93 });
94 }
95 let mut bytes = [0u8; 16];
96 bytes[..].copy_from_slice(&self.address[..]);
97 Ok(Ipv6Net::new_assert(Ipv6Addr::from(bytes), self.prefix_length as u8).into())
98 }
99 v => Err(AddrError::InvalidIPVersion(v)),
100 }
101 }
102}
103
104#[derive(Clone, PartialEq, ::prost::Message)]
106pub struct AddressAssignRequest {
107 #[prost(message, repeated, tag = "1")]
109 pub requested_addresses: Vec<AddressRange>,
110}
111
112#[derive(Clone, PartialEq, ::prost::Message)]
114pub struct AddressAssignResponse {
115 #[prost(message, repeated, tag = "1")]
117 pub assigned_addresses: Vec<AddressRange>,
118}
119
120impl TryInto<EndhostAddr> for &AddressRange {
121 type Error = AddrError;
122
123 fn try_into(self) -> Result<EndhostAddr, Self::Error> {
124 let addr: IpNet = self.ipnet()?;
125 let isd_as = IsdAsn::from(self.isd_as);
126 if isd_as.is_wildcard() {
127 return Err(AddrError::InvalidIsdAs);
128 }
129 Ok(EndhostAddr::new(isd_as, addr.addr()))
130 }
131}
132
133impl TryInto<(IsdAsn, IpNet)> for &AddressRange {
134 type Error = AddrError;
135
136 fn try_into(self) -> Result<(IsdAsn, IpNet), Self::Error> {
137 let addr: IpNet = self.ipnet()?;
138 let isd_as = IsdAsn::from(self.isd_as);
139 if isd_as.is_wildcard() {
140 return Err(AddrError::InvalidIsdAs);
141 }
142 Ok((isd_as, addr))
143 }
144}
145
146impl From<&EndhostAddr> for AddressRange {
147 fn from(addr: &EndhostAddr) -> Self {
148 let isd_as = addr.isd_asn().to_u64();
149 let (ip_version, prefix_length, address) = match addr.local_address() {
150 IpAddr::V4(a) => (4, 32, a.octets().to_vec()),
151 IpAddr::V6(a) => (6, 128, a.octets().to_vec()),
152 };
153 AddressRange {
154 isd_as,
155 ip_version,
156 prefix_length,
157 address,
158 }
159 }
160}
161
162#[derive(Debug, thiserror::Error)]
164pub enum AddrError {
165 #[error("unsupported IP version {0}")]
167 InvalidIPVersion(u32),
168 #[error("invalid address length")]
170 InvalidAddressLen {
171 actual: u8,
173 expected: u8,
175 },
176 #[error("invalid prefix length")]
178 InvalidPrefixLen {
179 actual: u8,
181 max: u8,
183 },
184 #[error("wildcard ISD-AS is not allowed")]
186 InvalidIsdAs,
187}
188
189#[cfg(test)]
190mod tests {
191 use std::net::IpAddr;
192
193 use assert_matches::assert_matches;
194 use scion_proto::address::{Asn, Isd};
195
196 use super::*;
197
198 const TEST_ISD_AS: IsdAsn = IsdAsn::new(Isd(1), Asn::new(0xff00_0000_0110));
199
200 #[test]
201 fn try_into_endhost_addr_ipv4_success() {
202 let address_range = AddressRange {
203 isd_as: TEST_ISD_AS.to_u64(),
204 ip_version: 4,
205 prefix_length: 32,
206 address: vec![192, 0, 2, 1],
207 };
208
209 let result: Result<EndhostAddr, _> = (&address_range).try_into();
210 let endhost_addr = result.expect("conversion should succeed");
211
212 let expected_addr = EndhostAddr::new(TEST_ISD_AS, IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
213 assert_eq!(endhost_addr, expected_addr);
214 }
215
216 #[test]
217 fn try_into_endhost_addr_ipv6_success() {
218 let address_range = AddressRange {
219 isd_as: TEST_ISD_AS.to_u64(),
220 ip_version: 6,
221 prefix_length: 128,
222 address: vec![
223 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70,
224 0x73, 0x34,
225 ],
226 };
227
228 let result: Result<EndhostAddr, _> = (&address_range).try_into();
229 let endhost_addr = result.expect("conversion should succeed");
230
231 let expected_addr = EndhostAddr::new(
232 TEST_ISD_AS,
233 IpAddr::V6(Ipv6Addr::new(
234 0x2001, 0x0db8, 0x85a3, 0, 0, 0x8a2e, 0x0370, 0x7334,
235 )),
236 );
237 assert_eq!(endhost_addr, expected_addr);
238 }
239
240 #[test]
241 fn try_into_endhost_addr_invalid_ip_version() {
242 let address_range = AddressRange {
243 isd_as: TEST_ISD_AS.to_u64(),
244 ip_version: 5, prefix_length: 32,
246 address: vec![192, 0, 2, 1],
247 };
248
249 let result: Result<EndhostAddr, _> = (&address_range).try_into();
250 assert_matches!(result, Err(AddrError::InvalidIPVersion(5)));
251 }
252
253 #[test]
254 fn try_into_endhost_addr_ipv4_invalid_prefix() {
255 let address_range = AddressRange {
256 isd_as: TEST_ISD_AS.to_u64(),
257 ip_version: 4,
258 prefix_length: 24, address: vec![192, 0, 2, 1],
260 };
261
262 let result: Result<EndhostAddr, _> = (&address_range).try_into();
263 assert_matches!(
264 result,
265 Err(AddrError::InvalidPrefixLen {
266 actual: 24,
267 max: 32
268 })
269 );
270 }
271
272 #[test]
273 fn try_into_endhost_addr_ipv6_invalid_prefix() {
274 let address_range = AddressRange {
275 isd_as: TEST_ISD_AS.to_u64(),
276 ip_version: 6,
277 prefix_length: 64, address: vec![0; 16],
279 };
280
281 let result: Result<EndhostAddr, _> = (&address_range).try_into();
282 assert_matches!(
283 result,
284 Err(AddrError::InvalidPrefixLen {
285 actual: 64,
286 max: 128
287 })
288 );
289 }
290
291 #[test]
292 fn try_into_endhost_addr_ipv4_invalid_addr_len() {
293 let address_range = AddressRange {
294 isd_as: TEST_ISD_AS.to_u64(),
295 ip_version: 4,
296 prefix_length: 32,
297 address: vec![192, 0, 2], };
299
300 let result: Result<EndhostAddr, _> = (&address_range).try_into();
301 assert_matches!(
302 result,
303 Err(AddrError::InvalidAddressLen {
304 actual: 3,
305 expected: 4
306 })
307 );
308 }
309
310 #[test]
311 fn try_into_endhost_addr_ipv6_invalid_addr_len() {
312 let address_range = AddressRange {
313 isd_as: TEST_ISD_AS.to_u64(),
314 ip_version: 6,
315 prefix_length: 128,
316 address: vec![0; 15], };
318
319 let result: Result<EndhostAddr, _> = (&address_range).try_into();
320 assert_matches!(
321 result,
322 Err(AddrError::InvalidAddressLen {
323 actual: 15,
324 expected: 16
325 })
326 );
327 }
328
329 #[test]
330 fn try_into_endhost_addr_wildcard_isd_as() {
331 let address_range = AddressRange {
332 isd_as: IsdAsn::WILDCARD.to_u64(), ip_version: 4,
334 prefix_length: 32,
335 address: vec![192, 0, 2, 1],
336 };
337
338 let result: Result<EndhostAddr, _> = (&address_range).try_into();
339 assert_matches!(result, Err(AddrError::InvalidIsdAs));
340 }
341}