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::{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    /// The network to be masqueraded.
41    pub src_subnet: fnet_matchers_ext::Subnet,
42    /// The interface through which to masquerade.
43    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/// State of a masquerade configuration, variant on the underlying filter API.
66#[derive(Clone, Debug)]
67enum MasqueradeFilterState {
68    /// The masquerade config is inactive.
69    Inactive,
70    /// The masquerade config is active in `fuchsia.net.filter.deprecated`.
71    ActiveDeprecated,
72    /// The masquerade config is active in `fuchsia.net.filter`.
73    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
98// Convert errors observed on `fuchsia.net.filter` to errors on the Masquerade
99// API.
100impl 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
134/// Updates the interface enabled state to acknowledge the change in masquerade
135/// configuration.
136///
137/// Note: It is incorrect to call this function if no change has occurred.
138async 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
170/// Adds or removes a masquerade rule.
171///
172/// If the existing state is inactive, a rule will be added. Otherwise, the
173/// existing rule is removed.
174async 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            // The current state is already the desired state; short circuit.
255            // This prevents calling `update_interface` in the no-change case.
256            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    /// Attempts to create a new fuchsia_net_masquerade/Control connection.
273    ///
274    /// On error, returns the original control handle back so that the caller
275    /// may terminate the connection.
276    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                // No need to modify the just-added state.
284                let _: &mut MasqueradeState = e.insert(MasqueradeState::new(control));
285                Ok(())
286            }
287            // TODO(https://fxbug.dev/374287551): At the moment, new controllers
288            // are rejected if their configuration exactly matches an existing
289            // controller. However, it would be preferable to also reject
290            // controllers that specify an overlapping configuration. E.g. a
291            // subnet that overlaps with an existing subnet on the same
292            // interface.
293            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                            // N.B. we always return Ok here because we don't
321                            // want to shut down the Control handle if replying
322                            // to the Factory request fails.
323                            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                                // Note: chaining a disconnect event onto the back of
339                                // the stream allows us to cleanup `active_controllers`
340                                // when the client hangs up.
341                                .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                    // Note: `NotFound` errors here aren't problematic. They may
371                    // happen when interface removal races with masquerade
372                    // controller disconnect.
373                    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        // This is not a permanent error, and should not cause a shutdown.
425        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                    // This is not a permanent error, and should not cause a shutdown.
434                }
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    // Note: Invalid because the host-bits are set.
477    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    /// A mock implementation of `fuchsia.net.filter.deprecated`.
485    #[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    /// A mock implementation of `fuchsia.net.filter`.
543    #[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        // Lists the masquerade configurations that are currently installed.
609        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        // Returns true if the provided interface is active.
664        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        /// Create a client (`FilterControl`), and server (future) from a mock.
675        ///
676        /// The server future must be polled in order for operations against the
677        /// client to make progress.
678        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                    // Note: we have to go through `fuchsia.net.filter/Control` to
697                    // get a connection to `fuchsia.net.filter/NamespaceController`.
698                    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    // Verifies errors that can only occur on the `fuchsia.net.filter.deprecated`
797    // API surface.
798    #[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}