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