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