1use std::fmt::Debug;
8use std::ops::RangeInclusive;
9
10use async_utils::{fold, stream};
11use fidl::endpoints::{DiscoverableProtocolMarker, ProtocolMarker, Proxy as _};
12use fidl_fuchsia_net_ext::{IntoExt as _, TryIntoExt as _};
13use futures::future::Either;
14use futures::{Stream, TryStreamExt as _};
15use net_types::ip::{GenericOverIp, Ip, Ipv4, Ipv6, Subnet};
16use thiserror::Error;
17use {
18 fidl_fuchsia_net as fnet, fidl_fuchsia_net_routes as fnet_routes,
19 fidl_fuchsia_net_routes_admin as fnet_routes_admin,
20};
21
22use crate::{impl_responder, FidlRouteIpExt, Responder, SliceResponder, WatcherCreationError};
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, Hash, PartialEq, Eq, Clone)]
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, Clone, Copy, PartialEq)]
368pub enum RuleFidlConversionError {
369 #[error("BaseMatcher is missing from the RuleMatcher")]
372 BaseMatcherMissing,
373 #[error("failed to convert `destination` to net_types subnet: {0:?}")]
375 DestinationSubnet(net_types::ip::SubnetError),
376 #[error("unexpected union variant for {name}, got ordinal = ({unknown_ordinal})")]
378 #[allow(missing_docs)]
379 UnknownOrdinal { name: &'static str, unknown_ordinal: u64 },
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
384pub struct RuleSetPriority(u32);
385
386impl RuleSetPriority {
387 pub const fn new(x: u32) -> Self {
389 Self(x)
390 }
391}
392
393pub const DEFAULT_RULE_SET_PRIORITY: RuleSetPriority =
396 RuleSetPriority(fnet_routes::DEFAULT_RULE_SET_PRIORITY);
397
398#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
400pub struct RuleIndex(u32);
401
402impl RuleIndex {
403 pub const fn new(x: u32) -> Self {
405 Self(x)
406 }
407}
408
409impl From<RuleSetPriority> for u32 {
410 fn from(RuleSetPriority(x): RuleSetPriority) -> Self {
411 x
412 }
413}
414
415impl From<u32> for RuleSetPriority {
416 fn from(x: u32) -> Self {
417 Self(x)
418 }
419}
420
421impl From<RuleIndex> for u32 {
422 fn from(RuleIndex(x): RuleIndex) -> Self {
423 x
424 }
425}
426
427impl From<u32> for RuleIndex {
428 fn from(x: u32) -> Self {
429 Self(x)
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq, Hash)]
435pub enum InterfaceMatcher {
436 DeviceName(String),
438}
439
440impl TryFrom<fnet_routes::InterfaceMatcher> for InterfaceMatcher {
441 type Error = RuleFidlConversionError;
442 fn try_from(matcher: fnet_routes::InterfaceMatcher) -> Result<Self, Self::Error> {
443 match matcher {
444 fnet_routes::InterfaceMatcher::DeviceName(name) => Ok(Self::DeviceName(name)),
445 fnet_routes::InterfaceMatcher::__SourceBreaking { unknown_ordinal } => {
446 Err(RuleFidlConversionError::UnknownOrdinal {
447 name: "InterfaceMatcher",
448 unknown_ordinal,
449 })
450 }
451 }
452 }
453}
454
455impl From<InterfaceMatcher> for fnet_routes::InterfaceMatcher {
456 fn from(matcher: InterfaceMatcher) -> Self {
457 match matcher {
458 InterfaceMatcher::DeviceName(name) => fnet_routes::InterfaceMatcher::DeviceName(name),
459 }
460 }
461}
462
463#[derive(Debug, Clone, Default, Hash, PartialEq, Eq)]
468pub struct RuleMatcher<I: Ip> {
469 pub from: Option<Subnet<I::Addr>>,
471 pub locally_generated: Option<bool>,
473 pub bound_device: Option<InterfaceMatcher>,
476 pub mark_1: Option<MarkMatcher>,
478 pub mark_2: Option<MarkMatcher>,
480}
481
482impl TryFrom<fnet_routes::RuleMatcherV4> for RuleMatcher<Ipv4> {
483 type Error = RuleFidlConversionError;
484 fn try_from(
485 fnet_routes::RuleMatcherV4 {
486 from,
487 base,
488 __source_breaking: fidl::marker::SourceBreaking,
489 }: fnet_routes::RuleMatcherV4,
490 ) -> Result<Self, Self::Error> {
491 let fnet_routes::BaseMatcher {
492 locally_generated,
493 bound_device,
494 mark_1,
495 mark_2,
496 __source_breaking: fidl::marker::SourceBreaking,
497 } = base.ok_or(RuleFidlConversionError::BaseMatcherMissing)?;
498 Ok(Self {
499 from: from
500 .map(|from| from.try_into_ext().map_err(RuleFidlConversionError::DestinationSubnet))
501 .transpose()?,
502 locally_generated,
503 bound_device: bound_device.map(InterfaceMatcher::try_from).transpose()?,
504 mark_1: mark_1.map(MarkMatcher::try_from).transpose()?,
505 mark_2: mark_2.map(MarkMatcher::try_from).transpose()?,
506 })
507 }
508}
509
510impl From<RuleMatcher<Ipv4>> for fnet_routes::RuleMatcherV4 {
511 fn from(
512 RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv4>,
513 ) -> Self {
514 fnet_routes::RuleMatcherV4 {
515 from: from.map(|from| fnet::Ipv4AddressWithPrefix {
516 addr: from.network().into_ext(),
517 prefix_len: from.prefix(),
518 }),
519 base: Some(fnet_routes::BaseMatcher {
520 locally_generated,
521 bound_device: bound_device.map(fnet_routes::InterfaceMatcher::from),
522 mark_1: mark_1.map(Into::into),
523 mark_2: mark_2.map(Into::into),
524 __source_breaking: fidl::marker::SourceBreaking,
525 }),
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.ok_or(RuleFidlConversionError::BaseMatcherMissing)?;
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.map(InterfaceMatcher::try_from).transpose()?,
553 mark_1: mark_1.map(MarkMatcher::try_from).transpose()?,
554 mark_2: mark_2.map(MarkMatcher::try_from).transpose()?,
555 })
556 }
557}
558
559impl From<RuleMatcher<Ipv6>> for fnet_routes::RuleMatcherV6 {
560 fn from(
561 RuleMatcher { from, locally_generated, bound_device, mark_1, mark_2 }: RuleMatcher<Ipv6>,
562 ) -> Self {
563 fnet_routes::RuleMatcherV6 {
564 from: from.map(|from| fnet::Ipv6AddressWithPrefix {
565 addr: from.network().into_ext(),
566 prefix_len: from.prefix(),
567 }),
568 base: Some(fnet_routes::BaseMatcher {
569 locally_generated,
570 bound_device: bound_device.map(fnet_routes::InterfaceMatcher::from),
571 mark_1: mark_1.map(Into::into),
572 mark_2: mark_2.map(Into::into),
573 __source_breaking: fidl::marker::SourceBreaking,
574 }),
575 __source_breaking: fidl::marker::SourceBreaking,
576 }
577 }
578}
579
580#[derive(Debug, Clone, Hash, PartialEq, Eq)]
581pub enum MarkMatcher {
583 Unmarked,
585 Marked {
587 mask: u32,
589 between: RangeInclusive<u32>,
591 },
592}
593
594impl TryFrom<fnet_routes::MarkMatcher> for MarkMatcher {
595 type Error = RuleFidlConversionError;
596
597 fn try_from(sel: fnet_routes::MarkMatcher) -> Result<Self, Self::Error> {
598 match sel {
599 fnet_routes::MarkMatcher::Unmarked(fnet_routes::Unmarked) => Ok(MarkMatcher::Unmarked),
600 fnet_routes::MarkMatcher::Marked(fnet_routes::Marked {
601 mask,
602 between: fnet_routes::Between { start, end },
603 }) => Ok(MarkMatcher::Marked { mask, between: RangeInclusive::new(start, end) }),
604 fnet_routes::MarkMatcher::__SourceBreaking { unknown_ordinal } => {
605 Err(RuleFidlConversionError::UnknownOrdinal {
606 name: "MarkMatcher",
607 unknown_ordinal,
608 })
609 }
610 }
611 }
612}
613
614impl From<MarkMatcher> for fnet_routes::MarkMatcher {
615 fn from(sel: MarkMatcher) -> Self {
616 match sel {
617 MarkMatcher::Unmarked => fnet_routes::MarkMatcher::Unmarked(fnet_routes::Unmarked),
618 MarkMatcher::Marked { mask, between } => {
619 let (start, end) = between.into_inner();
620 fnet_routes::MarkMatcher::Marked(fnet_routes::Marked {
621 mask,
622 between: fnet_routes::Between { start, end },
623 })
624 }
625 }
626 }
627}
628
629#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
630pub enum RuleAction {
632 Unreachable,
634 Lookup(crate::TableId),
638}
639
640impl From<fnet_routes::RuleAction> for RuleAction {
641 fn from(action: fnet_routes::RuleAction) -> Self {
642 match action {
643 fnet_routes::RuleAction::Lookup(table_id) => {
644 RuleAction::Lookup(crate::TableId::new(table_id))
645 }
646 fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable) => {
647 RuleAction::Unreachable
648 }
649 fnet_routes::RuleAction::__SourceBreaking { unknown_ordinal } => {
650 panic!("unexpected mark matcher variant, unknown ordinal: {unknown_ordinal}")
651 }
652 }
653 }
654}
655
656impl From<RuleAction> for fnet_routes::RuleAction {
657 fn from(action: RuleAction) -> Self {
658 match action {
659 RuleAction::Unreachable => {
660 fnet_routes::RuleAction::Unreachable(fnet_routes::Unreachable)
661 }
662 RuleAction::Lookup(table_id) => fnet_routes::RuleAction::Lookup(table_id.get()),
663 }
664 }
665}
666
667#[derive(GenericOverIp, Debug)]
669#[generic_over_ip(I, Ip)]
670pub enum RuleTableRequest<I: FidlRuleAdminIpExt> {
671 NewRuleSet {
673 priority: RuleSetPriority,
675 rule_set: fidl::endpoints::ServerEnd<I::RuleSetMarker>,
677 control_handle: I::RuleTableControlHandle,
679 },
680}
681
682impl From<fnet_routes_admin::RuleTableV4Request> for RuleTableRequest<Ipv4> {
683 fn from(value: fnet_routes_admin::RuleTableV4Request) -> Self {
684 match value {
685 fnet_routes_admin::RuleTableV4Request::NewRuleSet {
686 priority,
687 rule_set,
688 control_handle,
689 } => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
690 }
691 }
692}
693
694impl From<fnet_routes_admin::RuleTableV6Request> for RuleTableRequest<Ipv6> {
695 fn from(value: fnet_routes_admin::RuleTableV6Request) -> Self {
696 match value {
697 fnet_routes_admin::RuleTableV6Request::NewRuleSet {
698 priority,
699 rule_set,
700 control_handle,
701 } => Self::NewRuleSet { priority: RuleSetPriority(priority), rule_set, control_handle },
702 }
703 }
704}
705
706#[derive(GenericOverIp, Debug)]
708#[generic_over_ip(I, Ip)]
709pub enum RuleSetRequest<I: FidlRuleAdminIpExt> {
710 AddRule {
712 index: RuleIndex,
714 matcher: Result<RuleMatcher<I>, RuleFidlConversionError>,
716 action: RuleAction,
718 responder: I::RuleSetAddRuleResponder,
720 },
721 RemoveRule {
723 index: RuleIndex,
725 responder: I::RuleSetRemoveRuleResponder,
727 },
728 AuthenticateForRouteTable {
730 table: u32,
732 token: fidl::Event,
734 responder: I::RuleSetAuthenticateForRouteTableResponder,
736 },
737 Close {
739 control_handle: I::RuleSetControlHandle,
741 },
742}
743
744impl From<fnet_routes_admin::RuleSetV4Request> for RuleSetRequest<Ipv4> {
745 fn from(value: fnet_routes_admin::RuleSetV4Request) -> Self {
746 match value {
747 fnet_routes_admin::RuleSetV4Request::AddRule { index, matcher, action, responder } => {
748 RuleSetRequest::AddRule {
749 index: RuleIndex(index),
750 matcher: matcher.try_into(),
751 action: action.into(),
752 responder,
753 }
754 }
755 fnet_routes_admin::RuleSetV4Request::RemoveRule { index, responder } => {
756 RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
757 }
758 fnet_routes_admin::RuleSetV4Request::AuthenticateForRouteTable {
759 table,
760 token,
761 responder,
762 } => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
763 fnet_routes_admin::RuleSetV4Request::Close { control_handle } => {
764 RuleSetRequest::Close { control_handle }
765 }
766 }
767 }
768}
769impl From<fnet_routes_admin::RuleSetV6Request> for RuleSetRequest<Ipv6> {
770 fn from(value: fnet_routes_admin::RuleSetV6Request) -> Self {
771 match value {
772 fnet_routes_admin::RuleSetV6Request::AddRule { index, matcher, action, responder } => {
773 RuleSetRequest::AddRule {
774 index: RuleIndex(index),
775 matcher: matcher.try_into(),
776 action: action.into(),
777 responder,
778 }
779 }
780 fnet_routes_admin::RuleSetV6Request::RemoveRule { index, responder } => {
781 RuleSetRequest::RemoveRule { index: RuleIndex(index), responder }
782 }
783 fnet_routes_admin::RuleSetV6Request::AuthenticateForRouteTable {
784 table,
785 token,
786 responder,
787 } => RuleSetRequest::AuthenticateForRouteTable { table, token, responder },
788 fnet_routes_admin::RuleSetV6Request::Close { control_handle } => {
789 RuleSetRequest::Close { control_handle }
790 }
791 }
792 }
793}
794
795#[derive(Clone, Debug, Error)]
797pub enum RuleSetCreationError {
798 #[error("failed to create proxy: {0}")]
800 CreateProxy(fidl::Error),
801 #[error("failed to create route set: {0}")]
803 RuleSet(fidl::Error),
804}
805
806pub fn new_rule_set<I: Ip + FidlRuleAdminIpExt>(
808 rule_table_proxy: &<I::RuleTableMarker as ProtocolMarker>::Proxy,
809 priority: RuleSetPriority,
810) -> Result<<I::RuleSetMarker as ProtocolMarker>::Proxy, RuleSetCreationError> {
811 let (rule_set_proxy, rule_set_server_end) = fidl::endpoints::create_proxy::<I::RuleSetMarker>();
812
813 #[derive(GenericOverIp)]
814 #[generic_over_ip(I, Ip)]
815 struct NewRuleSetInput<'a, I: FidlRuleAdminIpExt> {
816 rule_set_server_end: fidl::endpoints::ServerEnd<I::RuleSetMarker>,
817 rule_table_proxy: &'a <I::RuleTableMarker as ProtocolMarker>::Proxy,
818 }
819 let result = I::map_ip_in(
820 NewRuleSetInput::<'_, I> { rule_set_server_end, rule_table_proxy },
821 |NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
822 rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
823 },
824 |NewRuleSetInput { rule_set_server_end, rule_table_proxy }| {
825 rule_table_proxy.new_rule_set(priority.into(), rule_set_server_end)
826 },
827 );
828
829 result.map_err(RuleSetCreationError::RuleSet)?;
830 Ok(rule_set_proxy)
831}
832
833pub async fn authenticate_for_route_table<I: Ip + FidlRuleAdminIpExt>(
836 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
837 table_id: u32,
838 token: fidl::Event,
839) -> Result<Result<(), fnet_routes_admin::AuthenticateForRouteTableError>, fidl::Error> {
840 #[derive(GenericOverIp)]
841 #[generic_over_ip(I, Ip)]
842 struct AuthenticateForRouteTableInput<'a, I: FidlRuleAdminIpExt> {
843 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
844 table_id: u32,
845 token: fidl::Event,
846 }
847
848 I::map_ip_in(
849 AuthenticateForRouteTableInput { rule_set, table_id, token },
850 |AuthenticateForRouteTableInput { rule_set, table_id, token }| {
851 Either::Left(rule_set.authenticate_for_route_table(table_id, token))
852 },
853 |AuthenticateForRouteTableInput { rule_set, table_id, token }| {
854 Either::Right(rule_set.authenticate_for_route_table(table_id, token))
855 },
856 )
857 .await
858}
859
860pub async fn add_rule<I: Ip + FidlRuleAdminIpExt>(
862 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
863 index: RuleIndex,
864 matcher: RuleMatcher<I>,
865 action: RuleAction,
866) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
867 #[derive(GenericOverIp)]
868 #[generic_over_ip(I, Ip)]
869 struct AddRuleInput<'a, I: FidlRuleAdminIpExt> {
870 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
871 index: RuleIndex,
872 matcher: RuleMatcher<I>,
873 action: RuleAction,
874 }
875
876 I::map_ip_in(
877 AddRuleInput { rule_set, index, matcher, action },
878 |AddRuleInput { rule_set, index, matcher, action }| {
879 Either::Left(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
880 },
881 |AddRuleInput { rule_set, index, matcher, action }| {
882 Either::Right(rule_set.add_rule(index.into(), &matcher.into(), &action.into()))
883 },
884 )
885 .await
886}
887
888pub async fn remove_rule<I: Ip + FidlRuleAdminIpExt>(
890 rule_set: &<I::RuleSetMarker as ProtocolMarker>::Proxy,
891 index: RuleIndex,
892) -> Result<Result<(), fnet_routes_admin::RuleSetError>, fidl::Error> {
893 #[derive(GenericOverIp)]
894 #[generic_over_ip(I, Ip)]
895 struct RemoveRuleInput<'a, I: FidlRuleAdminIpExt> {
896 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
897 index: RuleIndex,
898 }
899
900 I::map_ip_in(
901 RemoveRuleInput { rule_set, index },
902 |RemoveRuleInput { rule_set, index }| Either::Left(rule_set.remove_rule(index.into())),
903 |RemoveRuleInput { rule_set, index }| Either::Right(rule_set.remove_rule(index.into())),
904 )
905 .await
906}
907
908pub async fn close_rule_set<I: Ip + FidlRuleAdminIpExt>(
912 rule_set: <I::RuleSetMarker as ProtocolMarker>::Proxy,
913) -> Result<(), fidl::Error> {
914 #[derive(GenericOverIp)]
915 #[generic_over_ip(I, Ip)]
916 struct CloseInput<'a, I: FidlRuleAdminIpExt> {
917 rule_set: &'a <I::RuleSetMarker as ProtocolMarker>::Proxy,
918 }
919
920 let result = I::map_ip_in(
921 CloseInput { rule_set: &rule_set },
922 |CloseInput { rule_set }| rule_set.close(),
923 |CloseInput { rule_set }| rule_set.close(),
924 );
925
926 assert!(rule_set
927 .on_closed()
928 .await
929 .expect("failed to wait for signals")
930 .contains(fidl::Signals::CHANNEL_PEER_CLOSED));
931
932 result
933}
934
935pub fn get_rule_watcher<I: FidlRuleIpExt + FidlRouteIpExt>(
937 state_proxy: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
938) -> Result<<I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy, WatcherCreationError>
939{
940 let (watcher_proxy, watcher_server_end) =
941 fidl::endpoints::create_proxy::<I::RuleWatcherMarker>();
942
943 #[derive(GenericOverIp)]
944 #[generic_over_ip(I, Ip)]
945 struct GetWatcherInputs<'a, I: FidlRuleIpExt + FidlRouteIpExt> {
946 watcher_server_end: fidl::endpoints::ServerEnd<I::RuleWatcherMarker>,
947 state_proxy: &'a <I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
948 }
949 let result = I::map_ip_in(
950 GetWatcherInputs::<'_, I> { watcher_server_end, state_proxy },
951 |GetWatcherInputs { watcher_server_end, state_proxy }| {
952 state_proxy.get_rule_watcher_v4(
953 watcher_server_end,
954 &fnet_routes::RuleWatcherOptionsV4::default(),
955 )
956 },
957 |GetWatcherInputs { watcher_server_end, state_proxy }| {
958 state_proxy.get_rule_watcher_v6(
959 watcher_server_end,
960 &fnet_routes::RuleWatcherOptionsV6::default(),
961 )
962 },
963 );
964
965 result.map_err(WatcherCreationError::GetWatcher)?;
966 Ok(watcher_proxy)
967}
968
969pub async fn watch<'a, I: FidlRuleIpExt>(
971 watcher_proxy: &'a <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
972) -> Result<Vec<I::RuleEvent>, fidl::Error> {
973 #[derive(GenericOverIp)]
974 #[generic_over_ip(I, Ip)]
975 struct WatchInputs<'a, I: FidlRuleIpExt> {
976 watcher_proxy: &'a <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
977 }
978 #[derive(GenericOverIp)]
979 #[generic_over_ip(I, Ip)]
980 struct WatchOutputs<I: FidlRuleIpExt> {
981 watch_fut: fidl::client::QueryResponseFut<Vec<I::RuleEvent>>,
982 }
983 let WatchOutputs { watch_fut } = net_types::map_ip_twice!(
984 I,
985 WatchInputs { watcher_proxy },
986 |WatchInputs { watcher_proxy }| { WatchOutputs { watch_fut: watcher_proxy.watch() } }
987 );
988 watch_fut.await
989}
990
991#[derive(Clone, Debug, Error)]
993pub enum RuleWatchError {
994 #[error("the call to `Watch()` failed: {0}")]
996 Fidl(fidl::Error),
997 #[error("failed to convert event returned by `Watch()`: {0}")]
999 Conversion(RuleFidlConversionError),
1000 #[error("the call to `Watch()` returned an empty batch of events")]
1002 EmptyEventBatch,
1003}
1004
1005pub fn rule_event_stream_from_state<I: FidlRuleIpExt + FidlRouteIpExt>(
1007 state: &<I::StateMarker as fidl::endpoints::ProtocolMarker>::Proxy,
1008) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>>, WatcherCreationError> {
1009 let watcher = get_rule_watcher::<I>(state)?;
1010 rule_event_stream_from_watcher(watcher)
1011}
1012
1013pub fn rule_event_stream_from_watcher<I: FidlRuleIpExt>(
1020 watcher: <I::RuleWatcherMarker as fidl::endpoints::ProtocolMarker>::Proxy,
1021) -> Result<impl Stream<Item = Result<RuleEvent<I>, RuleWatchError>>, WatcherCreationError> {
1022 Ok(stream::ShortCircuit::new(
1023 futures::stream::try_unfold(watcher, |watcher| async {
1024 let events_batch = watch::<I>(&watcher).await.map_err(RuleWatchError::Fidl)?;
1025 if events_batch.is_empty() {
1026 return Err(RuleWatchError::EmptyEventBatch);
1027 }
1028 let events_batch = events_batch
1029 .into_iter()
1030 .map(|event| event.try_into().map_err(RuleWatchError::Conversion));
1031 let event_stream = futures::stream::iter(events_batch);
1032 Ok(Some((event_stream, watcher)))
1033 })
1034 .try_flatten(),
1036 ))
1037}
1038
1039#[derive(Clone, Debug, Error)]
1041pub enum CollectRulesUntilIdleError<I: FidlRuleIpExt> {
1042 #[error("there was an error in the event stream: {0}")]
1044 ErrorInStream(RuleWatchError),
1045 #[error("there was an unexpected event in the event stream: {0:?}")]
1048 UnexpectedEvent(RuleEvent<I>),
1049 #[error("the event stream unexpectedly ended")]
1051 StreamEnded,
1052}
1053
1054pub async fn collect_rules_until_idle<I: FidlRuleIpExt, C: Extend<InstalledRule<I>> + Default>(
1057 event_stream: impl futures::Stream<Item = Result<RuleEvent<I>, RuleWatchError>> + Unpin,
1058) -> Result<C, CollectRulesUntilIdleError<I>> {
1059 fold::fold_while(
1060 event_stream,
1061 Ok(C::default()),
1062 |existing_rules: Result<C, CollectRulesUntilIdleError<I>>, event| {
1063 futures::future::ready(match existing_rules {
1064 Err(_) => {
1065 unreachable!("`existing_rules` must be `Ok`, because we stop folding on err")
1066 }
1067 Ok(mut existing_rules) => match event {
1068 Err(e) => {
1069 fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::ErrorInStream(e)))
1070 }
1071 Ok(e) => match e {
1072 RuleEvent::Existing(e) => {
1073 existing_rules.extend([e]);
1074 fold::FoldWhile::Continue(Ok(existing_rules))
1075 }
1076 RuleEvent::Idle => fold::FoldWhile::Done(Ok(existing_rules)),
1077 e @ RuleEvent::Added(_) | e @ RuleEvent::Removed(_) => {
1078 fold::FoldWhile::Done(Err(CollectRulesUntilIdleError::UnexpectedEvent(
1079 e,
1080 )))
1081 }
1082 },
1083 },
1084 })
1085 },
1086 )
1087 .await
1088 .short_circuited()
1089 .map_err(|_accumulated_thus_far: Result<C, CollectRulesUntilIdleError<I>>| {
1090 CollectRulesUntilIdleError::StreamEnded
1091 })?
1092}
1093
1094#[cfg(test)]
1095mod tests {
1096 use assert_matches::assert_matches;
1097 use fnet_routes::BaseMatcher;
1098
1099 use super::*;
1100
1101 #[test]
1102 fn missing_base_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 assert_matches!(
1109 RuleMatcher::try_from(fidl_matcher),
1110 Err(RuleFidlConversionError::BaseMatcherMissing)
1111 );
1112 }
1113
1114 #[test]
1115 fn missing_base_matcher_v6() {
1116 let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
1117 from: None,
1118 base: None,
1119 __source_breaking: fidl::marker::SourceBreaking,
1120 };
1121 assert_matches!(
1122 RuleMatcher::try_from(fidl_matcher),
1123 Err(RuleFidlConversionError::BaseMatcherMissing)
1124 );
1125 }
1126
1127 #[test]
1128 fn invalid_destination_subnet_v4() {
1129 let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV4 {
1130 from: Some(net_declare::fidl_ip_v4_with_prefix!("192.168.0.1/24")),
1132 base: Some(BaseMatcher::default()),
1133 __source_breaking: fidl::marker::SourceBreaking,
1134 };
1135 assert_matches!(
1136 RuleMatcher::try_from(fidl_matcher),
1137 Err(RuleFidlConversionError::DestinationSubnet(_))
1138 );
1139 }
1140
1141 #[test]
1142 fn invalid_destination_subnet_v6() {
1143 let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
1144 from: Some(net_declare::fidl_ip_v6_with_prefix!("fe80::1/64")),
1146 base: Some(BaseMatcher::default()),
1147 __source_breaking: fidl::marker::SourceBreaking,
1148 };
1149 assert_matches!(
1150 RuleMatcher::try_from(fidl_matcher),
1151 Err(RuleFidlConversionError::DestinationSubnet(_))
1152 );
1153 }
1154
1155 #[test]
1156 fn all_unspecified_matcher_v4() {
1157 let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV4 {
1158 from: None,
1159 base: Some(BaseMatcher {
1160 locally_generated: None,
1161 bound_device: None,
1162 mark_1: None,
1163 mark_2: None,
1164 __source_breaking: fidl::marker::SourceBreaking,
1165 }),
1166 __source_breaking: fidl::marker::SourceBreaking,
1167 };
1168 assert_matches!(
1169 RuleMatcher::try_from(fidl_matcher),
1170 Ok(RuleMatcher {
1171 from: None,
1172 locally_generated: None,
1173 bound_device: None,
1174 mark_1: None,
1175 mark_2: None,
1176 })
1177 );
1178 }
1179
1180 #[test]
1181 fn all_unspecified_matcher_v6() {
1182 let fidl_matcher = fidl_fuchsia_net_routes::RuleMatcherV6 {
1183 from: None,
1184 base: Some(BaseMatcher {
1185 locally_generated: None,
1186 bound_device: None,
1187 mark_1: None,
1188 mark_2: None,
1189 __source_breaking: fidl::marker::SourceBreaking,
1190 }),
1191 __source_breaking: fidl::marker::SourceBreaking,
1192 };
1193 assert_matches!(
1194 RuleMatcher::try_from(fidl_matcher),
1195 Ok(RuleMatcher {
1196 from: None,
1197 locally_generated: None,
1198 bound_device: None,
1199 mark_1: None,
1200 mark_2: None,
1201 })
1202 );
1203 }
1204}