Skip to main content

netstack3/
main.rs

1// Copyright 2018 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.
4
5//! A networking stack.
6#![warn(clippy::unused_async)]
7#![warn(missing_docs, unreachable_patterns, unused)]
8#![recursion_limit = "512"]
9
10mod bindings;
11
12use std::num::NonZeroU8;
13
14use fidl::endpoints::RequestStream as _;
15use fidl_fuchsia_process_lifecycle as fprocess_lifecycle;
16use fuchsia_async as fasync;
17use fuchsia_async::SendExecutorBuilder;
18use fuchsia_component::server::{ServiceFs, ServiceFsDir};
19use futures::{Future, StreamExt as _};
20use log::{error, info, warn};
21
22use bindings::{GlobalConfig, InspectPublisher, InterfaceConfigDefaults, NetstackSeed, Service};
23
24/// Runs Netstack3.
25pub fn main() {
26    let mut config = ns3_config::Config::take_from_startup_handle();
27    if config.max_rolling_capture_buffer_size < fidl_fuchsia_net_debug::MIN_BUFFER_SIZE {
28        warn!(
29            "max rolling capture buffer size configured as {} will be clamped to the minimum {}",
30            config.max_rolling_capture_buffer_size,
31            fidl_fuchsia_net_debug::MIN_BUFFER_SIZE,
32        );
33        config.max_rolling_capture_buffer_size = fidl_fuchsia_net_debug::MIN_BUFFER_SIZE;
34    };
35    let ns3_config::Config {
36        num_threads,
37        debug_logs,
38        opaque_iids,
39        suspend_enabled,
40        sampled_stats_enabled,
41        multi_vmo,
42        max_rolling_capture_buffer_size,
43        tcp_gro_enabled,
44    } = &config;
45    let num_threads = NonZeroU8::new(*num_threads).expect("invalid 0 thread count value");
46    let mut executor = SendExecutorBuilder::new().num_threads(num_threads.get().into()).build();
47
48    let mut log_options = diagnostics_log::PublishOptions::default();
49
50    // NB: netstack3 is usually launched with a 'netstack' moniker already -
51    // which implies an automatic 'netstack' tag. However, the automatic tag has
52    // shown problems when extra tags are present in specific log lines (e.g.
53    // https://fxbug.dev/390252317, https://fxbug.dev/390252218). Given that,
54    // we always initialize with the netstack tag here.
55    log_options = log_options.tags(&["netstack"]);
56
57    if *debug_logs {
58        // When forcing debug logs, disable all the dynamic features from the
59        // logging framework, we want logs pegged at Severity::Debug.
60        log_options = log_options
61            .minimum_severity(diagnostics_log::Severity::Debug)
62            .listen_for_interest_updates(false);
63    }
64    diagnostics_log::initialize(log_options).expect("failed to initialize log");
65
66    fuchsia_trace_provider::trace_provider_create_with_fdio();
67
68    info!("starting netstack3 with {config:?}");
69
70    let mut fs = ServiceFs::new();
71    let _: &mut ServiceFsDir<'_, _> = fs
72        .dir("svc")
73        // TODO(https://fxbug.dev/42076541): This is transitional. Once the
74        // out-of-stack DHCP client is being used by both netstacks, it
75        // should be moved out of the netstack realm and into the network
76        // realm. The trip through Netstack3 allows for availability of DHCP
77        // client to be dependent on Netstack version when using
78        // netstack-proxy.
79        .add_proxy_service::<fidl_fuchsia_net_dhcp::ClientProviderMarker, _>()
80        .add_fidl_service(Service::Control)
81        .add_service_connector(Service::DebugDiagnostics)
82        .add_fidl_service(Service::DebugInterfaces)
83        .add_fidl_service(Service::FilterControl)
84        .add_fidl_service(Service::FilterState)
85        .add_fidl_service(Service::HealthCheck)
86        .add_fidl_service(Service::Interfaces)
87        .add_fidl_service(Service::InterfacesAdmin)
88        .add_fidl_service(Service::MulticastAdminV4)
89        .add_fidl_service(Service::MulticastAdminV6)
90        .add_fidl_service(Service::NdpWatcher)
91        .add_fidl_service(Service::Neighbor)
92        .add_fidl_service(Service::NeighborController)
93        .add_fidl_service(Service::PacketSocket)
94        .add_fidl_service(Service::PacketCapture)
95        .add_fidl_service(Service::RawSocket)
96        .add_fidl_service(Service::RootFilter)
97        .add_fidl_service(Service::RootInterfaces)
98        .add_fidl_service(Service::RootRoutesV4)
99        .add_fidl_service(Service::RootRoutesV6)
100        .add_fidl_service(Service::RoutesAdminV4)
101        .add_fidl_service(Service::RoutesAdminV6)
102        .add_fidl_service(Service::RoutesState)
103        .add_fidl_service(Service::RoutesStateV4)
104        .add_fidl_service(Service::RoutesStateV6)
105        .add_fidl_service(Service::RouteTableProviderV4)
106        .add_fidl_service(Service::RouteTableProviderV6)
107        .add_fidl_service(Service::RuleTableV4)
108        .add_fidl_service(Service::RuleTableV6)
109        .add_fidl_service(Service::SettingsControl)
110        .add_fidl_service(Service::SettingsState)
111        .add_fidl_service(Service::Socket)
112        .add_fidl_service(Service::SocketControl)
113        .add_fidl_service(Service::SocketDiagnostics)
114        .add_fidl_service(Service::Stack)
115        .add_fidl_service(Service::WakeGroupProvider);
116
117    let seed = NetstackSeed::new(
118        GlobalConfig {
119            suspend_enabled: *suspend_enabled,
120            sampled_stats_enabled: *sampled_stats_enabled,
121            multi_vmo: *multi_vmo,
122            max_rolling_capture_buffer_size: *max_rolling_capture_buffer_size,
123            tcp_gro_enabled: *tcp_gro_enabled,
124        },
125        &InterfaceConfigDefaults { opaque_iids: *opaque_iids },
126    );
127
128    let inspect_publisher = InspectPublisher::new();
129    inspect_publisher
130        .inspector()
131        .root()
132        .record_child("Config", |config_node| config.record_inspect(config_node));
133
134    let _: &mut ServiceFs<_> = fs.take_and_serve_directory_handle().expect("directory handle");
135
136    // Short circuit when we receive a lifecycle stop request.
137    let fs = fs.take_until(get_lifecycle_stop_fut());
138
139    executor.run(seed.serve(fs, inspect_publisher))
140}
141
142/// Takes the lifecycle handle from startup and returns a future that resolves
143/// whenever the system has requested shutdown.
144fn get_lifecycle_stop_fut() -> impl Future<Output = ()> {
145    // Lifecycle handle takes no args, must be set to zero.
146    // See zircon/processargs.h.
147    const LIFECYCLE_HANDLE_ARG: u16 = 0;
148    let handle = fuchsia_runtime::take_startup_handle(fuchsia_runtime::HandleInfo::new(
149        fuchsia_runtime::HandleType::Lifecycle,
150        LIFECYCLE_HANDLE_ARG,
151    ))
152    .expect("missing lifecycle handle");
153
154    async move {
155        let mut request_stream = fprocess_lifecycle::LifecycleRequestStream::from_channel(
156            fasync::Channel::from_channel(handle.into()).into(),
157        );
158        loop {
159            match request_stream.next().await {
160                Some(Ok(fprocess_lifecycle::LifecycleRequest::Stop { control_handle })) => {
161                    info!("received shutdown request");
162                    // Shutdown request is acknowledged by the lifecycle
163                    // channel shutting down. Intentionally leak the channel
164                    // so it'll only be closed on process termination,
165                    // allowing clean process termination to always be
166                    // observed.
167
168                    // Must drop the control_handle to unwrap the
169                    // lifecycle channel.
170                    std::mem::drop(control_handle);
171                    let (inner, _terminated): (_, bool) = request_stream.into_inner();
172                    let inner = std::sync::Arc::try_unwrap(inner)
173                        .expect("failed to retrieve lifecycle channel");
174                    let inner: zx::Channel = inner.into_channel().into_zx_channel();
175                    std::mem::forget(inner);
176                    break;
177                }
178                Some(Err(e)) => error!("observed error in lifecycle request stream: {e:?}"),
179                None => {
180                    // Something really bad must've happened here. We chose
181                    // not to panic because the system must be in a bad
182                    // state, log an error and hold forever.
183                    error!("lifecycle channel closed");
184                    futures::future::pending::<()>().await;
185                }
186            }
187        }
188    }
189}