1use std::collections::hash_map::Entry;
6use std::collections::{HashMap, HashSet};
7use std::num::NonZeroU64;
8
9use fidl_fuchsia_net_filter_ext::{
10 self as fnet_filter_ext, Action, Change, CommitError, Domain, InstalledIpRoutine,
11 InstalledNatRoutine, IpHook, Matchers, Namespace, NamespaceId, NatHook, PushChangesError,
12 Resource, ResourceId, Routine, RoutineId, RoutineType, Rule, RuleId,
13};
14use fuchsia_async::DurationExt as _;
15use {
16 fidl_fuchsia_net_filter as fnet_filter,
17 fidl_fuchsia_net_filter_deprecated as fnet_filter_deprecated,
18 fidl_fuchsia_net_masquerade as fnet_masquerade,
19 fidl_fuchsia_net_matchers_ext as fnet_matchers_ext,
20};
21
22use anyhow::{bail, Context as _};
23use log::{error, info, warn};
24
25use crate::{exit_with_fidl_error, FilterConfig, InterfaceId, InterfaceType};
26
27#[derive(Debug)]
29pub(crate) enum FilterError {
30 Push(PushChangesError),
31 Commit(CommitError),
32}
33
34#[allow(clippy::large_enum_variant)] pub(crate) enum FilterControl {
38 Deprecated(fnet_filter_deprecated::FilterProxy),
39 Current(FilterState),
40}
41
42impl FilterControl {
43 pub(super) async fn new(
47 deprecated_proxy: Option<fnet_filter_deprecated::FilterProxy>,
48 current_proxy: Option<fnet_filter::ControlProxy>,
49 ) -> Result<Self, anyhow::Error> {
50 if let Some(proxy) = deprecated_proxy {
51 if probe_for_presence(&proxy).await {
52 return Ok(FilterControl::Deprecated(proxy));
53 }
54 }
55
56 if let Some(proxy) = current_proxy {
57 let controller_id = fnet_filter_ext::ControllerId(String::from("netcfg"));
58 let filter = FilterControl::Current(FilterState {
59 controller: fnet_filter_ext::Controller::new(&proxy, &controller_id)
60 .await
61 .context("could not create controller from filter proxy")?,
62 uninstalled_ip_routines: filter_routines(false ),
63 installed_ip_routines: filter_routines(true ),
64 current_installed_rule_index: 0,
65 masquerade: MasqueradeState {
66 routine_id: masquerade_routine(),
67 next_rule_index: 0,
68 },
69 });
70 return Ok(filter);
71 }
72
73 Err(anyhow::anyhow!("no filtering proxy available!"))
74 }
75
76 pub(super) async fn update_filters(
79 &mut self,
80 config: FilterConfig,
81 ) -> Result<(), anyhow::Error> {
82 match self {
83 FilterControl::Deprecated(proxy) => update_filters_deprecated(proxy, config).await,
84 FilterControl::Current(state) => state.update_filters_current(config).await,
85 }
86 }
87}
88
89struct MasqueradeState {
91 routine_id: RoutineId,
93 next_rule_index: u32,
103}
104
105pub(super) struct FilterState {
107 controller: fnet_filter_ext::Controller,
108 uninstalled_ip_routines: netfilter::parser::FilterRoutines,
109 installed_ip_routines: netfilter::parser::FilterRoutines,
110 masquerade: MasqueradeState,
111 current_installed_rule_index: u32,
112 }
115
116impl FilterState {
117 async fn update_filters_current(&mut self, config: FilterConfig) -> Result<(), anyhow::Error> {
119 let FilterState {
120 controller,
121 uninstalled_ip_routines,
122 installed_ip_routines,
123 current_installed_rule_index: _,
124 masquerade,
125 } = self;
126 let changes = generate_initial_filter_changes(
127 uninstalled_ip_routines,
128 installed_ip_routines,
129 &masquerade.routine_id,
130 config,
131 )?;
132
133 controller
134 .push_changes(changes)
135 .await
136 .context("failed to push changes to filter controller")?;
137
138 controller.commit().await.context("failed to commit changes to filter controller")?;
139 info!("initial filter configuration has been committed successfully");
140 Ok(())
141 }
142}
143
144fn filter_routines(installed: bool) -> netfilter::parser::FilterRoutines {
148 let suffix = if !installed { "_uninstalled" } else { "" };
149 netfilter::parser::FilterRoutines {
150 local_ingress: Some(RoutineId {
151 namespace: namespace_id(),
152 name: format!("local_ingress{suffix}"),
153 }),
154 local_egress: Some(RoutineId {
155 namespace: namespace_id(),
156 name: format!("local_egress{suffix}"),
157 }),
158 }
159}
160
161fn masquerade_routine() -> RoutineId {
165 RoutineId { namespace: namespace_id(), name: format!("egress_masquerade") }
166}
167
168fn namespace_id() -> NamespaceId {
169 NamespaceId(String::from("netcfg"))
170}
171
172pub(super) async fn probe_for_presence(filter: &fnet_filter_deprecated::FilterProxy) -> bool {
173 match filter.check_presence().await {
174 Ok(()) => true,
175 Err(fidl::Error::ClientChannelClosed { .. }) => false,
176 Err(e) => panic!("unexpected error while probing: {e}"),
177 }
178}
179
180fn generate_initial_filter_changes(
184 uninstalled_ip_routines: &netfilter::parser::FilterRoutines,
185 installed_ip_routines: &netfilter::parser::FilterRoutines,
186 masquerade_routine: &RoutineId,
187 config: FilterConfig,
188) -> Result<Vec<Change>, anyhow::Error> {
189 let namespace = Change::Create(Resource::Namespace(Namespace {
190 id: NamespaceId(String::from("netcfg")),
191 domain: Domain::AllIp,
192 }));
193 let mut changes = vec![namespace];
194
195 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = uninstalled_ip_routines;
203 let uninstalled_local_ingress =
204 local_ingress.clone().map(|id| Routine { id, routine_type: RoutineType::Ip(None) });
205 let uninstalled_local_egress =
206 local_egress.clone().map(|id| Routine { id, routine_type: RoutineType::Ip(None) });
207
208 fn installed_routine_from_id(id: RoutineId, hook: IpHook) -> Routine {
211 Routine {
212 id: id,
213 routine_type: RoutineType::Ip(Some(InstalledIpRoutine { hook, priority: 0i32 })),
214 }
215 }
216 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = installed_ip_routines;
217 let local_ingress =
218 local_ingress.clone().map(|id| installed_routine_from_id(id, IpHook::LocalIngress));
219 let local_egress =
220 local_egress.clone().map(|id| installed_routine_from_id(id, IpHook::LocalEgress));
221
222 let masquerade = Routine {
223 id: masquerade_routine.clone(),
224 routine_type: RoutineType::Nat(Some(InstalledNatRoutine {
225 hook: NatHook::Egress,
226 priority: 0i32,
227 })),
228 };
229
230 let routine_changes = [
231 uninstalled_local_ingress,
232 local_ingress,
233 uninstalled_local_egress,
234 local_egress,
235 Some(masquerade),
236 ]
237 .into_iter()
238 .filter_map(|routine| routine)
239 .map(|routine| Change::Create(Resource::Routine(routine)));
240 changes.extend(routine_changes);
241
242 let FilterConfig { rules, nat_rules: _, rdr_rules: _ } = config;
245 if !rules.is_empty() {
246 let rules =
250 netfilter::parser::parse_str_to_rules(&rules.join(""), &uninstalled_ip_routines)
251 .context("error parsing filter rules")?;
252 let rule_changes = rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule)));
253 changes.extend(rule_changes);
254 }
255
256 Ok(changes)
257}
258
259fn generate_updated_filter_rules(
264 uninstalled_ip_routines: &netfilter::parser::FilterRoutines,
265 installed_ip_routines: &netfilter::parser::FilterRoutines,
266 interface_id: InterfaceId,
267 current_installed_rule_index: u32,
268) -> Vec<Rule> {
269 let netfilter::parser::FilterRoutines {
270 local_ingress: uninstalled_local_ingress,
271 local_egress: uninstalled_local_egress,
272 } = uninstalled_ip_routines;
273 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = installed_ip_routines;
274
275 let local_ingress_rule = local_ingress.clone().map(|routine_id| {
279 create_interface_matching_jump_rule(
280 routine_id,
281 current_installed_rule_index,
282 interface_id,
283 IpHook::LocalIngress,
284 &uninstalled_local_ingress
285 .as_ref()
286 .expect("there should be a corresponding uninstalled routine for local ingress")
287 .name,
288 )
289 });
290 let local_egress_rule = local_egress.clone().map(|routine_id| {
291 create_interface_matching_jump_rule(
292 routine_id,
293 current_installed_rule_index,
294 interface_id,
295 IpHook::LocalEgress,
296 &uninstalled_local_egress
297 .as_ref()
298 .expect("there should be a corresponding uninstalled routine for local egress")
299 .name,
300 )
301 });
302
303 let rules: Vec<_> =
304 vec![local_ingress_rule, local_egress_rule].into_iter().filter_map(|rule| rule).collect();
305
306 rules
307}
308
309fn create_interface_matching_jump_rule(
310 routine_id: RoutineId,
311 index: u32,
312 interface_id: InterfaceId,
313 hook: IpHook,
314 target_routine_name: &str,
315) -> Rule {
316 let (in_interface, out_interface) = match hook {
318 IpHook::LocalIngress | IpHook::Ingress => {
319 (Some(fnet_matchers_ext::Interface::Id(interface_id.into())), None)
320 }
321 IpHook::LocalEgress | IpHook::Egress => {
322 (None, Some(fnet_matchers_ext::Interface::Id(interface_id.into())))
323 }
324 IpHook::Forwarding => (
325 Some(fnet_matchers_ext::Interface::Id(interface_id.into())),
326 Some(fnet_matchers_ext::Interface::Id(interface_id.into())),
327 ),
328 };
329
330 Rule {
333 id: RuleId { routine: routine_id, index },
334 matchers: Matchers { in_interface, out_interface, ..Default::default() },
335 action: Action::Jump(target_routine_name.to_string()),
336 }
337}
338
339pub(crate) const FILTER_CAS_RETRY_MAX: i32 = 3;
345pub(crate) const FILTER_CAS_RETRY_INTERVAL_MILLIS: i64 = 500;
346
347macro_rules! cas_filter_rules {
348 ($filter:expr, $get_rules:ident, $update_rules:ident, $rules:expr, $error_type:ident) => {
349 for retry in 0..FILTER_CAS_RETRY_MAX {
350 let (_rules, generation) =
351 $filter.$get_rules().await.unwrap_or_else(|err| exit_with_fidl_error(err));
352
353 match $filter
354 .$update_rules(&$rules, generation)
355 .await
356 .unwrap_or_else(|err| exit_with_fidl_error(err))
357 {
358 Ok(()) => {
359 break;
360 }
361 Err(fnet_filter_deprecated::$error_type::GenerationMismatch)
362 if retry < FILTER_CAS_RETRY_MAX - 1 =>
363 {
364 fuchsia_async::Timer::new(
365 zx::MonotonicDuration::from_millis(FILTER_CAS_RETRY_INTERVAL_MILLIS)
366 .after_now(),
367 )
368 .await;
369 }
370 Err(e) => {
371 bail!("{} failed: {:?}", stringify!($update_rules), e);
372 }
373 }
374 }
375 };
376}
377
378macro_rules! no_update_filter_rules {
380 ($filter:expr, $get_rules:ident, $update_rules:ident, $rules:expr, $error_type:ident) => {
381 let (_rules, generation) =
382 $filter.$get_rules().await.unwrap_or_else(|err| exit_with_fidl_error(err));
383
384 match $filter
385 .$update_rules(&$rules, generation)
386 .await
387 .unwrap_or_else(|err| exit_with_fidl_error(err))
388 {
389 Ok(()) => {}
390 Err(fnet_filter_deprecated::$error_type::NotSupported) => {
391 error!("{} not supported", stringify!($update_rules));
392 }
393 }
394 };
395}
396
397async fn update_filters_deprecated(
398 filter: &mut fnet_filter_deprecated::FilterProxy,
399 config: FilterConfig,
400) -> Result<(), anyhow::Error> {
401 let FilterConfig { rules, nat_rules, rdr_rules } = config;
402
403 if !rules.is_empty() {
404 let rules = netfilter::parser_deprecated::parse_str_to_rules(&rules.join(""))
405 .context("error parsing filter rules")?;
406 cas_filter_rules!(filter, get_rules, update_rules, rules, FilterUpdateRulesError);
407 }
408
409 if !nat_rules.is_empty() {
410 let nat_rules = netfilter::parser_deprecated::parse_str_to_nat_rules(&nat_rules.join(""))
411 .context("error parsing NAT rules")?;
412 cas_filter_rules!(
413 filter,
414 get_nat_rules,
415 update_nat_rules,
416 nat_rules,
417 FilterUpdateNatRulesError
418 );
419 }
420
421 if !rdr_rules.is_empty() {
422 let rdr_rules = netfilter::parser_deprecated::parse_str_to_rdr_rules(&rdr_rules.join(""))
423 .context("error parsing RDR rules")?;
424 no_update_filter_rules!(
427 filter,
428 get_rdr_rules,
429 update_rdr_rules,
430 rdr_rules,
431 FilterUpdateRdrRulesError
432 );
433 }
434
435 Ok(())
436}
437
438#[derive(Debug)]
439struct MasqueradeCounter(NonZeroU64);
440
441impl MasqueradeCounter {
442 fn new() -> Self {
443 Self(NonZeroU64::new(1).unwrap())
444 }
445
446 fn increment(&mut self) {
447 *self = Self(self.0.checked_add(1).expect("integer_overflow on u64"));
448 }
449
450 fn decrement(&self) -> Option<Self> {
451 NonZeroU64::new(self.0.get() - 1).map(Self)
452 }
453}
454
455#[derive(Debug, Default)]
456pub(super) struct FilterEnabledState {
457 interface_types: HashSet<InterfaceType>,
458 masquerade_enabled_interface_ids: HashMap<InterfaceId, MasqueradeCounter>,
461 currently_enabled_interfaces: HashMap<InterfaceId, Vec<RuleId>>,
470}
471
472impl FilterEnabledState {
473 pub(super) fn new(interface_types: HashSet<InterfaceType>) -> Self {
474 Self { interface_types, ..Default::default() }
475 }
476
477 pub(super) async fn maybe_update(
484 &mut self,
485 interface_type: Option<InterfaceType>,
486 interface_id: InterfaceId,
487 filter: &mut FilterControl,
488 ) -> Result<(), anyhow::Error> {
489 match filter {
490 FilterControl::Deprecated(proxy) => self
491 .maybe_update_deprecated(interface_type, interface_id, proxy)
492 .await
493 .map_err(|e| anyhow::anyhow!("{e:?}")),
494 FilterControl::Current(filter_state) => self
495 .maybe_update_current(interface_type, interface_id, filter_state)
496 .await
497 .map_err(|e| anyhow::anyhow!("{e:?}")),
498 }
499 }
500
501 pub(super) async fn maybe_update_deprecated<
502 Filter: fnet_filter_deprecated::FilterProxyInterface,
503 >(
504 &mut self,
505 interface_type: Option<InterfaceType>,
506 interface_id: InterfaceId,
507 filter: &Filter,
508 ) -> Result<(), fnet_filter_deprecated::EnableDisableInterfaceError> {
509 let should_be_enabled = self.should_enable(interface_type, interface_id);
510 let is_enabled = self.currently_enabled_interfaces.entry(interface_id);
511
512 match (should_be_enabled, is_enabled) {
513 (true, Entry::Vacant(entry)) => {
514 if let Err(e) = filter
515 .enable_interface(interface_id.get())
516 .await
517 .unwrap_or_else(|err| exit_with_fidl_error(err))
518 {
519 warn!("failed to enable interface {interface_id}: {e:?}");
520 return Err(e);
521 }
522 let _ = entry.insert(vec![]);
523 }
524 (false, Entry::Occupied(entry)) => {
525 if let Err(e) = filter
526 .disable_interface(interface_id.get())
527 .await
528 .unwrap_or_else(|err| exit_with_fidl_error(err))
529 {
530 warn!("failed to disable interface {interface_id}: {e:?}");
531 return Err(e);
532 }
533 let _ = entry.remove();
534 }
535 (true, Entry::Occupied(_)) | (false, Entry::Vacant(_)) => {
536 }
539 }
540 Ok(())
541 }
542
543 pub(super) async fn maybe_update_current(
544 &mut self,
545 interface_type: Option<InterfaceType>,
546 interface_id: InterfaceId,
547 filter: &mut FilterState,
548 ) -> Result<(), FilterError> {
549 let should_be_enabled = self.should_enable(interface_type, interface_id);
550 let is_enabled = self.currently_enabled_interfaces.entry(interface_id);
551
552 match (should_be_enabled, is_enabled) {
553 (true, Entry::Vacant(entry)) => {
554 let FilterState {
555 controller,
556 uninstalled_ip_routines,
557 installed_ip_routines,
558 current_installed_rule_index,
559 masquerade: _,
560 } = filter;
561 let rules = generate_updated_filter_rules(
562 uninstalled_ip_routines,
563 installed_ip_routines,
564 interface_id,
565 *current_installed_rule_index,
566 );
567
568 if !rules.is_empty() {
569 let rule_changes = rules
570 .clone()
571 .into_iter()
572 .map(|rule| Change::Create(Resource::Rule(rule)))
573 .collect();
574 controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
575 controller.commit().await.map_err(FilterError::Commit)?;
576 info!(
577 "new filter rules for iface with id {interface_id:?} \
578 have been committed successfully"
579 );
580 *current_installed_rule_index = current_installed_rule_index.wrapping_add(1);
584 }
585
586 let rule_ids: Vec<_> = rules.into_iter().map(|rule| rule.id).collect();
589 let _ = entry.insert(rule_ids);
590 }
591 (false, Entry::Occupied(entry)) => {
592 let FilterState { controller, .. } = filter;
593 let rule_changes: Vec<_> = entry
594 .remove()
595 .into_iter()
596 .map(|rule_id| Change::Remove(ResourceId::Rule(rule_id)))
597 .collect();
598
599 if !rule_changes.is_empty() {
600 controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
601 controller.commit().await.map_err(FilterError::Commit)?;
602 info!(
603 "removal of filter rules for iface with id {interface_id:?} \
604 have been committed successfully"
605 );
606 }
607 }
608 (true, Entry::Occupied(_)) | (false, Entry::Vacant(_)) => {
609 }
612 }
613
614 Ok(())
615 }
616
617 fn should_enable(
623 &self,
624 interface_type: Option<InterfaceType>,
625 interface_id: InterfaceId,
626 ) -> bool {
627 interface_type
628 .as_ref()
629 .map(|ty| match ty {
630 InterfaceType::WlanClient | InterfaceType::Ethernet | InterfaceType::Blackhole => {
631 self.interface_types.contains(ty)
632 }
633 InterfaceType::WlanAp => {
635 self.interface_types.contains(ty)
636 | self.interface_types.contains(&InterfaceType::WlanClient)
637 }
638 })
639 .unwrap_or(false)
640 || self.masquerade_enabled_interface_ids.contains_key(&interface_id)
641 }
642
643 pub(crate) fn increment_masquerade_count_on_interface(&mut self, interface_id: InterfaceId) {
644 match self.masquerade_enabled_interface_ids.entry(interface_id) {
645 Entry::Vacant(entry) => {
646 let _new_count = entry.insert(MasqueradeCounter::new());
647 }
648 Entry::Occupied(mut entry) => entry.get_mut().increment(),
649 }
650 }
651
652 pub(crate) fn decrement_masquerade_count_on_interface(&mut self, interface_id: InterfaceId) {
653 match self.masquerade_enabled_interface_ids.entry(interface_id) {
654 Entry::Vacant(_) => panic!(
655 "asked to decrement the masquerade count for a non-configured interface: {}",
656 interface_id
657 ),
658 Entry::Occupied(mut entry) => match entry.get().decrement() {
659 None => {
661 let _old_count = entry.remove();
662 }
663 Some(count) => {
664 let _old_count = entry.insert(count);
665 }
666 },
667 }
668 }
669}
670
671async fn update_nat_rules_deprecated(
675 filter: &mut fnet_filter_deprecated::FilterProxy,
676 update_fn: impl Fn(&mut Vec<fnet_filter_deprecated::Nat>) -> Result<(), fnet_masquerade::Error>,
677) -> Result<(), fnet_masquerade::Error> {
678 for _ in 0..FILTER_CAS_RETRY_MAX {
679 let (mut rules, generation) =
680 filter.get_nat_rules().await.expect("call to GetNatRules failed");
681 update_fn(&mut rules)?;
682
683 match filter
684 .update_nat_rules(&rules, generation)
685 .await
686 .expect("call to UpdateNatRules failed")
687 {
688 Ok(()) => return Ok(()),
689 Err(fnet_filter_deprecated::FilterUpdateNatRulesError::GenerationMismatch) => {
690 fuchsia_async::Timer::new(
692 zx::MonotonicDuration::from_millis(
693 crate::filter::FILTER_CAS_RETRY_INTERVAL_MILLIS,
694 )
695 .after_now(),
696 )
697 .await;
698 }
699 Err(fnet_filter_deprecated::FilterUpdateNatRulesError::BadRule) => {
700 error!(
703 "Generated Nat rule was invalid. Perhaps the requested \
704 NIC has been removed {rules:?}"
705 );
706 return Err(fnet_masquerade::Error::BadRule);
708 }
709 }
710 }
711
712 error!("Failed to update Nat rules");
713 Err(fnet_masquerade::Error::RetryExceeded)
714}
715
716pub(crate) async fn add_masquerade_rule_deprecated(
718 filter: &mut fnet_filter_deprecated::FilterProxy,
719 rule: fnet_filter_deprecated::Nat,
720) -> Result<(), fnet_masquerade::Error> {
721 update_nat_rules_deprecated(filter, |rules| {
722 if rules.iter().any(|existing_rule| existing_rule == &rule) {
723 Err(fnet_masquerade::Error::AlreadyExists)
724 } else {
725 rules.push(rule.clone());
726 Ok(())
727 }
728 })
729 .await
730}
731
732pub(crate) async fn remove_masquerade_rule_deprecated(
734 filter: &mut fnet_filter_deprecated::FilterProxy,
735 rule: fnet_filter_deprecated::Nat,
736) -> Result<(), fnet_masquerade::Error> {
737 update_nat_rules_deprecated(filter, |rules| {
738 rules.retain(|existing_rule| existing_rule != &rule);
739 Ok(())
740 })
741 .await
742}
743
744pub(crate) async fn add_masquerade_rule_current(
746 filter: &mut FilterState,
747 matchers: Matchers,
748) -> Result<RuleId, FilterError> {
749 let MasqueradeState { routine_id, next_rule_index } = &mut filter.masquerade;
750 let rule_id = RuleId { routine: routine_id.clone(), index: *next_rule_index };
751 let rule_changes = vec![Change::Create(Resource::Rule(Rule {
752 id: rule_id.clone(),
753 matchers: matchers,
754 action: Action::Masquerade { src_port: None },
755 }))];
756 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
757 filter.controller.commit().await.map_err(FilterError::Commit)?;
758 *next_rule_index += 1;
759 Ok(rule_id)
760}
761
762pub(crate) async fn remove_masquerade_rule_current(
764 filter: &mut FilterState,
765 rule: &RuleId,
766) -> Result<(), FilterError> {
767 let rule_changes = vec![Change::Remove(ResourceId::Rule(rule.clone()))];
768 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
769 filter.controller.commit().await.map_err(FilterError::Commit)
770}
771
772#[cfg(test)]
773mod tests {
774 use test_case::test_case;
775
776 use crate::interface::DeviceInfoRef;
777 use crate::DeviceClass;
778
779 use super::*;
780
781 const INTERFACE_ID: InterfaceId = InterfaceId::new(1).unwrap();
782 const LOCAL_INGRESS: &str = "local_ingress";
783 const UNINSTALLED_LOCAL_INGRESS: &str = "local_ingress_uninstalled";
784 const LOCAL_EGRESS: &str = "local_egress";
785 const UNINSTALLED_LOCAL_EGRESS: &str = "local_egress_uninstalled";
786 const MASQUERADE: &str = "egress_masquerade";
787
788 fn get_foundational_changes() -> Vec<Change> {
789 let mut changes = vec![Change::Create(Resource::Namespace(Namespace {
790 id: namespace_id(),
791 domain: Domain::AllIp,
792 }))];
793
794 let local_ingress = (LOCAL_INGRESS, UNINSTALLED_LOCAL_INGRESS, IpHook::LocalIngress);
795 let local_egress = (LOCAL_EGRESS, UNINSTALLED_LOCAL_EGRESS, IpHook::LocalEgress);
796
797 let routine_changes = vec![local_ingress, local_egress]
798 .into_iter()
799 .map(|(installed_name, uninstalled_name, hook)| {
800 vec![
801 Routine {
802 id: RoutineId {
803 namespace: namespace_id(),
804 name: String::from(uninstalled_name),
805 },
806 routine_type: RoutineType::Ip(None),
807 },
808 Routine {
809 id: RoutineId {
810 namespace: namespace_id(),
811 name: String::from(installed_name),
812 },
813 routine_type: RoutineType::Ip(Some(InstalledIpRoutine {
814 hook,
815 priority: 0i32,
816 })),
817 },
818 ]
819 })
820 .flatten()
821 .chain([Routine {
822 id: RoutineId { namespace: namespace_id(), name: String::from(MASQUERADE) },
823 routine_type: RoutineType::Nat(Some(InstalledNatRoutine {
824 hook: NatHook::Egress,
825 priority: 0i32,
826 })),
827 }])
828 .map(|routine| Change::Create(Resource::Routine(routine)));
829 changes.extend(routine_changes);
830
831 changes
832 }
833
834 fn create_rule(routine: RoutineId, index: u32, action: Action) -> Rule {
835 Rule { id: RuleId { routine, index }, matchers: Matchers::default(), action }
836 }
837
838 fn create_routine_id(name: &str) -> RoutineId {
839 RoutineId { namespace: namespace_id(), name: String::from(name) }
840 }
841
842 fn create_filter_routines(
843 namespace: NamespaceId,
844 local_ingress: &str,
845 local_egress: &str,
846 ) -> netfilter::parser::FilterRoutines {
847 netfilter::parser::FilterRoutines {
848 local_ingress: Some(RoutineId {
849 namespace: namespace.clone(),
850 name: local_ingress.to_owned(),
851 }),
852 local_egress: Some(RoutineId { namespace, name: local_egress.to_owned() }),
853 }
854 }
855
856 #[test_case(vec![], vec![]; "no_rules")]
859 #[test_case(
860 vec!["pass in;"],
861 vec![create_rule(
862 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
863 0,
864 Action::Accept,
865 )]; "ingress_accept")]
866 #[test_case(
867 vec!["drop out;"],
868 vec![create_rule(
869 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
870 0,
871 Action::Drop,
872 )]; "egress_drop")]
873 #[test_case(
874 vec!["pass in; drop out;"],
875 vec![create_rule(
876 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
877 0,
878 Action::Accept),
879 create_rule(
880 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
881 1,
882 Action::Drop,
883 )]; "ingress_accept_egress_drop")]
884 fn test_initial_filter_changes(rules_input: Vec<&str>, expected_rules: Vec<Rule>) {
885 let namespace = namespace_id();
886 let installed_filter_routines =
887 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
888 let uninstalled_filter_routines =
889 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
890
891 let changes = generate_initial_filter_changes(
892 &uninstalled_filter_routines,
893 &installed_filter_routines,
894 &masquerade_routine(),
895 FilterConfig {
896 rules: rules_input.into_iter().map(|rule| rule.to_owned()).collect(),
897 nat_rules: vec![],
898 rdr_rules: vec![],
899 },
900 )
901 .expect("rules should be formatted correctly");
902
903 let mut expected_changes = get_foundational_changes();
904 let expected_rule_changes =
905 expected_rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule)));
906 expected_changes.extend(expected_rule_changes);
907
908 assert_eq!(changes, expected_changes);
909 }
910
911 #[test]
912 fn test_generate_updated_filter_rules() {
913 let namespace = namespace_id();
914 let installed_filter_routines =
915 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
916 let uninstalled_filter_routines =
917 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
918
919 let rules = generate_updated_filter_rules(
920 &uninstalled_filter_routines,
921 &installed_filter_routines,
922 INTERFACE_ID,
923 0,
924 );
925
926 let local_ingress = (
927 installed_filter_routines.local_ingress.unwrap(),
928 uninstalled_filter_routines.local_ingress.unwrap().name,
929 IpHook::LocalIngress,
930 );
931 let local_egress = (
932 installed_filter_routines.local_egress.unwrap(),
933 uninstalled_filter_routines.local_egress.unwrap().name,
934 IpHook::LocalEgress,
935 );
936 let expected_rules: Vec<_> = vec![local_ingress, local_egress]
937 .into_iter()
938 .map(|(installed_routine, uninstalled_routine_name, hook)| {
939 create_interface_matching_jump_rule(
940 installed_routine,
941 0,
942 INTERFACE_ID,
943 hook,
944 &uninstalled_routine_name,
945 )
946 })
947 .collect();
948
949 assert_eq!(rules, expected_rules);
950 }
951
952 #[test]
953 fn test_should_enable_filter() {
954 let types_empty: HashSet<InterfaceType> = [].iter().cloned().collect();
955 let types_ethernet: HashSet<InterfaceType> =
956 [InterfaceType::Ethernet].iter().cloned().collect();
957 let types_wlan: HashSet<InterfaceType> =
958 [InterfaceType::WlanClient].iter().cloned().collect();
959 let types_ap: HashSet<InterfaceType> = [InterfaceType::WlanAp].iter().cloned().collect();
960
961 let id = InterfaceId::new(10).unwrap();
962
963 let make_info = |device_class| DeviceInfoRef {
964 device_class,
965 mac: &fidl_fuchsia_net_ext::MacAddress { octets: [0x1, 0x1, 0x1, 0x1, 0x1, 0x1] },
966 topological_path: "",
967 };
968
969 let wlan_info = make_info(DeviceClass::WlanClient);
970 let wlan_ap_info = make_info(DeviceClass::WlanAp);
971 let ethernet_info = make_info(DeviceClass::Ethernet);
972
973 let mut fes = FilterEnabledState::new(types_empty.clone());
974 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
975 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
976 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
977
978 fes.increment_masquerade_count_on_interface(id);
979 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
980
981 let mut fes = FilterEnabledState::new(types_ethernet);
982 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
983 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
984 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
985
986 fes.increment_masquerade_count_on_interface(id);
987 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
988
989 let mut fes = FilterEnabledState::new(types_wlan);
990 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
991 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
992 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
993
994 fes.increment_masquerade_count_on_interface(id);
995 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
996
997 let mut fes = FilterEnabledState::new(types_ap);
998 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
999 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
1000 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
1001
1002 fes.increment_masquerade_count_on_interface(id);
1003 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
1004 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1005
1006 let mut fes = FilterEnabledState::new(types_empty);
1008 for _ in 0..3 {
1009 fes.increment_masquerade_count_on_interface(id);
1010 }
1011 for expect_enabled in [true, true, false] {
1012 fes.decrement_masquerade_count_on_interface(id);
1013 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), expect_enabled);
1014 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), expect_enabled);
1015 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), expect_enabled);
1016 }
1017 }
1018}