1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45//! Provides a transparent netstack proxy.
6//!
7//! The netstack proxy reads the network stack version it wants to use from
8//! fuchsia.net.stackmigrationdeprecated.Control and spawns the appropriate
9//! netstack binary from its own package.
10//!
11//! The directory request handle is passed directly to the spawned netstack.
12//!
13//! The incoming namespace for the spawned netstack is carefully constructed to
14//! extract out the capabilities that are routed to netstack-proxy that are not
15//! used by netstack itself.
1617use fidl::endpoints::{DiscoverableProtocolMarker, Proxy as _};
18use vfs::directory::helper::DirectlyMutable;
19use {fidl_fuchsia_net_stackmigrationdeprecated as fnet_migration, fuchsia_async as fasync};
2021#[fasync::run_singlethreaded]
22pub async fn main() -> std::process::ExitCode {
23// Start by getting the Netstack version we should use.
24let current_boot_version = {
25let migration =
26 fuchsia_component::client::connect_to_protocol::<fnet_migration::StateMarker>()
27 .expect("connect to protocol");
28let fnet_migration::InEffectVersion { current_boot, .. } =
29 migration.get_netstack_version().await.expect("failed to read netstack version");
30 current_boot
31 };
3233println!("netstack migration proxy using version {current_boot_version:?}");
34let bin_path = match current_boot_version {
35 fnet_migration::NetstackVersion::Netstack2 => c"/pkg/bin/netstack",
36 fnet_migration::NetstackVersion::Netstack3 => c"/pkg/bin/netstack3",
37 };
3839let ns = fdio::Namespace::installed().expect("failed to get namespace");
40let mut entries = ns
41 .export()
42 .expect("failed to export namespace entries")
43 .into_iter()
44 .filter_map(|fdio::NamespaceEntry { handle, path }| match path.as_str() {
45"/" => {
46panic!("unexpected non flat namespace, bad capabilities will bleed into netstack")
47 }
48"/svc" => None,
49 x => {
50Some((Some(handle), std::ffi::CString::new(x).expect("failed to create C string")))
51 }
52 })
53 .collect::<Vec<_>>();
5455let handle =
56 fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleType::DirectoryRequest.into())
57 .expect("missing startup handle");
5859let mut actions = vec![fdio::SpawnAction::add_handle(
60 fuchsia_runtime::HandleInfo::new(fuchsia_runtime::HandleType::DirectoryRequest, 0),
61 handle,
62 )];
6364 actions.extend(entries.iter_mut().map(|(handle, path)| {
65// Handle is always Some here, we use an option so we can take it from
66 // entries while entries keeps the CString backing.
67let handle = handle.take().unwrap();
68 fdio::SpawnAction::add_namespace_entry(path.as_c_str(), handle)
69 }));
7071let svc = vfs::directory::immutable::simple::simple();
72for s in std::fs::read_dir("/svc").expect("failed to get /svc entries") {
73let entry = s.expect("failed to get directory entry");
74let name = entry.file_name();
75let name = name.to_str().expect("failed to get file name");
7677// Don't allow Netstack to see the services that we use exclusively to
78 // enable proxying.
79let block_services = [
80 fidl_fuchsia_process::LauncherMarker::PROTOCOL_NAME,
81 fnet_migration::StateMarker::PROTOCOL_NAME,
82 ];
83if block_services.into_iter().any(|s| s == name) {
84continue;
85 }
86 svc.add_entry(
87 name,
88 vfs::service::endpoint(move |_, channel| {
89 fuchsia_component::client::connect_channel_to_protocol_at_path(
90 channel.into(),
91 entry.path().to_str().expect("failed to get entry path"),
92 )
93 .unwrap_or_else(|e| eprintln!("error connecting to protocol {:?}", e));
94 }),
95 )
96 .unwrap_or_else(|e| panic!("failed to add entry {name}: {e:?}"));
97 }
9899let svc_dir = vfs::directory::serve_read_only(svc);
100let handle = svc_dir.into_client_end().unwrap().into();
101 actions.push(fdio::SpawnAction::add_namespace_entry(c"/svc", handle));
102103// Pass down the configuration VMO if we have it.
104let config_vmo_handle_info = fuchsia_runtime::HandleType::ComponentConfigVmo.into();
105if let Some(config_vmo) = fuchsia_runtime::take_startup_handle(config_vmo_handle_info) {
106 actions.push(fdio::SpawnAction::add_handle(config_vmo_handle_info, config_vmo))
107 }
108109let proc = fdio::spawn_etc(
110&fuchsia_runtime::job_default(),
111 fdio::SpawnOptions::CLONE_ALL - fdio::SpawnOptions::CLONE_NAMESPACE,
112 bin_path,
113&[bin_path],
114None,
115&mut actions[..],
116 )
117 .expect("failed to spawn netstack");
118119let signals = fasync::OnSignals::new(&proc, zx::Signals::PROCESS_TERMINATED)
120 .await
121.expect("failed to observe process termination signals");
122println!("netstack exited unexpectedly with {signals:?}");
123124// TODO(https://fxbug.dev/380897722) Inherit the exit code of the proxied netstack process once
125 // netstack supports clean shutdown.
126std::process::ExitCode::FAILURE
127}