fidl_fuchsia_net_ext/
lib.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Extensions for types in the `fidl_fuchsia_net` crate.
6
7use std::convert::TryInto as _;
8use std::fmt::Display;
9
10use fidl_fuchsia_net as fidl;
11
12use net_types::{ethernet, ip, Witness as _};
13use paste::paste;
14
15/// Extension trait to provides access to FIDL types.
16pub trait NetTypesIpAddressExt: ip::IpAddress {
17    /// The equivalent FIDL address type.
18    type Fidl: IntoExt<fidl::IpAddress> + FromExt<Self>;
19}
20
21impl NetTypesIpAddressExt for ip::Ipv4Addr {
22    type Fidl = fidl::Ipv4Address;
23}
24
25impl NetTypesIpAddressExt for ip::Ipv6Addr {
26    type Fidl = fidl::Ipv6Address;
27}
28
29impl FromExt<ip::Ipv4Addr> for fidl::Ipv4Address {
30    fn from_ext(f: ip::Ipv4Addr) -> fidl::Ipv4Address {
31        fidl::Ipv4Address { addr: f.ipv4_bytes() }
32    }
33}
34
35impl FromExt<fidl::Ipv4Address> for ip::Ipv4Addr {
36    fn from_ext(f: fidl::Ipv4Address) -> ip::Ipv4Addr {
37        let fidl::Ipv4Address { addr } = f;
38        ip::Ipv4Addr::new(addr)
39    }
40}
41
42impl FromExt<ip::Ipv6Addr> for fidl::Ipv6Address {
43    fn from_ext(f: ip::Ipv6Addr) -> fidl::Ipv6Address {
44        fidl::Ipv6Address { addr: f.ipv6_bytes() }
45    }
46}
47
48impl FromExt<fidl::Ipv6Address> for ip::Ipv6Addr {
49    fn from_ext(f: fidl::Ipv6Address) -> ip::Ipv6Addr {
50        let fidl::Ipv6Address { addr } = f;
51        ip::Ipv6Addr::from_bytes(addr)
52    }
53}
54
55impl FromExt<ip::IpAddr> for fidl::IpAddress {
56    fn from_ext(f: ip::IpAddr) -> fidl::IpAddress {
57        match f {
58            ip::IpAddr::V4(v4) => {
59                <ip::Ipv4Addr as IntoExt<fidl::Ipv4Address>>::into_ext(v4).into_ext()
60            }
61            ip::IpAddr::V6(v6) => {
62                <ip::Ipv6Addr as IntoExt<fidl::Ipv6Address>>::into_ext(v6).into_ext()
63            }
64        }
65    }
66}
67
68impl FromExt<fidl::IpAddress> for ip::IpAddr {
69    fn from_ext(f: fidl::IpAddress) -> Self {
70        match f {
71            fidl::IpAddress::Ipv4(v4) => ip::IpAddr::V4(v4.into_ext()),
72            fidl::IpAddress::Ipv6(v6) => ip::IpAddr::V6(v6.into_ext()),
73        }
74    }
75}
76
77impl TryFromExt<fidl::Ipv4AddressWithPrefix> for ip::Subnet<ip::Ipv4Addr> {
78    type Error = ip::SubnetError;
79    fn try_from_ext(
80        fidl::Ipv4AddressWithPrefix { addr, prefix_len }: fidl::Ipv4AddressWithPrefix,
81    ) -> Result<ip::Subnet<ip::Ipv4Addr>, Self::Error> {
82        ip::Subnet::new(addr.into_ext(), prefix_len)
83    }
84}
85
86impl TryFromExt<fidl::Ipv6AddressWithPrefix> for ip::Subnet<ip::Ipv6Addr> {
87    type Error = ip::SubnetError;
88    fn try_from_ext(
89        fidl::Ipv6AddressWithPrefix { addr, prefix_len }: fidl::Ipv6AddressWithPrefix,
90    ) -> Result<ip::Subnet<ip::Ipv6Addr>, Self::Error> {
91        ip::Subnet::new(addr.into_ext(), prefix_len)
92    }
93}
94
95impl<A: ip::IpAddress> FromExt<ip::Subnet<A>> for fidl::Subnet {
96    fn from_ext(subnet: ip::Subnet<A>) -> fidl::Subnet {
97        let addr: ip::IpAddr = subnet.network().into();
98        fidl::Subnet { addr: addr.into_ext(), prefix_len: subnet.prefix() }
99    }
100}
101
102impl<A: ip::IpAddress> FromExt<ip::AddrSubnet<A>> for fidl::Subnet {
103    fn from_ext(subnet: ip::AddrSubnet<A>) -> fidl::Subnet {
104        let addr: ip::IpAddr = subnet.addr().get().into();
105        fidl::Subnet { addr: addr.into_ext(), prefix_len: subnet.subnet().prefix() }
106    }
107}
108
109impl FromExt<ip::AddrSubnetEither> for fidl::Subnet {
110    fn from_ext(addr_subnet: ip::AddrSubnetEither) -> fidl::Subnet {
111        match addr_subnet {
112            ip::AddrSubnetEither::V4(addr_subnet) => addr_subnet.into_ext(),
113            ip::AddrSubnetEither::V6(addr_subnet) => addr_subnet.into_ext(),
114        }
115    }
116}
117
118impl FromExt<ip::IpVersion> for fidl::IpVersion {
119    fn from_ext(version: ip::IpVersion) -> fidl::IpVersion {
120        match version {
121            ip::IpVersion::V4 => fidl::IpVersion::V4,
122            ip::IpVersion::V6 => fidl::IpVersion::V6,
123        }
124    }
125}
126
127/// Extension trait to allow user-friendly formatting.
128pub trait DisplayExt {
129    type Displayable: Display;
130
131    /// Returns a [`Display`]-able variant..
132    fn display_ext(&self) -> Self::Displayable;
133}
134
135/// Extension to IP types.
136pub trait IpExt {
137    /// Is the address a unicast and link-local address?
138    fn is_unicast_link_local(&self) -> bool;
139}
140
141impl IpExt for fidl::Ipv6Address {
142    fn is_unicast_link_local(&self) -> bool {
143        ip::Ipv6Addr::from_bytes(self.addr).is_unicast_link_local()
144    }
145}
146
147/// A manual implementation of `From`.
148pub trait FromExt<T> {
149    /// Performs the conversion.
150    fn from_ext(f: T) -> Self;
151}
152
153/// A manual implementation of `Into`.
154///
155/// A blanket implementation is provided for implementers of `FromExt<T>`.
156pub trait IntoExt<T> {
157    /// Performs the conversion.
158    fn into_ext(self) -> T;
159}
160
161impl<T, U> IntoExt<U> for T
162where
163    U: FromExt<T>,
164{
165    fn into_ext(self) -> U {
166        U::from_ext(self)
167    }
168}
169
170/// A manual implementation of `TryFrom`.
171pub trait TryFromExt<T>: Sized {
172    type Error;
173    /// Tries to perform the conversion.
174    fn try_from_ext(f: T) -> Result<Self, Self::Error>;
175}
176
177/// A manual implementation of `TryInto`.
178///
179/// A blanket implementation is provided for implementers of `TryFromExt<T>`.
180pub trait TryIntoExt<T>: Sized {
181    type Error;
182    /// Tries to perform the conversion.
183    fn try_into_ext(self) -> Result<T, Self::Error>;
184}
185
186impl<T, U> TryIntoExt<U> for T
187where
188    U: TryFromExt<T>,
189{
190    type Error = U::Error;
191    fn try_into_ext(self) -> Result<U, Self::Error> {
192        U::try_from_ext(self)
193    }
194}
195
196impl FromExt<fidl::Ipv4Address> for fidl::IpAddress {
197    fn from_ext(f: fidl::Ipv4Address) -> fidl::IpAddress {
198        fidl::IpAddress::Ipv4(f)
199    }
200}
201
202impl FromExt<fidl::Ipv6Address> for fidl::IpAddress {
203    fn from_ext(f: fidl::Ipv6Address) -> fidl::IpAddress {
204        fidl::IpAddress::Ipv6(f)
205    }
206}
207
208impl FromExt<fidl::Ipv4AddressWithPrefix> for fidl::Subnet {
209    fn from_ext(
210        fidl::Ipv4AddressWithPrefix { addr, prefix_len }: fidl::Ipv4AddressWithPrefix,
211    ) -> fidl::Subnet {
212        fidl::Subnet { addr: addr.into_ext(), prefix_len }
213    }
214}
215
216impl FromExt<fidl::Ipv6AddressWithPrefix> for fidl::Subnet {
217    fn from_ext(
218        fidl::Ipv6AddressWithPrefix { addr, prefix_len }: fidl::Ipv6AddressWithPrefix,
219    ) -> fidl::Subnet {
220        fidl::Subnet { addr: addr.into_ext(), prefix_len }
221    }
222}
223
224impl FromExt<fidl::Ipv4SocketAddress> for fidl::SocketAddress {
225    fn from_ext(f: fidl::Ipv4SocketAddress) -> fidl::SocketAddress {
226        fidl::SocketAddress::Ipv4(f)
227    }
228}
229
230impl FromExt<fidl::Ipv6SocketAddress> for fidl::SocketAddress {
231    fn from_ext(f: fidl::Ipv6SocketAddress) -> fidl::SocketAddress {
232        fidl::SocketAddress::Ipv6(f)
233    }
234}
235
236impl FromExt<fidl::MacAddress> for ethernet::Mac {
237    fn from_ext(fidl::MacAddress { octets }: fidl::MacAddress) -> Self {
238        ethernet::Mac::new(octets)
239    }
240}
241
242impl FromExt<ethernet::Mac> for fidl::MacAddress {
243    fn from_ext(mac: ethernet::Mac) -> fidl::MacAddress {
244        fidl::MacAddress { octets: mac.bytes() }
245    }
246}
247
248#[derive(PartialEq, Eq, Debug, Clone, Copy)]
249pub struct IpAddress(pub std::net::IpAddr);
250
251impl std::fmt::Display for IpAddress {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
253        let IpAddress(ip_address) = self;
254        write!(f, "{}", ip_address)
255    }
256}
257
258impl From<fidl::IpAddress> for IpAddress {
259    fn from(addr: fidl::IpAddress) -> IpAddress {
260        IpAddress(match addr {
261            fidl::IpAddress::Ipv4(fidl::Ipv4Address { addr }) => addr.into(),
262            fidl::IpAddress::Ipv6(fidl::Ipv6Address { addr }) => addr.into(),
263        })
264    }
265}
266
267impl From<IpAddress> for fidl::IpAddress {
268    fn from(IpAddress(ip_address): IpAddress) -> Self {
269        match ip_address {
270            std::net::IpAddr::V4(v4addr) => {
271                fidl::IpAddress::Ipv4(fidl::Ipv4Address { addr: v4addr.octets() })
272            }
273            std::net::IpAddr::V6(v6addr) => {
274                fidl::IpAddress::Ipv6(fidl::Ipv6Address { addr: v6addr.octets() })
275            }
276        }
277    }
278}
279
280impl std::str::FromStr for IpAddress {
281    type Err = anyhow::Error;
282    fn from_str(s: &str) -> Result<Self, Self::Err> {
283        Ok(IpAddress(s.parse()?))
284    }
285}
286
287macro_rules! generate_address_type {
288    ( $ip:ident ) => {
289        paste! {
290            #[derive(PartialEq, Eq, Debug, Clone, Copy)]
291            pub struct [<Ip $ip Address>](pub std::net::[<Ip $ip Addr>]);
292
293            impl std::fmt::Display for [<Ip $ip Address>] {
294                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
295                    let Self(addr) = self;
296                    write!(f, "{}", addr)
297                }
298            }
299
300            impl From<fidl::[<Ip $ip Address>]> for [<Ip $ip Address>] {
301                fn from(fidl::[<Ip $ip Address>] { addr }: fidl::[<Ip $ip Address>]) -> Self {
302                    Self(addr.into())
303                }
304            }
305
306            impl From<[<Ip $ip Address>]> for fidl::[<Ip $ip Address>] {
307                fn from([<Ip $ip Address>](addr): [<Ip $ip Address>]) -> Self {
308                    Self { addr: addr.octets() }
309                }
310            }
311
312            impl std::str::FromStr for [<Ip $ip Address>] {
313                type Err = std::net::AddrParseError;
314                fn from_str(s: &str) -> Result<Self, Self::Err> {
315                    Ok(Self(s.parse()?))
316                }
317            }
318        }
319    };
320}
321generate_address_type!(v4);
322generate_address_type!(v6);
323
324#[derive(PartialEq, Eq, Debug, Clone, Copy)]
325pub struct Subnet {
326    pub addr: IpAddress,
327    pub prefix_len: u8,
328}
329
330impl std::fmt::Display for Subnet {
331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
332        let Self { addr, prefix_len } = self;
333        write!(f, "{}/{}", addr, prefix_len)
334    }
335}
336
337impl std::str::FromStr for Subnet {
338    type Err = anyhow::Error;
339
340    // Parse a Subnet from a CIDR-notated IP address.
341    //
342    // NB: if we need additional CIDR related functionality in the future,
343    // we should consider pulling in https://crates.io/crates/cidr
344    fn from_str(s: &str) -> Result<Self, Self::Err> {
345        let mut pieces = s.split('/');
346        let addr = pieces
347            .next()
348            .expect("String#split should never return an empty iterator")
349            .parse::<std::net::IpAddr>()?;
350
351        let addr_len = match addr {
352            std::net::IpAddr::V4(_) => 32,
353            std::net::IpAddr::V6(_) => 128,
354        };
355        let validated_prefix = match pieces.next() {
356            Some(p) => {
357                let parsed_len = p.parse::<u8>()?;
358                if parsed_len > addr_len {
359                    Err(anyhow::format_err!(
360                        "prefix length provided ({} bits) too large. address {} is only {} bits long",
361                        parsed_len,
362                        addr,
363                        addr_len
364                    ))
365                } else {
366                    Ok(parsed_len)
367                }
368            }
369            None => Ok(addr_len),
370        };
371
372        let () = match pieces.next() {
373            Some(_) => Err(anyhow::format_err!(
374                "more than one '/' separator found while attempting to parse CIDR string {}",
375                s
376            )),
377            None => Ok(()),
378        }?;
379        let addr = IpAddress(addr);
380        Ok(Subnet { addr, prefix_len: validated_prefix? })
381    }
382}
383
384impl From<fidl::Subnet> for Subnet {
385    fn from(subnet: fidl::Subnet) -> Self {
386        let fidl::Subnet { addr, prefix_len } = subnet;
387        let addr = addr.into();
388        Self { addr, prefix_len }
389    }
390}
391
392impl From<Subnet> for fidl::Subnet {
393    fn from(subnet: Subnet) -> fidl::Subnet {
394        let Subnet { addr, prefix_len } = subnet;
395        let addr = addr.into();
396        fidl::Subnet { addr, prefix_len }
397    }
398}
399
400/// Returns a subnet which guarantees the masked bits on its IP address are
401/// zero.
402pub fn apply_subnet_mask(subnet: fidl::Subnet) -> fidl::Subnet {
403    let fidl::Subnet { addr, prefix_len } = subnet;
404    use net_types::ip::IpAddress as _;
405    let addr = match addr {
406        fidl::IpAddress::Ipv4(fidl::Ipv4Address { addr }) => {
407            let addr = net_types::ip::Ipv4Addr::from(addr).mask(prefix_len).ipv4_bytes();
408            fidl::IpAddress::Ipv4(fidl::Ipv4Address { addr })
409        }
410        fidl::IpAddress::Ipv6(fidl::Ipv6Address { addr }) => {
411            let addr = net_types::ip::Ipv6Addr::from(addr).mask(prefix_len).ipv6_bytes();
412            fidl::IpAddress::Ipv6(fidl::Ipv6Address { addr })
413        }
414    };
415    fidl::Subnet { addr, prefix_len }
416}
417
418macro_rules! generate_subnet_type {
419    ( $ip:ident, $prefix_len:literal ) => {
420        paste! {
421            #[derive(PartialEq, Eq, Debug, Clone, Copy)]
422            pub struct [<Subnet $ip:upper>] {
423                pub addr: [<Ip $ip Address>],
424                pub prefix_len: u8,
425            }
426
427            impl std::fmt::Display for [<Subnet $ip:upper>] {
428                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
429                    let Self { addr, prefix_len } = self;
430                    write!(f, "{}/{}", addr, prefix_len)
431                }
432            }
433
434            impl std::str::FromStr for [<Subnet $ip:upper>] {
435                type Err = anyhow::Error;
436
437                // Parse a Subnet from a CIDR-notated IP address.
438                //
439                // NB: if we need additional CIDR related functionality in the future,
440                // we should consider pulling in https://crates.io/crates/cidr
441                fn from_str(s: &str) -> Result<Self, Self::Err> {
442                    let mut pieces = s.split('/');
443                    let addr = pieces
444                        .next()
445                        .expect("String#split should never return an empty iterator")
446                        .parse::<std::net::[<Ip $ip Addr>]>()?;
447
448                    let addr_len = $prefix_len;
449                    let validated_prefix = match pieces.next() {
450                        Some(p) => {
451                            let parsed_len = p.parse::<u8>()?;
452                            if parsed_len > addr_len {
453                                Err(anyhow::format_err!(
454                                    "prefix length provided ({} bits) too large. address {} is only {} bits long",
455                                    parsed_len,
456                                    addr,
457                                    addr_len
458                                ))
459                            } else {
460                                Ok(parsed_len)
461                            }
462                        }
463                        None => Ok(addr_len),
464                    };
465
466                    let () = match pieces.next() {
467                        Some(_) => Err(anyhow::format_err!(
468                            "more than one '/' separator found while attempting to parse CIDR string {}",
469                            s
470                        )),
471                        None => Ok(()),
472                    }?;
473                    let addr = [<Ip $ip Address>](addr);
474                    Ok([<Subnet $ip:upper>] { addr, prefix_len: validated_prefix? })
475                }
476            }
477
478            impl From<fidl::[<Ip $ip AddressWithPrefix>]> for [<Subnet $ip:upper>] {
479                fn from(
480                    fidl::[<Ip $ip AddressWithPrefix>] { addr, prefix_len }:
481                        fidl::[<Ip $ip AddressWithPrefix>]
482                ) -> Self {
483                    Self { addr: addr.into(), prefix_len }
484                }
485            }
486
487            impl From<[<Subnet $ip:upper>]> for fidl::[<Ip $ip AddressWithPrefix>] {
488                fn from([<Subnet $ip:upper>] { addr, prefix_len }: [<Subnet $ip:upper>]) -> Self {
489                    Self { addr: addr.into(), prefix_len }
490                }
491            }
492        }
493    };
494}
495generate_subnet_type!(v4, 32);
496generate_subnet_type!(v6, 128);
497
498#[derive(PartialEq, Eq, Clone, Copy, Hash)]
499pub struct MacAddress {
500    pub octets: [u8; 6],
501}
502
503impl From<fidl::MacAddress> for MacAddress {
504    fn from(fidl::MacAddress { octets }: fidl::MacAddress) -> Self {
505        Self { octets }
506    }
507}
508
509impl From<MacAddress> for fidl::MacAddress {
510    fn from(MacAddress { octets }: MacAddress) -> fidl::MacAddress {
511        fidl::MacAddress { octets }
512    }
513}
514
515// We deliberately redirect Debug to use Display so that the MacAddress
516// will be printed using hex formatting.
517impl std::fmt::Debug for MacAddress {
518    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
519        write!(f, "{}", self)
520    }
521}
522
523impl std::fmt::Display for MacAddress {
524    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
525        let Self { octets } = self;
526        for (i, byte) in octets.iter().enumerate() {
527            if i > 0 {
528                write!(f, ":")?;
529            }
530            write!(f, "{:02x}", byte)?;
531        }
532        Ok(())
533    }
534}
535
536impl serde::Serialize for MacAddress {
537    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
538        serializer.collect_str(&self)
539    }
540}
541
542impl<'de> serde::Deserialize<'de> for MacAddress {
543    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
544        let s = <String as serde::Deserialize>::deserialize(deserializer)?;
545        <Self as std::str::FromStr>::from_str(&s).map_err(serde::de::Error::custom)
546    }
547}
548
549impl std::str::FromStr for MacAddress {
550    type Err = anyhow::Error;
551
552    fn from_str(s: &str) -> Result<Self, Self::Err> {
553        use anyhow::Context;
554
555        let mut octets = [0; 6];
556        let mut iter = s.split(':');
557        for (i, octet) in octets.iter_mut().enumerate() {
558            let next_octet = iter.next().ok_or_else(|| {
559                anyhow::format_err!("MAC address [{}] only specifies {} out of 6 octets", s, i)
560            })?;
561            *octet = u8::from_str_radix(next_octet, 16)
562                .with_context(|| format!("could not parse hex integer from {}", next_octet))?;
563        }
564        if iter.next().is_some() {
565            return Err(anyhow::format_err!("MAC address has more than six octets: {}", s));
566        }
567        Ok(MacAddress { octets })
568    }
569}
570
571#[derive(PartialEq, Eq, Debug, Clone, Copy)]
572pub struct SocketAddress(pub std::net::SocketAddr);
573
574impl std::fmt::Display for SocketAddress {
575    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576        let Self(socket_addr) = self;
577        std::fmt::Display::fmt(socket_addr, f)
578    }
579}
580
581impl DisplayExt for fidl::SocketAddress {
582    type Displayable = SocketAddress;
583    fn display_ext(&self) -> SocketAddress {
584        self.clone().into()
585    }
586}
587
588impl<T: IntoExt<fidl::SocketAddress> + Clone> DisplayExt for T {
589    type Displayable = SocketAddress;
590    fn display_ext(&self) -> SocketAddress {
591        IntoExt::into_ext(self.clone()).into()
592    }
593}
594
595impl From<fidl::SocketAddress> for SocketAddress {
596    fn from(f: fidl::SocketAddress) -> Self {
597        Self(match f {
598            fidl::SocketAddress::Ipv4(fidl::Ipv4SocketAddress {
599                address: fidl::Ipv4Address { addr },
600                port,
601            }) => std::net::SocketAddr::V4(std::net::SocketAddrV4::new(addr.into(), port)),
602            fidl::SocketAddress::Ipv6(fidl::Ipv6SocketAddress {
603                address: fidl::Ipv6Address { addr },
604                port,
605                zone_index,
606            }) => std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
607                addr.into(),
608                port,
609                0,
610                zone_index.try_into().unwrap_or(0),
611            )),
612        })
613    }
614}
615
616impl From<SocketAddress> for fidl::SocketAddress {
617    fn from(SocketAddress(addr): SocketAddress) -> Self {
618        match addr {
619            std::net::SocketAddr::V4(socket_addr) => {
620                fidl::SocketAddress::Ipv4(fidl::Ipv4SocketAddress {
621                    address: fidl::Ipv4Address { addr: socket_addr.ip().octets() },
622                    port: socket_addr.port(),
623                })
624            }
625            std::net::SocketAddr::V6(socket_addr) => {
626                fidl::SocketAddress::Ipv6(fidl::Ipv6SocketAddress {
627                    address: fidl::Ipv6Address { addr: socket_addr.ip().octets() },
628                    port: socket_addr.port(),
629                    zone_index: socket_addr.scope_id().into(),
630                })
631            }
632        }
633    }
634}
635
636/// This struct represents all the marks the netstack supports.
637#[derive(Debug, Default, Clone)]
638pub struct Marks {
639    /// The mark for the MARK_1 domain.
640    pub mark_1: Option<u32>,
641    /// The mark for the MARK_2 domain.
642    pub mark_2: Option<u32>,
643}
644
645/// The maximum number of mark domains supported.
646pub const MAX_MARK_DOMAIN_COUNTS: usize = 2;
647
648impl IntoIterator for Marks {
649    type Item = (fidl::MarkDomain, fidl::Mark);
650
651    type IntoIter = std::iter::Flatten<
652        std::array::IntoIter<Option<(fidl::MarkDomain, fidl::Mark)>, MAX_MARK_DOMAIN_COUNTS>,
653    >;
654
655    fn into_iter(self) -> Self::IntoIter {
656        let Self { mark_1, mark_2 } = self;
657        [
658            mark_1.map(|mark| (fidl::MarkDomain::Mark1, mark)),
659            mark_2.map(|mark| (fidl::MarkDomain::Mark2, mark)),
660        ]
661        .into_iter()
662        .flatten()
663    }
664}
665
666impl From<fidl::Marks> for Marks {
667    fn from(value: fidl::Marks) -> Self {
668        let fidl::Marks { mark_1, mark_2, __source_breaking } = value;
669        return Self { mark_1, mark_2 };
670    }
671}
672
673impl From<Marks> for fidl::Marks {
674    fn from(value: Marks) -> Self {
675        let Marks { mark_1, mark_2 } = value;
676        Self { mark_1, mark_2, __source_breaking: Default::default() }
677    }
678}
679
680#[cfg(test)]
681mod tests {
682    use super::*;
683
684    use assert_matches::assert_matches;
685    use net_declare::{
686        fidl_ip_v4_with_prefix, fidl_ip_v6_with_prefix, fidl_subnet, net_addr_subnet,
687        net_addr_subnet_v4, net_addr_subnet_v6, net_subnet_v4, net_subnet_v6, std_ip, std_ip_v4,
688        std_ip_v6,
689    };
690    use test_case::test_case;
691
692    use std::collections::HashMap;
693    use std::str::FromStr;
694
695    #[test]
696    fn test_from_into_ext() {
697        let a = fidl::Ipv4Address { addr: [0; 4] };
698        assert_eq!(fidl::IpAddress::Ipv4(a), a.into_ext());
699
700        let a = fidl::Ipv4Address { addr: [0; 4] };
701        assert_eq!(net_types::ip::Ipv4Addr::new([0; 4]), a.into_ext());
702
703        let a = fidl::Ipv6Address { addr: [0; 16] };
704        assert_eq!(fidl::IpAddress::Ipv6(a), a.into_ext());
705
706        let a = fidl::Ipv6Address { addr: [0; 16] };
707        assert_eq!(net_types::ip::Ipv6Addr::from_bytes([0; 16]), a.into_ext());
708
709        let a = fidl::Ipv4SocketAddress { address: fidl::Ipv4Address { addr: [0; 4] }, port: 1 };
710        assert_eq!(fidl::SocketAddress::Ipv4(a), a.into_ext());
711
712        let a = fidl::Ipv6SocketAddress {
713            address: fidl::Ipv6Address { addr: [0; 16] },
714            port: 1,
715            zone_index: 2,
716        };
717        assert_eq!(fidl::SocketAddress::Ipv6(a), a.into_ext());
718    }
719
720    #[test]
721    fn test_ipaddr() {
722        let want_ext = IpAddress(std::net::IpAddr::V4(std::net::Ipv4Addr::new(1, 2, 3, 4)));
723        let want_fidl = fidl::IpAddress::Ipv4(fidl::Ipv4Address { addr: [1, 2, 3, 4] });
724        let got_fidl: fidl::IpAddress = want_ext.into();
725        let got_ext = IpAddress::from(got_fidl);
726
727        assert_eq!(want_ext, got_ext);
728        assert_eq!(want_fidl, got_fidl);
729    }
730
731    #[test]
732    fn test_net_types_subnet_into_fidl_subnet() {
733        assert_eq!(fidl_subnet!("192.168.0.0/24"), net_subnet_v4!("192.168.0.0/24").into_ext());
734        assert_eq!(fidl_subnet!("fd::/64"), net_subnet_v6!("fd::/64").into_ext());
735    }
736
737    // Note "1.2.3.4" or "::" is a valid form. Subnet's FromStr trait allows
738    // missing prefix, and assumes the legally maximum prefix length.
739    macro_rules! subnet_from_str_invalid {
740        ($typ:ty) => {
741            paste! {
742                #[test_case("")]
743                #[test_case("/32")]                                   // no ip address
744                #[test_case(" /32"; "space_slash_32")]                // no ip address
745                #[test_case("192.0.2.0/8/8")]                         // too many slashes
746                #[test_case("192.0.2.0/33")]                          // prefix too long
747                #[test_case("192.0.2.0:8080")]                        // that's a port, not a prefix
748                #[test_case("2001:db8::e1bf:4fe9:fb62:e3f4/129")]     // prefix too long
749                #[test_case("2001:db8::e1bf:4fe9:fb62:e3f4/32%eth0")] // zone index
750                fn [<$typ:snake _from_str_invalid>](s: &str) {
751                    let _: anyhow::Error = $typ::from_str(s).expect_err(&format!(
752                        "a malformed str is wrongfully convertitable to Subnet struct: \"{}\"",
753                        s
754                    ));
755                }
756            }
757        };
758    }
759
760    subnet_from_str_invalid!(Subnet);
761    subnet_from_str_invalid!(SubnetV4);
762    subnet_from_str_invalid!(SubnetV6);
763
764    #[test_case(
765        "192.0.2.0/24",
766        Subnet { addr: IpAddress(std_ip!("192.0.2.0")), prefix_len: 24 },
767        fidl_subnet!("192.0.2.0/24")
768    )]
769    #[test_case(
770        "2001:db8::/32",
771        Subnet { addr: IpAddress(std_ip!("2001:db8::")), prefix_len: 32 },
772        fidl_subnet!("2001:db8::/32")
773    )]
774    fn subnet_conversions(want_str: &str, want_ext: Subnet, want_fidl: fidl::Subnet) {
775        let got_ext = Subnet::from_str(want_str).ok().expect("conversion error");
776        let got_fidl: fidl::Subnet = got_ext.into();
777        let got_ext_back = Subnet::from(got_fidl);
778        let got_str = &format!("{}", got_ext_back);
779
780        assert_eq!(want_ext, got_ext);
781        assert_eq!(want_fidl, got_fidl);
782        assert_eq!(got_ext, got_ext_back);
783        assert_eq!(want_str, got_str);
784    }
785
786    #[test]
787    fn subnet_v4_from_str() {
788        assert_eq!(
789            SubnetV4::from_str("192.0.2.0/24")
790                .expect("should be able to parse 192.0.2.0/24 into SubnetV4"),
791            SubnetV4 { addr: Ipv4Address(std_ip_v4!("192.0.2.0")), prefix_len: 24 }
792        );
793    }
794
795    #[test]
796    fn subnet_v4_from_v6_str() {
797        let _: anyhow::Error = SubnetV4::from_str("2001:db8::/24")
798            .expect_err("IPv6 subnet should not be parsed as SubnetV4 successfully");
799    }
800
801    #[test]
802    fn subnet_v6_from_str() {
803        assert_eq!(
804            SubnetV6::from_str("2001:db8::/32")
805                .expect("should be able to parse 2001:db8::/32 into SubnetV6"),
806            SubnetV6 { addr: Ipv6Address(std_ip_v6!("2001:db8::")), prefix_len: 32 }
807        );
808    }
809
810    #[test]
811    fn subnet_v6_from_v4_str() {
812        let _: anyhow::Error = SubnetV6::from_str("192.0.2.0/24")
813            .expect_err("IPv4 subnet should not be parsed as SubnetV6 successfully");
814    }
815
816    #[test]
817    fn test_subnet_try_from_ipaddress_with_prefix() {
818        // Test Success
819        assert_eq!(
820            Ok(net_subnet_v4!("192.168.0.0/24")),
821            fidl_ip_v4_with_prefix!("192.168.0.0/24").try_into_ext()
822        );
823        assert_eq!(
824            Ok(net_subnet_v6!("fe80::/64")),
825            fidl_ip_v6_with_prefix!("fe80::/64").try_into_ext()
826        );
827
828        // Test Failure (Host Bits Set)
829        assert_matches!(
830            ip::Subnet::<ip::Ipv4Addr>::try_from_ext(fidl_ip_v4_with_prefix!("192.168.0.1/24")),
831            Err(ip::SubnetError::HostBitsSet)
832        );
833        assert_matches!(
834            ip::Subnet::<ip::Ipv6Addr>::try_from_ext(fidl_ip_v6_with_prefix!("fe80::1/64")),
835            Err(ip::SubnetError::HostBitsSet)
836        );
837    }
838
839    #[test]
840    fn test_addr_subnet_fidl_subnet() {
841        assert_eq!(
842            fidl::Subnet::from_ext(net_addr_subnet_v4!("192.168.0.8/24")),
843            fidl_subnet!("192.168.0.8/24")
844        );
845
846        assert_eq!(
847            fidl::Subnet::from_ext(net_addr_subnet_v6!("fe80::1234/54")),
848            fidl_subnet!("fe80::1234/54")
849        );
850    }
851
852    #[test]
853    fn test_addr_subnet_either_fidl_subnet() {
854        assert_eq!(
855            fidl::Subnet::from_ext(net_addr_subnet!("192.168.0.8/24")),
856            fidl_subnet!("192.168.0.8/24")
857        );
858        assert_eq!(
859            fidl::Subnet::from_ext(net_addr_subnet!("fe80::1234/54")),
860            fidl_subnet!("fe80::1234/54")
861        );
862    }
863
864    #[test]
865    fn mac_addr_from_str_with_valid_str_returns_mac_addr() {
866        let result = MacAddress::from_str("AA:BB:CC:DD:EE:FF").unwrap();
867        let expected = MacAddress { octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF] };
868
869        assert_eq!(expected, result);
870    }
871
872    #[test]
873    fn mac_addr_from_str_with_invalid_digit_returns_err() {
874        let result = MacAddress::from_str("11:22:33:44:55:GG");
875
876        assert!(result.is_err());
877    }
878
879    #[test]
880    fn mac_addr_from_str_with_invalid_format_returns_err() {
881        let result = MacAddress::from_str("11-22-33-44-55-66");
882
883        assert!(result.is_err());
884    }
885
886    #[test]
887    fn mac_addr_from_str_with_empty_string_returns_err() {
888        let result = MacAddress::from_str("");
889
890        assert!(result.is_err());
891    }
892
893    #[test]
894    fn mac_addr_from_str_with_extra_quotes_returns_err() {
895        let result = MacAddress::from_str("\"11:22:33:44:55:66\"");
896
897        assert!(result.is_err());
898    }
899
900    #[test]
901    fn valid_mac_addr_array_deserializes_to_vec_of_mac_addrs() {
902        let result: Vec<MacAddress> =
903            serde_json::from_str("[\"11:11:11:11:11:11\", \"AA:AA:AA:AA:AA:AA\"]").unwrap();
904        let expected = vec![
905            MacAddress { octets: [0x11, 0x11, 0x11, 0x11, 0x11, 0x11] },
906            MacAddress { octets: [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA] },
907        ];
908
909        assert_eq!(expected, result);
910    }
911
912    #[test]
913    fn mac_addr_to_mac_addr_map_deserializes_to_hashmap() {
914        let result: HashMap<MacAddress, MacAddress> =
915            serde_json::from_str("{\"11:22:33:44:55:66\": \"AA:BB:CC:DD:EE:FF\"}").unwrap();
916        let expected: HashMap<_, _> = std::iter::once((
917            MacAddress { octets: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66] },
918            MacAddress { octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF] },
919        ))
920        .collect();
921
922        assert_eq!(expected, result);
923    }
924
925    #[test]
926    fn mac_addr_to_mac_addr_map_serializes_to_valid_json() {
927        let mac_addr_map: HashMap<_, _> = std::iter::once((
928            MacAddress { octets: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66] },
929            MacAddress { octets: [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF] },
930        ))
931        .collect();
932
933        let result = serde_json::to_string(&mac_addr_map).unwrap();
934
935        assert_eq!("{\"11:22:33:44:55:66\":\"aa:bb:cc:dd:ee:ff\"}", result);
936    }
937
938    #[test]
939    fn test_socket_addr() {
940        // V4.
941        let want_ext = SocketAddress(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
942            std::net::Ipv4Addr::new(1, 2, 3, 4),
943            5,
944        )));
945        let want_fidl = fidl::SocketAddress::Ipv4(fidl::Ipv4SocketAddress {
946            address: fidl::Ipv4Address { addr: [1, 2, 3, 4] },
947            port: 5,
948        });
949        let got_fidl: fidl::SocketAddress = want_ext.into();
950        let got_ext = SocketAddress::from(want_fidl);
951
952        assert_eq!(want_ext, got_ext);
953        assert_eq!(want_fidl, got_fidl);
954
955        // V6.
956        let want_ext = SocketAddress(std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
957            std::net::Ipv6Addr::new(0x0102, 0x0304, 0x0506, 0x0708, 0x090A, 0x0B0C, 0x0D0E, 0x0F10),
958            17,
959            0,
960            18,
961        )));
962        let want_fidl = fidl::SocketAddress::Ipv6(fidl::Ipv6SocketAddress {
963            address: fidl::Ipv6Address {
964                addr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
965            },
966            port: 17,
967            zone_index: 18,
968        });
969        let got_fidl: fidl::SocketAddress = want_ext.into();
970        let got_ext = SocketAddress::from(got_fidl);
971
972        assert_eq!(want_ext, got_ext);
973        assert_eq!(want_fidl, want_fidl);
974    }
975
976    #[test]
977    fn test_display_ext() {
978        let ipv4_sock_addr =
979            fidl::Ipv4SocketAddress { address: fidl::Ipv4Address { addr: [1, 2, 3, 4] }, port: 5 };
980        assert_eq!(
981            SocketAddress(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
982                std::net::Ipv4Addr::new(1, 2, 3, 4),
983                5,
984            ))),
985            fidl::SocketAddress::Ipv4(ipv4_sock_addr).display_ext()
986        );
987        assert_eq!(
988            SocketAddress(std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
989                std::net::Ipv4Addr::new(1, 2, 3, 4),
990                5,
991            ))),
992            ipv4_sock_addr.display_ext()
993        );
994        assert_eq!(
995            SocketAddress(std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
996                std::net::Ipv6Addr::new(
997                    0x0102, 0x0304, 0x0506, 0x0708, 0x090A, 0x0B0C, 0x0D0E, 0x0F10
998                ),
999                17,
1000                0,
1001                18,
1002            ))),
1003            fidl::Ipv6SocketAddress {
1004                address: fidl::Ipv6Address {
1005                    addr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
1006                },
1007                port: 17,
1008                zone_index: 18,
1009            }
1010            .display_ext()
1011        );
1012    }
1013}