netcfg/
masquerade.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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    /// The network to be masqueraded.
43    pub src_subnet: Subnet,
44    /// The interface through which to masquerade.
45    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/// State of a masquerade configuration, variant on the underlying filter API.
70#[derive(Clone, Debug)]
71enum MasqueradeFilterState {
72    /// The masquerade config is inactive.
73    Inactive,
74    /// The masquerade config is active in `fuchsia.net.filter.deprecated`.
75    ActiveDeprecated,
76    /// The masquerade config is active in `fuchsia.net.filter`.
77    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
102// Convert errors observed on `fuchsia.net.filter` to errors on the Masquerade
103// API.
104impl 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
138/// Updates the interface enabled state to acknowledge the change in masquerade
139/// configuration.
140///
141/// Note: It is incorrect to call this function if no change has occurred.
142async 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
174/// Adds or removes a masquerade rule.
175///
176/// If the existing state is inactive, a rule will be added. Otherwise, the
177/// existing rule is removed.
178async 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            // The current state is already the desired state; short circuit.
259            // This prevents calling `update_interface` in the no-change case.
260            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    /// Attempts to create a new fuchsia_net_masquerade/Control connection.
277    ///
278    /// On error, returns the original control handle back so that the caller
279    /// may terminate the connection.
280    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                // No need to modify the just-added state.
288                let _: &mut MasqueradeState = e.insert(MasqueradeState::new(control));
289                Ok(())
290            }
291            // TODO(https://fxbug.dev/374287551): At the moment, new controllers
292            // are rejected if their configuration exactly matches an existing
293            // controller. However, it would be preferable to also reject
294            // controllers that specify an overlapping configuration. E.g. a
295            // subnet that overlaps with an existing subnet on the same
296            // interface.
297            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                            // N.B. we always return Ok here because we don't
325                            // want to shut down the Control handle if replying
326                            // to the Factory request fails.
327                            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                                // Note: chaining a disconnect event onto the back of
343                                // the stream allows us to cleanup `active_controllers`
344                                // when the client hangs up.
345                                .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                    // Note: `NotFound` errors here aren't problematic. They may
375                    // happen when interface removal races with masquerade
376                    // controller disconnect.
377                    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        // This is not a permanent error, and should not cause a shutdown.
429        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                    // This is not a permanent error, and should not cause a shutdown.
438                }
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    // Note: Invalid because the host-bits are set.
482    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    /// A mock implementation of `fuchsia.net.filter.deprecated`.
490    #[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    /// A mock implementation of `fuchsia.net.filter`.
548    #[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        // Lists the masquerade configurations that are currently installed.
614        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        // Returns true if the provided interface is active.
669        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        /// Create a client (`FilterControl`), and server (future) from a mock.
682        ///
683        /// The server future must be polled in order for operations against the
684        /// client to make progress.
685        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                    // Note: we have to go through `fuchsia.net.filter/Control` to
707                    // get a connection to `fuchsia.net.filter/NamespaceController`.
708                    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    // Verifies errors that can only occur on the `fuchsia.net.filter.deprecated`
810    // API surface.
811    #[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}