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 error!(
702 "Generated Nat rule was invalid. Perhaps the requested \
703 NIC has been removed {rules:?}"
704 );
705 return Err(fnet_masquerade::Error::BadRule);
707 }
708 }
709 }
710
711 error!("Failed to update Nat rules");
712 Err(fnet_masquerade::Error::RetryExceeded)
713}
714
715pub(crate) async fn add_masquerade_rule_deprecated(
717 filter: &mut fnet_filter_deprecated::FilterProxy,
718 rule: fnet_filter_deprecated::Nat,
719) -> Result<(), fnet_masquerade::Error> {
720 update_nat_rules_deprecated(filter, |rules| {
721 if rules.iter().any(|existing_rule| existing_rule == &rule) {
722 Err(fnet_masquerade::Error::AlreadyExists)
723 } else {
724 rules.push(rule.clone());
725 Ok(())
726 }
727 })
728 .await
729}
730
731pub(crate) async fn remove_masquerade_rule_deprecated(
733 filter: &mut fnet_filter_deprecated::FilterProxy,
734 rule: fnet_filter_deprecated::Nat,
735) -> Result<(), fnet_masquerade::Error> {
736 update_nat_rules_deprecated(filter, |rules| {
737 rules.retain(|existing_rule| existing_rule != &rule);
738 Ok(())
739 })
740 .await
741}
742
743pub(crate) async fn add_masquerade_rule_current(
745 filter: &mut FilterState,
746 matchers: Matchers,
747) -> Result<RuleId, FilterError> {
748 let MasqueradeState { routine_id, next_rule_index } = &mut filter.masquerade;
749 let rule_id = RuleId { routine: routine_id.clone(), index: *next_rule_index };
750 let rule_changes = vec![Change::Create(Resource::Rule(Rule {
751 id: rule_id.clone(),
752 matchers: matchers,
753 action: Action::Masquerade { src_port: None },
754 }))];
755 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
756 filter.controller.commit().await.map_err(FilterError::Commit)?;
757 *next_rule_index += 1;
758 Ok(rule_id)
759}
760
761pub(crate) async fn remove_masquerade_rule_current(
763 filter: &mut FilterState,
764 rule: &RuleId,
765) -> Result<(), FilterError> {
766 let rule_changes = vec![Change::Remove(ResourceId::Rule(rule.clone()))];
767 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
768 filter.controller.commit().await.map_err(FilterError::Commit)
769}
770
771#[cfg(test)]
772mod tests {
773 use test_case::test_case;
774
775 use crate::interface::DeviceInfoRef;
776 use crate::DeviceClass;
777
778 use super::*;
779
780 const INTERFACE_ID: InterfaceId = InterfaceId::new(1).unwrap();
781 const LOCAL_INGRESS: &str = "local_ingress";
782 const UNINSTALLED_LOCAL_INGRESS: &str = "local_ingress_uninstalled";
783 const LOCAL_EGRESS: &str = "local_egress";
784 const UNINSTALLED_LOCAL_EGRESS: &str = "local_egress_uninstalled";
785 const MASQUERADE: &str = "egress_masquerade";
786
787 fn get_foundational_changes() -> Vec<Change> {
788 let mut changes = vec![Change::Create(Resource::Namespace(Namespace {
789 id: namespace_id(),
790 domain: Domain::AllIp,
791 }))];
792
793 let local_ingress = (LOCAL_INGRESS, UNINSTALLED_LOCAL_INGRESS, IpHook::LocalIngress);
794 let local_egress = (LOCAL_EGRESS, UNINSTALLED_LOCAL_EGRESS, IpHook::LocalEgress);
795
796 let routine_changes = vec![local_ingress, local_egress]
797 .into_iter()
798 .map(|(installed_name, uninstalled_name, hook)| {
799 vec![
800 Routine {
801 id: RoutineId {
802 namespace: namespace_id(),
803 name: String::from(uninstalled_name),
804 },
805 routine_type: RoutineType::Ip(None),
806 },
807 Routine {
808 id: RoutineId {
809 namespace: namespace_id(),
810 name: String::from(installed_name),
811 },
812 routine_type: RoutineType::Ip(Some(InstalledIpRoutine {
813 hook,
814 priority: 0i32,
815 })),
816 },
817 ]
818 })
819 .flatten()
820 .chain([Routine {
821 id: RoutineId { namespace: namespace_id(), name: String::from(MASQUERADE) },
822 routine_type: RoutineType::Nat(Some(InstalledNatRoutine {
823 hook: NatHook::Egress,
824 priority: 0i32,
825 })),
826 }])
827 .map(|routine| Change::Create(Resource::Routine(routine)));
828 changes.extend(routine_changes);
829
830 changes
831 }
832
833 fn create_rule(routine: RoutineId, index: u32, action: Action) -> Rule {
834 Rule { id: RuleId { routine, index }, matchers: Matchers::default(), action }
835 }
836
837 fn create_routine_id(name: &str) -> RoutineId {
838 RoutineId { namespace: namespace_id(), name: String::from(name) }
839 }
840
841 fn create_filter_routines(
842 namespace: NamespaceId,
843 local_ingress: &str,
844 local_egress: &str,
845 ) -> netfilter::parser::FilterRoutines {
846 netfilter::parser::FilterRoutines {
847 local_ingress: Some(RoutineId {
848 namespace: namespace.clone(),
849 name: local_ingress.to_owned(),
850 }),
851 local_egress: Some(RoutineId { namespace, name: local_egress.to_owned() }),
852 }
853 }
854
855 #[test_case(vec![], vec![]; "no_rules")]
858 #[test_case(
859 vec!["pass in;"],
860 vec![create_rule(
861 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
862 0,
863 Action::Accept,
864 )]; "ingress_accept")]
865 #[test_case(
866 vec!["drop out;"],
867 vec![create_rule(
868 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
869 0,
870 Action::Drop,
871 )]; "egress_drop")]
872 #[test_case(
873 vec!["pass in; drop out;"],
874 vec![create_rule(
875 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
876 0,
877 Action::Accept),
878 create_rule(
879 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
880 1,
881 Action::Drop,
882 )]; "ingress_accept_egress_drop")]
883 fn test_initial_filter_changes(rules_input: Vec<&str>, expected_rules: Vec<Rule>) {
884 let namespace = namespace_id();
885 let installed_filter_routines =
886 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
887 let uninstalled_filter_routines =
888 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
889
890 let changes = generate_initial_filter_changes(
891 &uninstalled_filter_routines,
892 &installed_filter_routines,
893 &masquerade_routine(),
894 FilterConfig {
895 rules: rules_input.into_iter().map(|rule| rule.to_owned()).collect(),
896 nat_rules: vec![],
897 rdr_rules: vec![],
898 },
899 )
900 .expect("rules should be formatted correctly");
901
902 let mut expected_changes = get_foundational_changes();
903 let expected_rule_changes =
904 expected_rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule)));
905 expected_changes.extend(expected_rule_changes);
906
907 assert_eq!(changes, expected_changes);
908 }
909
910 #[test]
911 fn test_generate_updated_filter_rules() {
912 let namespace = namespace_id();
913 let installed_filter_routines =
914 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
915 let uninstalled_filter_routines =
916 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
917
918 let rules = generate_updated_filter_rules(
919 &uninstalled_filter_routines,
920 &installed_filter_routines,
921 INTERFACE_ID,
922 0,
923 );
924
925 let local_ingress = (
926 installed_filter_routines.local_ingress.unwrap(),
927 uninstalled_filter_routines.local_ingress.unwrap().name,
928 IpHook::LocalIngress,
929 );
930 let local_egress = (
931 installed_filter_routines.local_egress.unwrap(),
932 uninstalled_filter_routines.local_egress.unwrap().name,
933 IpHook::LocalEgress,
934 );
935 let expected_rules: Vec<_> = vec![local_ingress, local_egress]
936 .into_iter()
937 .map(|(installed_routine, uninstalled_routine_name, hook)| {
938 create_interface_matching_jump_rule(
939 installed_routine,
940 0,
941 INTERFACE_ID,
942 hook,
943 &uninstalled_routine_name,
944 )
945 })
946 .collect();
947
948 assert_eq!(rules, expected_rules);
949 }
950
951 #[test]
952 fn test_should_enable_filter() {
953 let types_empty: HashSet<InterfaceType> = [].iter().cloned().collect();
954 let types_ethernet: HashSet<InterfaceType> =
955 [InterfaceType::Ethernet].iter().cloned().collect();
956 let types_wlan: HashSet<InterfaceType> =
957 [InterfaceType::WlanClient].iter().cloned().collect();
958 let types_ap: HashSet<InterfaceType> = [InterfaceType::WlanAp].iter().cloned().collect();
959
960 let id = InterfaceId::new(10).unwrap();
961
962 let make_info = |device_class| DeviceInfoRef {
963 device_class,
964 mac: &fidl_fuchsia_net_ext::MacAddress { octets: [0x1, 0x1, 0x1, 0x1, 0x1, 0x1] },
965 topological_path: "",
966 };
967
968 let wlan_info = make_info(DeviceClass::WlanClient);
969 let wlan_ap_info = make_info(DeviceClass::WlanAp);
970 let ethernet_info = make_info(DeviceClass::Ethernet);
971
972 let mut fes = FilterEnabledState::new(types_empty.clone());
973 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
974 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
975 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
976
977 fes.increment_masquerade_count_on_interface(id);
978 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
979
980 let mut fes = FilterEnabledState::new(types_ethernet);
981 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
982 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
983 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
984
985 fes.increment_masquerade_count_on_interface(id);
986 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
987
988 let mut fes = FilterEnabledState::new(types_wlan);
989 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
990 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
991 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
992
993 fes.increment_masquerade_count_on_interface(id);
994 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
995
996 let mut fes = FilterEnabledState::new(types_ap);
997 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
998 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
999 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
1000
1001 fes.increment_masquerade_count_on_interface(id);
1002 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
1003 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1004
1005 let mut fes = FilterEnabledState::new(types_empty);
1007 for _ in 0..3 {
1008 fes.increment_masquerade_count_on_interface(id);
1009 }
1010 for expect_enabled in [true, true, false] {
1011 fes.decrement_masquerade_count_on_interface(id);
1012 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), expect_enabled);
1013 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), expect_enabled);
1014 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), expect_enabled);
1015 }
1016 }
1017}