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