1use std::fmt::Debug;
8
9use async_utils::{fold, stream};
10use fidl_fuchsia_net_ext::{IntoExt as _, TryIntoExt as _};
11#[cfg(not(feature = "fdomain"))]
12use fidl_fuchsia_net_matchers_ext as fnet_matchers_ext;
13#[cfg(feature = "fdomain")]
14use fidl_fuchsia_net_matchers_ext_fdomain as fnet_matchers_ext;
15use flex_client::ProxyHasDomain as _;
16use flex_client::fidl::{DiscoverableProtocolMarker, ProtocolMarker};
17use flex_fuchsia_net as fnet;
18use flex_fuchsia_net_matchers as fnet_matchers;
19use flex_fuchsia_net_routes as fnet_routes;
20use flex_fuchsia_net_routes_admin as fnet_routes_admin;
21use futures::future::Either;
22use futures::{Stream, TryStreamExt as _};
23use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
24use thiserror::Error;
25
26use crate::{FidlRouteIpExt, Responder, SliceResponder, WatcherCreationError, impl_responder};
27
28pub trait FidlRuleIpExt: Ip {
30 type RuleWatcherMarker: ProtocolMarker<RequestStream = Self::RuleWatcherRequestStream>;
32 type RuleWatcherRequestStream: flex_client::fidl::RequestStream<Ok: Send, ControlHandle: Send>;
34 type RuleEvent: From<RuleEvent<Self>>
36 + TryInto<RuleEvent<Self>, Error = RuleFidlConversionError>
37 + Unpin;
38 type RuleWatcherWatchResponder: SliceResponder<Self::RuleEvent>;
40
41 fn into_rule_watcher_request(
43 request: flex_client::fidl::Request<Self::RuleWatcherMarker>,
44 ) -> RuleWatcherRequest<Self>;
45}
46
47impl_responder!(fnet_routes::RuleWatcherV4WatchResponder, &[fnet_routes::RuleEventV4]);
48impl_responder!(fnet_routes::RuleWatcherV6WatchResponder, &[fnet_routes::RuleEventV6]);
49
50impl FidlRuleIpExt for Ipv4 {
51 type RuleWatcherMarker = fnet_routes::RuleWatcherV4Marker;
52 type RuleWatcherRequestStream = fnet_routes::RuleWatcherV4RequestStream;
53 type RuleEvent = fnet_routes::RuleEventV4;
54 type RuleWatcherWatchResponder = fnet_routes::RuleWatcherV4WatchResponder;
55
56 fn into_rule_watcher_request(
57 request: flex_client::fidl::Request<Self::RuleWatcherMarker>,
58 ) -> RuleWatcherRequest<Self> {
59 RuleWatcherRequest::from(request)
60 }
61}
62
63impl FidlRuleIpExt for Ipv6 {
64 type RuleWatcherMarker = fnet_routes::RuleWatcherV6Marker;
65 type RuleWatcherRequestStream = fnet_routes::RuleWatcherV6RequestStream;
66 type RuleEvent = fnet_routes::RuleEventV6;
67 type RuleWatcherWatchResponder = fnet_routes::RuleWatcherV6WatchResponder;
68
69 fn into_rule_watcher_request(
70 request: flex_client::fidl::Request<Self::RuleWatcherMarker>,
71 ) -> RuleWatcherRequest<Self> {
72 RuleWatcherRequest::from(request)
73 }
74}
75
76pub enum RuleWatcherRequest<I: FidlRuleIpExt> {
78 Watch {
80 responder: I::RuleWatcherWatchResponder,
82 },
83}
84
85impl From<fnet_routes::RuleWatcherV4Request> for RuleWatcherRequest<Ipv4> {
86 fn from(req: fnet_routes::RuleWatcherV4Request) -> Self {
87 match req {
88 fnet_routes::RuleWatcherV4Request::Watch { responder } => {
89 RuleWatcherRequest::Watch { responder }
90 }
91 }
92 }
93}
94
95impl From<fnet_routes::RuleWatcherV6Request> for RuleWatcherRequest<Ipv6> {
96 fn from(req: fnet_routes::RuleWatcherV6Request) -> Self {
97 match req {
98 fnet_routes::RuleWatcherV6Request::Watch { responder } => {
99 RuleWatcherRequest::Watch { responder }
100 }
101 }
102 }
103}
104
105#[derive(Debug, PartialEq, Eq, Clone, Hash)]
107pub struct InstalledRule<I: Ip> {
108 pub priority: RuleSetPriority,
111 pub index: RuleIndex,
114 pub matcher: RuleMatcher<I>,
117 pub action: RuleAction,
120}
121
122impl TryFrom<fnet_routes::InstalledRuleV4> for InstalledRule<Ipv4> {
123 type Error = RuleFidlConversionError;
124 fn try_from(
125 fnet_routes::InstalledRuleV4 {
126 rule_set_priority,
127 rule_index,
128 matcher,
129 action,
130 }: fnet_routes::InstalledRuleV4,
131 ) -> Result<Self, Self::Error> {
132 Ok(Self {
133 priority: rule_set_priority.into(),
134 index: rule_index.into(),
135 matcher: matcher.try_into()?,
136 action: action.into(),
137 })
138 }
139}
140
141impl TryFrom<fnet_routes::InstalledRuleV6> for InstalledRule<Ipv6> {
142 type Error = RuleFidlConversionError;
143 fn try_from(
144 fnet_routes::InstalledRuleV6 {
145 rule_set_priority,
146 rule_index,
147 matcher,
148 action,
149 }: fnet_routes::InstalledRuleV6,
150 ) -> Result<Self, Self::Error> {
151 Ok(Self {
152 priority: rule_set_priority.into(),
153 index: rule_index.into(),
154 matcher: matcher.try_into()?,
155 action: action.into(),
156 })
157 }
158}
159
160impl From<InstalledRule<Ipv4>> for fnet_routes::InstalledRuleV4 {
161 fn from(InstalledRule { priority, index, matcher, action }: InstalledRule<Ipv4>) -> Self {
162 Self {
163 rule_set_priority: priority.into(),
164 rule_index: index.into(),
165 matcher: matcher.into(),
166 action: action.into(),
167 }
168 }
169}
170
171impl From<InstalledRule<Ipv6>> for fnet_routes::InstalledRuleV6 {
172 fn from(InstalledRule { priority, index, matcher, action }: InstalledRule<Ipv6>) -> Self {
173 Self {
174 rule_set_priority: priority.into(),
175 rule_index: index.into(),
176 matcher: matcher.into(),
177 action: action.into(),
178 }
179 }
180}
181
182#[derive(Debug, Clone)]
184pub enum RuleEvent<I: Ip> {
185 Existing(InstalledRule<I>),
187 Idle,
190 Added(InstalledRule<I>),
192 Removed(InstalledRule<I>),
194}
195
196impl TryFrom<fnet_routes::RuleEventV4> for RuleEvent<Ipv4> {
197 type Error = RuleFidlConversionError;
198 fn try_from(event: fnet_routes::RuleEventV4) -> Result<Self, Self::Error> {
199 match event {
200 fnet_routes::RuleEventV4::Existing(rule) => Ok(RuleEvent::Existing(rule.try_into()?)),
201 fnet_routes::RuleEventV4::Idle(fnet_routes::Empty) => Ok(RuleEvent::Idle),
202 fnet_routes::RuleEventV4::Added(rule) => Ok(RuleEvent::Added(rule.try_into()?)),
203 fnet_routes::RuleEventV4::Removed(rule) => Ok(RuleEvent::Removed(rule.try_into()?)),
204 fnet_routes::RuleEventV4::__SourceBreaking { unknown_ordinal } => {
205 Err(RuleFidlConversionError::UnknownOrdinal {
206 name: "RuleEventV4",
207 unknown_ordinal,
208 })
209 }
210 }
211 }
212}
213
214impl TryFrom<fnet_routes::RuleEventV6> for RuleEvent<Ipv6> {
215 type Error = RuleFidlConversionError;
216 fn try_from(event: fnet_routes::RuleEventV6) -> Result<Self, Self::Error> {
217 match event {
218 fnet_routes::RuleEventV6::Existing(rule) => Ok(RuleEvent::Existing(rule.try_into()?)),
219 fnet_routes::RuleEventV6::Idle(fnet_routes::Empty) => Ok(RuleEvent::Idle),
220 fnet_routes::RuleEventV6::Added(rule) => Ok(RuleEvent::Added(rule.try_into()?)),
221 fnet_routes::RuleEventV6::Removed(rule) => Ok(RuleEvent::Removed(rule.try_into()?)),
222 fnet_routes::RuleEventV6::__SourceBreaking { unknown_ordinal } => {
223 Err(RuleFidlConversionError::UnknownOrdinal {
224 name: "RuleEventV6",
225 unknown_ordinal,
226 })
227 }
228 }
229 }
230}
231
232impl From<RuleEvent<Ipv4>> for fnet_routes::RuleEventV4 {
233 fn from(event: RuleEvent<Ipv4>) -> Self {
234 match event {
235 RuleEvent::Existing(r) => Self::Existing(r.into()),
236 RuleEvent::Idle => Self::Idle(fnet_routes::Empty),
237 RuleEvent::Added(r) => Self::Added(r.into()),
238 RuleEvent::Removed(r) => Self::Removed(r.into()),
239 }
240 }
241}
242
243impl From<RuleEvent<Ipv6>> for fnet_routes::RuleEventV6 {
244 fn from(event: RuleEvent<Ipv6>) -> Self {
245 match event {
246 RuleEvent::Existing(r) => Self::Existing(r.into()),
247 RuleEvent::Idle => Self::Idle(fnet_routes::Empty),
248 RuleEvent::Added(r) => Self::Added(r.into()),
249 RuleEvent::Removed(r) => Self::Removed(r.into()),
250 }
251 }
252}
253
254pub trait FidlRuleAdminIpExt: Ip {
256 type RuleTableMarker: DiscoverableProtocolMarker<RequestStream = Self::RuleTableRequestStream>;
258 type RuleSetMarker: ProtocolMarker<RequestStream = Self::RuleSetRequestStream>;
260 type RuleTableRequestStream: flex_client::fidl::RequestStream<Ok: Send, ControlHandle: Send>;
262 type RuleSetRequestStream: flex_client::fidl::RequestStream<Ok: Send, ControlHandle: Send>;
264 type RuleSetAddRuleResponder: Responder<
266 Payload = Result<(), fnet_routes_admin::RuleSetError>,
267 ControlHandle = Self::RuleSetControlHandle,
268 >;
269 type RuleSetRemoveRuleResponder: Responder<
271 Payload = Result<(), fnet_routes_admin::RuleSetError>,
272 ControlHandle = Self::RuleSetControlHandle,
273 >;
274 type RuleSetAuthenticateForRouteTableResponder: Responder<
276 Payload = Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
277 ControlHandle = Self::RuleSetControlHandle,
278 >;
279 type RuleTableControlHandle: flex_client::fidl::ControlHandle + Send + Clone;
281 type RuleSetControlHandle: flex_client::fidl::ControlHandle + Send + Clone;
283
284 fn into_rule_set_request(
286 request: flex_client::fidl::Request<Self::RuleSetMarker>,
287 ) -> RuleSetRequest<Self>;
288
289 fn into_rule_table_request(
291 request: flex_client::fidl::Request<Self::RuleTableMarker>,
292 ) -> RuleTableRequest<Self>;
293}
294
295impl FidlRuleAdminIpExt for Ipv4 {
296 type RuleTableMarker = fnet_routes_admin::RuleTableV4Marker;
297 type RuleSetMarker = fnet_routes_admin::RuleSetV4Marker;
298 type RuleTableRequestStream = fnet_routes_admin::RuleTableV4RequestStream;
299 type RuleSetRequestStream = fnet_routes_admin::RuleSetV4RequestStream;
300 type RuleSetAddRuleResponder = fnet_routes_admin::RuleSetV4AddRuleResponder;
301 type RuleSetRemoveRuleResponder = fnet_routes_admin::RuleSetV4RemoveRuleResponder;
302 type RuleSetAuthenticateForRouteTableResponder =
303 fnet_routes_admin::RuleSetV4AuthenticateForRouteTableResponder;
304 type RuleTableControlHandle = fnet_routes_admin::RuleTableV4ControlHandle;
305 type RuleSetControlHandle = fnet_routes_admin::RuleSetV4ControlHandle;
306
307 fn into_rule_set_request(
308 request: flex_client::fidl::Request<Self::RuleSetMarker>,
309 ) -> RuleSetRequest<Self> {
310 RuleSetRequest::from(request)
311 }
312
313 fn into_rule_table_request(
314 request: flex_client::fidl::Request<Self::RuleTableMarker>,
315 ) -> RuleTableRequest<Self> {
316 RuleTableRequest::from(request)
317 }
318}
319
320impl FidlRuleAdminIpExt for Ipv6 {
321 type RuleTableMarker = fnet_routes_admin::RuleTableV6Marker;
322 type RuleSetMarker = fnet_routes_admin::RuleSetV6Marker;
323 type RuleTableRequestStream = fnet_routes_admin::RuleTableV6RequestStream;
324 type RuleSetRequestStream = fnet_routes_admin::RuleSetV6RequestStream;
325 type RuleSetAddRuleResponder = fnet_routes_admin::RuleSetV6AddRuleResponder;
326 type RuleSetRemoveRuleResponder = fnet_routes_admin::RuleSetV6RemoveRuleResponder;
327 type RuleSetAuthenticateForRouteTableResponder =
328 fnet_routes_admin::RuleSetV6AuthenticateForRouteTableResponder;
329 type RuleTableControlHandle = fnet_routes_admin::RuleTableV6ControlHandle;
330 type RuleSetControlHandle = fnet_routes_admin::RuleSetV6ControlHandle;
331
332 fn into_rule_set_request(
333 request: flex_client::fidl::Request<Self::RuleSetMarker>,
334 ) -> RuleSetRequest<Self> {
335 RuleSetRequest::from(request)
336 }
337
338 fn into_rule_table_request(
339 request: flex_client::fidl::Request<Self::RuleTableMarker>,
340 ) -> RuleTableRequest<Self> {
341 RuleTableRequest::from(request)
342 }
343}
344
345impl_responder!(
346 fnet_routes_admin::RuleSetV4AddRuleResponder,
347 Result<(), fnet_routes_admin::RuleSetError>,
348);
349impl_responder!(
350 fnet_routes_admin::RuleSetV4RemoveRuleResponder,
351 Result<(), fnet_routes_admin::RuleSetError>,
352);
353impl_responder!(
354 fnet_routes_admin::RuleSetV4AuthenticateForRouteTableResponder,
355 Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
356);
357impl_responder!(
358 fnet_routes_admin::RuleSetV6AddRuleResponder,
359 Result<(), fnet_routes_admin::RuleSetError>,
360);
361impl_responder!(
362 fnet_routes_admin::RuleSetV6RemoveRuleResponder,
363 Result<(), fnet_routes_admin::RuleSetError>,
364);
365impl_responder!(
366 fnet_routes_admin::RuleSetV6AuthenticateForRouteTableResponder,
367 Result<(), fnet_routes_admin::AuthenticateForRouteTableError>,
368);
369
370#[derive(Debug, Error, PartialEq)]
372pub enum RuleFidlConversionError {
373 #[error("failed to convert `destination` to net_types subnet: {0:?}")]
375 DestinationSubnet(net_types::ip::SubnetError),
376 #[error("failed to convert `bound_device`: {0:?}")]
378 Interface(fnet_matchers_ext::InterfaceError),
379 #[error("unexpected union variant for {name}, got ordinal = ({unknown_ordinal})")]
381 #[allow(missing_docs)]
382 UnknownOrdinal { name: &'static str, unknown_ordinal: u64 },
383}
384
385impl From<fnet_matchers_ext::BoundInterfaceError> for RuleFidlConversionError {
386 fn from(value: fnet_matchers_ext::BoundInterfaceError) -> Self {
387 match value {
388 fnet_matchers_ext::BoundInterfaceError::UnknownUnionVariant(unknown_ordinal) => {
389 RuleFidlConversionError::UnknownOrdinal { name: "BoundInterface", unknown_ordinal }
390 }
391 fnet_matchers_ext::BoundInterfaceError::Interface(interface) => {
392 RuleFidlConversionError::Interface(interface)
393 }
394 }
395 }
396}
397
398impl From<fnet_matchers_ext::MarkError> for RuleFidlConversionError {
399 fn from(value: fnet_matchers_ext::MarkError) -> Self {
400 match value {
401 fnet_matchers_ext::MarkError::UnknownUnionVariant(unknown_ordinal) => {
402 RuleFidlConversionError::UnknownOrdinal { name: "Mark", unknown_ordinal }
403 }
404 }
405 }
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
410pub struct RuleSetPriority(u32);
411
412impl RuleSetPriority {
413 pub const fn new(x: u32) -> Self {
415 Self(x)
416 }
417}
418
419pub const DEFAULT_RULE_SET_PRIORITY: RuleSetPriority =
422 RuleSetPriority(fnet_routes::DEFAULT_RULE_SET_PRIORITY);
423
424#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
426pub struct RuleIndex(u32);
427
428impl RuleIndex {
429 pub const fn new(x: u32) -> Self {
431 Self(x)
432 }
433}
434
435impl From<RuleSetPriority> for u32 {
436 fn from(RuleSetPriority(x): RuleSetPriority) -> Self {
437 x
438 }
439}
440
441impl From<u32> for RuleSetPriority {
442 fn from(x: u32) -> Self {
443 Self(x)
444 }
445}
446
447impl From<RuleIndex> for u32 {
448 fn from(RuleIndex(x): RuleIndex) -> Self {
449 x
450 }
451}
452
453impl From<u32> for RuleIndex {
454 fn from(x: u32) -> Self {
455 Self(x)
456 }
457}
458
459#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
464pub struct RuleMatcher<I: Ip> {
465 pub from: Option<Subnet<I::Addr>>,
467 pub locally_generated: Option<bool>,
469 pub bound_device: Option<fnet_matchers_ext::BoundInterface>,
472 pub mark_1: Option<fnet_matchers_ext::Mark>,
474 pub mark_2: Option<fnet_matchers_ext::Mark>,
476}
477
478impl TryFrom<fnet_routes::RuleMatcherV4> for RuleMatcher<Ipv4> {
479 type Error = RuleFidlConversionError;
480 fn try_from(
481 fnet_routes::RuleMatcherV4 {
482 from,
483 base,
484 __source_breaking: fidl::marker::SourceBreaking,
485 }: fnet_routes::RuleMatcherV4,
486 ) -> Result<Self, Self::Error> {
487 let fnet_routes::BaseMatcher {
488 locally_generated,
489 bound_device,
490 mark_1,
491 mark_2,
492 __source_breaking: fidl::marker::SourceBreaking,
493 } = base.unwrap_or_default();
494 Ok(Self {
495 from: from
496 .map(|from| from.try_into_ext().map_err(RuleFidlConversionError::DestinationSubnet))
497 .transpose()?,
498 locally_generated,
499 bound_device: bound_device
500 .map(fnet_matchers_ext::BoundInterface::try_from)
501 .transpose()?,
502 mark_1: mark_1.map(fnet_matchers_ext::Mark::try_from).transpose()?,
503 mark_2: mark_2.map(fnet_matchers_ext::Mark::try_from).transpose()?,
504 })
505 }
506}
507
508impl From<RuleMatcher<Ipv4>> for fnet_routes::RuleMatcherV4 {
509 fn from(
510 RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv4>,
511 ) -> Self {
512 let base = fnet_routes::BaseMatcher {
513 locally_generated,
514 bound_device: bound_device.map(fnet_matchers::BoundInterface::from),
515 mark_1: mark_1.map(Into::into),
516 mark_2: mark_2.map(Into::into),
517 __source_breaking: fidl::marker::SourceBreaking,
518 };
519 let base = (base != Default::default()).then_some(base);
520 fnet_routes::RuleMatcherV4 {
521 from: from.map(|from| fnet::Ipv4AddressWithPrefix {
522 addr: from.network().into_ext(),
523 prefix_len: from.prefix(),
524 }),
525 base,
526 __source_breaking: fidl::marker::SourceBreaking,
527 }
528 }
529}
530
531impl TryFrom<fnet_routes::RuleMatcherV6> for RuleMatcher<Ipv6> {
532 type Error = RuleFidlConversionError;
533 fn try_from(
534 fnet_routes::RuleMatcherV6 {
535 from,
536 base,
537 __source_breaking: fidl::marker::SourceBreaking,
538 }: fnet_routes::RuleMatcherV6,
539 ) -> Result<Self, Self::Error> {
540 let fnet_routes::BaseMatcher {
541 locally_generated,
542 bound_device,
543 mark_1,
544 mark_2,
545 __source_breaking: fidl::marker::SourceBreaking,
546 } = base.unwrap_or_default();
547 Ok(Self {
548 from: from
549 .map(|from| from.try_into_ext().map_err(RuleFidlConversionError::DestinationSubnet))
550 .transpose()?,
551 locally_generated,
552 bound_device: bound_device
553 .map(fnet_matchers_ext::BoundInterface::try_from)
554 .transpose()?,
555 mark_1: mark_1.map(fnet_matchers_ext::Mark::try_from).transpose()?,
556 mark_2: mark_2.map(fnet_matchers_ext::Mark::try_from).transpose()?,
557 })
558 }
559}
560
561impl From<RuleMatcher<Ipv6>> for fnet_routes::RuleMatcherV6 {
562 fn from(
563 RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv6>,
564 ) -> Self {
565 let base = fnet_routes::BaseMatcher {
566 locally_generated,
567 bound_device: bound_device.map(fnet_matchers::BoundInterface::from),
568 mark_1: mark_1.map(Into::into),
569 mark_2: mark_2.map(Into::into),
570 __source_breaking: fidl::marker::SourceBreaking,
571 };
572 let base = (base != Default::default()).then_some(base);
573 fnet_routes::RuleMatcherV6 {
574 from: from.map(|from| fnet::Ipv6AddressWithPrefix {
575 addr: from.network().into_ext(),
576 prefix_len: from.prefix(),
577 }),
578 base,
579 __source_breaking: fidl::marker::SourceBreaking,
580 }
581 }
582}
583
584#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
585pub enum RuleAction {
587 Unreachable,
589 Lookup(crate::TableId),
593}
594
595impl From<fnet_routes::RuleAction> for RuleAction {
596 fn from(action: fnet_routes::RuleAction) -> Self {
597 match action {
598 fnet_routes::RuleAction::Lookup(table_id) => {
599 RuleAction::Lookup(crate::TableId::new(table_id))
600 }
601 fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable) => {
602 RuleAction::Unreachable
603 }
604 fnet_routes::RuleAction::__SourceBreaking { unknown_ordinal } => {
605 panic!("unexpected mark matcher variant, unknown ordinal: {unknown_ordinal}")
606 }
607 }
608 }
609}
610
611impl From<RuleAction> for fnet_routes::RuleAction {
612 fn from(action: RuleAction) -> Self {
613 match action {
614 RuleAction::Unreachable => {
615 fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable)
616 }
617 RuleAction::Lookup(table_id) => fnet_routes::RuleAction::Lookup(table_id.get()),
618 }
619 }
620}
621
622#[derive(GenericOverIp, Debug)]
624#[generic_over_ip(I, Ip)]
625pub enum RuleTableRequest<I: FidlRuleAdminIpExt> {
626 NewRuleSet {
628 priority: RuleSetPriority,
630 rule_set: flex_client::fidl::ServerEnd<I::RuleSetMarker>,
632 control_handle: I::RuleTableControlHandle,
634 },
635}
636
637impl From<fnet_routes_admin::RuleTableV4Request> for RuleTableRequest<Ipv4> {
638 fn from(value: fnet_routes_admin::RuleTableV4Request) -> Self {
639 match value {
640 fnet_routes_admin::RuleTableV4Request::NewRuleSet {
641 priority,
642 rule_set,
643 control_handle,
644 } => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
645 }
646 }
647}
648
649impl From<fnet_routes_admin::RuleTableV6Request> for RuleTableRequest<Ipv6> {
650 fn from(value: fnet_routes_admin::RuleTableV6Request) -> Self {
651 match value {
652 fnet_routes_admin::RuleTableV6Request::NewRuleSet {
653 priority,
654 rule_set,
655 control_handle,
656 } => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
657 }
658 }
659}
660
661#[derive(GenericOverIp, Debug)]
663#[generic_over_ip(I, Ip)]
664pub enum RuleSetRequest<I: FidlRuleAdminIpExt> {
665 AddRule {
667 index: RuleIndex,
669 matcher: Result<RuleMatcher<I>, RuleFidlConversionError>,
671 action: RuleAction,
673 responder: I::RuleSetAddRuleResponder,
675 },
676 RemoveRule {
678 index: RuleIndex,
680 responder: I::RuleSetRemoveRuleResponder,
682 },
683 AuthenticateForRouteTable {
685 table: u32,
687 token: flex_client::Event,
689 responder: I::RuleSetAuthenticateForRouteTableResponder,
691 },
692 Close {
694 control_handle: I::RuleSetControlHandle,
696 },
697}
698
699impl From<fnet_routes_admin::RuleSetV4Request> for RuleSetRequest<Ipv4> {
700 fn from(value: fnet_routes_admin::RuleSetV4Request) -> Self {
701 match value {
702 fnet_routes_admin::RuleSetV4Request::AddRule { index, matcher, action, responder } => {
703 RuleSetRequest::AddRule {
704 index: RuleIndex(index),
705 matcher: matcher.try_into(),
706 action: action.into(),
707 responder,
708 }
709 }
710 fnet_routes_admin::RuleSetV4Request::RemoveRule { index, responder } => {
711 RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
712 }
713 fnet_routes_admin::RuleSetV4Request::AuthenticateForRouteTable {
714 table,
715 token,
716 responder,
717 } => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
718 fnet_routes_admin::RuleSetV4Request::Close { control_handle } => {
719 RuleSetRequest::Close { control_handle }
720 }
721 }
722 }
723}
724impl From<fnet_routes_admin::RuleSetV6Request> for RuleSetRequest<Ipv6> {
725 fn from(value: fnet_routes_admin::RuleSetV6Request) -> Self {
726 match value {
727 fnet_routes_admin::RuleSetV6Request::AddRule { index, matcher, action, responder } => {
728 RuleSetRequest::AddRule {
729 index: RuleIndex(index),
730 matcher: matcher.try_into(),
731 action: action.into(),
732 responder,
733 }
734 }
735 fnet_routes_admin::RuleSetV6Request::RemoveRule { index, responder } => {
736 RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
737 }
738 fnet_routes_admin::RuleSetV6Request::AuthenticateForRouteTable {
739 table,
740 token,
741 responder,
742 } => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
743 fnet_routes_admin::RuleSetV6Request::Close { control_handle } => {
744 RuleSetRequest::Close { control_handle }
745 }
746 }
747 }
748}
749
750#[derive(Clone, Debug, Error)]
752pub enum RuleSetCreationError {
753 #[error("failed to create proxy: {0}")]
755 CreateProxy(fidl::Error),
756 #[error("failed to create route set: {0}")]
758 RuleSet(fidl::Error),
759}
760
761pub fn new_rule_set<I: Ip + FidlRuleAdminIpExt>(
763 rule_table_proxy: &<I::RuleTableMarker as ProtocolMarker>::Proxy,
764 priority: RuleSetPriority,
765) -> Result<<I::RuleSetMarker as ProtocolMarker>::Proxy, RuleSetCreationError> {
766 let (rule_set_proxy, rule_set_server_end) =
767 rule_table_proxy.domain().create_proxy::<I::RuleSetMarker>();
768
769 #[derive(GenericOverIp)]
770 #[generic_over_ip(I, Ip)]
771 struct NewRuleSetInput<'a, I: FidlRuleAdminIpExt> {
772 rule_set_server_end: flex_client::fidl::ServerEnd<I::RuleSetMarker>,
773 rule_table_proxy: &'a <I::RuleTableMarker as ProtocolMarker>::Proxy,
774 }
775 let result = I::map_ip_in(
776 NewRuleSetInput::<'_, I> { rule_set_server_end, rule_table_proxy },
777 |NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
778 rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
779 },
780 |NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
781 rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
782 },
783 );
784
785 result.map_err(RuleSetCreationError::RuleSet)?;
786 Ok(rule_set_proxy)
787}
788
789pub async fn authenticate_for_route_table<I: Ip + FidlRuleAdminIpExt>(
792 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
793 table_id: u32,
794 token: flex_client::Event,
795) -> Result<Result<(), fnet_routes_admin::AuthenticateForRouteTableError>, fidl::Error> {
796 #[derive(GenericOverIp)]
797 #[generic_over_ip(I, Ip)]
798 struct AuthenticateForRouteTableInput<'a, I: FidlRuleAdminIpExt> {
799 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
800 table_id: u32,
801 token: flex_client::Event,
802 }
803
804 I::map_ip_in(
805 AuthenticateForRouteTableInput { rule_set, table_id, token },
806 |AuthenticateForRouteTableInput { rule_set, table_id, token }| {
807 Either::Left(rule_set.authenticate_for_route_table(table_id, token))
808 },
809 |AuthenticateForRouteTableInput { rule_set, table_id, token }| {
810 Either::Right(rule_set.authenticate_for_route_table(table_id, token))
811 },
812 )
813 .await
814}
815
816pub async fn add_rule<I: Ip + FidlRuleAdminIpExt>(
818 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
819 index: RuleIndex,
820 matcher: RuleMatcher<I>,
821 action: RuleAction,
822) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
823 #[derive(GenericOverIp)]
824 #[generic_over_ip(I, Ip)]
825 struct AddRuleInput<'a, I: FidlRuleAdminIpExt> {
826 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
827 index: RuleIndex,
828 matcher: RuleMatcher<I>,
829 action: RuleAction,
830 }
831
832 I::map_ip_in(
833 AddRuleInput { rule_set, index, matcher, action },
834 |AddRuleInput { rule_set, index, matcher, action }| {
835 Either::Left(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
836 },
837 |AddRuleInput { rule_set, index, matcher, action }| {
838 Either::Right(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
839 },
840 )
841 .await
842}
843
844pub async fn remove_rule<I: Ip + FidlRuleAdminIpExt>(
846 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
847 index: RuleIndex,
848) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
849 #[derive(GenericOverIp)]
850 #[generic_over_ip(I, Ip)]
851 struct RemoveRuleInput<'a, I: FidlRuleAdminIpExt> {
852 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
853 index: RuleIndex,
854 }
855
856 I::map_ip_in(
857 RemoveRuleInput { rule_set, index },
858 |RemoveRuleInput { rule_set, index }| Either::Left(rule_set.remove_rule(index.into())),
859 |RemoveRuleInput { rule_set, index }| Either::Right(rule_set.remove_rule(index.into())),
860 )
861 .await
862}
863
864pub async fn close_rule_set<I: Ip + FidlRuleAdminIpExt>(
868 rule_set: <I::RuleSetMarker as ProtocolMarker>::Proxy,
869) -> Result<(), fidl::Error> {
870 use flex_client::fidl::Proxy as _;
871 #[derive(GenericOverIp)]
872 #[generic_over_ip(I, Ip)]
873 struct CloseInput<'a, I: FidlRuleAdminIpExt> {
874 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
875 }
876
877 let result = I::map_ip_in(
878 CloseInput { rule_set: &rule_set },
879 |CloseInput { rule_set }| rule_set.close(),
880 |CloseInput { rule_set }| rule_set.close(),
881 );
882
883 assert!(
884 rule_set
885 .on_closed()
886 .await
887 .expect("failed to wait for signals")
888 .contains(fidl::Signals::CHANNEL_PEER_CLOSED)
889 );
890
891 result
892}
893
894pub fn get_rule_watcher<I: FidlRuleIpExt + FidlRouteIpExt>(
896 state_proxy: &<I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
897) -> Result<<I::RuleWatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy, WatcherCreationError>
898{
899 let (watcher_proxy, watcher_server_end) =
900 state_proxy.domain().create_proxy::<I::RuleWatcherMarker>();
901
902 #[derive(GenericOverIp)]
903 #[generic_over_ip(I, Ip)]
904 struct GetWatcherInputs<'a, I: FidlRuleIpExt + FidlRouteIpExt> {
905 watcher_server_end: flex_client::fidl::ServerEnd<I::RuleWatcherMarker>,
906 state_proxy: &'a <I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
907 }
908 let result = I::map_ip_in(
909 GetWatcherInputs::<'_, I> { watcher_server_end, state_proxy },
910 |GetWatcherInputs { watcher_server_end, state_proxy }| {
911 state_proxy.get_rule_watcher_v4(
912 watcher_server_end,
913 &fnet_routes::RuleWatcherOptionsV4::default(),
914 )
915 },
916 |GetWatcherInputs { watcher_server_end, state_proxy }| {
917 state_proxy.get_rule_watcher_v6(
918 watcher_server_end,
919 &fnet_routes::RuleWatcherOptionsV6::default(),
920 )
921 },
922 );
923
924 result.map_err(WatcherCreationError::GetWatcher)?;
925 Ok(watcher_proxy)
926}
927
928pub async fn watch<'a, I: FidlRuleIpExt>(
930 watcher_proxy: &'a <I::RuleWatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
931) -> Result<Vec<I::RuleEvent>, fidl::Error> {
932 #[derive(GenericOverIp)]
933 #[generic_over_ip(I, Ip)]
934 struct WatchInputs<'a, I: FidlRuleIpExt> {
935 watcher_proxy: &'a <I::RuleWatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
936 }
937 #[derive(GenericOverIp)]
938 #[generic_over_ip(I, Ip)]
939 struct WatchOutputs<I: FidlRuleIpExt> {
940 watch_fut: fidl::client::QueryResponseFut<Vec<I::RuleEvent>, flex_client::Dialect>,
941 }
942 let WatchOutputs { watch_fut } = net_types::map_ip_twice!(
943 I,
944 WatchInputs { watcher_proxy },
945 |WatchInputs { watcher_proxy }| { WatchOutputs { watch_fut: watcher_proxy.watch() } }
946 );
947 watch_fut.await
948}
949
950#[derive(Debug, Error)]
952pub enum RuleWatchError {
953 #[error("the call to `Watch()` failed: {0}")]
955 Fidl(fidl::Error),
956 #[error("failed to convert event returned by `Watch()`: {0}")]
958 Conversion(RuleFidlConversionError),
959 #[error("the call to `Watch()` returned an empty batch of events")]
961 EmptyEventBatch,
962}
963
964pub fn rule_event_stream_from_state<I: FidlRuleIpExt + FidlRouteIpExt>(
966 state: &<I::StateMarker as flex_client::fidl::ProtocolMarker>::Proxy,
967) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>> + use<I>, WatcherCreationError>
968{
969 let watcher = get_rule_watcher::<I>(state)?;
970 rule_event_stream_from_watcher(watcher)
971}
972
973pub fn rule_event_stream_from_watcher<I: FidlRuleIpExt>(
980 watcher: <I::RuleWatcherMarker as flex_client::fidl::ProtocolMarker>::Proxy,
981) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>> + use<I>, WatcherCreationError>
982{
983 Ok(stream::ShortCircuit::new(
984 futures::stream::try_unfold(watcher, |watcher| async {
985 let events_batch = watch::<I>(&watcher).await.map_err(RuleWatchError::Fidl)?;
986 if events_batch.is_empty() {
987 return Err(RuleWatchError::EmptyEventBatch);
988 }
989 let events_batch = events_batch
990 .into_iter()
991 .map(|event| event.try_into().map_err(RuleWatchError::Conversion));
992 let event_stream = futures::stream::iter(events_batch);
993 Ok(Some((event_stream, watcher)))
994 })
995 .try_flatten(),
997 ))
998}
999
1000#[derive(Debug, Error)]
1002pub enum CollectRulesUntilIdleError<I: FidlRuleIpExt> {
1003 #[error("there was an error in the event stream: {0}")]
1005 ErrorInStream(RuleWatchError),
1006 #[error("there was an unexpected event in the event stream: {0:?}")]
1009 UnexpectedEvent(RuleEvent<I>),
1010 #[error("the event stream unexpectedly ended")]
1012 StreamEnded,
1013}
1014
1015pub async fn collect_rules_until_idle<I: FidlRuleIpExt, C: Extend<InstalledRule<I>> + Default>(
1018 event_stream: impl futures::Stream<Item = Result<RuleEvent<I>, RuleWatchError>> + Unpin,
1019) -> Result<C, CollectRulesUntilIdleError<I>> {
1020 fold::fold_while(
1021 event_stream,
1022 Ok(C::default()),
1023 |existing_rules: Result<C, CollectRulesUntilIdleError<I>>, event| {
1024 futures::future::ready(match existing_rules {
1025 Err(_) => {
1026 unreachable!("`existing_rules` must be `Ok`, because we stop folding on err")
1027 }
1028 Ok(mut existing_rules) => match event {
1029 Err(e) => {
1030 fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::ErrorInStream(e)))
1031 }
1032 Ok(e) => match e {
1033 RuleEvent::Existing(e) => {
1034 existing_rules.extend([e]);
1035 fold::FoldWhile::Continue(Ok(existing_rules))
1036 }
1037 RuleEvent::Idle => fold::FoldWhile::Done(Ok(existing_rules)),
1038 e @ RuleEvent::Added(_) | e @ RuleEvent::Removed(_) => {
1039 fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::UnexpectedEvent(
1040 e,
1041 )))
1042 }
1043 },
1044 },
1045 })
1046 },
1047 )
1048 .await
1049 .short_circuited()
1050 .map_err(|_accumulated_thus_far: Result<C, CollectRulesUntilIdleError<I>>| {
1051 CollectRulesUntilIdleError::StreamEnded
1052 })?
1053}
1054
1055#[cfg(test)]
1056mod tests {
1057 use assert_matches::assert_matches;
1058
1059 use super::*;
1060
1061 #[test]
1062 fn missing_base_matcher_default_v4() {
1063 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV4 {
1064 from: None,
1065 base: None,
1066 __source_breaking: fidl::marker::SourceBreaking,
1067 };
1068 assert_eq!(RuleMatcher::try_from(fidl_matcher), Ok(Default::default()));
1069 }
1070
1071 #[test]
1072 fn missing_base_matcher_default_v6() {
1073 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV6 {
1074 from: None,
1075 base: None,
1076 __source_breaking: fidl::marker::SourceBreaking,
1077 };
1078 assert_eq!(RuleMatcher::try_from(fidl_matcher), Ok(Default::default()));
1079 }
1080
1081 #[test]
1082 fn invalid_destination_subnet_v4() {
1083 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV4 {
1084 from: Some(net_declare::fidl_ip_v4_with_prefix!("192.168.0.1/24")),
1086 base: None,
1087 __source_breaking: fidl::marker::SourceBreaking,
1088 };
1089 assert_matches!(
1090 RuleMatcher::try_from(fidl_matcher),
1091 Err(RuleFidlConversionError::DestinationSubnet(_))
1092 );
1093 }
1094
1095 #[test]
1096 fn invalid_destination_subnet_v6() {
1097 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV6 {
1098 from: Some(net_declare::fidl_ip_v6_with_prefix!("fe80::1/64")),
1100 base: None,
1101 __source_breaking: fidl::marker::SourceBreaking,
1102 };
1103 assert_matches!(
1104 RuleMatcher::try_from(fidl_matcher),
1105 Err(RuleFidlConversionError::DestinationSubnet(_))
1106 );
1107 }
1108
1109 #[test]
1110 fn all_unspecified_matcher_v4() {
1111 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV4 {
1112 from: None,
1113 base: None,
1114 __source_breaking: fidl::marker::SourceBreaking,
1115 };
1116 let ext_matcher = RuleMatcher {
1117 from: None,
1118 locally_generated: None,
1119 bound_device: None,
1120 mark_1: None,
1121 mark_2: None,
1122 };
1123 assert_eq!(RuleMatcher::try_from(fidl_matcher.clone()), Ok(ext_matcher.clone()));
1124 assert_eq!(flex_fuchsia_net_routes::RuleMatcherV4::from(ext_matcher), fidl_matcher,)
1125 }
1126
1127 #[test]
1128 fn all_unspecified_matcher_v6() {
1129 let fidl_matcher = flex_fuchsia_net_routes::RuleMatcherV6 {
1130 from: None,
1131 base: None,
1132 __source_breaking: fidl::marker::SourceBreaking,
1133 };
1134 let ext_matcher = RuleMatcher {
1135 from: None,
1136 locally_generated: None,
1137 bound_device: None,
1138 mark_1: None,
1139 mark_2: None,
1140 };
1141 assert_eq!(RuleMatcher::try_from(fidl_matcher.clone()), Ok(ext_matcher.clone()));
1142 assert_eq!(flex_fuchsia_net_routes::RuleMatcherV6::from(ext_matcher), fidl_matcher,)
1143 }
1144}