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