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