Skip to main content

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