1use 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#[derive(Debug, Clone, PartialEq)]
29pub enum Interface {
30 Id(NonZeroU64),
31 Name(fnet_interfaces::Name),
32 PortClass(fnet_interfaces_ext::PortClass),
33}
34
35#[derive(Debug, Error, PartialEq)]
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#[derive(Clone, Copy, Eq, Hash, PartialEq)]
82pub struct Subnet(fnet::Subnet);
83
84#[derive(Debug, Error, PartialEq)]
86pub enum SubnetError {
87 #[error("prefix length of subnet is longer than number of bits in IP address")]
88 PrefixTooLong,
89 #[error("host bits are set in subnet network")]
90 HostBitsSet,
91}
92
93impl Subnet {
94 pub fn get(&self) -> fnet::Subnet {
95 let Subnet(subnet) = &self;
96 *subnet
97 }
98}
99
100impl From<Subnet> for fnet::Subnet {
101 fn from(subnet: Subnet) -> Self {
102 let Subnet(subnet) = subnet;
103 subnet
104 }
105}
106
107impl TryFrom<fnet::Subnet> for Subnet {
108 type Error = SubnetError;
109
110 fn try_from(subnet: fnet::Subnet) -> Result<Self, Self::Error> {
111 let fnet::Subnet { addr, prefix_len } = subnet;
112
113 let result = match addr {
118 fnet::IpAddress::Ipv4(v4) => {
119 net_types::ip::Subnet::<net_types::ip::Ipv4Addr>::new(v4.into_ext(), prefix_len)
120 .map(|_| Subnet(subnet))
121 }
122 fnet::IpAddress::Ipv6(v6) => {
123 net_types::ip::Subnet::<net_types::ip::Ipv6Addr>::new(v6.into_ext(), prefix_len)
124 .map(|_| Subnet(subnet))
125 }
126 };
127 result.map_err(|e| match e {
128 net_types::ip::SubnetError::PrefixTooLong => SubnetError::PrefixTooLong,
129 net_types::ip::SubnetError::HostBitsSet => SubnetError::HostBitsSet,
130 })
131 }
132}
133
134impl Debug for Subnet {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 let fnet::Subnet { addr, prefix_len } = self.0;
137
138 match addr {
139 fnet::IpAddress::Ipv4(v4) => {
140 let subnet = net_types::ip::Subnet::<net_types::ip::Ipv4Addr>::new(
141 v4.into_ext(),
142 prefix_len,
143 );
144
145 match subnet {
146 Ok(inner) => inner.fmt(f),
147 Err(err) => err.fmt(f),
148 }
149 }
150 fnet::IpAddress::Ipv6(v6) => {
151 let subnet = net_types::ip::Subnet::<net_types::ip::Ipv6Addr>::new(
152 v6.into_ext(),
153 prefix_len,
154 );
155
156 match subnet {
157 Ok(inner) => inner.fmt(f),
158 Err(err) => err.fmt(f),
159 }
160 }
161 }
162 }
163}
164
165#[derive(Debug, Clone, PartialEq)]
171pub struct AddressRange {
172 range: RangeInclusive<fnet::IpAddress>,
173}
174
175#[derive(Debug, Error, PartialEq)]
177pub enum AddressRangeError {
178 #[error("invalid address range (start must be <= end)")]
179 Invalid,
180 #[error("address range start and end addresses are not the same IP family")]
181 FamilyMismatch,
182}
183
184impl AddressRange {
185 pub fn start(&self) -> fnet::IpAddress {
186 *self.range.start()
187 }
188
189 pub fn end(&self) -> fnet::IpAddress {
190 *self.range.end()
191 }
192}
193
194impl From<AddressRange> for fnet_matchers::AddressRange {
195 fn from(range: AddressRange) -> Self {
196 Self { start: range.start(), end: range.end() }
197 }
198}
199
200impl TryFrom<fnet_matchers::AddressRange> for AddressRange {
201 type Error = AddressRangeError;
202
203 fn try_from(range: fnet_matchers::AddressRange) -> Result<Self, Self::Error> {
204 let fnet_matchers::AddressRange { start, end } = range;
205 match (start, end) {
206 (
207 fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: start_bytes }),
208 fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: end_bytes }),
209 ) => {
210 if u32::from_be_bytes(start_bytes) > u32::from_be_bytes(end_bytes) {
211 Err(AddressRangeError::Invalid)
212 } else {
213 Ok(Self { range: start..=end })
214 }
215 }
216 (
217 fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: start_bytes }),
218 fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: end_bytes }),
219 ) => {
220 if u128::from_be_bytes(start_bytes) > u128::from_be_bytes(end_bytes) {
221 Err(AddressRangeError::Invalid)
222 } else {
223 Ok(Self { range: start..=end })
224 }
225 }
226 _ => Err(AddressRangeError::FamilyMismatch),
227 }
228 }
229}
230
231#[derive(Clone, PartialEq)]
233pub enum AddressMatcherType {
234 Subnet(Subnet),
235 Range(AddressRange),
236}
237
238#[derive(Debug, Error, PartialEq)]
240pub enum AddressMatcherTypeError {
241 #[error("AddressMatcher is of an unknown variant")]
242 UnknownUnionVariant,
243 #[error("subnet conversion error: {0}")]
244 Subnet(SubnetError),
245 #[error("address range conversion error: {0}")]
246 AddressRange(AddressRangeError),
247}
248
249impl From<SubnetError> for AddressMatcherTypeError {
250 fn from(value: SubnetError) -> Self {
251 AddressMatcherTypeError::Subnet(value)
252 }
253}
254impl From<AddressRangeError> for AddressMatcherTypeError {
255 fn from(value: AddressRangeError) -> Self {
256 AddressMatcherTypeError::AddressRange(value)
257 }
258}
259
260impl From<AddressMatcherType> for fnet_matchers::AddressMatcherType {
261 fn from(matcher: AddressMatcherType) -> Self {
262 match matcher {
263 AddressMatcherType::Subnet(subnet) => Self::Subnet(subnet.into()),
264 AddressMatcherType::Range(range) => Self::Range(range.into()),
265 }
266 }
267}
268
269impl TryFrom<fnet_matchers::AddressMatcherType> for AddressMatcherType {
270 type Error = AddressMatcherTypeError;
271
272 fn try_from(matcher: fnet_matchers::AddressMatcherType) -> Result<Self, Self::Error> {
273 match matcher {
274 fnet_matchers::AddressMatcherType::Subnet(subnet) => {
275 Ok(Self::Subnet(subnet.try_into()?))
276 }
277 fnet_matchers::AddressMatcherType::Range(range) => Ok(Self::Range(range.try_into()?)),
278 fnet_matchers::AddressMatcherType::__SourceBreaking { .. } => {
279 Err(AddressMatcherTypeError::UnknownUnionVariant)
280 }
281 }
282 }
283}
284
285impl Debug for AddressMatcherType {
286 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 match self {
288 AddressMatcherType::Subnet(subnet) => subnet.fmt(f),
289 AddressMatcherType::Range(address_range) => address_range.fmt(f),
290 }
291 }
292}
293
294#[derive(Debug, Clone, PartialEq)]
296pub struct Address {
297 pub matcher: AddressMatcherType,
298 pub invert: bool,
299}
300
301#[derive(Debug, Error, PartialEq)]
303pub enum AddressError {
304 #[error("address matcher conversion failure: {0}")]
305 AddressMatcherType(AddressMatcherTypeError),
306}
307
308impl From<AddressMatcherTypeError> for AddressError {
309 fn from(value: AddressMatcherTypeError) -> Self {
310 Self::AddressMatcherType(value)
311 }
312}
313
314impl From<Address> for fnet_matchers::Address {
315 fn from(matcher: Address) -> Self {
316 let Address { matcher, invert } = matcher;
317 Self { matcher: matcher.into(), invert }
318 }
319}
320
321impl TryFrom<fnet_matchers::Address> for Address {
322 type Error = AddressError;
323
324 fn try_from(matcher: fnet_matchers::Address) -> Result<Self, Self::Error> {
325 let fnet_matchers::Address { matcher, invert } = matcher;
326 Ok(Self { matcher: matcher.try_into()?, invert })
327 }
328}
329
330#[derive(Debug, Clone, PartialEq)]
334pub struct Port {
335 range: RangeInclusive<u16>,
336 pub invert: bool,
337}
338
339#[derive(Debug, Error, PartialEq)]
341pub enum PortError {
342 #[error("invalid port range (start must be <= end)")]
343 InvalidPortRange,
344}
345
346impl Port {
347 pub fn new(start: u16, end: u16, invert: bool) -> Result<Self, PortError> {
348 if start > end {
349 return Err(PortError::InvalidPortRange);
350 }
351 Ok(Self { range: start..=end, invert })
352 }
353
354 pub fn range(&self) -> &RangeInclusive<u16> {
355 &self.range
356 }
357
358 pub fn start(&self) -> u16 {
359 *self.range.start()
360 }
361
362 pub fn end(&self) -> u16 {
363 *self.range.end()
364 }
365}
366
367impl From<Port> for fnet_matchers::Port {
368 fn from(matcher: Port) -> Self {
369 let Port { range, invert } = matcher;
370 Self { start: *range.start(), end: *range.end(), invert }
371 }
372}
373
374impl TryFrom<fnet_matchers::Port> for Port {
375 type Error = PortError;
376
377 fn try_from(matcher: fnet_matchers::Port) -> Result<Self, Self::Error> {
378 let fnet_matchers::Port { start, end, invert } = matcher;
379 if start > end {
380 return Err(PortError::InvalidPortRange);
381 }
382 Ok(Self { range: start..=end, invert })
383 }
384}
385
386#[derive(Clone, PartialEq)]
388pub enum TransportProtocol {
389 Tcp { src_port: Option<Port>, dst_port: Option<Port> },
390 Udp { src_port: Option<Port>, dst_port: Option<Port> },
391 Icmp,
392 Icmpv6,
393}
394
395#[derive(Debug, Error, PartialEq)]
397pub enum TransportProtocolError {
398 #[error("invalid port: {0}")]
399 Port(PortError),
400 #[error("TransportProtocol is of an unknown variant")]
401 UnknownUnionVariant,
402}
403
404impl From<PortError> for TransportProtocolError {
405 fn from(value: PortError) -> Self {
406 TransportProtocolError::Port(value)
407 }
408}
409
410impl From<TransportProtocol> for fnet_matchers::PacketTransportProtocol {
411 fn from(matcher: TransportProtocol) -> Self {
412 match matcher {
413 TransportProtocol::Tcp { src_port, dst_port } => Self::Tcp(fnet_matchers::TcpPacket {
414 src_port: src_port.map(Into::into),
415 dst_port: dst_port.map(Into::into),
416 __source_breaking: SourceBreaking,
417 }),
418 TransportProtocol::Udp { src_port, dst_port } => Self::Udp(fnet_matchers::UdpPacket {
419 src_port: src_port.map(Into::into),
420 dst_port: dst_port.map(Into::into),
421 __source_breaking: SourceBreaking,
422 }),
423 TransportProtocol::Icmp => Self::Icmp(fnet_matchers::IcmpPacket::default()),
424 TransportProtocol::Icmpv6 => Self::Icmpv6(fnet_matchers::Icmpv6Packet::default()),
425 }
426 }
427}
428
429impl TryFrom<fnet_matchers::PacketTransportProtocol> for TransportProtocol {
430 type Error = TransportProtocolError;
431
432 fn try_from(matcher: fnet_matchers::PacketTransportProtocol) -> Result<Self, Self::Error> {
433 match matcher {
434 fnet_matchers::PacketTransportProtocol::Tcp(fnet_matchers::TcpPacket {
435 src_port,
436 dst_port,
437 __source_breaking,
438 }) => Ok(Self::Tcp {
439 src_port: src_port.map(TryInto::try_into).transpose()?,
440 dst_port: dst_port.map(TryInto::try_into).transpose()?,
441 }),
442 fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
443 src_port,
444 dst_port,
445 __source_breaking,
446 }) => Ok(Self::Udp {
447 src_port: src_port.map(TryInto::try_into).transpose()?,
448 dst_port: dst_port.map(TryInto::try_into).transpose()?,
449 }),
450 fnet_matchers::PacketTransportProtocol::Icmp(fnet_matchers::IcmpPacket {
451 __source_breaking,
452 }) => Ok(Self::Icmp),
453 fnet_matchers::PacketTransportProtocol::Icmpv6(fnet_matchers::Icmpv6Packet {
454 __source_breaking,
455 }) => Ok(Self::Icmpv6),
456 fnet_matchers::PacketTransportProtocol::__SourceBreaking { .. } => {
457 Err(TransportProtocolError::UnknownUnionVariant)
458 }
459 }
460 }
461}
462
463impl Debug for TransportProtocol {
464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465 match self {
467 TransportProtocol::Tcp { src_port, dst_port } => {
468 let mut debug_struct = f.debug_struct("Tcp");
469
470 if let Some(port) = &src_port {
472 let _ = debug_struct.field("src_port", port);
473 }
474
475 if let Some(port) = &dst_port {
476 let _ = debug_struct.field("dst_port", port);
477 }
478
479 debug_struct.finish()
480 }
481 TransportProtocol::Udp { src_port, dst_port } => {
482 let mut debug_struct = f.debug_struct("Udp");
483
484 if let Some(port) = &src_port {
486 let _ = debug_struct.field("src_port", port);
487 }
488
489 if let Some(port) = &dst_port {
490 let _ = debug_struct.field("dst_port", port);
491 }
492
493 debug_struct.finish()
494 }
495 TransportProtocol::Icmp => f.write_str("Icmp"),
496 TransportProtocol::Icmpv6 => f.write_str("Icmpv6"),
497 }
498 }
499}
500
501#[cfg(test)]
502mod tests {
503 use net_declare::{fidl_ip, fidl_subnet};
504 use test_case::test_case;
505
506 use super::*;
507
508 #[test_case(
509 fnet_matchers::Interface::Id(1),
510 Interface::Id(NonZeroU64::new(1).unwrap());
511 "Interface"
512 )]
513 #[test_case(
514 fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
515 AddressMatcherType::Subnet(Subnet(fidl_subnet!("192.0.2.0/24")));
516 "AddressMatcherType"
517 )]
518 #[test_case(
519 fnet_matchers::Address {
520 matcher: fnet_matchers::AddressMatcherType::Subnet(fidl_subnet!("192.0.2.0/24")),
521 invert: true,
522 },
523 Address {
524 matcher: AddressMatcherType::Subnet(Subnet(fidl_subnet!("192.0.2.0/24"))),
525 invert: true,
526 };
527 "Address"
528 )]
529 #[test_case(
530 fnet_matchers::AddressRange {
531 start: fidl_ip!("192.0.2.0"),
532 end: fidl_ip!("192.0.2.1"),
533 },
534 AddressRange {
535 range: fidl_ip!("192.0.2.0")..=fidl_ip!("192.0.2.1"),
536 };
537 "AddressRange"
538 )]
539 #[test_case(
540 fnet_matchers::PacketTransportProtocol::Udp(fnet_matchers::UdpPacket {
541 src_port: Some(fnet_matchers::Port { start: 1024, end: u16::MAX, invert: false }),
542 dst_port: None,
543 ..Default::default()
544 }),
545 TransportProtocol::Udp {
546 src_port: Some(Port { range: 1024..=u16::MAX, invert: false }),
547 dst_port: None,
548 };
549 "TransportProtocol"
550 )]
551 fn convert_from_fidl_and_back<F, E>(fidl_type: F, local_type: E)
552 where
553 E: TryFrom<F> + Clone + Debug + PartialEq,
554 <E as TryFrom<F>>::Error: Debug + PartialEq,
555 F: From<E> + Clone + Debug + PartialEq,
556 {
557 assert_eq!(fidl_type.clone().try_into(), Ok(local_type.clone()));
558 assert_eq!(<_ as Into<F>>::into(local_type), fidl_type.clone());
559 }
560
561 #[test]
562 fn interface_matcher_try_from_unknown_variant() {
563 assert_eq!(
564 Interface::try_from(fnet_matchers::Interface::__SourceBreaking { unknown_ordinal: 0 }),
565 Err(InterfaceError::UnknownUnionVariant)
566 );
567 }
568
569 #[test]
570 fn interface_matcher_try_from_invalid() {
571 assert_eq!(
572 Interface::try_from(fnet_matchers::Interface::Id(0)),
573 Err(InterfaceError::ZeroId)
574 );
575 }
576
577 #[test]
578 fn address_matcher_type_try_from_unknown_variant() {
579 assert_eq!(
580 AddressMatcherType::try_from(fnet_matchers::AddressMatcherType::__SourceBreaking {
581 unknown_ordinal: 0
582 }),
583 Err(AddressMatcherTypeError::UnknownUnionVariant)
584 );
585 }
586
587 #[test]
588 fn subnet_try_from_invalid() {
589 assert_eq!(
590 Subnet::try_from(fnet::Subnet { addr: fidl_ip!("192.0.2.1"), prefix_len: 33 }),
591 Err(SubnetError::PrefixTooLong)
592 );
593 assert_eq!(Subnet::try_from(fidl_subnet!("192.0.2.1/24")), Err(SubnetError::HostBitsSet));
594 }
595
596 #[test]
597 fn address_range_try_from_invalid() {
598 assert_eq!(
599 AddressRange::try_from(fnet_matchers::AddressRange {
600 start: fidl_ip!("192.0.2.1"),
601 end: fidl_ip!("192.0.2.0"),
602 }),
603 Err(AddressRangeError::Invalid)
604 );
605 assert_eq!(
606 AddressRange::try_from(fnet_matchers::AddressRange {
607 start: fidl_ip!("2001:db8::1"),
608 end: fidl_ip!("2001:db8::"),
609 }),
610 Err(AddressRangeError::Invalid)
611 );
612 }
613
614 #[test]
615 fn address_range_try_from_family_mismatch() {
616 assert_eq!(
617 AddressRange::try_from(fnet_matchers::AddressRange {
618 start: fidl_ip!("192.0.2.0"),
619 end: fidl_ip!("2001:db8::"),
620 }),
621 Err(AddressRangeError::FamilyMismatch)
622 );
623 }
624
625 #[test]
626 fn port_matcher_try_from_invalid() {
627 assert_eq!(
628 Port::try_from(fnet_matchers::Port { start: 1, end: 0, invert: false }),
629 Err(PortError::InvalidPortRange)
630 );
631 }
632
633 #[test]
634 fn transport_protocol_try_from_unknown_variant() {
635 assert_eq!(
636 TransportProtocol::try_from(fnet_matchers::PacketTransportProtocol::__SourceBreaking {
637 unknown_ordinal: 0
638 }),
639 Err(TransportProtocolError::UnknownUnionVariant)
640 );
641 }
642}