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::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 => {
96                fnet_matchers::BoundInterface::Unbound(fnet_matchers::Unbound)
97            }
98            BoundInterface::Bound(interface) => {
99                fnet_matchers::BoundInterface::Bound(interface.into())
100            }
101        }
102    }
103}
104
105impl TryFrom<fnet_matchers::BoundInterface> for BoundInterface {
106    type Error = BoundInterfaceError;
107
108    fn try_from(matcher: fnet_matchers::BoundInterface) -> Result<Self, Self::Error> {
109        match matcher {
110            fnet_matchers::BoundInterface::Unbound(fnet_matchers::Unbound) => {
111                Ok(BoundInterface::Unbound)
112            }
113            fnet_matchers::BoundInterface::Bound(interface) => Ok(BoundInterface::Bound(
114                interface.try_into().map_err(|e| BoundInterfaceError::Interface(e))?,
115            )),
116            fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal } => {
117                Err(BoundInterfaceError::UnknownUnionVariant(unknown_ordinal))
118            }
119        }
120    }
121}
122
123/// Extension type for the `Subnet` variant of [`fnet_matchers::Address`].
124///
125/// This type witnesses to the invariant that the prefix length of the subnet is
126/// no greater than the number of bits in the IP address, and that no host bits
127/// in the network address are set.
128#[derive(Clone, Copy, Eq, Hash, PartialEq)]
129pub struct Subnet(fnet::Subnet);
130
131/// Errors when creating a [`Subnet`].
132#[derive(Debug, Error, PartialEq)]
133pub enum SubnetError {
134    #[error("prefix length of subnet is longer than number of bits in IP address")]
135    PrefixTooLong,
136    #[error("host bits are set in subnet network")]
137    HostBitsSet,
138}
139
140impl Subnet {
141    pub fn get(&self) -> fnet::Subnet {
142        let Subnet(subnet) = &self;
143        *subnet
144    }
145}
146
147impl From<Subnet> for fnet::Subnet {
148    fn from(subnet: Subnet) -> Self {
149        let Subnet(subnet) = subnet;
150        subnet
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        // We convert to `net_types::ip::Subnet` to validate the subnet's
161        // properties, but we don't store the subnet as that type because we
162        // want to avoid forcing all `Resource` types in this library to be
163        // parameterized on IP version.
164        let result = match addr {
165            fnet::IpAddress::Ipv4(v4) => {
166                net_types::ip::Subnet::<net_types::ip::Ipv4Addr>::new(v4.into_ext(), prefix_len)
167                    .map(|_| Subnet(subnet))
168            }
169            fnet::IpAddress::Ipv6(v6) => {
170                net_types::ip::Subnet::<net_types::ip::Ipv6Addr>::new(v6.into_ext(), prefix_len)
171                    .map(|_| Subnet(subnet))
172            }
173        };
174        result.map_err(|e| match e {
175            net_types::ip::SubnetError::PrefixTooLong => SubnetError::PrefixTooLong,
176            net_types::ip::SubnetError::HostBitsSet => SubnetError::HostBitsSet,
177        })
178    }
179}
180
181impl Debug for Subnet {
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        let fnet::Subnet { addr, prefix_len } = self.0;
184
185        match addr {
186            fnet::IpAddress::Ipv4(v4) => {
187                let subnet = net_types::ip::Subnet::<net_types::ip::Ipv4Addr>::new(
188                    v4.into_ext(),
189                    prefix_len,
190                );
191
192                match subnet {
193                    Ok(inner) => inner.fmt(f),
194                    Err(err) => err.fmt(f),
195                }
196            }
197            fnet::IpAddress::Ipv6(v6) => {
198                let subnet = net_types::ip::Subnet::<net_types::ip::Ipv6Addr>::new(
199                    v6.into_ext(),
200                    prefix_len,
201                );
202
203                match subnet {
204                    Ok(inner) => inner.fmt(f),
205                    Err(err) => err.fmt(f),
206                }
207            }
208        }
209    }
210}
211
212/// Extension type for [`fnet_matchers::AddressRange`].
213///
214/// This type witnesses to the invariant that `start` is in the same IP family
215/// as `end`, and that `start <= end`. (Comparisons are performed on the
216/// numerical big-endian representation of the IP address.)
217#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct AddressRange {
219    range: RangeInclusive<fnet::IpAddress>,
220}
221
222/// Errors when creating an [`AddressRange`].
223#[derive(Debug, Error, PartialEq)]
224pub enum AddressRangeError {
225    #[error("invalid address range (start must be <= end)")]
226    Invalid,
227    #[error("address range start and end addresses are not the same IP family")]
228    FamilyMismatch,
229}
230
231impl AddressRange {
232    pub fn start(&self) -> fnet::IpAddress {
233        *self.range.start()
234    }
235
236    pub fn end(&self) -> fnet::IpAddress {
237        *self.range.end()
238    }
239}
240
241impl From<AddressRange> for fnet_matchers::AddressRange {
242    fn from(range: AddressRange) -> Self {
243        Self { start: range.start(), end: range.end() }
244    }
245}
246
247impl TryFrom<fnet_matchers::AddressRange> for AddressRange {
248    type Error = AddressRangeError;
249
250    fn try_from(range: fnet_matchers::AddressRange) -> Result<Self, Self::Error> {
251        let fnet_matchers::AddressRange { start, end } = range;
252        match (start, end) {
253            (
254                fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: start_bytes }),
255                fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: end_bytes }),
256            ) => {
257                if u32::from_be_bytes(start_bytes) > u32::from_be_bytes(end_bytes) {
258                    Err(AddressRangeError::Invalid)
259                } else {
260                    Ok(Self { range: start..=end })
261                }
262            }
263            (
264                fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: start_bytes }),
265                fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: end_bytes }),
266            ) => {
267                if u128::from_be_bytes(start_bytes) > u128::from_be_bytes(end_bytes) {
268                    Err(AddressRangeError::Invalid)
269                } else {
270                    Ok(Self { range: start..=end })
271                }
272            }
273            _ => Err(AddressRangeError::FamilyMismatch),
274        }
275    }
276}
277
278/// Extension type for [`fnet_matchers::Address`].
279#[derive(Clone, PartialEq, Eq)]
280pub enum AddressMatcherType {
281    Subnet(Subnet),
282    Range(AddressRange),
283}
284
285/// Errors when creating an [`AddressMatcherType`].
286#[derive(Debug, Error, PartialEq)]
287pub enum AddressMatcherTypeError {
288    #[error("AddressMatcher is of an unknown variant")]
289    UnknownUnionVariant,
290    #[error("subnet conversion error: {0}")]
291    Subnet(SubnetError),
292    #[error("address range conversion error: {0}")]
293    AddressRange(AddressRangeError),
294}
295
296impl From<SubnetError> for AddressMatcherTypeError {
297    fn from(value: SubnetError) -> Self {
298        AddressMatcherTypeError::Subnet(value)
299    }
300}
301impl From<AddressRangeError> for AddressMatcherTypeError {
302    fn from(value: AddressRangeError) -> Self {
303        AddressMatcherTypeError::AddressRange(value)
304    }
305}
306
307impl From<AddressMatcherType> for fnet_matchers::AddressMatcherType {
308    fn from(matcher: AddressMatcherType) -> Self {
309        match matcher {
310            AddressMatcherType::Subnet(subnet) => Self::Subnet(subnet.into()),
311            AddressMatcherType::Range(range) => Self::Range(range.into()),
312        }
313    }
314}
315
316impl TryFrom<fnet_matchers::AddressMatcherType> for AddressMatcherType {
317    type Error = AddressMatcherTypeError;
318
319    fn try_from(matcher: fnet_matchers::AddressMatcherType) -> Result<Self, Self::Error> {
320        match matcher {
321            fnet_matchers::AddressMatcherType::Subnet(subnet) => {
322                Ok(Self::Subnet(subnet.try_into()?))
323            }
324            fnet_matchers::AddressMatcherType::Range(range) => Ok(Self::Range(range.try_into()?)),
325            fnet_matchers::AddressMatcherType::__SourceBreaking { .. } => {
326                Err(AddressMatcherTypeError::UnknownUnionVariant)
327            }
328        }
329    }
330}
331
332impl Debug for AddressMatcherType {
333    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334        match self {
335            AddressMatcherType::Subnet(subnet) => subnet.fmt(f),
336            AddressMatcherType::Range(address_range) => address_range.fmt(f),
337        }
338    }
339}
340
341/// Extension type for [`fnet_matchers::Address`].
342#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct Address {
344    pub matcher: AddressMatcherType,
345    pub invert: bool,
346}
347
348/// Errors when creating an [`Address`].
349#[derive(Debug, Error, PartialEq)]
350pub enum AddressError {
351    #[error("address matcher conversion failure: {0}")]
352    AddressMatcherType(AddressMatcherTypeError),
353}
354
355impl From<AddressMatcherTypeError> for AddressError {
356    fn from(value: AddressMatcherTypeError) -> Self {
357        Self::AddressMatcherType(value)
358    }
359}
360
361impl From<Address> for fnet_matchers::Address {
362    fn from(matcher: Address) -> Self {
363        let Address { matcher, invert } = matcher;
364        Self { matcher: matcher.into(), invert }
365    }
366}
367
368impl TryFrom<fnet_matchers::Address> for Address {
369    type Error = AddressError;
370
371    fn try_from(matcher: fnet_matchers::Address) -> Result<Self, Self::Error> {
372        let fnet_matchers::Address { matcher, invert } = matcher;
373        Ok(Self { matcher: matcher.try_into()?, invert })
374    }
375}
376
377/// Extension type for [`fnet_matchers::Port`].
378///
379/// This type witnesses to the invariant that `start <= end`.
380#[derive(Debug, Clone, PartialEq, Eq)]
381pub struct Port {
382    range: RangeInclusive<u16>,
383    pub invert: bool,
384}
385
386/// Errors when creating a `Port`.
387#[derive(Debug, Error, PartialEq, Eq)]
388pub enum PortError {
389    #[error("invalid port range (start must be <= end)")]
390    InvalidPortRange,
391}
392
393impl Port {
394    pub fn new(start: u16, end: u16, invert: bool) -> Result<Self, PortError> {
395        if start > end {
396            return Err(PortError::InvalidPortRange);
397        }
398        Ok(Self { range: start..=end, invert })
399    }
400
401    pub fn range(&self) -> &RangeInclusive<u16> {
402        &self.range
403    }
404
405    pub fn start(&self) -> u16 {
406        *self.range.start()
407    }
408
409    pub fn end(&self) -> u16 {
410        *self.range.end()
411    }
412}
413
414impl From<Port> for fnet_matchers::Port {
415    fn from(matcher: Port) -> Self {
416        let Port { range, invert } = matcher;
417        Self { start: *range.start(), end: *range.end(), invert }
418    }
419}
420
421impl TryFrom<fnet_matchers::Port> for Port {
422    type Error = PortError;
423
424    fn try_from(matcher: fnet_matchers::Port) -> Result<Self, Self::Error> {
425        let fnet_matchers::Port { start, end, invert } = matcher;
426        if start > end {
427            return Err(PortError::InvalidPortRange);
428        }
429        Ok(Self { range: start..=end, invert })
430    }
431}
432
433/// Extension type for [`fnet_matchers::PacketTransportProtocol`].
434#[derive(Clone, PartialEq)]
435pub enum TransportProtocol {
436    Tcp { src_port: Option<Port>, dst_port: Option<Port> },
437    Udp { src_port: Option<Port>, dst_port: Option<Port> },
438    Icmp,
439    Icmpv6,
440}
441
442/// Errors when creating a [`TransportProtocol`].
443#[derive(Debug, Error, PartialEq)]
444pub enum TransportProtocolError {
445    #[error("invalid port: {0}")]
446    Port(PortError),
447    #[error("TransportProtocol is of an unknown variant")]
448    UnknownUnionVariant,
449}
450
451impl From<PortError> for TransportProtocolError {
452    fn from(value: PortError) -> Self {
453        TransportProtocolError::Port(value)
454    }
455}
456
457impl From<TransportProtocol> for fnet_matchers::PacketTransportProtocol {
458    fn from(matcher: TransportProtocol) -> Self {
459        match matcher {
460            TransportProtocol::Tcp { src_port, dst_port } => Self::Tcp(fnet_matchers::TcpPacket {
461                src_port: src_port.map(Into::into),
462                dst_port: dst_port.map(Into::into),
463                __source_breaking: SourceBreaking,
464            }),
465            TransportProtocol::Udp { src_port, dst_port } => Self::Udp(fnet_matchers::UdpPacket {
466                src_port: src_port.map(Into::into),
467                dst_port: dst_port.map(Into::into),
468                __source_breaking: SourceBreaking,
469            }),
470            TransportProtocol::Icmp => Self::Icmp(fnet_matchers::IcmpPacket::default()),
471            TransportProtocol::Icmpv6 => Self::Icmpv6(fnet_matchers::Icmpv6Packet::default()),
472        }
473    }
474}
475
476impl TryFrom<fnet_matchers::PacketTransportProtocol> for TransportProtocol {
477    type Error = TransportProtocolError;
478
479    fn try_from(matcher: fnet_matchers::PacketTransportProtocol) -> Result<Self, Self::Error> {
480        match matcher {
481            fnet_matchers::PacketTransportProtocol::Tcp(fnet_matchers::TcpPacket {
482                src_port,
483                dst_port,
484                __source_breaking,
485            }) => Ok(Self::Tcp {
486                src_port: src_port.map(TryInto::try_into).transpose()?,
487                dst_port: dst_port.map(TryInto::try_into).transpose()?,
488            }),
489            fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
490                src_port,
491                dst_port,
492                __source_breaking,
493            }) => Ok(Self::Udp {
494                src_port: src_port.map(TryInto::try_into).transpose()?,
495                dst_port: dst_port.map(TryInto::try_into).transpose()?,
496            }),
497            fnet_matchers::PacketTransportProtocol::Icmp(fnet_matchers::IcmpPacket {
498                __source_breaking,
499            }) => Ok(Self::Icmp),
500            fnet_matchers::PacketTransportProtocol::Icmpv6(fnet_matchers::Icmpv6Packet {
501                __source_breaking,
502            }) => Ok(Self::Icmpv6),
503            fnet_matchers::PacketTransportProtocol::__SourceBreaking { .. } => {
504                Err(TransportProtocolError::UnknownUnionVariant)
505            }
506        }
507    }
508}
509
510impl Debug for TransportProtocol {
511    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512        // Omit empty fields.
513        match self {
514            TransportProtocol::Tcp { src_port, dst_port } => {
515                let mut debug_struct = f.debug_struct("Tcp");
516
517                // Omit empty fields.
518                if let Some(port) = &src_port {
519                    let _ = debug_struct.field("src_port", port);
520                }
521
522                if let Some(port) = &dst_port {
523                    let _ = debug_struct.field("dst_port", port);
524                }
525
526                debug_struct.finish()
527            }
528            TransportProtocol::Udp { src_port, dst_port } => {
529                let mut debug_struct = f.debug_struct("Udp");
530
531                // Omit empty fields.
532                if let Some(port) = &src_port {
533                    let _ = debug_struct.field("src_port", port);
534                }
535
536                if let Some(port) = &dst_port {
537                    let _ = debug_struct.field("dst_port", port);
538                }
539
540                debug_struct.finish()
541            }
542            TransportProtocol::Icmp => f.write_str("Icmp"),
543            TransportProtocol::Icmpv6 => f.write_str("Icmpv6"),
544        }
545    }
546}
547
548/// An extension type for [`fnet_matchers::Mark`]
549#[derive(Debug, Clone, PartialEq, Eq, Hash)]
550pub enum Mark {
551    Unmarked,
552    Marked { mask: u32, between: RangeInclusive<u32>, invert: bool },
553}
554
555#[derive(Debug, Clone, PartialEq, Error)]
556pub enum MarkError {
557    #[error("mark union is of an unknown variant")]
558    UnknownUnionVariant(u64),
559}
560
561impl TryFrom<fnet_matchers::Mark> for Mark {
562    type Error = MarkError;
563
564    fn try_from(matcher: fnet_matchers::Mark) -> Result<Self, Self::Error> {
565        match matcher {
566            fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked) => Ok(Mark::Unmarked),
567            fnet_matchers::Mark::Marked(fnet_matchers::Marked {
568                mask,
569                between: fnet_matchers::Between { start, end },
570                invert,
571            }) => Ok(Mark::Marked { mask, between: RangeInclusive::new(start, end), invert }),
572            fnet_matchers::Mark::__SourceBreaking { unknown_ordinal } => {
573                Err(MarkError::UnknownUnionVariant(unknown_ordinal))
574            }
575        }
576    }
577}
578
579impl From<Mark> for fnet_matchers::Mark {
580    fn from(matcher: Mark) -> Self {
581        match matcher {
582            Mark::Unmarked => fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
583            Mark::Marked { mask, between, invert } => {
584                let (start, end) = between.into_inner();
585                fnet_matchers::Mark::Marked(fnet_matchers::Marked {
586                    mask,
587                    between: fnet_matchers::Between { start, end },
588                    invert,
589                })
590            }
591        }
592    }
593}
594
595/// An extension type for [`fnet_matchers::TcpSocket`]
596#[derive(Debug, Clone, PartialEq, Eq)]
597pub enum TcpSocket {
598    Empty,
599    SrcPort(Port),
600    DstPort(Port),
601    States(fnet_matchers::TcpState),
602}
603
604#[derive(Debug, PartialEq, Eq, Error)]
605pub enum TcpSocketError {
606    #[error("port matcher conversion failed: {0}")]
607    Port(PortError),
608    #[error("tcp union is of an unknown variant")]
609    UnknownUnionVariant(u64),
610}
611
612impl TryFrom<fnet_matchers::TcpSocket> for TcpSocket {
613    type Error = TcpSocketError;
614
615    fn try_from(matcher: fnet_matchers::TcpSocket) -> Result<Self, Self::Error> {
616        match matcher {
617            fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
618            fnet_matchers::TcpSocket::SrcPort(port) => {
619                Ok(Self::SrcPort(port.try_into().map_err(|e| TcpSocketError::Port(e))?))
620            }
621            fnet_matchers::TcpSocket::DstPort(port) => {
622                Ok(Self::DstPort(port.try_into().map_err(|e| TcpSocketError::Port(e))?))
623            }
624            fnet_matchers::TcpSocket::States(states) => Ok(Self::States(states)),
625            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal } => {
626                Err(TcpSocketError::UnknownUnionVariant(unknown_ordinal))
627            }
628        }
629    }
630}
631
632impl From<TcpSocket> for fnet_matchers::TcpSocket {
633    fn from(matcher: TcpSocket) -> Self {
634        match matcher {
635            TcpSocket::Empty => Self::Empty(fnet_matchers::Empty),
636            TcpSocket::SrcPort(port) => Self::SrcPort(port.into()),
637            TcpSocket::DstPort(port) => Self::DstPort(port.into()),
638            TcpSocket::States(states) => Self::States(states),
639        }
640    }
641}
642
643/// An extension type for [`fnet_matchers::UdpSocket`]
644#[derive(Debug, Clone, PartialEq, Eq)]
645pub enum UdpSocket {
646    Empty,
647    SrcPort(Port),
648    DstPort(Port),
649    States(fnet_matchers::UdpState),
650}
651
652#[derive(Debug, PartialEq, Eq, Error)]
653pub enum UdpSocketError {
654    #[error("port matcher conversion failed: {0}")]
655    Port(PortError),
656    #[error("udp union is of an unknown variant")]
657    UnknownUnionVariant(u64),
658}
659
660impl TryFrom<fnet_matchers::UdpSocket> for UdpSocket {
661    type Error = UdpSocketError;
662
663    fn try_from(matcher: fnet_matchers::UdpSocket) -> Result<Self, Self::Error> {
664        match matcher {
665            fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty) => Ok(Self::Empty),
666            fnet_matchers::UdpSocket::SrcPort(port) => {
667                Ok(Self::SrcPort(port.try_into().map_err(|e| UdpSocketError::Port(e))?))
668            }
669            fnet_matchers::UdpSocket::DstPort(port) => {
670                Ok(Self::DstPort(port.try_into().map_err(|e| UdpSocketError::Port(e))?))
671            }
672            fnet_matchers::UdpSocket::States(states) => Ok(Self::States(states)),
673            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal } => {
674                Err(UdpSocketError::UnknownUnionVariant(unknown_ordinal))
675            }
676        }
677    }
678}
679
680impl From<UdpSocket> for fnet_matchers::UdpSocket {
681    fn from(matcher: UdpSocket) -> Self {
682        match matcher {
683            UdpSocket::Empty => Self::Empty(fnet_matchers::Empty),
684            UdpSocket::SrcPort(port) => Self::SrcPort(port.into()),
685            UdpSocket::DstPort(port) => Self::DstPort(port.into()),
686            UdpSocket::States(states) => Self::States(states),
687        }
688    }
689}
690
691/// An extension type for [`fnet_matchers::SocketTransportProtocol`].
692#[derive(Debug, Clone, PartialEq, Eq)]
693pub enum SocketTransportProtocol {
694    Tcp(TcpSocket),
695    Udp(UdpSocket),
696}
697
698#[derive(Debug, PartialEq, Eq, Error)]
699pub enum SocketTransportProtocolError {
700    #[error("invalid tcp matcher: {0}")]
701    Tcp(TcpSocketError),
702    #[error("invalid udp matcher: {0}")]
703    Udp(UdpSocketError),
704    #[error("socket transport protocol union is of an unknown variant")]
705    UnknownUnionVariant(u64),
706}
707
708impl TryFrom<fnet_matchers::SocketTransportProtocol> for SocketTransportProtocol {
709    type Error = SocketTransportProtocolError;
710
711    fn try_from(matcher: fnet_matchers::SocketTransportProtocol) -> Result<Self, Self::Error> {
712        match matcher {
713            fnet_matchers::SocketTransportProtocol::Tcp(tcp) => {
714                Ok(Self::Tcp(tcp.try_into().map_err(|e| SocketTransportProtocolError::Tcp(e))?))
715            }
716            fnet_matchers::SocketTransportProtocol::Udp(udp) => {
717                Ok(Self::Udp(udp.try_into().map_err(|e| SocketTransportProtocolError::Udp(e))?))
718            }
719            fnet_matchers::SocketTransportProtocol::__SourceBreaking { unknown_ordinal } => {
720                Err(SocketTransportProtocolError::UnknownUnionVariant(unknown_ordinal))
721            }
722        }
723    }
724}
725
726impl From<SocketTransportProtocol> for fnet_matchers::SocketTransportProtocol {
727    fn from(matcher: SocketTransportProtocol) -> Self {
728        match matcher {
729            SocketTransportProtocol::Tcp(tcp) => Self::Tcp(tcp.into()),
730            SocketTransportProtocol::Udp(udp) => Self::Udp(udp.into()),
731        }
732    }
733}
734
735#[cfg(test)]
736mod tests {
737    use net_declare::{fidl_ip, fidl_subnet};
738    use test_case::test_case;
739
740    use super::*;
741
742    #[test_case(
743        fnet_matchers::Interface::Id(1),
744        Interface::Id(NonZeroU64::new(1).unwrap());
745        "Interface"
746    )]
747    #[test_case(
748        fnet_matchers::BoundInterface::Unbound(fnet_matchers::Unbound),
749        BoundInterface::Unbound;
750        "BoundInterface Unbound"
751    )]
752    #[test_case(
753        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(1)),
754        BoundInterface::Bound(Interface::Id(NonZeroU64::new(1).unwrap()));
755        "BoundInterface Bound"
756    )]
757    #[test_case(
758        fnet_matchers::Mark::Unmarked(fnet_matchers::Unmarked),
759        Mark::Unmarked;
760        "Unmarked"
761    )]
762    #[test_case(
763        fnet_matchers::Mark::Marked(fnet_matchers::Marked {
764            mask: 0xFF,
765            between: fnet_matchers::Between { start: 10, end: 20 },
766            invert: true,
767        }),
768        Mark::Marked { mask: 0xFF, between: 10..=20, invert: true };
769        "Marked"
770    )]
771    #[test_case(
772        fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
773        AddressMatcherType::Subnet(Subnet(fidl_subnet!("192.0.2.0/24")));
774        "AddressMatcherType"
775    )]
776    #[test_case(
777        fnet_matchers::Address {
778            matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
779            invert: true,
780        },
781        Address {
782            matcher: AddressMatcherType::Subnet(Subnet(fidl_subnet!("192.0.2.0/24"))),
783            invert: true,
784        };
785        "Address"
786    )]
787    #[test_case(
788        fnet_matchers::AddressRange {
789            start: fidl_ip!("192.0.2.0"),
790            end: fidl_ip!("192.0.2.1"),
791        },
792        AddressRange {
793            range: fidl_ip!("192.0.2.0")..=fidl_ip!("192.0.2.1"),
794        };
795        "AddressRange"
796    )]
797    #[test_case(
798        fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
799            src_port: Some(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }),
800            dst_port: None,
801            ..Default::default()
802        }),
803        TransportProtocol::Udp {
804            src_port: Some(Port { range: 1024..=u16::MAX, invert: false }),
805            dst_port: None,
806        };
807        "TransportProtocol"
808    )]
809    #[test_case(
810        fnet_matchers::TcpSocket::Empty(fnet_matchers::Empty),
811        TcpSocket::Empty;
812        "TcpSocketEmpty"
813    )]
814    #[test_case(
815        fnet_matchers::TcpSocket::SrcPort(
816            fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }
817        ),
818        TcpSocket::SrcPort(Port { range: 1024..=u16::MAX, invert: false });
819        "TcpSocketSrcPort"
820    )]
821    #[test_case(
822        fnet_matchers::TcpSocket::DstPort(
823            fnet_matchers::Port { start: 80, end: 80, invert: true }
824        ),
825        TcpSocket::DstPort(Port { range: 80..=80, invert: true });
826        "TcpSocketDstPort"
827    )]
828    #[test_case(
829        fnet_matchers::TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED),
830        TcpSocket::States(fnet_matchers::TcpState::ESTABLISHED);
831        "TcpSocketStates"
832    )]
833    #[test_case(
834        fnet_matchers::UdpSocket::Empty(fnet_matchers::Empty),
835        UdpSocket::Empty;
836        "UdpSocketEmpty"
837    )]
838    #[test_case(
839        fnet_matchers::UdpSocket::SrcPort(
840            fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }
841        ),
842        UdpSocket::SrcPort(Port { range: 1024..=u16::MAX, invert: false });
843        "UdpSocketSrcPort"
844    )]
845    #[test_case(
846        fnet_matchers::UdpSocket::DstPort(
847            fnet_matchers::Port { start: 53, end: 53, invert: true }
848        ),
849        UdpSocket::DstPort(Port { range: 53..=53, invert: true });
850        "UdpSocketDstPort"
851    )]
852    #[test_case(
853        fnet_matchers::UdpSocket::States(fnet_matchers::UdpState::BOUND),
854        UdpSocket::States(fnet_matchers::UdpState::BOUND);
855        "UdpSocketStates"
856    )]
857    #[test_case(
858        fnet_matchers::SocketTransportProtocol::Tcp(
859            fnet_matchers::TcpSocket::SrcPort(
860                fnet_matchers::Port { start: 123, end: 123, invert: false }
861            )
862        ),
863        SocketTransportProtocol::Tcp(TcpSocket::SrcPort(Port { range: 123..=123, invert: false }));
864        "SocketTransportProtocolTcp"
865    )]
866    #[test_case(
867        fnet_matchers::SocketTransportProtocol::Udp(
868            fnet_matchers::UdpSocket::SrcPort(
869                fnet_matchers::Port { start: 123, end: 123, invert: false }
870            )
871        ),
872        SocketTransportProtocol::Udp(UdpSocket::SrcPort(Port { range: 123..=123, invert: false }));
873        "SocketTransportProtocolUdp"
874    )]
875    fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
876    where
877        E: TryFrom<F> + Clone + Debug + PartialEq,
878        <E as TryFrom<F>>::Error: Debug + PartialEq,
879        F: From<E> + Clone + Debug + PartialEq,
880    {
881        assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
882        assert_eq!(<_ as Into<F>>::into(local_type), fidl_type.clone());
883    }
884
885    #[test_case(
886        fnet_matchers::BoundInterface::__SourceBreaking { unknown_ordinal: 0 } =>
887            Err(BoundInterfaceError::UnknownUnionVariant(0));
888        "UnknownUnionVariant"
889    )]
890    #[test_case(
891        fnet_matchers::BoundInterface::Bound(fnet_matchers::Interface::Id(0)) =>
892            Err(BoundInterfaceError::Interface(InterfaceError::ZeroId));
893        "InterfaceError"
894    )]
895    fn bound_interface_try_from_error(
896        fidl: fnet_matchers::BoundInterface,
897    ) -> Result<BoundInterface, BoundInterfaceError> {
898        BoundInterface::try_from(fidl)
899    }
900
901    #[test_case(
902        fnet_matchers::Mark::__SourceBreaking { unknown_ordinal: 0 } =>
903            Err(MarkError::UnknownUnionVariant(0));
904        "UnknownUnionVariant"
905    )]
906    fn mark_try_from_error(fidl: fnet_matchers::Mark) -> Result<Mark, MarkError> {
907        Mark::try_from(fidl)
908    }
909
910    #[test]
911    fn address_matcher_type_try_from_unknown_variant() {
912        assert_eq!(
913            AddressMatcherType::try_from(fnet_matchers::AddressMatcherType::__SourceBreaking {
914                unknown_ordinal: 0
915            }),
916            Err(AddressMatcherTypeError::UnknownUnionVariant)
917        );
918    }
919
920    #[test]
921    fn subnet_try_from_invalid() {
922        assert_eq!(
923            Subnet::try_from(fnet::Subnet { addr: fidl_ip!("192.0.2.1"), prefix_len: 33 }),
924            Err(SubnetError::PrefixTooLong)
925        );
926        assert_eq!(Subnet::try_from(fidl_subnet!("192.0.2.1/24")), Err(SubnetError::HostBitsSet));
927    }
928
929    #[test]
930    fn address_range_try_from_invalid() {
931        assert_eq!(
932            AddressRange::try_from(fnet_matchers::AddressRange {
933                start: fidl_ip!("192.0.2.1"),
934                end: fidl_ip!("192.0.2.0"),
935            }),
936            Err(AddressRangeError::Invalid)
937        );
938        assert_eq!(
939            AddressRange::try_from(fnet_matchers::AddressRange {
940                start: fidl_ip!("2001:db8::1"),
941                end: fidl_ip!("2001:db8::"),
942            }),
943            Err(AddressRangeError::Invalid)
944        );
945    }
946
947    #[test]
948    fn address_range_try_from_family_mismatch() {
949        assert_eq!(
950            AddressRange::try_from(fnet_matchers::AddressRange {
951                start: fidl_ip!("192.0.2.0"),
952                end: fidl_ip!("2001:db8::"),
953            }),
954            Err(AddressRangeError::FamilyMismatch)
955        );
956    }
957
958    #[test]
959    fn port_matcher_try_from_invalid() {
960        assert_eq!(
961            Port::try_from(fnet_matchers::Port { start: 1, end: 0, invert: false }),
962            Err(PortError::InvalidPortRange)
963        );
964    }
965
966    #[test]
967    fn transport_protocol_try_from_unknown_variant() {
968        assert_eq!(
969            TransportProtocol::try_from(fnet_matchers::PacketTransportProtocol::__SourceBreaking {
970                unknown_ordinal: 0
971            }),
972            Err(TransportProtocolError::UnknownUnionVariant)
973        );
974    }
975
976    #[test_case(
977        fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
978            Err(TcpSocketError::UnknownUnionVariant(100));
979        "TcpSocket UnknownUnionVariant"
980    )]
981    #[test_case(
982        fnet_matchers::TcpSocket::SrcPort(fnet_matchers::Port {
983            start: 1,
984            end: 0,
985            invert: false,
986        }) => Err(TcpSocketError::Port(PortError::InvalidPortRange));
987        "TcpSocket SrcPort Error"
988    )]
989    #[test_case(
990        fnet_matchers::TcpSocket::DstPort(fnet_matchers::Port {
991            start: 1,
992            end: 0,
993            invert: false,
994        }) => Err(TcpSocketError::Port(PortError::InvalidPortRange));
995        "TcpSocket DstPort Error"
996    )]
997    fn tcp_socket_try_from_error(
998        fidl: fnet_matchers::TcpSocket,
999    ) -> Result<TcpSocket, TcpSocketError> {
1000        TcpSocket::try_from(fidl)
1001    }
1002
1003    #[test_case(
1004        fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 } =>
1005            Err(UdpSocketError::UnknownUnionVariant(100));
1006        "UdpSocket UnknownUnionVariant"
1007    )]
1008    #[test_case(
1009        fnet_matchers::UdpSocket::SrcPort(fnet_matchers::Port {
1010            start: 1,
1011            end: 0,
1012            invert: false,
1013        }) => Err(UdpSocketError::Port(PortError::InvalidPortRange));
1014        "UdpSocket SrcPort Error"
1015    )]
1016    #[test_case(
1017        fnet_matchers::UdpSocket::DstPort(fnet_matchers::Port {
1018            start: 1,
1019            end: 0,
1020            invert: false,
1021        }) => Err(UdpSocketError::Port(PortError::InvalidPortRange));
1022        "UdpSocket DstPort Error"
1023    )]
1024    fn udp_socket_try_from_error(
1025        fidl: fnet_matchers::UdpSocket,
1026    ) -> Result<UdpSocket, UdpSocketError> {
1027        UdpSocket::try_from(fidl)
1028    }
1029
1030    #[test_case(
1031        fnet_matchers::SocketTransportProtocol::__SourceBreaking {
1032            unknown_ordinal: 100
1033        } => Err(SocketTransportProtocolError::UnknownUnionVariant(100));
1034        "SocketTransportProtocol UnknownUnionVariant"
1035    )]
1036    #[test_case(
1037        fnet_matchers::SocketTransportProtocol::Tcp(
1038            fnet_matchers::TcpSocket::__SourceBreaking { unknown_ordinal: 100 }
1039        ) => Err(SocketTransportProtocolError::Tcp(TcpSocketError::UnknownUnionVariant(100)));
1040        "SocketTransportProtocol Tcp Error"
1041    )]
1042    #[test_case(
1043        fnet_matchers::SocketTransportProtocol::Udp(
1044            fnet_matchers::UdpSocket::__SourceBreaking { unknown_ordinal: 100 }
1045        ) => Err(SocketTransportProtocolError::Udp(UdpSocketError::UnknownUnionVariant(100)));
1046        "SocketTransportProtocol Udp Error"
1047    )]
1048    fn socket_transport_protocol_try_from_error(
1049        fidl: fnet_matchers::SocketTransportProtocol,
1050    ) -> Result<SocketTransportProtocol, SocketTransportProtocolError> {
1051        SocketTransportProtocol::try_from(fidl)
1052    }
1053}