1use std::collections::HashMap;
6
7use derivative::Derivative;
8use fidl::endpoints::ControlHandle;
9use fidl_fuchsia_net_filter_ext::{CommitError, Matchers, PushChangesError, RuleId};
10use fnet_masquerade::Error;
11use futures::stream::LocalBoxStream;
12use futures::{StreamExt as _, TryStreamExt as _, future};
13use log::{error, warn};
14use net_declare::fidl_subnet;
15use {
16 fidl_fuchsia_net as fnet, fidl_fuchsia_net_filter_deprecated as fnet_filter_deprecated,
17 fidl_fuchsia_net_masquerade as fnet_masquerade,
18 fidl_fuchsia_net_matchers_ext as fnet_matchers_ext,
19};
20
21use crate::filter::{FilterControl, FilterEnabledState, FilterError};
22use crate::{InterfaceId, InterfaceState};
23
24const V4_UNSPECIFIED_SUBNET: fnet::Subnet = fidl_subnet!("0.0.0.0/0");
25const V6_UNSPECIFIED_SUBNET: fnet::Subnet = fidl_subnet!("::/0");
26
27#[derive(Derivative)]
28#[derivative(Debug)]
29pub(super) enum Event {
30 FactoryRequestStream(#[derivative(Debug = "ignore")] fnet_masquerade::FactoryRequestStream),
31 FactoryRequest(fnet_masquerade::FactoryRequest),
32 ControlRequest(ValidatedConfig, fnet_masquerade::ControlRequest),
33 Disconnect(ValidatedConfig),
34}
35
36pub(super) type EventStream = LocalBoxStream<'static, Result<Event, fidl::Error>>;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39pub(super) struct ValidatedConfig {
40 pub src_subnet: fnet_matchers_ext::Subnet,
42 pub output_interface: InterfaceId,
44}
45
46impl TryFrom<fnet_masquerade::ControlConfig> for ValidatedConfig {
47 type Error = fnet_masquerade::Error;
48
49 fn try_from(
50 fnet_masquerade::ControlConfig {
51 src_subnet,
52 output_interface
53 }: fnet_masquerade::ControlConfig,
54 ) -> Result<Self, Self::Error> {
55 if src_subnet == V4_UNSPECIFIED_SUBNET || src_subnet == V6_UNSPECIFIED_SUBNET {
56 return Err(Error::Unsupported);
57 }
58 Ok(Self {
59 src_subnet: src_subnet.try_into().map_err(|_| Error::InvalidArguments)?,
60 output_interface: InterfaceId::new(output_interface).ok_or(Error::InvalidArguments)?,
61 })
62 }
63}
64
65#[derive(Clone, Debug)]
67enum MasqueradeFilterState {
68 Inactive,
70 ActiveDeprecated,
72 ActiveCurrent { rule: RuleId },
74}
75
76impl MasqueradeFilterState {
77 fn is_active(&self) -> bool {
78 match self {
79 MasqueradeFilterState::Inactive => false,
80 MasqueradeFilterState::ActiveDeprecated
81 | MasqueradeFilterState::ActiveCurrent { rule: _ } => true,
82 }
83 }
84}
85
86#[derive(Debug, Clone)]
87struct MasqueradeState {
88 filter_state: MasqueradeFilterState,
89 control: fnet_masquerade::ControlControlHandle,
90}
91
92impl MasqueradeState {
93 fn new(control: fnet_masquerade::ControlControlHandle) -> Self {
94 Self { filter_state: MasqueradeFilterState::Inactive, control }
95 }
96}
97
98impl From<FilterError> for Error {
101 fn from(error: FilterError) -> Error {
102 match error {
103 FilterError::Push(e) => {
104 error!("failed to push filtering changes: {e}");
105 match e {
106 PushChangesError::CallMethod(e) => crate::exit_with_fidl_error(e),
107 PushChangesError::TooManyChanges
108 | PushChangesError::FidlConversion(_)
109 | PushChangesError::ErrorOnChange(_) => {
110 panic!("failed to push: generated filtering state was invalid.")
111 }
112 }
113 }
114 FilterError::Commit(e) => {
115 error!("failed to commit filtering changes: {e}");
116 match e {
117 CommitError::CallMethod(e) => crate::exit_with_fidl_error(e),
118 CommitError::CyclicalRoutineGraph(_)
119 | CommitError::MasqueradeWithInvalidMatcher(_)
120 | CommitError::TransparentProxyWithInvalidMatcher(_)
121 | CommitError::RedirectWithInvalidMatcher(_)
122 | CommitError::RuleWithInvalidAction(_)
123 | CommitError::RuleWithInvalidMatcher(_)
124 | CommitError::ErrorOnChange(_)
125 | CommitError::FidlConversion(_) => {
126 panic!("failed to commit: generated filtering state was invalid.")
127 }
128 }
129 }
130 }
131 }
132}
133
134async fn update_interface(
139 filter: &mut FilterControl,
140 interface: InterfaceId,
141 enabled: bool,
142 filter_enabled_state: &mut FilterEnabledState,
143 interface_states: &HashMap<InterfaceId, InterfaceState>,
144) -> Result<(), Error> {
145 if enabled {
146 filter_enabled_state.increment_masquerade_count_on_interface(interface);
147 } else {
148 filter_enabled_state.decrement_masquerade_count_on_interface(interface);
149 }
150
151 let interface_type = interface_states.get(&interface).map(|is| is.device_class.into());
152
153 match filter {
154 FilterControl::Deprecated(f) => filter_enabled_state
155 .maybe_update_deprecated(interface_type, interface, f)
156 .await
157 .map_err(|e| match e {
158 fnet_filter_deprecated::EnableDisableInterfaceError::NotFound => {
159 warn!("specified input_interface not found: {interface}");
160 Error::NotFound
161 }
162 }),
163 FilterControl::Current(f) => filter_enabled_state
164 .maybe_update_current(interface_type, interface, f)
165 .await
166 .map_err(Error::from),
167 }
168}
169
170async fn add_or_remove_masquerade_rule(
175 filter: &mut FilterControl,
176 config: ValidatedConfig,
177 existing_state: &MasqueradeFilterState,
178) -> Result<MasqueradeFilterState, Error> {
179 let ValidatedConfig { src_subnet, output_interface } = config;
180 match (filter, existing_state) {
181 (FilterControl::Deprecated(filter), MasqueradeFilterState::Inactive) => {
182 crate::filter::add_masquerade_rule_deprecated(
183 filter,
184 fnet_filter_deprecated::Nat {
185 proto: fnet_filter_deprecated::SocketProtocol::Any,
186 src_subnet: src_subnet.into(),
187 outgoing_nic: output_interface.get(),
188 },
189 )
190 .await?;
191 Ok(MasqueradeFilterState::ActiveDeprecated)
192 }
193 (FilterControl::Deprecated(filter), MasqueradeFilterState::ActiveDeprecated) => {
194 crate::filter::remove_masquerade_rule_deprecated(
195 filter,
196 fnet_filter_deprecated::Nat {
197 proto: fnet_filter_deprecated::SocketProtocol::Any,
198 src_subnet: src_subnet.into(),
199 outgoing_nic: output_interface.get(),
200 },
201 )
202 .await?;
203 Ok(MasqueradeFilterState::Inactive)
204 }
205 (FilterControl::Current(filter), MasqueradeFilterState::Inactive) => {
206 let rule = crate::filter::add_masquerade_rule_current(
207 filter,
208 Matchers {
209 out_interface: Some(fnet_matchers_ext::Interface::Id(output_interface.into())),
210 src_addr: Some(fnet_matchers_ext::Address {
211 matcher: fnet_matchers_ext::AddressMatcherType::Subnet(src_subnet),
212 invert: false,
213 }),
214 ..Default::default()
215 },
216 )
217 .await
218 .map_err(Error::from)?;
219 Ok(MasqueradeFilterState::ActiveCurrent { rule })
220 }
221 (FilterControl::Current(filter), MasqueradeFilterState::ActiveCurrent { rule }) => {
222 crate::filter::remove_masquerade_rule_current(filter, rule)
223 .await
224 .map_err(Error::from)?;
225 Ok(MasqueradeFilterState::Inactive)
226 }
227 (FilterControl::Deprecated(_), MasqueradeFilterState::ActiveCurrent { rule: _ }) => {
228 panic!("deprecated `filter` with current `existing_state` is impossible")
229 }
230 (FilterControl::Current(_), MasqueradeFilterState::ActiveDeprecated) => {
231 panic!("current `filter` with deprecated `existing_state` is impossible")
232 }
233 }
234}
235
236#[derive(Debug, Default)]
237pub(super) struct MasqueradeHandler {
238 active_controllers: HashMap<ValidatedConfig, MasqueradeState>,
239}
240
241impl MasqueradeHandler {
242 async fn set_enabled(
243 &mut self,
244 filter: &mut FilterControl,
245 config: ValidatedConfig,
246 enabled: bool,
247 filter_enabled_state: &mut FilterEnabledState,
248 interface_states: &HashMap<InterfaceId, InterfaceState>,
249 ) -> Result<bool, Error> {
250 let state = self.active_controllers.get_mut(&config).ok_or(Error::InvalidArguments)?;
251
252 let original_state = state.filter_state.is_active();
253 if original_state == enabled {
254 return Ok(original_state);
257 }
258 update_interface(
259 filter,
260 config.output_interface,
261 enabled,
262 filter_enabled_state,
263 interface_states,
264 )
265 .await?;
266 let new_state = add_or_remove_masquerade_rule(filter, config, &state.filter_state).await?;
267
268 state.filter_state = new_state;
269 Ok(original_state)
270 }
271
272 fn create_control(
277 &mut self,
278 config: ValidatedConfig,
279 control: fnet_masquerade::ControlControlHandle,
280 ) -> Result<(), (Error, fnet_masquerade::ControlControlHandle)> {
281 match self.active_controllers.entry(config) {
282 std::collections::hash_map::Entry::Vacant(e) => {
283 let _: &mut MasqueradeState = e.insert(MasqueradeState::new(control));
285 Ok(())
286 }
287 std::collections::hash_map::Entry::Occupied(_) => Err((Error::AlreadyExists, control)),
294 }
295 }
296
297 pub(super) async fn handle_event(
298 &mut self,
299 event: Event,
300 events: &mut futures::stream::SelectAll<EventStream>,
301 filter: &mut FilterControl,
302 filter_enabled_state: &mut FilterEnabledState,
303 interface_states: &HashMap<InterfaceId, InterfaceState>,
304 ) {
305 match event {
306 Event::FactoryRequestStream(stream) => events.push(
307 stream.try_filter_map(|r| future::ok(Some(Event::FactoryRequest(r)))).boxed(),
308 ),
309 Event::FactoryRequest(fnet_masquerade::FactoryRequest::Create {
310 config,
311 control,
312 responder,
313 }) => {
314 let (stream, control) = control.into_stream_and_control_handle();
315 let config = match ValidatedConfig::try_from(config) {
316 Ok(config) => config,
317 Err(e) => {
318 control.respond_and_maybe_shutdown(Err(e), |r| {
319 let _: Result<(), fidl::Error> = responder.send(r);
320 Ok(())
324 });
325 return;
326 }
327 };
328 match self.create_control(config, control) {
329 Ok(()) => {
330 if let Err(e) = responder.send(Ok(())) {
331 error!("failed to notify control of successful creation: {e:?}");
332 }
333 events.push(
334 stream
335 .try_filter_map(move |r| {
336 future::ok(Some(Event::ControlRequest(config, r)))
337 })
338 .chain(futures::stream::once(future::ok(Event::Disconnect(config))))
342 .boxed(),
343 );
344 }
345 Err((e, control)) => {
346 warn!("failed to create control: {e:?}");
347 control.respond_and_maybe_shutdown(Err(e), |r| responder.send(r));
348 }
349 }
350 }
351 Event::ControlRequest(
352 config,
353 fnet_masquerade::ControlRequest::SetEnabled { enabled, responder },
354 ) => {
355 let response = self
356 .set_enabled(filter, config, enabled, filter_enabled_state, interface_states)
357 .await;
358 let state = self
359 .active_controllers
360 .get_mut(&config)
361 .expect("no active_controller for the given interface");
362 state.respond_and_maybe_shutdown(response, |r| responder.send(r));
363 }
364 Event::Disconnect(config) => {
365 match self
366 .set_enabled(filter, config, false, filter_enabled_state, interface_states)
367 .await
368 {
369 Ok(_prev_enabled) => {}
370 Err(Error::NotFound) => {}
374 Err(Error::RetryExceeded) => error!(
375 "Failed to removed masquerade configuration for disconnected client \
376 (RetryExceeded): {config:?}"
377 ),
378 Err(Error::AlreadyExists)
379 | Err(Error::BadRule)
380 | Err(Error::InvalidArguments)
381 | Err(Error::Unsupported) => {
382 panic!("removing existing configuration cannot fail")
383 }
384 Err(Error::__SourceBreaking { unknown_ordinal: _ }) => {}
385 }
386 match self.active_controllers.remove(&config) {
387 None => panic!("controller was unexpectedly missing on disconnect"),
388 Some(_masquerade_state) => {}
389 }
390 }
391 }
392 }
393}
394
395trait RespondAndMaybeShutdown {
396 fn respond_and_maybe_shutdown<T: Clone, Sender>(
397 &self,
398 response: Result<T, fnet_masquerade::Error>,
399 sender: Sender,
400 ) where
401 Sender: FnOnce(Result<T, fnet_masquerade::Error>) -> Result<(), fidl::Error>;
402}
403
404fn to_epitaph(e: Error) -> fidl::Status {
405 match e {
406 Error::Unsupported => fidl::Status::NOT_SUPPORTED,
407 Error::InvalidArguments => fidl::Status::INVALID_ARGS,
408 Error::NotFound => fidl::Status::NOT_FOUND,
409 Error::AlreadyExists => fidl::Status::ALREADY_BOUND,
410 Error::BadRule => fidl::Status::BAD_PATH,
411 Error::RetryExceeded => fidl::Status::TIMED_OUT,
412 e => panic!("Unhandled error {e:?}"),
413 }
414}
415
416impl RespondAndMaybeShutdown for fnet_masquerade::ControlControlHandle {
417 fn respond_and_maybe_shutdown<T: Clone, Sender>(
418 &self,
419 response: Result<T, fnet_masquerade::Error>,
420 sender: Sender,
421 ) where
422 Sender: FnOnce(Result<T, fnet_masquerade::Error>) -> Result<(), fidl::Error>,
423 {
424 if let Err(err) = sender(response.clone()) {
426 error!("Shutting down due to fidl error: {err:?}");
427 self.shutdown_with_epitaph(fidl::Status::INTERNAL);
428 return;
429 }
430 if let Err(e) = response {
431 match e {
432 Error::RetryExceeded => {
433 }
435 e => {
436 warn!("Shutting down due to permanent error: {e:?}");
437 self.shutdown_with_epitaph(to_epitaph(e));
438 }
439 }
440 }
441 }
442}
443
444impl RespondAndMaybeShutdown for MasqueradeState {
445 fn respond_and_maybe_shutdown<T: Clone, Sender>(
446 &self,
447 response: Result<T, fnet_masquerade::Error>,
448 sender: Sender,
449 ) where
450 Sender: FnOnce(Result<T, fnet_masquerade::Error>) -> Result<(), fidl::Error>,
451 {
452 self.control.respond_and_maybe_shutdown(response, sender)
453 }
454}
455
456#[cfg(test)]
457pub mod test {
458 use fuchsia_sync::Mutex;
459 use std::collections::HashSet;
460 use std::sync::Arc;
461
462 use assert_matches::assert_matches;
463 use fidl_fuchsia_net_filter::{ControlRequest, NamespaceControllerRequest};
464 use fidl_fuchsia_net_filter_deprecated::FilterRequest;
465 use fidl_fuchsia_net_filter_ext::{Action, Change, Resource, ResourceId};
466 use futures::FutureExt;
467 use futures::future::FusedFuture;
468 use test_case::test_case;
469
470 use super::*;
471
472 const VALID_OUTPUT_INTERFACE: u64 = 11;
473 const NON_EXISTENT_INTERFACE: u64 = 1005;
474
475 const VALID_SUBNET: fnet::Subnet = fidl_subnet!("192.0.2.0/24");
476 const INVALID_SUBNET: fnet::Subnet = fidl_subnet!("192.0.2.1/24");
478
479 const DEFAULT_CONFIG: fnet_masquerade::ControlConfig = fnet_masquerade::ControlConfig {
480 src_subnet: VALID_SUBNET,
481 output_interface: VALID_OUTPUT_INTERFACE,
482 };
483
484 #[derive(Default)]
486 struct MockFilterStateDeprecated {
487 active_interfaces: HashSet<u64>,
488 nat_rules: Vec<fnet_filter_deprecated::Nat>,
489 nat_rules_generation: u32,
490 fail_generations: i32,
491 }
492
493 impl MockFilterStateDeprecated {
494 fn handle_request(&mut self, req: FilterRequest) {
495 match req {
496 FilterRequest::EnableInterface { id, responder } => {
497 let result = if id == NON_EXISTENT_INTERFACE {
498 Err(fnet_filter_deprecated::EnableDisableInterfaceError::NotFound)
499 } else {
500 let _: bool = self.active_interfaces.insert(id);
501 Ok(())
502 };
503 responder.send(result).expect("failed to respond")
504 }
505 FilterRequest::DisableInterface { id, responder } => {
506 let result = if id == NON_EXISTENT_INTERFACE {
507 Err(fnet_filter_deprecated::EnableDisableInterfaceError::NotFound)
508 } else {
509 let _: bool = self.active_interfaces.remove(&id);
510 Ok(())
511 };
512 responder.send(result).expect("failed to respond")
513 }
514 FilterRequest::GetNatRules { responder } => {
515 responder
516 .send(&self.nat_rules[..], self.nat_rules_generation)
517 .expect("failed to respond");
518 if self.fail_generations > 0 {
519 self.nat_rules_generation += 1;
520 self.fail_generations -= 1;
521 }
522 }
523 FilterRequest::UpdateNatRules { rules, generation, responder } => {
524 let result = if self.nat_rules_generation != generation {
525 Err(fnet_filter_deprecated::FilterUpdateNatRulesError::GenerationMismatch)
526 } else {
527 let new_nat_rules: Vec<fnet_filter_deprecated::Nat> =
528 rules.iter().map(|r| r.clone()).collect();
529 self.nat_rules = new_nat_rules;
530 self.nat_rules_generation += 1;
531 Ok(())
532 };
533 responder.send(result).expect("failed to respond")
534 }
535 _ => unimplemented!(
536 "fuchsia.net.filter.deprecated mock called with unsupported request"
537 ),
538 }
539 }
540 }
541
542 #[derive(Default)]
544 struct MockFilterStateCurrent {
545 pending_changes: Vec<Change>,
546 resources: HashMap<ResourceId, Resource>,
547 }
548
549 impl MockFilterStateCurrent {
550 fn handle_request(&mut self, req: NamespaceControllerRequest) {
551 match req {
552 NamespaceControllerRequest::PushChanges { changes, responder } => {
553 let changes = changes
554 .into_iter()
555 .map(|change| Change::try_from(change).expect("invalid change"));
556 self.pending_changes.extend(changes);
557 responder
558 .send(fidl_fuchsia_net_filter::ChangeValidationResult::Ok(
559 fidl_fuchsia_net_filter::Empty,
560 ))
561 .expect("failed to respond");
562 }
563 NamespaceControllerRequest::Commit { payload: _, responder } => {
564 for change in self.pending_changes.drain(..) {
565 match change {
566 Change::Create(resource) => {
567 let id = resource.id();
568 assert_matches!(
569 self.resources.insert(id.clone(), resource),
570 None,
571 "resource {id:?} already exists"
572 );
573 }
574 Change::Remove(resource) => {
575 assert_matches!(
576 self.resources.remove(&resource),
577 Some(_),
578 "resource {resource:?} does not exist"
579 );
580 }
581 }
582 }
583 responder
584 .send(fidl_fuchsia_net_filter::CommitResult::Ok(
585 fidl_fuchsia_net_filter::Empty,
586 ))
587 .expect("failed to respond");
588 }
589 _ => unimplemented!("fuchsia.net.filter mock called with unsupported request"),
590 }
591 }
592 }
593
594 #[derive(Clone)]
595 enum MockFilter {
596 Deprecated(Arc<Mutex<MockFilterStateDeprecated>>),
597 Current(Arc<Mutex<MockFilterStateCurrent>>),
598 }
599
600 impl MockFilter {
601 fn new_deprecated(initial_state: MockFilterStateDeprecated) -> Self {
602 Self::Deprecated(Arc::new(Mutex::new(initial_state)))
603 }
604 fn new_current(initial_state: MockFilterStateCurrent) -> Self {
605 Self::Current(Arc::new(Mutex::new(initial_state)))
606 }
607
608 fn list_configurations(&self) -> Vec<fnet_masquerade::ControlConfig> {
610 match self {
611 Self::Deprecated(state) => state
612 .lock()
613 .nat_rules
614 .iter()
615 .map(|fnet_filter_deprecated::Nat { src_subnet, outgoing_nic, proto: _ }| {
616 fnet_masquerade::ControlConfig {
617 src_subnet: *src_subnet,
618 output_interface: *outgoing_nic,
619 }
620 })
621 .collect(),
622 Self::Current(state) => state
623 .lock()
624 .resources
625 .values()
626 .filter_map(|resource| match resource {
627 Resource::Rule(rule) => match rule.action {
628 Action::Masquerade { src_port: _ } => {
629 let output_interface = rule
630 .matchers
631 .out_interface
632 .clone()
633 .expect("out_interface should be Some");
634 let output_interface = match output_interface {
635 fnet_matchers_ext::Interface::Id(value) => value.get(),
636 matcher => panic!("unexpected interface matcher: {matcher:?}"),
637 };
638 let src_subnet = rule
639 .matchers
640 .src_addr
641 .clone()
642 .expect("src_addr should be Some");
643 assert!(!src_subnet.invert);
644 let src_subnet = match src_subnet.matcher {
645 fnet_matchers_ext::AddressMatcherType::Subnet(value) => {
646 value.into()
647 }
648 matcher => panic!("unexpected address matcher: {matcher:?}"),
649 };
650 Some(fnet_masquerade::ControlConfig {
651 output_interface,
652 src_subnet,
653 })
654 }
655 _ => None,
656 },
657 _ => None,
658 })
659 .collect(),
660 }
661 }
662
663 fn is_interface_active(&self, interface_id: u64) -> bool {
665 match self {
666 Self::Deprecated(state) => state.lock().active_interfaces.contains(&interface_id),
667 Self::Current(_) => self
668 .list_configurations()
669 .iter()
670 .any(|config| config.output_interface == interface_id),
671 }
672 }
673
674 async fn into_client_and_server(self) -> (FilterControl, impl FusedFuture<Output = ()>) {
679 match self {
680 MockFilter::Deprecated(state) => {
681 let (client, server) = fidl::endpoints::create_endpoints::<
682 fidl_fuchsia_net_filter_deprecated::FilterMarker,
683 >();
684 let client = client.into_proxy();
685 let server_fut = server
686 .into_stream()
687 .fold(state, |state, req| {
688 state.lock().handle_request(req.expect("failed to receive request"));
689 futures::future::ready(state)
690 })
691 .map(|_state| ())
692 .fuse();
693 (FilterControl::Deprecated(client), futures::future::Either::Left(server_fut))
694 }
695 MockFilter::Current(state) => {
696 let (control_client, control_server) = fidl::endpoints::create_endpoints::<
699 fidl_fuchsia_net_filter::ControlMarker,
700 >();
701 let client_fut = FilterControl::new(None, Some(control_client.into_proxy()))
702 .map(|result| result.expect("error creating controller"));
703 let mut control_stream = control_server.into_stream();
704 let control_server_fut = control_stream.next().map(|req| {
705 match req
706 .expect("stream shouldn't close")
707 .expect("stream shouldn't have an error")
708 {
709 ControlRequest::OpenController { id, request, control_handle: _ } => {
710 let (request_stream, control_handle) =
711 request.into_stream_and_control_handle();
712 control_handle
713 .send_on_id_assigned(id.as_str())
714 .expect("failed to respond");
715 request_stream
716 }
717 ControlRequest::ReopenDetachedController {
718 key: _,
719 request: _,
720 control_handle: _,
721 } => unimplemented!(
722 "fuchsia.net.filter mock called with unsupported request"
723 ),
724 }
725 });
726 let (client, server_request_stream) =
727 futures::join!(client_fut, control_server_fut);
728
729 let server_fut = server_request_stream
730 .fold(state, |state, req| {
731 state.lock().handle_request(req.expect("failed to receive request"));
732 futures::future::ready(state)
733 })
734 .map(|_state| ())
735 .fuse();
736 (client, futures::future::Either::Right(server_fut))
737 }
738 }
739 }
740 }
741
742 enum FilterBackend {
743 Deprecated,
744 Current,
745 }
746
747 impl FilterBackend {
748 fn into_mock(self) -> MockFilter {
749 match self {
750 FilterBackend::Deprecated => MockFilter::new_deprecated(Default::default()),
751 FilterBackend::Current => MockFilter::new_current(Default::default()),
752 }
753 }
754 }
755
756 #[test_case(FilterBackend::Deprecated)]
757 #[test_case(FilterBackend::Current)]
758 #[fuchsia::test]
759 async fn enable_disable_masquerade(filter_backend: FilterBackend) {
760 let config = ValidatedConfig::try_from(DEFAULT_CONFIG).unwrap();
761
762 let mock = filter_backend.into_mock();
763 let (mut filter_control, mut server_fut) = mock.clone().into_client_and_server().await;
764
765 let mut filter_enabled_state = FilterEnabledState::default();
766 let interface_states = HashMap::new();
767
768 let mut masq = MasqueradeHandler::default();
769 let (_client, server) =
770 fidl::endpoints::create_endpoints::<fidl_fuchsia_net_masquerade::ControlMarker>();
771 let (_request_stream, control) = server.into_stream_and_control_handle();
772
773 assert_matches!(masq.create_control(config, control), Ok(()));
774
775 for (enable, expected_configs) in [(true, vec![DEFAULT_CONFIG]), (false, vec![])] {
776 let set_enabled_fut = masq
777 .set_enabled(
778 &mut filter_control,
779 config,
780 enable,
781 &mut filter_enabled_state,
782 &interface_states,
783 )
784 .fuse();
785 futures::pin_mut!(set_enabled_fut);
786 let response = futures::select!(
787 r = set_enabled_fut => r,
788 () = server_fut => panic!("mock filter server should never exit"),
789 );
790 pretty_assertions::assert_eq!(response, Ok(!enable));
791 assert_eq!(mock.list_configurations(), expected_configs);
792 assert_eq!(mock.is_interface_active(DEFAULT_CONFIG.output_interface), enable);
793 }
794 }
795
796 #[test_case(
799 DEFAULT_CONFIG,
800 Some(crate::filter::FILTER_CAS_RETRY_MAX),
801 Ok(()),
802 Err(Error::RetryExceeded),
803 Ok(false);
804 "repeated generation mismatch"
805 )]
806 #[test_case(
807 fnet_masquerade::ControlConfig {
808 output_interface: NON_EXISTENT_INTERFACE,
809 ..DEFAULT_CONFIG
810 },
811 None,
812 Ok(()),
813 Err(Error::NotFound),
814 Err(Error::NotFound);
815 "non existent interface"
816 )]
817 #[fuchsia::test]
818 async fn masquerade_errors_deprecated(
819 config: fnet_masquerade::ControlConfig,
820 fail_generations: Option<i32>,
821 create_control_response: Result<(), Error>,
822 first_response: Result<bool, Error>,
823 second_response: Result<bool, Error>,
824 ) {
825 let config = ValidatedConfig::try_from(config).unwrap();
826
827 let filter_state = if let Some(generations) = fail_generations {
828 MockFilterStateDeprecated { fail_generations: generations, ..Default::default() }
829 } else {
830 Default::default()
831 };
832 let mock = MockFilter::new_deprecated(filter_state);
833 let (mut filter_control, mut server_fut) = mock.into_client_and_server().await;
834
835 let mut filter_enabled_state = FilterEnabledState::default();
836 let interface_states = HashMap::new();
837
838 let (_client, server) =
839 fidl::endpoints::create_endpoints::<fidl_fuchsia_net_masquerade::ControlMarker>();
840 let (_request_stream, control) = server.into_stream_and_control_handle();
841 let mut masq = MasqueradeHandler::default();
842 pretty_assertions::assert_eq!(
843 masq.create_control(config.clone(), control).map_err(|(e, _control)| e),
844 create_control_response
845 );
846
847 for expected_response in [first_response, second_response] {
848 let set_enabled_fut = masq
849 .set_enabled(
850 &mut filter_control,
851 config,
852 true,
853 &mut filter_enabled_state,
854 &interface_states,
855 )
856 .fuse();
857 futures::pin_mut!(set_enabled_fut);
858 let response = futures::select!(
859 r = set_enabled_fut => r,
860 () = server_fut => panic!("mock filter server should never exit"),
861 );
862 pretty_assertions::assert_eq!(response, expected_response);
863 }
864 }
865
866 #[test_case(
867 DEFAULT_CONFIG => Ok(());
868 "valid_config"
869 )]
870 #[test_case(
871 fnet_masquerade::ControlConfig {
872 src_subnet: V4_UNSPECIFIED_SUBNET,
873 .. DEFAULT_CONFIG
874 } => Err(Error::Unsupported);
875 "v4_unspecified_subnet"
876 )]
877 #[test_case(
878 fnet_masquerade::ControlConfig {
879 src_subnet: V6_UNSPECIFIED_SUBNET,
880 .. DEFAULT_CONFIG
881 } => Err(Error::Unsupported);
882 "v6_unspecified_subnet"
883 )]
884 #[test_case(
885 fnet_masquerade::ControlConfig {
886 src_subnet: INVALID_SUBNET,
887 .. DEFAULT_CONFIG
888 } => Err(Error::InvalidArguments);
889 "invalid_subnet"
890 )]
891 #[test_case(
892 fnet_masquerade::ControlConfig {
893 output_interface: 0,
894 .. DEFAULT_CONFIG
895 } => Err(Error::InvalidArguments);
896 "invalid_output_interface"
897 )]
898 #[fuchsia::test]
899 fn validate_config(config: fnet_masquerade::ControlConfig) -> Result<(), Error> {
900 ValidatedConfig::try_from(config).map(|_| ())
901 }
902}