fidl_fuchsia_net_matchers_ext/
lib.rs

1// Copyright 2025 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 the fuchsia.net.matchers FIDL library.
6//!
7//! Note that this library as written is not meant for inclusion in the SDK. It
8//! is only meant to be used in conjunction with a netstack that is compiled
9//! against the same API level of the `fuchsia.net.matchers` FIDL library. This
10//! library opts in to compile-time and runtime breakage when the FIDL library
11//! is evolved in order to enforce that it is updated along with the FIDL
12//! library itself.
13
14use std::fmt::Debug;
15use std::num::NonZeroU64;
16use std::ops::RangeInclusive;
17
18use fidl::marker::SourceBreaking;
19use fidl_fuchsia_net_ext::{FromExt, IntoExt};
20use thiserror::Error;
21use {
22    fidl_fuchsia_net as fnet, fidl_fuchsia_net_interfaces as fnet_interfaces,
23    fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext,
24    fidl_fuchsia_net_matchers as fnet_matchers,
25};
26
27/// Extension type for [`fnet_matchers::Interface`].
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub enum Interface {
30    Id(NonZeroU64),
31    Name(fnet_interfaces::Name),
32    PortClass(fnet_interfaces_ext::PortClass),
33}
34
35/// Errors when creating an [`Interface`].
36#[derive(Debug, Error, PartialEq, Eq)]
37pub enum InterfaceError {
38    #[error("interface matcher specified an invalid ID of 0")]
39    ZeroId,
40    #[error(transparent)]
41    UnknownPortClass(fnet_interfaces_ext::UnknownPortClassError),
42    #[error("interface union is of an unknown variant")]
43    UnknownUnionVariant,
44}
45
46impl From<Interface> for fnet_matchers::Interface {
47    fn from(matcher: Interface) -> Self {
48        match matcher {
49            Interface::Id(id) => Self::Id(id.get()),
50            Interface::Name(name) => Self::Name(name),
51            Interface::PortClass(port_class) => Self::PortClass(port_class.into()),
52        }
53    }
54}
55
56impl TryFrom<fnet_matchers::Interface> for Interface {
57    type Error = InterfaceError;
58
59    fn try_from(matcher: fnet_matchers::Interface) -> Result<Self, Self::Error> {
60        match matcher {
61            fnet_matchers::Interface::Id(id) => {
62                let id = NonZeroU64::new(id).ok_or(InterfaceError::ZeroId)?;
63                Ok(Self::Id(id))
64            }
65            fnet_matchers::Interface::Name(name) => Ok(Self::Name(name)),
66            fnet_matchers::Interface::PortClass(port_class) => {
67                port_class.try_into().map(Self::PortClass).map_err(InterfaceError::UnknownPortClass)
68            }
69            fnet_matchers::Interface::__SourceBreaking { .. } => {
70                Err(InterfaceError::UnknownUnionVariant)
71            }
72        }
73    }
74}
75
76/// Extension type for [`fnet_matchers::BoundInterface`].
77#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub enum BoundInterface {
79    Unbound,
80    Bound(Interface),
81}
82
83/// Errors when creating an [`BoundInterface`].
84#[derive(Debug, Error, PartialEq)]
85pub enum BoundInterfaceError {
86    #[error(transparent)]
87    Interface(InterfaceError),
88    #[error("interface union is of an unknown variant")]
89    UnknownUnionVariant(u64),
90}
91
92impl From<BoundInterface> for fnet_matchers::BoundInterface {
93    fn from(matcher: BoundInterface) -> Self {
94        match matcher {
95            BoundInterface::Unbound => fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty),
96            BoundInterface::Bound(interface) => {
97                fnet_matchers::BoundInterface::Bound(interface.into())
98            }
99        }
100    }
101}
102
103impl TryFrom<fnet_matchers::BoundInterface> for BoundInterface {
104    type Error = BoundInterfaceError;
105
106    fn try_from(matcher: fnet_matchers::BoundInterface) -> Result<Self, Self::Error> {
107        match matcher {
108            fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty) => {
109                Ok(BoundInterface::Unbound)
110            }
111            fnet_matchers::BoundInterface::Bound(interface) => Ok(BoundInterface::Bound(
112                interface.try_into().map_err(|e| BoundInterfaceError::Interface(e))?,
113            )),
114            fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal } => {
115                Err(BoundInterfaceError::UnknownUnionVariant(unknown_ordinal))
116            }
117        }
118    }
119}
120
121/// Extension type for the `Subnet` variant of [`fnet_matchers::Address`].
122///
123/// This type witnesses to the invariant that the prefix length of the subnet is
124/// no greater than the number of bits in the IP address, and that no host bits
125/// in the network address are set.
126#[derive(Clone, Copy, Eq, Hash, PartialEq)]
127pub struct Subnet(net_types::ip::SubnetEither);
128
129/// Errors when creating a [`Subnet`].
130#[derive(Debug, Error, PartialEq)]
131pub enum SubnetError {
132    #[error("prefix length of subnet is longer than number of bits in IP address")]
133    PrefixTooLong,
134    #[error("host bits are set in subnet network")]
135    HostBitsSet,
136}
137
138impl From<Subnet> for net_types::ip::SubnetEither {
139    fn from(subnet: Subnet) -> Self {
140        let Subnet(subnet) = subnet;
141        subnet
142    }
143}
144
145impl From<Subnet> for fnet::Subnet {
146    fn from(subnet: Subnet) -> Self {
147        let Subnet(subnet) = subnet;
148        let (addr, prefix_len) = subnet.net_prefix();
149
150        Self { addr: fnet::IpAddress::from_ext(addr), prefix_len }
151    }
152}
153
154impl TryFrom<fnet::Subnet> for Subnet {
155    type Error = SubnetError;
156
157    fn try_from(subnet: fnet::Subnet) -> Result<Self, Self::Error> {
158        let fnet::Subnet { addr, prefix_len } = subnet;
159
160        match net_types::ip::SubnetEither::new(addr.into_ext(), prefix_len) {
161            Ok(inner) => Ok(Self(inner)),
162            Err(err) => Err(match err {
163                net_types::ip::SubnetError::PrefixTooLong => SubnetError::PrefixTooLong,
164                net_types::ip::SubnetError::HostBitsSet => SubnetError::HostBitsSet,
165            }),
166        }
167    }
168}
169
170impl Debug for Subnet {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        let Self(subnet) = self;
173        match subnet {
174            net_types::ip::SubnetEither::V4(subnet) => subnet.fmt(f),
175            net_types::ip::SubnetEither::V6(subnet) => subnet.fmt(f),
176        }
177    }
178}
179
180/// Extension type for [`fnet_matchers::AddressRange`].
181///
182/// This type witnesses that `start <= end`. (Comparisons are performed on the
183/// numerical big-endian representation of the IP address.)
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub enum AddressRange {
186    V4(RangeInclusive<fnet::Ipv4Address>),
187    V6(RangeInclusive<fnet::Ipv6Address>),
188}
189
190/// Errors when creating an [`AddressRange`].
191#[derive(Debug, Error, PartialEq)]
192pub enum AddressRangeError {
193    #[error("invalid address range (start must be <= end)")]
194    Invalid,
195    #[error("address range start and end addresses are not the same IP family")]
196    FamilyMismatch,
197}
198
199impl From<AddressRange> for fnet_matchers::AddressRange {
200    fn from(range: AddressRange) -> Self {
201        let (start, end) = match range {
202            AddressRange::V4(range) => ((*range.start()).into_ext(), (*range.end()).into_ext()),
203            AddressRange::V6(range) => ((*range.start()).into_ext(), (*range.end()).into_ext()),
204        };
205
206        Self { start, end }
207    }
208}
209
210impl TryFrom<fnet_matchers::AddressRange> for AddressRange {
211    type Error = AddressRangeError;
212
213    fn try_from(range: fnet_matchers::AddressRange) -> Result<Self, Self::Error> {
214        let fnet_matchers::AddressRange { start, end } = range;
215        match (start, end) {
216            (fnet::IpAddress::Ipv4(start), fnet::IpAddress::Ipv4(end)) => {
217                if u32::from_be_bytes(start.addr) > u32::from_be_bytes(end.addr) {
218                    Err(AddressRangeError::Invalid)
219                } else {
220                    Ok(Self::V4(start..=end))
221                }
222            }
223            (fnet::IpAddress::Ipv6(start), fnet::IpAddress::Ipv6(end)) => {
224                if u128::from_be_bytes(start.addr) > u128::from_be_bytes(end.addr) {
225                    Err(AddressRangeError::Invalid)
226                } else {
227                    Ok(Self::V6(start..=end))
228                }
229            }
230            _ => Err(AddressRangeError::FamilyMismatch),
231        }
232    }
233}
234
235/// Extension type for [`fnet_matchers::Address`].
236#[derive(Clone, PartialEq, Eq)]
237pub enum AddressMatcherType {
238    Subnet(Subnet),
239    Range(AddressRange),
240}
241
242/// Errors when creating an [`AddressMatcherType`].
243#[derive(Debug, Error, PartialEq)]
244pub enum AddressMatcherTypeError {
245    #[error("AddressMatcher is of an unknown variant")]
246    UnknownUnionVariant,
247    #[error("subnet conversion error: {0}")]
248    Subnet(SubnetError),
249    #[error("address range conversion error: {0}")]
250    AddressRange(AddressRangeError),
251}
252
253impl From<SubnetError> for AddressMatcherTypeError {
254    fn from(value: SubnetError) -> Self {
255        AddressMatcherTypeError::Subnet(value)
256    }
257}
258impl From<AddressRangeError> for AddressMatcherTypeError {
259    fn from(value: AddressRangeError) -> Self {
260        AddressMatcherTypeError::AddressRange(value)
261    }
262}
263
264impl From<AddressMatcherType> for fnet_matchers::AddressMatcherType {
265    fn from(matcher: AddressMatcherType) -> Self {
266        match matcher {
267            AddressMatcherType::Subnet(subnet) => Self::Subnet(subnet.into()),
268            AddressMatcherType::Range(range) => Self::Range(range.into()),
269        }
270    }
271}
272
273impl TryFrom<fnet_matchers::AddressMatcherType> for AddressMatcherType {
274    type Error = AddressMatcherTypeError;
275
276    fn try_from(matcher: fnet_matchers::AddressMatcherType) -> Result<Self, Self::Error> {
277        match matcher {
278            fnet_matchers::AddressMatcherType::Subnet(subnet) => {
279                Ok(Self::Subnet(subnet.try_into()?))
280            }
281            fnet_matchers::AddressMatcherType::Range(range) => Ok(Self::Range(range.try_into()?)),
282            fnet_matchers::AddressMatcherType::__SourceBreaking { .. } => {
283                Err(AddressMatcherTypeError::UnknownUnionVariant)
284            }
285        }
286    }
287}
288
289impl Debug for AddressMatcherType {
290    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
291        match self {
292            AddressMatcherType::Subnet(subnet) => subnet.fmt(f),
293            AddressMatcherType::Range(address_range) => address_range.fmt(f),
294        }
295    }
296}
297
298/// Extension type for [`fnet_matchers::Address`].
299#[derive(Debug, Clone, PartialEq, Eq)]
300pub struct Address {
301    pub matcher: AddressMatcherType,
302    pub invert: bool,
303}
304
305/// Errors when creating an [`Address`].
306#[derive(Debug, Error, PartialEq)]
307pub enum AddressError {
308    #[error("address matcher conversion failure: {0}")]
309    AddressMatcherType(AddressMatcherTypeError),
310}
311
312impl From<AddressMatcherTypeError> for AddressError {
313    fn from(value: AddressMatcherTypeError) -> Self {
314        Self::AddressMatcherType(value)
315    }
316}
317
318impl From<Address> for fnet_matchers::Address {
319    fn from(matcher: Address) -> Self {
320        let Address { matcher, invert } = matcher;
321        Self { matcher: matcher.into(), invert }
322    }
323}
324
325impl TryFrom<fnet_matchers::Address> for Address {
326    type Error = AddressError;
327
328    fn try_from(matcher: fnet_matchers::Address) -> Result<Self, Self::Error> {
329        let fnet_matchers::Address { matcher, invert } = matcher;
330        Ok(Self { matcher: matcher.try_into()?, invert })
331    }
332}
333
334/// Extension type for [`fnet_matchers::BoundAddress`].
335#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum BoundAddress {
337    Unbound,
338    Bound(Address),
339}
340
341/// Errors when creating an [`BoundAddress`].
342#[derive(Debug, Error, PartialEq)]
343pub enum BoundAddressError {
344    #[error(transparent)]
345    Address(AddressError),
346    #[error("bound address union is of an unknown variant")]
347    UnknownUnionVariant(u64),
348}
349
350impl From<BoundAddress> for fnet_matchers::BoundAddress {
351    fn from(matcher: BoundAddress) -> Self {
352        match matcher {
353            BoundAddress::Unbound => fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty),
354            BoundAddress::Bound(address) => fnet_matchers::BoundAddress::Bound(address.into()),
355        }
356    }
357}
358
359impl TryFrom<fnet_matchers::BoundAddress> for BoundAddress {
360    type Error = BoundAddressError;
361
362    fn try_from(matcher: fnet_matchers::BoundAddress) -> Result<Self, Self::Error> {
363        match matcher {
364            fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty) => Ok(BoundAddress::Unbound),
365            fnet_matchers::BoundAddress::Bound(address) => {
366                Ok(BoundAddress::Bound(address.try_into().map_err(BoundAddressError::Address)?))
367            }
368            fnet_matchers::BoundAddress::__SourceBreaking { unknown_ordinal } => {
369                Err(BoundAddressError::UnknownUnionVariant(unknown_ordinal))
370            }
371        }
372    }
373}
374
375/// Extension type for [`fnet_matchers::Port`].
376///
377/// This type witnesses to the invariant that `start <= end`.
378#[derive(Debug, Clone, PartialEq, Eq, Hash)]
379pub struct Port {
380    range: RangeInclusive<u16>,
381    invert: bool,
382}
383
384/// Extension type for [`fnet_matchers::BoundPort`].
385#[derive(Debug, Clone, PartialEq, Eq, Hash)]
386pub enum BoundPort {
387    Unbound,
388    Bound(Port),
389}
390
391/// Errors when creating an [`BoundPort`].
392#[derive(Debug, Error, PartialEq, Eq)]
393pub enum BoundPortError {
394    #[error(transparent)]
395    Port(PortError),
396    #[error("bound port union is of an unknown variant")]
397    UnknownUnionVariant(u64),
398}
399
400impl From<BoundPort> for fnet_matchers::BoundPort {
401    fn from(matcher: BoundPort) -> Self {
402        match matcher {
403            BoundPort::Unbound => fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty),
404            BoundPort::Bound(port) => fnet_matchers::BoundPort::Bound(port.into()),
405        }
406    }
407}
408
409impl TryFrom<fnet_matchers::BoundPort> for BoundPort {
410    type Error = BoundPortError;
411
412    fn try_from(matcher: fnet_matchers::BoundPort) -> Result<Self, Self::Error> {
413        match matcher {
414            fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty) => Ok(BoundPort::Unbound),
415            fnet_matchers::BoundPort::Bound(port) => {
416                Ok(BoundPort::Bound(port.try_into().map_err(BoundPortError::Port)?))
417            }
418            fnet_matchers::BoundPort::__SourceBreaking { unknown_ordinal } => {
419                Err(BoundPortError::UnknownUnionVariant(unknown_ordinal))
420            }
421        }
422    }
423}
424
425/// Errors when creating a `Port`.
426#[derive(Debug, Error, PartialEq, Eq)]
427pub enum PortError {
428    #[error("invalid port range (start must be <= end)")]
429    InvalidPortRange,
430}
431
432impl Port {
433    pub fn new(start: u16, end: u16, invert: bool) -> Result<Self, PortError> {
434        if start > end {
435            return Err(PortError::InvalidPortRange);
436        }
437        Ok(Self { range: start..=end, invert })
438    }
439
440    pub fn range(&self) -> &RangeInclusive<u16> {
441        &self.range
442    }
443
444    pub fn start(&self) -> u16 {
445        *self.range.start()
446    }
447
448    pub fn end(&self) -> u16 {
449        *self.range.end()
450    }
451
452    pub fn invert(&self) -> bool {
453        self.invert
454    }
455}
456
457impl From<Port> for fnet_matchers::Port {
458    fn from(matcher: Port) -> Self {
459        let Port { range, invert } = matcher;
460        Self { start: *range.start(), end: *range.end(), invert }
461    }
462}
463
464impl TryFrom<fnet_matchers::Port> for Port {
465    type Error = PortError;
466
467    fn try_from(matcher: fnet_matchers::Port) -> Result<Self, Self::Error> {
468        let fnet_matchers::Port { start, end, invert } = matcher;
469        if start > end {
470            return Err(PortError::InvalidPortRange);
471        }
472        Ok(Self { range: start..=end, invert })
473    }
474}
475
476/// Extension type for [`fnet_matchers::PacketTransportProtocol`].
477#[derive(Clone, PartialEq)]
478pub enum TransportProtocol {
479    Tcp { src_port: Option<Port>, dst_port: Option<Port> },
480    Udp { src_port: Option<Port>, dst_port: Option<Port> },
481    Icmp,
482    Icmpv6,
483}
484
485/// Errors when creating a [`TransportProtocol`].
486#[derive(Debug, Error, PartialEq)]
487pub enum TransportProtocolError {
488    #[error("invalid port: {0}")]
489    Port(PortError),
490    #[error("TransportProtocol is of an unknown variant")]
491    UnknownUnionVariant,
492}
493
494impl From<PortError> for TransportProtocolError {
495    fn from(value: PortError) -> Self {
496        TransportProtocolError::Port(value)
497    }
498}
499
500impl From<TransportProtocol> for fnet_matchers::PacketTransportProtocol {
501    fn from(matcher: TransportProtocol) -> Self {
502        match matcher {
503            TransportProtocol::Tcp { src_port, dst_port } => Self::Tcp(fnet_matchers::TcpPacket {
504                src_port: src_port.map(Into::into),
505                dst_port: dst_port.map(Into::into),
506                __source_breaking: SourceBreaking,
507            }),
508            TransportProtocol::Udp { src_port, dst_port } => Self::Udp(fnet_matchers::UdpPacket {
509                src_port: src_port.map(Into::into),
510                dst_port: dst_port.map(Into::into),
511                __source_breaking: SourceBreaking,
512            }),
513            TransportProtocol::Icmp => Self::Icmp(fnet_matchers::IcmpPacket::default()),
514            TransportProtocol::Icmpv6 => Self::Icmpv6(fnet_matchers::Icmpv6Packet::default()),
515        }
516    }
517}
518
519impl TryFrom<fnet_matchers::PacketTransportProtocol> for TransportProtocol {
520    type Error = TransportProtocolError;
521
522    fn try_from(matcher: fnet_matchers::PacketTransportProtocol) -> Result<Self, Self::Error> {
523        match matcher {
524            fnet_matchers::PacketTransportProtocol::Tcp(fnet_matchers::TcpPacket {
525                src_port,
526                dst_port,
527                __source_breaking,
528            }) => Ok(Self::Tcp {
529                src_port: src_port.map(TryInto::try_into).transpose()?,
530                dst_port: dst_port.map(TryInto::try_into).transpose()?,
531            }),
532            fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
533                src_port,
534                dst_port,
535                __source_breaking,
536            }) => Ok(Self::Udp {
537                src_port: src_port.map(TryInto::try_into).transpose()?,
538                dst_port: dst_port.map(TryInto::try_into).transpose()?,
539            }),
540            fnet_matchers::PacketTransportProtocol::Icmp(fnet_matchers::IcmpPacket {
541                __source_breaking,
542            }) => Ok(Self::Icmp),
543            fnet_matchers::PacketTransportProtocol::Icmpv6(fnet_matchers::Icmpv6Packet {
544                __source_breaking,
545            }) => Ok(Self::Icmpv6),
546            fnet_matchers::PacketTransportProtocol::__SourceBreaking { .. } => {
547                Err(TransportProtocolError::UnknownUnionVariant)
548            }
549        }
550    }
551}
552
553impl Debug for TransportProtocol {
554    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
555        // Omit empty fields.
556        match self {
557            TransportProtocol::Tcp { src_port, dst_port } => {
558                let mut debug_struct = f.debug_struct("Tcp");
559
560                // Omit empty fields.
561                if let Some(port) = &src_port {
562                    let _ = debug_struct.field("src_port", port);
563                }
564
565                if let Some(port) = &dst_port {
566                    let _ = debug_struct.field("dst_port", port);
567                }
568
569                debug_struct.finish()
570            }
571            TransportProtocol::Udp { src_port, dst_port } => {
572                let mut debug_struct = f.debug_struct("Udp");
573
574                // Omit empty fields.
575                if let Some(port) = &src_port {
576                    let _ = debug_struct.field("src_port", port);
577                }
578
579                if let Some(port) = &dst_port {
580                    let _ = debug_struct.field("dst_port", port);
581                }
582
583                debug_struct.finish()
584            }
585            TransportProtocol::Icmp => f.write_str("Icmp"),
586            TransportProtocol::Icmpv6 => f.write_str("Icmpv6"),
587        }
588    }
589}
590
591/// An extension type for [`fnet_matchers::Mark`]
592#[derive(Debug, Clone, PartialEq, Eq, Hash)]
593pub enum Mark {
594    Unmarked,
595    Marked { mask: u32, between: RangeInclusive<u32>, invert: bool },
596}
597
598#[derive(Debug, Clone, PartialEq, Error)]
599pub enum MarkError {
600    #[error("mark union is of an unknown variant")]
601    UnknownUnionVariant(u64),
602}
603
604impl TryFrom<fnet_matchers::Mark> for Mark {
605    type Error = MarkError;
606
607    fn try_from(matcher: fnet_matchers::Mark) -> Result<Self, Self::Error> {
608        match matcher {
609            fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked) => Ok(Mark::Unmarked),
610            fnet_matchers::Mark::Marked(fnet_matchers::Marked {
611                mask,
612                between: fnet_matchers::Between { start, end },
613                invert,
614            }) => Ok(Mark::Marked { mask, between: RangeInclusive::new(start, end), invert }),
615            fnet_matchers::Mark::__SourceBreaking { unknown_ordinal } => {
616                Err(MarkError::UnknownUnionVariant(unknown_ordinal))
617            }
618        }
619    }
620}
621
622impl From<Mark> for fnet_matchers::Mark {
623    fn from(matcher: Mark) -> Self {
624        match matcher {
625            Mark::Unmarked => fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
626            Mark::Marked { mask, between, invert } => {
627                let (start, end) = between.into_inner();
628                fnet_matchers::Mark::Marked(fnet_matchers::Marked {
629                    mask,
630                    between: fnet_matchers::Between { start, end },
631                    invert,
632                })
633            }
634        }
635    }
636}
637
638/// An extension type for [`fnet_matchers::MarkInDomain`].
639#[derive(Debug, Clone, PartialEq, Eq, Hash)]
640pub struct MarkInDomain {
641    pub domain: fnet::MarkDomain,
642    pub mark: Mark,
643}
644
645#[derive(Debug, Clone, PartialEq, Error)]
646pub enum MarkInDomainError {
647    #[error("mark conversion failed: {0}")]
648    Mark(MarkError),
649}
650
651impl TryFrom<fnet_matchers::MarkInDomain> for MarkInDomain {
652    type Error = MarkInDomainError;
653
654    fn try_from(matcher: fnet_matchers::MarkInDomain) -> Result<Self, Self::Error> {
655        let fnet_matchers::MarkInDomain { domain, mark } = matcher;
656        Ok(Self { domain, mark: mark.try_into().map_err(MarkInDomainError::Mark)? })
657    }
658}
659
660impl From<MarkInDomain> for fnet_matchers::MarkInDomain {
661    fn from(matcher: MarkInDomain) -> Self {
662        let MarkInDomain { domain, mark } = matcher;
663        Self { domain, mark: mark.into() }
664    }
665}
666
667/// An extension type for [`fnet_matchers::TcpSocket`]
668#[derive(Debug, Clone, PartialEq, Eq)]
669pub enum TcpSocket {
670    Empty,
671    SrcPort(BoundPort),
672    DstPort(BoundPort),
673    States(fnet_matchers::TcpState),
674}
675
676#[derive(Debug, PartialEq, Eq, Error)]
677pub enum TcpSocketError {
678    #[error("bound port matcher conversion failed: {0}")]
679    BoundPort(BoundPortError),
680    #[error("tcp union is of an unknown variant")]
681    UnknownUnionVariant(u64),
682}
683
684impl TryFrom<fnet_matchers::TcpSocket> for TcpSocket {
685    type Error = TcpSocketError;
686
687    fn try_from(matcher: fnet_matchers::TcpSocket) -> Result<Self, Self::Error> {
688        match matcher {
689            fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
690            fnet_matchers::TcpSocket::SrcPort(port) => {
691                Ok(Self::SrcPort(port.try_into().map_err(|e| TcpSocketError::BoundPort(e))?))
692            }
693            fnet_matchers::TcpSocket::DstPort(port) => {
694                Ok(Self::DstPort(port.try_into().map_err(|e| TcpSocketError::BoundPort(e))?))
695            }
696            fnet_matchers::TcpSocket::States(states) => Ok(Self::States(states)),
697            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal } => {
698                Err(TcpSocketError::UnknownUnionVariant(unknown_ordinal))
699            }
700        }
701    }
702}
703
704impl From<TcpSocket> for fnet_matchers::TcpSocket {
705    fn from(matcher: TcpSocket) -> Self {
706        match matcher {
707            TcpSocket::Empty => Self::Empty(fnet_matchers::Empty),
708            TcpSocket::SrcPort(port) => Self::SrcPort(port.into()),
709            TcpSocket::DstPort(port) => Self::DstPort(port.into()),
710            TcpSocket::States(states) => Self::States(states),
711        }
712    }
713}
714
715/// An extension type for [`fnet_matchers::UdpSocket`]
716#[derive(Debug, Clone, PartialEq, Eq)]
717pub enum UdpSocket {
718    Empty,
719    SrcPort(BoundPort),
720    DstPort(BoundPort),
721    States(fnet_matchers::UdpState),
722}
723
724#[derive(Debug, PartialEq, Eq, Error)]
725pub enum UdpSocketError {
726    #[error("bound port matcher conversion failed: {0}")]
727    BoundPort(BoundPortError),
728    #[error("udp union is of an unknown variant")]
729    UnknownUnionVariant(u64),
730}
731
732impl TryFrom<fnet_matchers::UdpSocket> for UdpSocket {
733    type Error = UdpSocketError;
734
735    fn try_from(matcher: fnet_matchers::UdpSocket) -> Result<Self, Self::Error> {
736        match matcher {
737            fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
738            fnet_matchers::UdpSocket::SrcPort(port) => {
739                Ok(Self::SrcPort(port.try_into().map_err(|e| UdpSocketError::BoundPort(e))?))
740            }
741            fnet_matchers::UdpSocket::DstPort(port) => {
742                Ok(Self::DstPort(port.try_into().map_err(|e| UdpSocketError::BoundPort(e))?))
743            }
744            fnet_matchers::UdpSocket::States(states) => Ok(Self::States(states)),
745            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal } => {
746                Err(UdpSocketError::UnknownUnionVariant(unknown_ordinal))
747            }
748        }
749    }
750}
751
752impl From<UdpSocket> for fnet_matchers::UdpSocket {
753    fn from(matcher: UdpSocket) -> Self {
754        match matcher {
755            UdpSocket::Empty => Self::Empty(fnet_matchers::Empty),
756            UdpSocket::SrcPort(port) => Self::SrcPort(port.into()),
757            UdpSocket::DstPort(port) => Self::DstPort(port.into()),
758            UdpSocket::States(states) => Self::States(states),
759        }
760    }
761}
762
763/// An extension type for [`fnet_matchers::SocketTransportProtocol`].
764#[derive(Debug, Clone, PartialEq, Eq)]
765pub enum SocketTransportProtocol {
766    Tcp(TcpSocket),
767    Udp(UdpSocket),
768}
769
770#[derive(Debug, PartialEq, Eq, Error)]
771pub enum SocketTransportProtocolError {
772    #[error("invalid tcp matcher: {0}")]
773    Tcp(TcpSocketError),
774    #[error("invalid udp matcher: {0}")]
775    Udp(UdpSocketError),
776    #[error("socket transport protocol union is of an unknown variant")]
777    UnknownUnionVariant(u64),
778}
779
780impl TryFrom<fnet_matchers::SocketTransportProtocol> for SocketTransportProtocol {
781    type Error = SocketTransportProtocolError;
782
783    fn try_from(matcher: fnet_matchers::SocketTransportProtocol) -> Result<Self, Self::Error> {
784        match matcher {
785            fnet_matchers::SocketTransportProtocol::Tcp(tcp) => {
786                Ok(Self::Tcp(tcp.try_into().map_err(|e| SocketTransportProtocolError::Tcp(e))?))
787            }
788            fnet_matchers::SocketTransportProtocol::Udp(udp) => {
789                Ok(Self::Udp(udp.try_into().map_err(|e| SocketTransportProtocolError::Udp(e))?))
790            }
791            fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal } => {
792                Err(SocketTransportProtocolError::UnknownUnionVariant(unknown_ordinal))
793            }
794        }
795    }
796}
797
798impl From<SocketTransportProtocol> for fnet_matchers::SocketTransportProtocol {
799    fn from(matcher: SocketTransportProtocol) -> Self {
800        match matcher {
801            SocketTransportProtocol::Tcp(tcp) => Self::Tcp(tcp.into()),
802            SocketTransportProtocol::Udp(udp) => Self::Udp(udp.into()),
803        }
804    }
805}
806
807#[cfg(test)]
808mod tests {
809    use net_declare::{fidl_ip, fidl_ip_v4, fidl_ip_v6, fidl_subnet, net_subnet_v4, net_subnet_v6};
810    use test_case::test_case;
811
812    use super::*;
813
814    #[test_case(
815        fnet_matchers::Interface::Id(1),
816        Interface::Id(NonZeroU64::new(1).unwrap());
817        "Interface"
818    )]
819    #[test_case(
820        fnet_matchers::BoundInterface::Unbound(fnet_matchers::Empty),
821        BoundInterface::Unbound;
822        "BoundInterface Unbound"
823    )]
824    #[test_case(
825        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(1)),
826        BoundInterface::Bound(Interface::Id(NonZeroU64::new(1).unwrap()));
827        "BoundInterface Bound"
828    )]
829    #[test_case(
830        fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
831        Mark::Unmarked;
832        "Unmarked"
833    )]
834    #[test_case(
835        fnet_matchers::Mark::Marked(fnet_matchers::Marked {
836            mask: 0xFF,
837            between: fnet_matchers::Between { start: 10, end: 20 },
838            invert: true,
839        }),
840        Mark::Marked { mask: 0xFF, between: 10..=20, invert: true };
841        "Marked"
842    )]
843    #[test_case(
844        fnet_matchers::MarkInDomain {
845            domain: fnet::MarkDomain::Mark1,
846            mark: fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
847        },
848        MarkInDomain {
849            domain: fnet::MarkDomain::Mark1,
850            mark: Mark::Unmarked,
851        };
852        "MarkInDomain"
853    )]
854    #[test_case(
855        fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
856        AddressMatcherType::Subnet(Subnet(net_subnet_v4!("192.0.2.0/24").into()));
857        "AddressMatcherTypeV4"
858    )]
859    #[test_case(
860        fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/64")),
861        AddressMatcherType::Subnet(Subnet(net_subnet_v6!("2001:db8::/64").into()));
862        "AddressMatcherTypeV6"
863    )]
864    #[test_case(
865        fnet_matchers::Address {
866            matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("2001:db8::/64")),
867            invert: true,
868        },
869        Address {
870            matcher: AddressMatcherType::Subnet(Subnet(net_subnet_v6!("2001:db8::/64").into())),
871            invert: true,
872        };
873        "AddressV6"
874    )]
875    #[test_case(
876        fnet_matchers::AddressRange {
877            start: fidl_ip!("192.0.2.0"),
878            end: fidl_ip!("192.0.2.1"),
879        },
880        AddressRange::V4(
881            fidl_ip_v4!("192.0.2.0")..=fidl_ip_v4!("192.0.2.1"),
882        );
883        "AddressRangeV4"
884    )]
885    #[test_case(
886        fnet_matchers::AddressRange {
887            start: fidl_ip!("2001:db8::0"),
888            end: fidl_ip!("2001:db8::8"),
889        },
890        AddressRange::V6(
891            fidl_ip_v6!("2001:db8::0")..=fidl_ip_v6!("2001:db8::8"),
892        );
893        "AddressRangeV6"
894    )]
895    #[test_case(
896        fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
897            src_port: Some(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }),
898            dst_port: None,
899            ..Default::default()
900        }),
901        TransportProtocol::Udp {
902            src_port: Some(Port { range: 1024..=u16::MAX, invert: false }),
903            dst_port: None,
904        };
905        "TransportProtocol"
906    )]
907    #[test_case(
908        fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty),
909        TcpSocket::Empty;
910        "TcpSocketEmpty"
911    )]
912    #[test_case(
913        fnet_matchers::TcpSocket::SrcPort(
914            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false })
915        ),
916        TcpSocket::SrcPort(BoundPort::Bound(Port { range: 1024..=u16::MAX, invert: false }));
917        "TcpSocketSrcPort"
918    )]
919    #[test_case(
920        fnet_matchers::TcpSocket::DstPort(
921            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 80, end: 80, invert: true })
922        ),
923        TcpSocket::DstPort(BoundPort::Bound(Port { range: 80..=80, invert: true }));
924        "TcpSocketDstPort"
925    )]
926    #[test_case(
927        fnet_matchers::TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED),
928        TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED);
929        "TcpSocketStates"
930    )]
931    #[test_case(
932        fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty),
933        UdpSocket::Empty;
934        "UdpSocketEmpty"
935    )]
936    #[test_case(
937        fnet_matchers::UdpSocket::SrcPort(
938            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false })
939        ),
940        UdpSocket::SrcPort(BoundPort::Bound(Port { range: 1024..=u16::MAX, invert: false }));
941        "UdpSocketSrcPort"
942    )]
943    #[test_case(
944        fnet_matchers::UdpSocket::DstPort(
945            fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 53, end: 53, invert: true })
946        ),
947        UdpSocket::DstPort(BoundPort::Bound(Port { range: 53..=53, invert: true }));
948        "UdpSocketDstPort"
949    )]
950    #[test_case(
951        fnet_matchers::UdpSocket::States(fnet_matchers::UdpState::BOUND),
952        UdpSocket::States(fnet_matchers::UdpState::BOUND);
953        "UdpSocketStates"
954    )]
955    #[test_case(
956        fnet_matchers::SocketTransportProtocol::Tcp(
957            fnet_matchers::TcpSocket::SrcPort(
958                fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 123, end: 123, invert: false })
959            )
960        ),
961        SocketTransportProtocol::Tcp(TcpSocket::SrcPort(BoundPort::Bound(Port { range: 123..=123, invert: false })));
962        "SocketTransportProtocolTcp"
963    )]
964    #[test_case(
965        fnet_matchers::SocketTransportProtocol::Udp(
966            fnet_matchers::UdpSocket::SrcPort(
967                fnet_matchers::BoundPort::Bound(fnet_matchers::Port { start: 123, end: 123, invert: false })
968            )
969        ),
970        SocketTransportProtocol::Udp(UdpSocket::SrcPort(BoundPort::Bound(Port { range: 123..=123, invert: false })));
971        "SocketTransportProtocolUdp"
972    )]
973    #[test_case(
974        fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty),
975        BoundPort::Unbound;
976        "BoundPort Unbound"
977    )]
978    #[test_case(
979        fnet_matchers::BoundAddress::Unbound(fnet_matchers::Empty),
980        BoundAddress::Unbound;
981        "BoundAddress Unbound"
982    )]
983    #[test_case(
984        fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
985        TcpSocket::SrcPort(BoundPort::Unbound);
986        "TcpSocket SrcPort Unbound"
987    )]
988    #[test_case(
989        fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
990        TcpSocket::DstPort(BoundPort::Unbound);
991        "TcpSocket DstPort Unbound"
992    )]
993    #[test_case(
994        fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
995        UdpSocket::SrcPort(BoundPort::Unbound);
996        "UdpSocket SrcPort Unbound"
997    )]
998    #[test_case(
999        fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Unbound(fnet_matchers::Empty)),
1000        UdpSocket::DstPort(BoundPort::Unbound);
1001        "UdpSocket DstPort Unbound"
1002    )]
1003    fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
1004    where
1005        E: TryFrom<F> + Clone + Debug + PartialEq,
1006        <E as TryFrom<F>>::Error: Debug + PartialEq,
1007        F: From<E> + Clone + Debug + PartialEq,
1008    {
1009        assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
1010        assert_eq!(<_ as Into<F>>::into(local_type), fidl_type.clone());
1011    }
1012
1013    #[test_case(
1014        fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 0 } =>
1015            Err(BoundInterfaceError::UnknownUnionVariant(0));
1016        "UnknownUnionVariant"
1017    )]
1018    #[test_case(
1019        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(0)) =>
1020            Err(BoundInterfaceError::Interface(InterfaceError::ZeroId));
1021        "InterfaceError"
1022    )]
1023    fn bound_interface_try_from_error(
1024        fidl: fnet_matchers::BoundInterface,
1025    ) -> Result<BoundInterface, BoundInterfaceError> {
1026        BoundInterface::try_from(fidl)
1027    }
1028
1029    #[test_case(
1030        fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 0 } =>
1031            Err(MarkError::UnknownUnionVariant(0));
1032        "UnknownUnionVariant"
1033    )]
1034    fn mark_try_from_error(fidl: fnet_matchers::Mark) -> Result<Mark, MarkError> {
1035        Mark::try_from(fidl)
1036    }
1037
1038    #[test_case(
1039        fnet_matchers::MarkInDomain {
1040            domain: fnet::MarkDomain::Mark1,
1041            mark: fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 0 },
1042        } => Err(MarkInDomainError::Mark(MarkError::UnknownUnionVariant(0)));
1043        "MarkInDomain Mark Error"
1044    )]
1045    fn mark_and_domain_try_from_error(
1046        fidl: fnet_matchers::MarkInDomain,
1047    ) -> Result<MarkInDomain, MarkInDomainError> {
1048        MarkInDomain::try_from(fidl)
1049    }
1050
1051    #[test]
1052    fn address_matcher_type_try_from_unknown_variant() {
1053        assert_eq!(
1054            AddressMatcherType::try_from(fnet_matchers::AddressMatcherType::__SourceBreaking {
1055                unknown_ordinal: 0
1056            }),
1057            Err(AddressMatcherTypeError::UnknownUnionVariant)
1058        );
1059    }
1060
1061    #[test]
1062    fn subnet_try_from_invalid() {
1063        assert_eq!(
1064            Subnet::try_from(fnet::Subnet { addr: fidl_ip!("192.0.2.1"), prefix_len: 33 }),
1065            Err(SubnetError::PrefixTooLong)
1066        );
1067        assert_eq!(Subnet::try_from(fidl_subnet!("192.0.2.1/24")), Err(SubnetError::HostBitsSet));
1068    }
1069
1070    #[test]
1071    fn address_range_try_from_invalid() {
1072        assert_eq!(
1073            AddressRange::try_from(fnet_matchers::AddressRange {
1074                start: fidl_ip!("192.0.2.1"),
1075                end: fidl_ip!("192.0.2.0"),
1076            }),
1077            Err(AddressRangeError::Invalid)
1078        );
1079        assert_eq!(
1080            AddressRange::try_from(fnet_matchers::AddressRange {
1081                start: fidl_ip!("2001:db8::1"),
1082                end: fidl_ip!("2001:db8::"),
1083            }),
1084            Err(AddressRangeError::Invalid)
1085        );
1086    }
1087
1088    #[test]
1089    fn address_range_try_from_family_mismatch() {
1090        assert_eq!(
1091            AddressRange::try_from(fnet_matchers::AddressRange {
1092                start: fidl_ip!("192.0.2.0"),
1093                end: fidl_ip!("2001:db8::"),
1094            }),
1095            Err(AddressRangeError::FamilyMismatch)
1096        );
1097    }
1098
1099    #[test]
1100    fn port_matcher_try_from_invalid() {
1101        assert_eq!(
1102            Port::try_from(fnet_matchers::Port { start: 1, end: 0, invert: false }),
1103            Err(PortError::InvalidPortRange)
1104        );
1105    }
1106
1107    #[test]
1108    fn transport_protocol_try_from_unknown_variant() {
1109        assert_eq!(
1110            TransportProtocol::try_from(fnet_matchers::PacketTransportProtocol::__SourceBreaking {
1111                unknown_ordinal: 0
1112            }),
1113            Err(TransportProtocolError::UnknownUnionVariant)
1114        );
1115    }
1116
1117    #[test_case(
1118        fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
1119            Err(TcpSocketError::UnknownUnionVariant(100));
1120        "TcpSocket UnknownUnionVariant"
1121    )]
1122    #[test_case(
1123        fnet_matchers::TcpSocket::SrcPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1124            start: 1,
1125            end: 0,
1126            invert: false,
1127        })) => Err(TcpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1128        "TcpSocket SrcPort Error"
1129    )]
1130    #[test_case(
1131        fnet_matchers::TcpSocket::DstPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1132            start: 1,
1133            end: 0,
1134            invert: false,
1135        })) => Err(TcpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1136        "TcpSocket DstPort Error"
1137    )]
1138    fn tcp_socket_try_from_error(
1139        fidl: fnet_matchers::TcpSocket,
1140    ) -> Result<TcpSocket, TcpSocketError> {
1141        TcpSocket::try_from(fidl)
1142    }
1143
1144    #[test_case(
1145        fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
1146            Err(UdpSocketError::UnknownUnionVariant(100));
1147        "UdpSocket UnknownUnionVariant"
1148    )]
1149    #[test_case(
1150        fnet_matchers::UdpSocket::SrcPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1151            start: 1,
1152            end: 0,
1153            invert: false,
1154        })) => Err(UdpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1155        "UdpSocket SrcPort Error"
1156    )]
1157    #[test_case(
1158        fnet_matchers::UdpSocket::DstPort(fnet_matchers::BoundPort::Bound(fnet_matchers::Port {
1159            start: 1,
1160            end: 0,
1161            invert: false,
1162        })) => Err(UdpSocketError::BoundPort(BoundPortError::Port(PortError::InvalidPortRange)));
1163        "UdpSocket DstPort Error"
1164    )]
1165    fn udp_socket_try_from_error(
1166        fidl: fnet_matchers::UdpSocket,
1167    ) -> Result<UdpSocket, UdpSocketError> {
1168        UdpSocket::try_from(fidl)
1169    }
1170
1171    #[test_case(
1172        fnet_matchers::SocketTransportProtocol::__SourceBreaking {
1173            unknown_ordinal: 100
1174        } => Err(SocketTransportProtocolError::UnknownUnionVariant(100));
1175        "SocketTransportProtocol UnknownUnionVariant"
1176    )]
1177    #[test_case(
1178        fnet_matchers::SocketTransportProtocol::Tcp(
1179            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 }
1180        ) => Err(SocketTransportProtocolError::Tcp(TcpSocketError::UnknownUnionVariant(100)));
1181        "SocketTransportProtocol Tcp Error"
1182    )]
1183    #[test_case(
1184        fnet_matchers::SocketTransportProtocol::Udp(
1185            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 }
1186        ) => Err(SocketTransportProtocolError::Udp(UdpSocketError::UnknownUnionVariant(100)));
1187        "SocketTransportProtocol Udp Error"
1188    )]
1189    fn socket_transport_protocol_try_from_error(
1190        fidl: fnet_matchers::SocketTransportProtocol,
1191    ) -> Result<SocketTransportProtocol, SocketTransportProtocolError> {
1192        SocketTransportProtocol::try_from(fidl)
1193    }
1194}