1use std::{
17 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
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 TokenUpdateResponse {
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 SocketAddrAssignmentRequest {}
107
108#[derive(Clone, PartialEq, ::prost::Message)]
110pub struct SocketAddrAssignmentResponse {
111 #[prost(uint32, tag = "1")]
113 pub ip_version: u32,
114 #[prost(bytes = "vec", tag = "2")]
117 pub address: Vec<u8>,
118 #[prost(uint32, tag = "3")]
120 pub port: u32,
121}
122
123impl SocketAddrAssignmentResponse {
124 pub fn socket_addr(&self) -> Result<SocketAddr, AddrError> {
126 let port = self
127 .port
128 .try_into()
129 .map_err(|_| AddrError::InvalidPort(self.port))?;
130
131 match self.ip_version {
132 4 => {
133 if self.address.len() != 4 {
134 return Err(AddrError::InvalidAddressLen {
135 actual: self.address.len() as u8,
136 expected: 4,
137 });
138 }
139 let mut bytes = [0u8; 4];
140 bytes.copy_from_slice(&self.address);
141 let addr = Ipv4Addr::from(bytes);
142 Ok(SocketAddr::new(addr.into(), port))
143 }
144 6 => {
145 if self.address.len() != 16 {
146 return Err(AddrError::InvalidAddressLen {
147 actual: self.address.len() as u8,
148 expected: 16,
149 });
150 }
151 let mut bytes = [0u8; 16];
152 bytes.copy_from_slice(&self.address);
153 let addr = Ipv6Addr::from(bytes);
154 Ok(SocketAddr::new(addr.into(), port))
155 }
156 v => Err(AddrError::InvalidIPVersion(v)),
157 }
158 }
159}
160
161impl From<SocketAddr> for SocketAddrAssignmentResponse {
162 fn from(socket_addr: SocketAddr) -> Self {
163 match socket_addr {
164 SocketAddr::V4(addr) => {
165 Self {
166 ip_version: 4,
167 address: addr.ip().octets().to_vec(),
168 port: addr.port() as u32,
169 }
170 }
171 SocketAddr::V6(addr) => {
172 Self {
173 ip_version: 6,
174 address: addr.ip().octets().to_vec(),
175 port: addr.port() as u32,
176 }
177 }
178 }
179 }
180}
181
182impl TryInto<EndhostAddr> for &AddressRange {
183 type Error = AddrError;
184
185 fn try_into(self) -> Result<EndhostAddr, Self::Error> {
186 let addr: IpNet = self.ipnet()?;
187 let isd_as = IsdAsn::from(self.isd_as);
188 if isd_as.is_wildcard() {
189 return Err(AddrError::InvalidIsdAs);
190 }
191 Ok(EndhostAddr::new(isd_as, addr.addr()))
192 }
193}
194
195impl TryInto<(IsdAsn, IpNet)> for &AddressRange {
196 type Error = AddrError;
197
198 fn try_into(self) -> Result<(IsdAsn, IpNet), Self::Error> {
199 let addr: IpNet = self.ipnet()?;
200 let isd_as = IsdAsn::from(self.isd_as);
201 if isd_as.is_wildcard() {
202 return Err(AddrError::InvalidIsdAs);
203 }
204 Ok((isd_as, addr))
205 }
206}
207
208impl From<&EndhostAddr> for AddressRange {
209 fn from(addr: &EndhostAddr) -> Self {
210 let isd_as = addr.isd_asn().to_u64();
211 let (ip_version, prefix_length, address) = match addr.local_address() {
212 IpAddr::V4(a) => (4, 32, a.octets().to_vec()),
213 IpAddr::V6(a) => (6, 128, a.octets().to_vec()),
214 };
215 AddressRange {
216 isd_as,
217 ip_version,
218 prefix_length,
219 address,
220 }
221 }
222}
223
224#[derive(Debug, thiserror::Error)]
226pub enum AddrError {
227 #[error("unsupported IP version {0}")]
229 InvalidIPVersion(u32),
230 #[error("invalid address length")]
232 InvalidAddressLen {
233 actual: u8,
235 expected: u8,
237 },
238 #[error("invalid prefix length")]
240 InvalidPrefixLen {
241 actual: u8,
243 max: u8,
245 },
246 #[error("wildcard ISD-AS is not allowed")]
248 InvalidIsdAs,
249 #[error("port outside of allowed range: {0}")]
251 InvalidPort(u32),
252}
253
254#[cfg(test)]
255mod tests {
256 use std::net::IpAddr;
257
258 use assert_matches::assert_matches;
259 use scion_proto::address::{Asn, Isd};
260
261 use super::*;
262
263 const TEST_ISD_AS: IsdAsn = IsdAsn::new(Isd(1), Asn::new(0xff00_0000_0110));
264
265 #[test]
266 fn try_into_endhost_addr_ipv4_success() {
267 let address_range = AddressRange {
268 isd_as: TEST_ISD_AS.to_u64(),
269 ip_version: 4,
270 prefix_length: 32,
271 address: vec![192, 0, 2, 1],
272 };
273
274 let result: Result<EndhostAddr, _> = (&address_range).try_into();
275 let endhost_addr = result.expect("conversion should succeed");
276
277 let expected_addr = EndhostAddr::new(TEST_ISD_AS, IpAddr::V4(Ipv4Addr::new(192, 0, 2, 1)));
278 assert_eq!(endhost_addr, expected_addr);
279 }
280
281 #[test]
282 fn try_into_endhost_addr_ipv6_success() {
283 let address_range = AddressRange {
284 isd_as: TEST_ISD_AS.to_u64(),
285 ip_version: 6,
286 prefix_length: 128,
287 address: vec![
288 0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70,
289 0x73, 0x34,
290 ],
291 };
292
293 let result: Result<EndhostAddr, _> = (&address_range).try_into();
294 let endhost_addr = result.expect("conversion should succeed");
295
296 let expected_addr = EndhostAddr::new(
297 TEST_ISD_AS,
298 IpAddr::V6(Ipv6Addr::new(
299 0x2001, 0x0db8, 0x85a3, 0, 0, 0x8a2e, 0x0370, 0x7334,
300 )),
301 );
302 assert_eq!(endhost_addr, expected_addr);
303 }
304
305 #[test]
306 fn try_into_endhost_addr_invalid_ip_version() {
307 let address_range = AddressRange {
308 isd_as: TEST_ISD_AS.to_u64(),
309 ip_version: 5, prefix_length: 32,
311 address: vec![192, 0, 2, 1],
312 };
313
314 let result: Result<EndhostAddr, _> = (&address_range).try_into();
315 assert_matches!(result, Err(AddrError::InvalidIPVersion(5)));
316 }
317
318 #[test]
319 fn try_into_endhost_addr_ipv4_invalid_prefix() {
320 let address_range = AddressRange {
321 isd_as: TEST_ISD_AS.to_u64(),
322 ip_version: 4,
323 prefix_length: 24, address: vec![192, 0, 2, 1],
325 };
326
327 let result: Result<EndhostAddr, _> = (&address_range).try_into();
328 assert_matches!(
329 result,
330 Err(AddrError::InvalidPrefixLen {
331 actual: 24,
332 max: 32
333 })
334 );
335 }
336
337 #[test]
338 fn try_into_endhost_addr_ipv6_invalid_prefix() {
339 let address_range = AddressRange {
340 isd_as: TEST_ISD_AS.to_u64(),
341 ip_version: 6,
342 prefix_length: 64, address: vec![0; 16],
344 };
345
346 let result: Result<EndhostAddr, _> = (&address_range).try_into();
347 assert_matches!(
348 result,
349 Err(AddrError::InvalidPrefixLen {
350 actual: 64,
351 max: 128
352 })
353 );
354 }
355
356 #[test]
357 fn try_into_endhost_addr_ipv4_invalid_addr_len() {
358 let address_range = AddressRange {
359 isd_as: TEST_ISD_AS.to_u64(),
360 ip_version: 4,
361 prefix_length: 32,
362 address: vec![192, 0, 2], };
364
365 let result: Result<EndhostAddr, _> = (&address_range).try_into();
366 assert_matches!(
367 result,
368 Err(AddrError::InvalidAddressLen {
369 actual: 3,
370 expected: 4
371 })
372 );
373 }
374
375 #[test]
376 fn try_into_endhost_addr_ipv6_invalid_addr_len() {
377 let address_range = AddressRange {
378 isd_as: TEST_ISD_AS.to_u64(),
379 ip_version: 6,
380 prefix_length: 128,
381 address: vec![0; 15], };
383
384 let result: Result<EndhostAddr, _> = (&address_range).try_into();
385 assert_matches!(
386 result,
387 Err(AddrError::InvalidAddressLen {
388 actual: 15,
389 expected: 16
390 })
391 );
392 }
393
394 #[test]
395 fn try_into_endhost_addr_wildcard_isd_as() {
396 let address_range = AddressRange {
397 isd_as: IsdAsn::WILDCARD.to_u64(), ip_version: 4,
399 prefix_length: 32,
400 address: vec![192, 0, 2, 1],
401 };
402
403 let result: Result<EndhostAddr, _> = (&address_range).try_into();
404 assert_matches!(result, Err(AddrError::InvalidIsdAs));
405 }
406}