1use std::collections::hash_map::Entry;
6use std::collections::{HashMap, HashSet};
7use std::num::NonZeroU64;
8
9use fidl_fuchsia_net_filter as fnet_filter;
10use fidl_fuchsia_net_filter_deprecated as fnet_filter_deprecated;
11use fidl_fuchsia_net_filter_ext::{
12 self as fnet_filter_ext, Action, Change, CommitError, Domain, InstalledIpRoutine,
13 InstalledNatRoutine, IpHook, Matchers, Namespace, NamespaceId, NatHook, PushChangesError,
14 Resource, ResourceId, Routine, RoutineId, RoutineType, Rule, RuleId,
15};
16use fidl_fuchsia_net_interfaces_ext as fnet_interfaces_ext;
17use fidl_fuchsia_net_masquerade as fnet_masquerade;
18use fidl_fuchsia_net_matchers_ext as fnet_matchers_ext;
19use fuchsia_async::DurationExt as _;
20
21use anyhow::{Context as _, bail};
22use log::{error, info, warn};
23
24use crate::{FilterConfig, InterfaceId, InterfaceType, exit_with_fidl_error};
25
26#[derive(Debug)]
28pub(crate) enum FilterError {
29 Push(PushChangesError),
30 Commit(CommitError),
31}
32
33pub(crate) enum FilterControl {
36 Deprecated(fnet_filter_deprecated::FilterProxy),
37 Current(Box<FilterState>),
38}
39
40impl FilterControl {
41 pub(super) async fn new(
45 deprecated_proxy: Option<fnet_filter_deprecated::FilterProxy>,
46 current_proxy: Option<fnet_filter::ControlProxy>,
47 ) -> Result<Self, anyhow::Error> {
48 if let Some(proxy) = deprecated_proxy {
49 if probe_for_presence(&proxy).await {
50 return Ok(FilterControl::Deprecated(proxy));
51 }
52 }
53
54 if let Some(proxy) = current_proxy {
55 let controller_id = fnet_filter_ext::ControllerId(String::from("netcfg"));
56 let filter = FilterControl::Current(Box::new(FilterState {
57 controller: fnet_filter_ext::Controller::new(&proxy, &controller_id)
58 .await
59 .context("could not create controller from filter proxy")?,
60 uninstalled_ip_routines: filter_routines(false ),
61 installed_ip_routines: filter_routines(true ),
62 current_installed_rule_index: 0,
63 masquerade: MasqueradeState {
64 routine_id: masquerade_routine(),
65 next_rule_index: 0,
66 },
67 }));
68 return Ok(filter);
69 }
70
71 Err(anyhow::anyhow!("no filtering proxy available!"))
72 }
73
74 pub(super) async fn update_filters(
77 &mut self,
78 config: FilterConfig,
79 filter_enabled_state: &FilterEnabledState,
80 ) -> Result<(), anyhow::Error> {
81 match self {
82 FilterControl::Deprecated(proxy) => update_filters_deprecated(proxy, config).await,
83 FilterControl::Current(state) => {
84 state.update_filters_current(config, filter_enabled_state).await
85 }
86 }
87 }
88}
89
90struct MasqueradeState {
92 routine_id: RoutineId,
94 next_rule_index: u32,
104}
105
106pub(super) struct FilterState {
108 controller: fnet_filter_ext::Controller,
109 uninstalled_ip_routines: netfilter::parser::FilterRoutines,
110 installed_ip_routines: netfilter::parser::FilterRoutines,
111 masquerade: MasqueradeState,
112 current_installed_rule_index: u32,
113 }
116
117impl FilterState {
118 async fn update_filters_current(
120 &mut self,
121 config: FilterConfig,
122 filter_enabled_state: &FilterEnabledState,
123 ) -> Result<(), anyhow::Error> {
124 let FilterState {
125 controller,
126 uninstalled_ip_routines,
127 installed_ip_routines,
128 current_installed_rule_index,
129 masquerade,
130 } = self;
131 let (changes, num_installed_rules) = generate_initial_filter_changes(
132 uninstalled_ip_routines,
133 installed_ip_routines,
134 &masquerade.routine_id,
135 config,
136 &filter_enabled_state.interface_types,
137 *current_installed_rule_index,
138 )?;
139
140 for batch in changes.chunks(usize::from(fnet_filter::MAX_BATCH_SIZE)) {
141 controller
142 .push_changes(batch.to_vec())
143 .await
144 .context("failed to push changes to filter controller")?;
145 }
146
147 controller.commit().await.context("failed to commit changes to filter controller")?;
148 info!("initial filter configuration has been committed successfully");
149 *current_installed_rule_index =
150 current_installed_rule_index.wrapping_add(num_installed_rules);
151 Ok(())
152 }
153}
154
155fn filter_routines(installed: bool) -> netfilter::parser::FilterRoutines {
159 let suffix = if !installed { "_uninstalled" } else { "" };
160 netfilter::parser::FilterRoutines {
161 local_ingress: Some(RoutineId {
162 namespace: namespace_id(),
163 name: format!("local_ingress{suffix}"),
164 }),
165 local_egress: Some(RoutineId {
166 namespace: namespace_id(),
167 name: format!("local_egress{suffix}"),
168 }),
169 }
170}
171
172fn masquerade_routine() -> RoutineId {
176 RoutineId { namespace: namespace_id(), name: format!("egress_masquerade") }
177}
178
179fn namespace_id() -> NamespaceId {
180 NamespaceId(String::from("netcfg"))
181}
182
183pub(super) async fn probe_for_presence(filter: &fnet_filter_deprecated::FilterProxy) -> bool {
184 match filter.check_presence().await {
185 Ok(()) => true,
186 Err(fidl::Error::ClientChannelClosed { .. }) => false,
187 Err(e) => panic!("unexpected error while probing: {e}"),
188 }
189}
190
191fn generate_initial_filter_changes(
195 uninstalled_ip_routines: &netfilter::parser::FilterRoutines,
196 installed_ip_routines: &netfilter::parser::FilterRoutines,
197 masquerade_routine: &RoutineId,
198 config: FilterConfig,
199 filter_enabled_interface_types: &HashSet<InterfaceType>,
200 current_installed_rule_index: u32,
201) -> Result<(Vec<Change>, u32), anyhow::Error> {
202 let namespace = Change::Create(Resource::Namespace(Namespace {
203 id: NamespaceId(String::from("netcfg")),
204 domain: Domain::AllIp,
205 }));
206 let mut changes = vec![namespace];
207
208 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = uninstalled_ip_routines;
216 let uninstalled_local_ingress =
217 local_ingress.clone().map(|id| Routine { id, routine_type: RoutineType::Ip(None) });
218 let uninstalled_local_egress =
219 local_egress.clone().map(|id| Routine { id, routine_type: RoutineType::Ip(None) });
220
221 fn installed_routine_from_id(id: RoutineId, hook: IpHook) -> Routine {
224 Routine {
225 id: id,
226 routine_type: RoutineType::Ip(Some(InstalledIpRoutine { hook, priority: 0i32 })),
227 }
228 }
229 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = installed_ip_routines;
230 let local_ingress =
231 local_ingress.clone().map(|id| installed_routine_from_id(id, IpHook::LocalIngress));
232 let local_egress =
233 local_egress.clone().map(|id| installed_routine_from_id(id, IpHook::LocalEgress));
234
235 let masquerade = Routine {
236 id: masquerade_routine.clone(),
237 routine_type: RoutineType::Nat(Some(InstalledNatRoutine {
238 hook: NatHook::Egress,
239 priority: 0i32,
240 })),
241 };
242
243 let routine_changes = [
244 uninstalled_local_ingress,
245 local_ingress,
246 uninstalled_local_egress,
247 local_egress,
248 Some(masquerade),
249 ]
250 .into_iter()
251 .filter_map(|routine| routine)
252 .map(|routine| Change::Create(Resource::Routine(routine)));
253 changes.extend(routine_changes);
254
255 let FilterConfig { rules, nat_rules: _, rdr_rules: _ } = config;
258 if !rules.is_empty() {
259 let rules =
263 netfilter::parser::parse_str_to_rules(&rules.join(""), &uninstalled_ip_routines)
264 .context("error parsing filter rules")?;
265 let rule_changes = rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule)));
266 changes.extend(rule_changes);
267 }
268
269 let mut num_installed_rules = 0;
272 if filter_enabled_interface_types.contains(&InterfaceType::Lowpan) {
273 let lowpan_rules = generate_static_port_class_filter_rules(
274 uninstalled_ip_routines,
275 installed_ip_routines,
276 fnet_interfaces_ext::PortClass::Lowpan,
277 current_installed_rule_index.wrapping_add(num_installed_rules),
278 );
279 changes.extend(lowpan_rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule))));
280 num_installed_rules += 1;
281 }
282
283 Ok((changes, num_installed_rules))
284}
285
286fn generate_updated_filter_rules(
291 uninstalled_ip_routines: &netfilter::parser::FilterRoutines,
292 installed_ip_routines: &netfilter::parser::FilterRoutines,
293 interface_id: InterfaceId,
294 current_installed_rule_index: u32,
295) -> Vec<Rule> {
296 let netfilter::parser::FilterRoutines {
297 local_ingress: uninstalled_local_ingress,
298 local_egress: uninstalled_local_egress,
299 } = uninstalled_ip_routines;
300 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = installed_ip_routines;
301
302 let local_ingress_rule = local_ingress.clone().map(|routine_id| {
306 create_interface_matching_jump_rule(
307 routine_id,
308 current_installed_rule_index,
309 interface_id,
310 IpHook::LocalIngress,
311 &uninstalled_local_ingress
312 .as_ref()
313 .expect("there should be a corresponding uninstalled routine for local ingress")
314 .name,
315 )
316 });
317 let local_egress_rule = local_egress.clone().map(|routine_id| {
318 create_interface_matching_jump_rule(
319 routine_id,
320 current_installed_rule_index,
321 interface_id,
322 IpHook::LocalEgress,
323 &uninstalled_local_egress
324 .as_ref()
325 .expect("there should be a corresponding uninstalled routine for local egress")
326 .name,
327 )
328 });
329
330 [local_ingress_rule, local_egress_rule].into_iter().flatten().collect()
331}
332
333fn create_jump_rule(
334 routine_id: RoutineId,
335 index: u32,
336 interface: fnet_matchers_ext::Interface,
337 hook: IpHook,
338 target_routine_name: &str,
339) -> Rule {
340 let (in_interface, out_interface) = match hook {
341 IpHook::LocalIngress | IpHook::Ingress => (Some(interface), None),
342 IpHook::LocalEgress | IpHook::Egress => (None, Some(interface)),
343 IpHook::Forwarding => (Some(interface.clone()), Some(interface)),
344 };
345
346 Rule {
347 id: RuleId { routine: routine_id, index },
348 matchers: Matchers { in_interface, out_interface, ..Default::default() },
349 action: Action::Jump(target_routine_name.to_string()),
350 }
351}
352
353fn create_interface_matching_jump_rule(
354 routine_id: RoutineId,
355 index: u32,
356 interface_id: InterfaceId,
357 hook: IpHook,
358 target_routine_name: &str,
359) -> Rule {
360 create_jump_rule(
361 routine_id,
362 index,
363 fnet_matchers_ext::Interface::Id(interface_id.into()),
364 hook,
365 target_routine_name,
366 )
367}
368
369fn generate_static_port_class_filter_rules(
372 uninstalled_ip_routines: &netfilter::parser::FilterRoutines,
373 installed_ip_routines: &netfilter::parser::FilterRoutines,
374 port_class: fnet_interfaces_ext::PortClass,
375 current_installed_rule_index: u32,
376) -> Vec<Rule> {
377 let netfilter::parser::FilterRoutines {
378 local_ingress: uninstalled_local_ingress,
379 local_egress: uninstalled_local_egress,
380 } = uninstalled_ip_routines;
381 let netfilter::parser::FilterRoutines { local_ingress, local_egress } = installed_ip_routines;
382
383 let local_ingress_rule = local_ingress.clone().map(|routine_id| {
384 create_port_class_matching_jump_rule(
385 routine_id,
386 current_installed_rule_index,
387 port_class,
388 IpHook::LocalIngress,
389 &uninstalled_local_ingress
390 .as_ref()
391 .expect("there should be a corresponding uninstalled routine for local ingress")
392 .name,
393 )
394 });
395 let local_egress_rule = local_egress.clone().map(|routine_id| {
396 create_port_class_matching_jump_rule(
397 routine_id,
398 current_installed_rule_index,
399 port_class,
400 IpHook::LocalEgress,
401 &uninstalled_local_egress
402 .as_ref()
403 .expect("there should be a corresponding uninstalled routine for local egress")
404 .name,
405 )
406 });
407
408 [local_ingress_rule, local_egress_rule].into_iter().flatten().collect()
409}
410
411fn create_port_class_matching_jump_rule(
414 routine_id: RoutineId,
415 index: u32,
416 port_class: fnet_interfaces_ext::PortClass,
417 hook: IpHook,
418 target_routine_name: &str,
419) -> Rule {
420 create_jump_rule(
421 routine_id,
422 index,
423 fnet_matchers_ext::Interface::PortClass(port_class),
424 hook,
425 target_routine_name,
426 )
427}
428
429pub(crate) const FILTER_CAS_RETRY_MAX: i32 = 3;
435pub(crate) const FILTER_CAS_RETRY_INTERVAL_MILLIS: i64 = 500;
436
437macro_rules! cas_filter_rules {
438 ($filter:expr, $get_rules:ident, $update_rules:ident, $rules:expr, $error_type:ident) => {
439 for retry in 0..FILTER_CAS_RETRY_MAX {
440 let (_rules, generation) =
441 $filter.$get_rules().await.unwrap_or_else(|err| exit_with_fidl_error(err));
442
443 match $filter
444 .$update_rules(&$rules, generation)
445 .await
446 .unwrap_or_else(|err| exit_with_fidl_error(err))
447 {
448 Ok(()) => {
449 break;
450 }
451 Err(fnet_filter_deprecated::$error_type::GenerationMismatch)
452 if retry < FILTER_CAS_RETRY_MAX - 1 =>
453 {
454 fuchsia_async::Timer::new(
455 zx::MonotonicDuration::from_millis(FILTER_CAS_RETRY_INTERVAL_MILLIS)
456 .after_now(),
457 )
458 .await;
459 }
460 Err(e) => {
461 bail!("{} failed: {:?}", stringify!($update_rules), e);
462 }
463 }
464 }
465 };
466}
467
468macro_rules! no_update_filter_rules {
470 ($filter:expr, $get_rules:ident, $update_rules:ident, $rules:expr, $error_type:ident) => {
471 let (_rules, generation) =
472 $filter.$get_rules().await.unwrap_or_else(|err| exit_with_fidl_error(err));
473
474 match $filter
475 .$update_rules(&$rules, generation)
476 .await
477 .unwrap_or_else(|err| exit_with_fidl_error(err))
478 {
479 Ok(()) => {}
480 Err(fnet_filter_deprecated::$error_type::NotSupported) => {
481 error!("{} not supported", stringify!($update_rules));
482 }
483 }
484 };
485}
486
487async fn update_filters_deprecated(
488 filter: &mut fnet_filter_deprecated::FilterProxy,
489 config: FilterConfig,
490) -> Result<(), anyhow::Error> {
491 let FilterConfig { rules, nat_rules, rdr_rules } = config;
492
493 if !rules.is_empty() {
494 let rules = netfilter::parser_deprecated::parse_str_to_rules(&rules.join(""))
495 .context("error parsing filter rules")?;
496 cas_filter_rules!(filter, get_rules, update_rules, rules, FilterUpdateRulesError);
497 }
498
499 if !nat_rules.is_empty() {
500 let nat_rules = netfilter::parser_deprecated::parse_str_to_nat_rules(&nat_rules.join(""))
501 .context("error parsing NAT rules")?;
502 cas_filter_rules!(
503 filter,
504 get_nat_rules,
505 update_nat_rules,
506 nat_rules,
507 FilterUpdateNatRulesError
508 );
509 }
510
511 if !rdr_rules.is_empty() {
512 let rdr_rules = netfilter::parser_deprecated::parse_str_to_rdr_rules(&rdr_rules.join(""))
513 .context("error parsing RDR rules")?;
514 no_update_filter_rules!(
517 filter,
518 get_rdr_rules,
519 update_rdr_rules,
520 rdr_rules,
521 FilterUpdateRdrRulesError
522 );
523 }
524
525 Ok(())
526}
527
528#[derive(Debug)]
529struct MasqueradeCounter(NonZeroU64);
530
531impl MasqueradeCounter {
532 fn new() -> Self {
533 Self(NonZeroU64::new(1).unwrap())
534 }
535
536 fn increment(&mut self) {
537 *self = Self(self.0.checked_add(1).expect("integer_overflow on u64"));
538 }
539
540 fn decrement(&self) -> Option<Self> {
541 NonZeroU64::new(self.0.get() - 1).map(Self)
542 }
543}
544
545#[derive(Debug, Default)]
546pub(super) struct FilterEnabledState {
547 interface_types: HashSet<InterfaceType>,
548 masquerade_enabled_interface_ids: HashMap<InterfaceId, MasqueradeCounter>,
551 currently_enabled_interfaces: HashMap<InterfaceId, Vec<RuleId>>,
560}
561
562impl FilterEnabledState {
563 pub(super) fn new(interface_types: HashSet<InterfaceType>) -> Self {
564 Self { interface_types, ..Default::default() }
565 }
566
567 pub(super) async fn maybe_update(
574 &mut self,
575 interface_type: Option<InterfaceType>,
576 interface_id: InterfaceId,
577 filter: &mut FilterControl,
578 ) -> Result<(), anyhow::Error> {
579 match filter {
580 FilterControl::Deprecated(proxy) => self
581 .maybe_update_deprecated(interface_type, interface_id, proxy)
582 .await
583 .map_err(|e| anyhow::anyhow!("{e:?}")),
584 FilterControl::Current(filter_state) => self
585 .maybe_update_current(interface_type, interface_id, filter_state)
586 .await
587 .map_err(|e| anyhow::anyhow!("{e:?}")),
588 }
589 }
590
591 pub(super) fn remove_interface(&mut self, interface_id: InterfaceId) {
594 let _removed_rules: Option<Vec<RuleId>> =
595 self.currently_enabled_interfaces.remove(&interface_id);
596 let _removed_count: Option<MasqueradeCounter> =
597 self.masquerade_enabled_interface_ids.remove(&interface_id);
598 }
599
600 pub(super) async fn maybe_update_deprecated<
601 Filter: fnet_filter_deprecated::FilterProxyInterface,
602 >(
603 &mut self,
604 interface_type: Option<InterfaceType>,
605 interface_id: InterfaceId,
606 filter: &Filter,
607 ) -> Result<(), fnet_filter_deprecated::EnableDisableInterfaceError> {
608 let should_be_enabled = self.should_enable(interface_type, interface_id);
609 let is_enabled = self.currently_enabled_interfaces.entry(interface_id);
610
611 match (should_be_enabled, is_enabled) {
612 (true, Entry::Vacant(entry)) => {
613 if let Err(e) = filter
614 .enable_interface(interface_id.get())
615 .await
616 .unwrap_or_else(|err| exit_with_fidl_error(err))
617 {
618 warn!("failed to enable interface {interface_id}: {e:?}");
619 return Err(e);
620 }
621 let _ = entry.insert(vec![]);
622 }
623 (false, Entry::Occupied(entry)) => {
624 if let Err(e) = filter
625 .disable_interface(interface_id.get())
626 .await
627 .unwrap_or_else(|err| exit_with_fidl_error(err))
628 {
629 warn!("failed to disable interface {interface_id}: {e:?}");
630 return Err(e);
631 }
632 let _ = entry.remove();
633 }
634 (true, Entry::Occupied(_)) | (false, Entry::Vacant(_)) => {
635 }
638 }
639 Ok(())
640 }
641
642 pub(super) async fn maybe_update_current(
643 &mut self,
644 interface_type: Option<InterfaceType>,
645 interface_id: InterfaceId,
646 filter: &mut FilterState,
647 ) -> Result<(), FilterError> {
648 let should_be_enabled = self.should_enable(interface_type, interface_id);
649 let is_enabled = self.currently_enabled_interfaces.entry(interface_id);
650
651 match (should_be_enabled, is_enabled) {
652 (true, Entry::Vacant(entry)) => {
653 let FilterState {
654 controller,
655 uninstalled_ip_routines,
656 installed_ip_routines,
657 current_installed_rule_index,
658 masquerade: _,
659 } = filter;
660 let rules = generate_updated_filter_rules(
661 uninstalled_ip_routines,
662 installed_ip_routines,
663 interface_id,
664 *current_installed_rule_index,
665 );
666
667 if !rules.is_empty() {
668 let rule_changes = rules
669 .clone()
670 .into_iter()
671 .map(|rule| Change::Create(Resource::Rule(rule)))
672 .collect();
673 controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
674 controller.commit().await.map_err(FilterError::Commit)?;
675 info!(
676 "new filter rules for iface with id {interface_id:?} \
677 have been committed successfully"
678 );
679 *current_installed_rule_index = current_installed_rule_index.wrapping_add(1);
683 }
684
685 let rule_ids: Vec<_> = rules.into_iter().map(|rule| rule.id).collect();
688 let _ = entry.insert(rule_ids);
689 }
690 (false, Entry::Occupied(entry)) => {
691 let FilterState { controller, .. } = filter;
692 let rule_changes: Vec<_> = entry
693 .remove()
694 .into_iter()
695 .map(|rule_id| Change::Remove(ResourceId::Rule(rule_id)))
696 .collect();
697
698 if !rule_changes.is_empty() {
699 controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
700 controller.commit().await.map_err(FilterError::Commit)?;
701 info!(
702 "removal of filter rules for iface with id {interface_id:?} \
703 have been committed successfully"
704 );
705 }
706 }
707 (true, Entry::Occupied(_)) | (false, Entry::Vacant(_)) => {
708 }
711 }
712
713 Ok(())
714 }
715
716 fn should_enable(
722 &self,
723 interface_type: Option<InterfaceType>,
724 interface_id: InterfaceId,
725 ) -> bool {
726 interface_type
727 .as_ref()
728 .map(|ty| match ty {
729 InterfaceType::WlanClient
730 | InterfaceType::Ethernet
731 | InterfaceType::Blackhole
732 | InterfaceType::Lowpan => self.interface_types.contains(ty),
733 InterfaceType::WlanAp => {
735 self.interface_types.contains(ty)
736 | self.interface_types.contains(&InterfaceType::WlanClient)
737 }
738 })
739 .unwrap_or(false)
740 || self.masquerade_enabled_interface_ids.contains_key(&interface_id)
741 }
742
743 pub(crate) fn increment_masquerade_count_on_interface(&mut self, interface_id: InterfaceId) {
744 match self.masquerade_enabled_interface_ids.entry(interface_id) {
745 Entry::Vacant(entry) => {
746 let _new_count = entry.insert(MasqueradeCounter::new());
747 }
748 Entry::Occupied(mut entry) => entry.get_mut().increment(),
749 }
750 }
751
752 pub(crate) fn decrement_masquerade_count_on_interface(&mut self, interface_id: InterfaceId) {
753 match self.masquerade_enabled_interface_ids.entry(interface_id) {
754 Entry::Vacant(_) => panic!(
755 "asked to decrement the masquerade count for a non-configured interface: {}",
756 interface_id
757 ),
758 Entry::Occupied(mut entry) => match entry.get().decrement() {
759 None => {
761 let _old_count = entry.remove();
762 }
763 Some(count) => {
764 let _old_count = entry.insert(count);
765 }
766 },
767 }
768 }
769}
770
771async fn update_nat_rules_deprecated(
775 filter: &mut fnet_filter_deprecated::FilterProxy,
776 update_fn: impl Fn(&mut Vec<fnet_filter_deprecated::Nat>) -> Result<(), fnet_masquerade::Error>,
777) -> Result<(), fnet_masquerade::Error> {
778 for _ in 0..FILTER_CAS_RETRY_MAX {
779 let (mut rules, generation) =
780 filter.get_nat_rules().await.expect("call to GetNatRules failed");
781 update_fn(&mut rules)?;
782
783 match filter
784 .update_nat_rules(&rules, generation)
785 .await
786 .expect("call to UpdateNatRules failed")
787 {
788 Ok(()) => return Ok(()),
789 Err(fnet_filter_deprecated::FilterUpdateNatRulesError::GenerationMismatch) => {
790 fuchsia_async::Timer::new(
792 zx::MonotonicDuration::from_millis(
793 crate::filter::FILTER_CAS_RETRY_INTERVAL_MILLIS,
794 )
795 .after_now(),
796 )
797 .await;
798 }
799 Err(fnet_filter_deprecated::FilterUpdateNatRulesError::BadRule) => {
800 error!(
803 "Generated Nat rule was invalid. Perhaps the requested \
804 NIC has been removed {rules:?}"
805 );
806 return Err(fnet_masquerade::Error::BadRule);
808 }
809 }
810 }
811
812 error!("Failed to update Nat rules");
813 Err(fnet_masquerade::Error::RetryExceeded)
814}
815
816pub(crate) async fn add_masquerade_rule_deprecated(
818 filter: &mut fnet_filter_deprecated::FilterProxy,
819 rule: fnet_filter_deprecated::Nat,
820) -> Result<(), fnet_masquerade::Error> {
821 update_nat_rules_deprecated(filter, |rules| {
822 if rules.iter().any(|existing_rule| existing_rule == &rule) {
823 Err(fnet_masquerade::Error::AlreadyExists)
824 } else {
825 rules.push(rule.clone());
826 Ok(())
827 }
828 })
829 .await
830}
831
832pub(crate) async fn remove_masquerade_rule_deprecated(
834 filter: &mut fnet_filter_deprecated::FilterProxy,
835 rule: fnet_filter_deprecated::Nat,
836) -> Result<(), fnet_masquerade::Error> {
837 update_nat_rules_deprecated(filter, |rules| {
838 rules.retain(|existing_rule| existing_rule != &rule);
839 Ok(())
840 })
841 .await
842}
843
844pub(crate) async fn add_masquerade_rule_current(
846 filter: &mut FilterState,
847 matchers: Matchers,
848) -> Result<RuleId, FilterError> {
849 let MasqueradeState { routine_id, next_rule_index } = &mut filter.masquerade;
850 let rule_id = RuleId { routine: routine_id.clone(), index: *next_rule_index };
851 let rule_changes = vec![Change::Create(Resource::Rule(Rule {
852 id: rule_id.clone(),
853 matchers: matchers,
854 action: Action::Masquerade { src_port: None },
855 }))];
856 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
857 filter.controller.commit().await.map_err(FilterError::Commit)?;
858 *next_rule_index += 1;
859 Ok(rule_id)
860}
861
862pub(crate) async fn remove_masquerade_rule_current(
864 filter: &mut FilterState,
865 rule: &RuleId,
866) -> Result<(), FilterError> {
867 let rule_changes = vec![Change::Remove(ResourceId::Rule(rule.clone()))];
868 filter.controller.push_changes(rule_changes).await.map_err(FilterError::Push)?;
869 filter.controller.commit().await.map_err(FilterError::Commit)
870}
871
872#[cfg(test)]
873mod tests {
874 use test_case::test_case;
875
876 use crate::DeviceClass;
877 use crate::interface::DeviceInfoRef;
878
879 use super::*;
880
881 const INTERFACE_ID: InterfaceId = InterfaceId::new(1).unwrap();
882 const LOCAL_INGRESS: &str = "local_ingress";
883 const UNINSTALLED_LOCAL_INGRESS: &str = "local_ingress_uninstalled";
884 const LOCAL_EGRESS: &str = "local_egress";
885 const UNINSTALLED_LOCAL_EGRESS: &str = "local_egress_uninstalled";
886 const MASQUERADE: &str = "egress_masquerade";
887
888 fn get_foundational_changes() -> Vec<Change> {
889 let mut changes = vec![Change::Create(Resource::Namespace(Namespace {
890 id: namespace_id(),
891 domain: Domain::AllIp,
892 }))];
893
894 let local_ingress = (LOCAL_INGRESS, UNINSTALLED_LOCAL_INGRESS, IpHook::LocalIngress);
895 let local_egress = (LOCAL_EGRESS, UNINSTALLED_LOCAL_EGRESS, IpHook::LocalEgress);
896
897 let routine_changes = vec![local_ingress, local_egress]
898 .into_iter()
899 .map(|(installed_name, uninstalled_name, hook)| {
900 vec![
901 Routine {
902 id: RoutineId {
903 namespace: namespace_id(),
904 name: String::from(uninstalled_name),
905 },
906 routine_type: RoutineType::Ip(None),
907 },
908 Routine {
909 id: RoutineId {
910 namespace: namespace_id(),
911 name: String::from(installed_name),
912 },
913 routine_type: RoutineType::Ip(Some(InstalledIpRoutine {
914 hook,
915 priority: 0i32,
916 })),
917 },
918 ]
919 })
920 .flatten()
921 .chain([Routine {
922 id: RoutineId { namespace: namespace_id(), name: String::from(MASQUERADE) },
923 routine_type: RoutineType::Nat(Some(InstalledNatRoutine {
924 hook: NatHook::Egress,
925 priority: 0i32,
926 })),
927 }])
928 .map(|routine| Change::Create(Resource::Routine(routine)));
929 changes.extend(routine_changes);
930
931 changes
932 }
933
934 fn create_rule(routine: RoutineId, index: u32, action: Action) -> Rule {
935 Rule { id: RuleId { routine, index }, matchers: Matchers::default(), action }
936 }
937
938 fn create_routine_id(name: &str) -> RoutineId {
939 RoutineId { namespace: namespace_id(), name: String::from(name) }
940 }
941
942 fn create_filter_routines(
943 namespace: NamespaceId,
944 local_ingress: &str,
945 local_egress: &str,
946 ) -> netfilter::parser::FilterRoutines {
947 netfilter::parser::FilterRoutines {
948 local_ingress: Some(RoutineId {
949 namespace: namespace.clone(),
950 name: local_ingress.to_owned(),
951 }),
952 local_egress: Some(RoutineId { namespace, name: local_egress.to_owned() }),
953 }
954 }
955
956 #[test_case(vec![], vec![]; "no_rules")]
959 #[test_case(
960 vec!["pass in;"],
961 vec![create_rule(
962 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
963 0,
964 Action::Accept,
965 )]; "ingress_accept")]
966 #[test_case(
967 vec!["drop out;"],
968 vec![create_rule(
969 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
970 0,
971 Action::Drop,
972 )]; "egress_drop")]
973 #[test_case(
974 vec!["pass in; drop out;"],
975 vec![create_rule(
976 create_routine_id(UNINSTALLED_LOCAL_INGRESS),
977 0,
978 Action::Accept),
979 create_rule(
980 create_routine_id(UNINSTALLED_LOCAL_EGRESS),
981 1,
982 Action::Drop,
983 )]; "ingress_accept_egress_drop")]
984 fn test_initial_filter_changes(rules_input: Vec<&str>, expected_rules: Vec<Rule>) {
985 let namespace = namespace_id();
986 let installed_filter_routines =
987 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
988 let uninstalled_filter_routines =
989 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
990
991 let (changes, _num_installed_rules) = generate_initial_filter_changes(
992 &uninstalled_filter_routines,
993 &installed_filter_routines,
994 &masquerade_routine(),
995 FilterConfig {
996 rules: rules_input.into_iter().map(|rule| rule.to_owned()).collect(),
997 nat_rules: vec![],
998 rdr_rules: vec![],
999 },
1000 &HashSet::new(),
1001 0,
1002 )
1003 .expect("rules should be formatted correctly");
1004
1005 let mut expected_changes = get_foundational_changes();
1006 let expected_rule_changes =
1007 expected_rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule)));
1008 expected_changes.extend(expected_rule_changes);
1009
1010 assert_eq!(changes, expected_changes);
1011 }
1012
1013 #[test]
1014 fn test_generate_updated_filter_rules() {
1015 let namespace = namespace_id();
1016 let installed_filter_routines =
1017 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
1018 let uninstalled_filter_routines =
1019 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
1020
1021 let rules = generate_updated_filter_rules(
1022 &uninstalled_filter_routines,
1023 &installed_filter_routines,
1024 INTERFACE_ID,
1025 0,
1026 );
1027
1028 let local_ingress = (
1029 installed_filter_routines.local_ingress.unwrap(),
1030 uninstalled_filter_routines.local_ingress.unwrap().name,
1031 IpHook::LocalIngress,
1032 );
1033 let local_egress = (
1034 installed_filter_routines.local_egress.unwrap(),
1035 uninstalled_filter_routines.local_egress.unwrap().name,
1036 IpHook::LocalEgress,
1037 );
1038 let expected_rules: Vec<_> = vec![local_ingress, local_egress]
1039 .into_iter()
1040 .map(|(installed_routine, uninstalled_routine_name, hook)| {
1041 create_interface_matching_jump_rule(
1042 installed_routine,
1043 0,
1044 INTERFACE_ID,
1045 hook,
1046 &uninstalled_routine_name,
1047 )
1048 })
1049 .collect();
1050
1051 assert_eq!(rules, expected_rules);
1052 }
1053
1054 #[test]
1055 fn test_generate_static_port_class_filter_rules() {
1056 let namespace = namespace_id();
1057 let installed_filter_routines =
1058 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
1059 let uninstalled_filter_routines =
1060 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
1061
1062 let rules = generate_static_port_class_filter_rules(
1063 &uninstalled_filter_routines,
1064 &installed_filter_routines,
1065 fnet_interfaces_ext::PortClass::Lowpan,
1066 0,
1067 );
1068
1069 let local_ingress = (
1070 installed_filter_routines.local_ingress.unwrap(),
1071 uninstalled_filter_routines.local_ingress.unwrap().name,
1072 IpHook::LocalIngress,
1073 );
1074 let local_egress = (
1075 installed_filter_routines.local_egress.unwrap(),
1076 uninstalled_filter_routines.local_egress.unwrap().name,
1077 IpHook::LocalEgress,
1078 );
1079 let expected_rules: Vec<_> = vec![local_ingress, local_egress]
1080 .into_iter()
1081 .map(|(installed_routine, uninstalled_routine_name, hook)| {
1082 create_port_class_matching_jump_rule(
1083 installed_routine,
1084 0,
1085 fnet_interfaces_ext::PortClass::Lowpan,
1086 hook,
1087 &uninstalled_routine_name,
1088 )
1089 })
1090 .collect();
1091
1092 assert_eq!(rules, expected_rules);
1093 }
1094
1095 #[test]
1096 fn test_initial_filter_changes_with_lowpan() {
1097 let namespace = namespace_id();
1098 let installed_filter_routines =
1099 create_filter_routines(namespace.clone(), LOCAL_INGRESS, LOCAL_EGRESS);
1100 let uninstalled_filter_routines =
1101 create_filter_routines(namespace, UNINSTALLED_LOCAL_INGRESS, UNINSTALLED_LOCAL_EGRESS);
1102
1103 let (changes, num_installed_rules) = generate_initial_filter_changes(
1104 &uninstalled_filter_routines,
1105 &installed_filter_routines,
1106 &masquerade_routine(),
1107 FilterConfig { rules: vec![], nat_rules: vec![], rdr_rules: vec![] },
1108 &[InterfaceType::Lowpan].into_iter().collect(),
1109 0,
1110 )
1111 .expect("rules should be formatted correctly");
1112
1113 let mut expected_changes = get_foundational_changes();
1114
1115 let lowpan_rules = generate_static_port_class_filter_rules(
1116 &uninstalled_filter_routines,
1117 &installed_filter_routines,
1118 fnet_interfaces_ext::PortClass::Lowpan,
1119 0,
1120 );
1121 expected_changes
1122 .extend(lowpan_rules.into_iter().map(|rule| Change::Create(Resource::Rule(rule))));
1123
1124 assert_eq!(changes, expected_changes);
1125 assert_eq!(num_installed_rules, 1);
1126 }
1127
1128 #[test]
1129 fn test_should_enable_filter() {
1130 let types_empty: HashSet<InterfaceType> = [].iter().cloned().collect();
1131 let types_ethernet: HashSet<InterfaceType> =
1132 [InterfaceType::Ethernet].iter().cloned().collect();
1133 let types_wlan: HashSet<InterfaceType> =
1134 [InterfaceType::WlanClient].iter().cloned().collect();
1135 let types_ap: HashSet<InterfaceType> = [InterfaceType::WlanAp].iter().cloned().collect();
1136
1137 let id = InterfaceId::new(10).unwrap();
1138
1139 let make_info = |device_class| DeviceInfoRef {
1140 device_class,
1141 mac: &fidl_fuchsia_net_ext::MacAddress { octets: [0x1, 0x1, 0x1, 0x1, 0x1, 0x1] },
1142 topological_path: "",
1143 };
1144
1145 let wlan_info = make_info(DeviceClass::WlanClient);
1146 let wlan_ap_info = make_info(DeviceClass::WlanAp);
1147 let ethernet_info = make_info(DeviceClass::Ethernet);
1148
1149 let mut fes = FilterEnabledState::new(types_empty.clone());
1150 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
1151 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
1152 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
1153
1154 fes.increment_masquerade_count_on_interface(id);
1155 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1156
1157 let mut fes = FilterEnabledState::new(types_ethernet);
1158 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
1159 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), false);
1160 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1161
1162 fes.increment_masquerade_count_on_interface(id);
1163 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
1164
1165 let mut fes = FilterEnabledState::new(types_wlan);
1166 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
1167 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
1168 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
1169
1170 fes.increment_masquerade_count_on_interface(id);
1171 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1172
1173 let mut fes = FilterEnabledState::new(types_ap);
1174 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), false);
1175 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), true);
1176 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), false);
1177
1178 fes.increment_masquerade_count_on_interface(id);
1179 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), true);
1180 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), true);
1181
1182 let mut fes = FilterEnabledState::new(types_empty);
1184 for _ in 0..3 {
1185 fes.increment_masquerade_count_on_interface(id);
1186 }
1187 for expect_enabled in [true, true, false] {
1188 fes.decrement_masquerade_count_on_interface(id);
1189 assert_eq!(fes.should_enable(Some(wlan_info.interface_type()), id), expect_enabled);
1190 assert_eq!(fes.should_enable(Some(wlan_ap_info.interface_type()), id), expect_enabled);
1191 assert_eq!(fes.should_enable(Some(ethernet_info.interface_type()), id), expect_enabled);
1192 }
1193 }
1194
1195 #[fuchsia::test]
1196 async fn test_update_filters_current_large_batch() {
1197 use futures::StreamExt as _;
1198
1199 let (control_client, control_server) =
1200 fidl::endpoints::create_endpoints::<fnet_filter::ControlMarker>();
1201 let client_fut = FilterControl::new(None, Some(control_client.into_proxy()));
1202 let mut control_stream = control_server.into_stream();
1203 let control_server_fut = async move {
1204 match control_stream
1205 .next()
1206 .await
1207 .expect("stream shouldn't close")
1208 .expect("stream shouldn't have an error")
1209 {
1210 fnet_filter::ControlRequest::OpenController { id, request, control_handle: _ } => {
1211 let (request_stream, control_handle) = request.into_stream_and_control_handle();
1212 control_handle.send_on_id_assigned(id.as_str()).expect("failed to respond");
1213 request_stream
1214 }
1215 _ => panic!("unexpected request"),
1216 }
1217 };
1218 let (filter_control, mut server_request_stream) =
1219 futures::join!(client_fut, control_server_fut);
1220 let mut filter_control = filter_control.expect("failed to create filter control");
1221
1222 let config = FilterConfig {
1223 rules: std::iter::repeat("pass in;".to_string()).take(50).collect(),
1224 nat_rules: vec![],
1225 rdr_rules: vec![],
1226 };
1227
1228 let server_fut = async move {
1229 let mut push_changes_count = 0;
1230 while let Some(req) = server_request_stream.next().await {
1231 match req.expect("stream shouldn't have an error") {
1232 fnet_filter::NamespaceControllerRequest::PushChanges { changes, responder } => {
1233 assert!(
1234 changes.len() <= usize::from(fnet_filter::MAX_BATCH_SIZE),
1235 "batch size {} exceeds MAX_BATCH_SIZE",
1236 changes.len()
1237 );
1238 push_changes_count += 1;
1239 responder
1240 .send(fnet_filter::ChangeValidationResult::Ok(fnet_filter::Empty))
1241 .expect("failed to respond");
1242 }
1243 fnet_filter::NamespaceControllerRequest::Commit { payload: _, responder } => {
1244 responder
1245 .send(fnet_filter::CommitResult::Ok(fnet_filter::Empty))
1246 .expect("failed to respond");
1247 break;
1248 }
1249 _ => panic!("unexpected request"),
1250 }
1251 }
1252 push_changes_count
1253 };
1254
1255 let filter_enabled_state = FilterEnabledState::default();
1256 let (client_res, push_changes_count) = futures::join!(
1257 filter_control.update_filters(config, &filter_enabled_state),
1258 server_fut
1259 );
1260
1261 client_res.expect("update_filters should succeed");
1262 assert_eq!(push_changes_count, 2);
1263 }
1264}