1use std::sync::atomic::Ordering;
6
7use crate::sysctl_directory::SysctlDirectory;
8use fidl::endpoints::DiscoverableProtocolMarker as _;
9use fidl_fuchsia_net_interfaces_admin as fnet_interfaces_admin;
10use fidl_fuchsia_net_root as fnet_root;
11use fidl_fuchsia_net_settings as fnet_settings;
12use fuchsia_component::client::connect_to_protocol_sync;
13use net_types::ip::{Ip, IpVersion, Ipv4, Ipv6};
14use netlink::{SysctlError, SysctlInterfaceSelector};
15use starnix_core::task::CurrentTask;
16use starnix_core::vfs::pseudo::simple_file::{
17 BytesFile, BytesFileOps, SimpleFileNode, parse_i32_file, serialize_for_file,
18};
19use starnix_core::vfs::pseudo::stub_bytes_file::StubBytesFile;
20use starnix_core::vfs::{
21 DirectoryEntryType, DirentSink, FileObject, FileOps, FsNode, FsNodeHandle, FsNodeOps, FsStr,
22 emit_dotdot, fileops_impl_directory, fileops_impl_noop_sync, fileops_impl_unbounded_seek,
23 fs_node_impl_dir_readonly,
24};
25use starnix_logging::{bug_ref, log_error, log_warn, track_stub};
26
27use starnix_uapi::errors::Errno;
28use starnix_uapi::file_mode::{FileMode, mode};
29use starnix_uapi::open_flags::OpenFlags;
30use starnix_uapi::vfs::FdEvents;
31use starnix_uapi::{errno, error, uapi};
32use std::borrow::Cow;
33
34const FILE_MODE: FileMode = mode!(IFREG, 0o644);
35
36fn netstack_devices_readdir(
37 file: &FileObject,
38 current_task: &CurrentTask,
39 sink: &mut dyn DirentSink,
40) -> Result<(), Errno> {
41 file.blocking_op(current_task, FdEvents::empty(), None, || {
42 let (initialized, _) = ¤t_task.kernel().netstack_devices.initialized_and_wq;
43 if !initialized.load(Ordering::SeqCst) {
44 let _ = current_task.kernel().network_netlink();
46 return error!(EAGAIN);
47 }
48 emit_dotdot(file, sink)?;
49
50 if sink.offset() == 2 {
51 sink.add(
52 file.fs.allocate_ino(),
53 sink.offset() + 1,
54 DirectoryEntryType::from_mode(FILE_MODE),
55 "all".into(),
56 )?;
57 }
58
59 if sink.offset() == 3 {
60 sink.add(
61 file.fs.allocate_ino(),
62 sink.offset() + 1,
63 DirectoryEntryType::from_mode(FILE_MODE),
64 "default".into(),
65 )?;
66 }
67
68 let devices = current_task.kernel().netstack_devices.snapshot_devices();
69 for (name, _) in devices.iter().skip(sink.offset() as usize - 4) {
70 let inode_num = file.fs.allocate_ino();
71 sink.add(
72 inode_num,
73 sink.offset() + 1,
74 DirectoryEntryType::from_mode(FILE_MODE),
75 name.as_ref(),
76 )?;
77 }
78 Ok(())
79 })
80}
81
82macro_rules! fileops_impl_netstack_devices {
83 () => {
84 fn readdir(
85 &self,
86 file: &FileObject,
87 current_task: &CurrentTask,
88 sink: &mut dyn DirentSink,
89 ) -> Result<(), Errno> {
90 netstack_devices_readdir(file, current_task, sink)
91 }
92
93 fn wait_async(
94 &self,
95 _file: &FileObject,
96 current_task: &CurrentTask,
97 waiter: &starnix_core::task::Waiter,
98 _events: FdEvents,
99 _handler: starnix_core::task::EventHandler,
100 ) -> Option<starnix_core::task::WaitCanceler> {
101 let (_initialized, wq) = ¤t_task.kernel().netstack_devices.initialized_and_wq;
102 Some(wq.wait_async(waiter))
103 }
104 };
105}
106
107fn get_netstack_device(
108 current_task: &CurrentTask,
109 name: &FsStr,
110) -> Option<SysctlInterfaceSelector> {
111 let _ = current_task.kernel().network_netlink();
113 if name == "all" {
123 return Some(SysctlInterfaceSelector::All);
124 }
125 if name == "default" {
126 return Some(SysctlInterfaceSelector::Default);
127 }
128 if let Some(dev) = current_task.kernel().netstack_devices.get_device(name) {
129 return Some(SysctlInterfaceSelector::Id(dev.interface_id));
130 }
131 None
132}
133
134#[derive(Clone)]
135pub struct ProcSysNetIpv4Conf;
136
137impl FsNodeOps for ProcSysNetIpv4Conf {
138 fs_node_impl_dir_readonly!();
139
140 fn create_file_ops(
141 &self,
142 _node: &FsNode,
143 _current_task: &CurrentTask,
144 _flags: OpenFlags,
145 ) -> Result<Box<dyn FileOps>, Errno> {
146 Ok(Box::new(self.clone()))
147 }
148
149 fn lookup(
150 &self,
151 node: &FsNode,
152 current_task: &CurrentTask,
153 name: &FsStr,
154 ) -> Result<FsNodeHandle, Errno> {
155 if get_netstack_device(current_task, name).is_some() {
156 let fs = node.fs();
157 let dir = SysctlDirectory::<{ uapi::CAP_NET_ADMIN }>::new();
158 dir.edit(&fs, |dir| {
159 dir.entry(
160 "accept_redirects",
161 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423646442")),
162 FILE_MODE,
163 );
164 });
165 return Ok(dir.into_node(&fs, 0o777));
167 }
168 error!(ENOENT, "looking for {name}")
169 }
170}
171
172impl FileOps for ProcSysNetIpv4Conf {
173 fileops_impl_directory!();
174 fileops_impl_noop_sync!();
175 fileops_impl_unbounded_seek!();
176 fileops_impl_netstack_devices!();
177}
178
179#[derive(Clone)]
180pub struct ProcSysNetIpv4Neigh;
181
182impl FsNodeOps for ProcSysNetIpv4Neigh {
183 fs_node_impl_dir_readonly!();
184
185 fn create_file_ops(
186 &self,
187 _node: &FsNode,
188 _current_task: &CurrentTask,
189 _flags: OpenFlags,
190 ) -> Result<Box<dyn FileOps>, Errno> {
191 Ok(Box::new(self.clone()))
192 }
193
194 fn lookup(
195 &self,
196 node: &FsNode,
197 current_task: &CurrentTask,
198 name: &FsStr,
199 ) -> Result<FsNodeHandle, Errno> {
200 if let Some(interface) = get_netstack_device(current_task, name) {
201 let fs = node.fs();
202 let dir = SysctlDirectory::<{ uapi::CAP_NET_ADMIN }>::new();
203 dir.edit(&fs, |dir| {
204 dir.entry(
205 "ucast_solicit",
206 new_interface_config_file_node::<UcastSolicit<Ipv4>>(interface),
207 FILE_MODE,
208 );
209 dir.entry(
210 "retrans_time_ms",
211 new_interface_config_file_node::<RetransTimeMs<Ipv4>>(interface),
212 FILE_MODE,
213 );
214 dir.entry(
215 "mcast_resolicit",
216 new_interface_config_file_node::<McastResolicit<Ipv4>>(interface),
217 FILE_MODE,
218 );
219 dir.entry(
220 "base_reachable_time_ms",
221 new_interface_config_file_node::<BaseReachableTimeMs<Ipv4>>(interface),
222 FILE_MODE,
223 );
224 });
225 return Ok(dir.into_node(&fs, 0o777));
227 }
228 error!(ENOENT, "looking for {name}")
229 }
230}
231
232impl FileOps for ProcSysNetIpv4Neigh {
233 fileops_impl_directory!();
234 fileops_impl_noop_sync!();
235 fileops_impl_unbounded_seek!();
236 fileops_impl_netstack_devices!();
237}
238
239#[derive(Clone)]
240pub struct ProcSysNetIpv6Conf;
241
242impl FsNodeOps for ProcSysNetIpv6Conf {
243 fs_node_impl_dir_readonly!();
244
245 fn create_file_ops(
246 &self,
247 _node: &FsNode,
248 _current_task: &CurrentTask,
249 _flags: OpenFlags,
250 ) -> Result<Box<dyn FileOps>, Errno> {
251 Ok(Box::new(self.clone()))
252 }
253
254 fn lookup(
255 &self,
256 node: &FsNode,
257 current_task: &CurrentTask,
258 name: &FsStr,
259 ) -> Result<FsNodeHandle, Errno> {
260 if let Some(interface) = get_netstack_device(current_task, name) {
261 let fs = node.fs();
262 let dir = SysctlDirectory::<{ uapi::CAP_NET_ADMIN }>::new();
263 dir.edit(&fs, |dir| {
264 dir.entry(
265 "accept_ra",
266 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423646365")),
267 FILE_MODE,
268 );
269 dir.entry(
270 "accept_ra_defrtr",
271 new_interface_config_file_node::<AcceptRaDefrtr>(interface),
272 FILE_MODE,
273 );
274 dir.entry(
275 "accept_ra_info_min_plen",
276 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423645816")),
277 FILE_MODE,
278 );
279 dir.entry(
280 "accept_ra_rt_info_min_plen",
281 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322908046")),
282 FILE_MODE,
283 );
284 dir.entry(
285 "accept_ra_rt_table",
286 NetworkNetlinkSysctlFile::new_node(interface),
287 FILE_MODE,
288 );
289 dir.entry(
290 "accept_redirects",
291 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423646442")),
292 FILE_MODE,
293 );
294 dir.entry(
295 "dad_transmits",
296 new_interface_config_file_node::<Ipv6DadTransmits>(interface),
297 FILE_MODE,
298 );
299 dir.entry(
300 "use_tempaddr",
301 new_interface_config_file_node::<UseTempAddr>(interface),
302 FILE_MODE,
303 );
304 dir.entry(
305 "addr_gen_mode",
306 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423645864")),
307 FILE_MODE,
308 );
309 dir.entry(
310 "stable_secret",
311 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423646722")),
312 FILE_MODE,
313 );
314 dir.entry(
315 "disable_ipv6",
316 new_interface_config_file_node::<DisableIpv6>(interface),
317 FILE_MODE,
318 );
319 dir.entry(
320 "optimistic_dad",
321 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423646584")),
322 FILE_MODE,
323 );
324 dir.entry(
325 "use_oif_addrs_only",
326 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423645421")),
327 FILE_MODE,
328 );
329 dir.entry(
330 "use_optimistic",
331 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/423645883")),
332 FILE_MODE,
333 );
334 dir.entry(
335 "forwarding",
336 StubBytesFile::new_node(bug_ref!("https://fxbug.dev/322907925")),
337 FILE_MODE,
338 );
339 });
340 return Ok(dir.into_node(&fs, 0o777));
342 }
343 error!(ENOENT, "looking for {name}")
344 }
345}
346
347impl FileOps for ProcSysNetIpv6Conf {
348 fileops_impl_directory!();
349 fileops_impl_noop_sync!();
350 fileops_impl_unbounded_seek!();
351 fileops_impl_netstack_devices!();
352}
353
354#[derive(Clone)]
355pub struct ProcSysNetIpv6Neigh;
356
357impl FsNodeOps for ProcSysNetIpv6Neigh {
358 fs_node_impl_dir_readonly!();
359
360 fn create_file_ops(
361 &self,
362 _node: &FsNode,
363 _current_task: &CurrentTask,
364 _flags: OpenFlags,
365 ) -> Result<Box<dyn FileOps>, Errno> {
366 Ok(Box::new(self.clone()))
367 }
368
369 fn lookup(
370 &self,
371 node: &FsNode,
372 current_task: &CurrentTask,
373 name: &FsStr,
374 ) -> Result<FsNodeHandle, Errno> {
375 if let Some(interface) = get_netstack_device(current_task, name) {
376 let fs = node.fs();
377 let dir = SysctlDirectory::<{ uapi::CAP_NET_ADMIN }>::new();
378 dir.edit(&fs, |dir| {
379 dir.entry(
380 "ucast_solicit",
381 new_interface_config_file_node::<UcastSolicit<Ipv6>>(interface),
382 FILE_MODE,
383 );
384 dir.entry(
385 "retrans_time_ms",
386 new_interface_config_file_node::<RetransTimeMs<Ipv6>>(interface),
387 FILE_MODE,
388 );
389 dir.entry(
390 "mcast_resolicit",
391 new_interface_config_file_node::<McastResolicit<Ipv6>>(interface),
392 FILE_MODE,
393 );
394 dir.entry(
395 "base_reachable_time_ms",
396 new_interface_config_file_node::<BaseReachableTimeMs<Ipv6>>(interface),
397 FILE_MODE,
398 );
399 });
400 return Ok(dir.into_node(&fs, 0o777));
402 }
403 error!(ENOENT, "looking for {name}")
404 }
405}
406
407impl FileOps for ProcSysNetIpv6Neigh {
408 fileops_impl_directory!();
409 fileops_impl_noop_sync!();
410 fileops_impl_unbounded_seek!();
411 fileops_impl_netstack_devices!();
412}
413
414struct NetworkNetlinkSysctlFile {
415 interface: SysctlInterfaceSelector,
416}
417
418impl NetworkNetlinkSysctlFile {
419 fn new_node(interface: SysctlInterfaceSelector) -> impl FsNodeOps {
420 SimpleFileNode::new(move |_| Ok(BytesFile::new(Self { interface })))
421 }
422}
423
424fn to_errno(error: SysctlError) -> Errno {
425 match error {
426 SysctlError::Disconnected => errno!(EIO),
427 SysctlError::NoInterface => errno!(ENODEV),
428 SysctlError::Unsupported => errno!(ENOTSUP),
429 }
430}
431
432impl BytesFileOps for NetworkNetlinkSysctlFile {
433 fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
434 let value = parse_i32_file(&data)?;
435 current_task
436 .kernel()
437 .network_netlink()
438 .write_accept_ra_rt_table(self.interface, value)
439 .map_err(|err| {
440 log_error!("failed to write to {:?}: {:?}", self.interface, err);
441 to_errno(err)
442 })
443 }
444
445 fn read(&self, current_task: &CurrentTask) -> Result<std::borrow::Cow<'_, [u8]>, Errno> {
446 let value = current_task
447 .kernel()
448 .network_netlink()
449 .read_accept_ra_rt_table(self.interface)
450 .map_err(|err| {
451 log_error!("failed to read from {:?}: {:?}", self.interface, err);
452 to_errno(err)
453 })?;
454 Ok(serialize_for_file(value).into())
455 }
456}
457
458pub struct PingGroupRangeFile;
459
460impl PingGroupRangeFile {
461 const MAX_GID: u32 = 4294967294;
462
463 pub fn new_node() -> impl FsNodeOps {
464 BytesFile::new_node(Self)
465 }
466}
467
468impl BytesFileOps for PingGroupRangeFile {
469 fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
470 let mut params = std::str::from_utf8(&data)
471 .map_err(|_| errno!(EINVAL))?
472 .trim_ascii()
473 .split_ascii_whitespace();
474 let min = params
475 .next()
476 .ok_or_else(|| errno!(EINVAL))?
477 .parse::<u32>()
478 .map_err(|_| errno!(EINVAL))?;
479 if min > Self::MAX_GID {
480 return error!(EINVAL);
481 }
482
483 let max = match params.next() {
485 Some(v) => {
486 let v = v.parse::<u32>().map_err(|_| errno!(EINVAL))?;
487 if v > Self::MAX_GID {
488 return error!(EINVAL);
489 }
490 Some(v + 1)
491 }
492 None => None,
493 };
494
495 let mut range = current_task.kernel().system_limits.socket.icmp_ping_gids.lock();
496 range.start = min;
497 if let Some(max) = max {
498 range.end = max;
499 }
500 if range.is_empty() {
501 *range = 1..1;
504 }
505
506 Ok(())
507 }
508 fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
509 let range = current_task.kernel().system_limits.socket.icmp_ping_gids.lock().clone();
510 Ok(format!("{}\t{}\n", range.start, range.end - 1).into_bytes().into())
511 }
512}
513
514trait InterfaceConfig: Sync + Send + 'static {
515 fn try_from_i32(value: i32) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno>;
516 fn try_into_i32(config: fidl_fuchsia_net_interfaces_admin::Configuration)
517 -> Result<i32, Errno>;
518}
519
520fn new_interface_config_file_node<Config>(selector: SysctlInterfaceSelector) -> impl FsNodeOps
521where
522 Config: InterfaceConfig,
523{
524 SimpleFileNode::new(move |_| {
525 Ok(BytesFile::new(InterfaceConfigFile {
526 selector,
527 _marker: std::marker::PhantomData::<Config>,
528 }))
529 })
530}
531
532struct InterfaceConfigFile<Config> {
533 selector: SysctlInterfaceSelector,
534 _marker: std::marker::PhantomData<Config>,
535}
536
537impl<Config> BytesFileOps for InterfaceConfigFile<Config>
538where
539 Config: InterfaceConfig,
540{
541 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
542 let config = Config::try_from_i32(parse_i32_file(&data)?)?;
543 set_interface_config(self.selector, &config)?;
544 Ok(())
545 }
546 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
547 let config = Config::try_into_i32(get_interface_config(self.selector)?)?;
548 Ok(serialize_for_file::<i32>(config).into())
549 }
550}
551
552fn set_interface_config(
553 selector: SysctlInterfaceSelector,
554 config: &fidl_fuchsia_net_interfaces_admin::Configuration,
555) -> Result<(), Errno> {
556 match selector {
557 SysctlInterfaceSelector::All => {
558 log_warn!("setting config for all network interfaces is ignored");
559 Ok(())
560 }
561 SysctlInterfaceSelector::Default => {
562 let control =
563 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
564 log_error!(
565 "failed to connect to {}: {:?}",
566 fnet_settings::ControlMarker::PROTOCOL_NAME,
567 err
568 );
569 errno!(EIO)
570 })?;
571 control
572 .update_interface_defaults(config, zx::MonotonicInstant::INFINITE)
573 .map_err(|err| {
574 log_error!("failed to set network interface config: {:?}", err);
575 errno!(EIO)
576 })?
577 .map_err(map_update_error)?;
578 Ok(())
579 }
580 SysctlInterfaceSelector::Id(id) => {
581 let root =
582 connect_to_protocol_sync::<fnet_root::InterfacesMarker>().map_err(|err| {
583 log_error!(
584 "failed to connect to {}: {:?}",
585 fnet_root::InterfacesMarker::PROTOCOL_NAME,
586 err
587 );
588 errno!(EIO)
589 })?;
590 let (control, server) = fidl::endpoints::create_sync_proxy();
591 root.get_admin(id.get(), server).map_err(|err| {
592 log_error!("failed to get network interface: {:?}", err);
593 errno!(EIO)
594 })?;
595 let _prev = control
596 .set_configuration(config, zx::MonotonicInstant::INFINITE)
597 .map_err(|err| {
598 if err.is_closed() {
599 log_error!("network interface {} went away", id);
600 errno!(ENODEV)
601 } else {
602 log_error!("failed to set network interface config: {:?}", err);
603 errno!(EIO)
604 }
605 })?
606 .map_err(|err| {
607 use fnet_interfaces_admin::ControlSetConfigurationError;
608 match err {
609 ControlSetConfigurationError::Ipv4ForwardingUnsupported
610 | ControlSetConfigurationError::Ipv4MulticastForwardingUnsupported
611 | ControlSetConfigurationError::Ipv4IgmpVersionUnsupported
612 | ControlSetConfigurationError::Ipv6ForwardingUnsupported
613 | ControlSetConfigurationError::Ipv6MulticastForwardingUnsupported
614 | ControlSetConfigurationError::Ipv6MldVersionUnsupported
615 | ControlSetConfigurationError::ArpNotSupported
616 | ControlSetConfigurationError::NdpNotSupported => errno!(ENOTSUP),
617 ControlSetConfigurationError::IllegalZeroValue
618 | ControlSetConfigurationError::IllegalNegativeValue => errno!(EINVAL),
619 ControlSetConfigurationError::__SourceBreaking { unknown_ordinal } => {
620 log_error!("unknown error with ordinal: {unknown_ordinal}");
621 errno!(EIO)
622 }
623 }
624 });
625 Ok(())
626 }
627 }
628}
629
630fn get_interface_defaults() -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
631 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
632 log_error!("failed to connect to {}: {:?}", fnet_settings::StateMarker::PROTOCOL_NAME, err);
633 errno!(EIO)
634 })?;
635 let config = state.get_interface_defaults(zx::MonotonicInstant::INFINITE).map_err(|err| {
636 log_error!("failed to get network interface defaults: {:?}", err);
637 if err.is_closed() { errno!(ENODEV) } else { errno!(EIO) }
638 })?;
639 Ok(config)
640}
641
642fn get_interface_config(
643 selector: SysctlInterfaceSelector,
644) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
645 match selector {
646 SysctlInterfaceSelector::All => {
647 track_stub!(
648 TODO("https://fxbug.dev/521344735"),
649 "procfs sys net 'all' interface config"
650 );
651 get_interface_defaults()
652 }
653 SysctlInterfaceSelector::Default => get_interface_defaults(),
654 SysctlInterfaceSelector::Id(id) => {
655 let root =
656 connect_to_protocol_sync::<fnet_root::InterfacesMarker>().map_err(|err| {
657 log_error!(
658 "failed to connect to {}: {:?}",
659 fnet_root::InterfacesMarker::PROTOCOL_NAME,
660 err
661 );
662 errno!(EIO)
663 })?;
664 let (control, server) = fidl::endpoints::create_sync_proxy();
665 root.get_admin(id.get(), server).map_err(|err| {
666 log_error!("failed to get network interface: {:?}", err);
667 errno!(EIO)
668 })?;
669 let config = control
670 .get_configuration(zx::MonotonicInstant::INFINITE)
671 .map_err(|err| {
672 log_error!("failed to get network interface config: {:?}", err);
673 if err.is_closed() { errno!(ENODEV) } else { errno!(EIO) }
674 })?
675 .map_err(|err| match err {
676 fnet_interfaces_admin::ControlGetConfigurationError::__SourceBreaking {
677 unknown_ordinal,
678 } => {
679 log_error!("unknown error with ordinal: {unknown_ordinal}");
680 errno!(EIO)
681 }
682 })?;
683 Ok(config)
684 }
685 }
686}
687
688struct DisableIpv6;
689
690impl InterfaceConfig for DisableIpv6 {
691 fn try_from_i32(value: i32) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
692 Ok(fidl_fuchsia_net_interfaces_admin::Configuration {
693 ipv6: Some(fidl_fuchsia_net_interfaces_admin::Ipv6Configuration {
694 enabled: Some(value == 0),
695 ..Default::default()
696 }),
697 ..Default::default()
698 })
699 }
700 fn try_into_i32(
701 config: fidl_fuchsia_net_interfaces_admin::Configuration,
702 ) -> Result<i32, Errno> {
703 let ipv6 = config.ipv6.ok_or_else(|| {
704 log_error!("network interface config missing ipv6");
705 errno!(EIO)
706 })?;
707 let enabled = ipv6.enabled.ok_or_else(|| {
708 log_error!("network interface config missing ipv6 enabled");
709 errno!(EIO)
710 })?;
711 Ok(i32::from(!enabled))
712 }
713}
714
715struct UcastSolicit<I: Ip> {
716 _marker: core::marker::PhantomData<I>,
717}
718
719impl<I: Ip> InterfaceConfig for UcastSolicit<I> {
720 fn try_from_i32(value: i32) -> Result<fnet_interfaces_admin::Configuration, Errno> {
721 let max_unicast_solicitations = u16::try_from(value).map_err(|_| errno!(EINVAL))?;
722 let nud_config = fnet_interfaces_admin::NudConfiguration {
723 max_unicast_solicitations: Some(max_unicast_solicitations),
724 ..Default::default()
725 };
726 let mut config = fnet_interfaces_admin::Configuration::default();
727 match I::VERSION {
728 IpVersion::V4 => {
729 config.ipv4 = Some(fidl_fuchsia_net_interfaces_admin::Ipv4Configuration {
730 arp: Some(fnet_interfaces_admin::ArpConfiguration {
731 nud: Some(nud_config),
732 ..Default::default()
733 }),
734 ..Default::default()
735 })
736 }
737 IpVersion::V6 => {
738 config.ipv6 = Some(fidl_fuchsia_net_interfaces_admin::Ipv6Configuration {
739 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
740 nud: Some(nud_config),
741 ..Default::default()
742 }),
743 ..Default::default()
744 })
745 }
746 }
747 Ok(config)
748 }
749
750 fn try_into_i32(config: fnet_interfaces_admin::Configuration) -> Result<i32, Errno> {
751 let max_unicast_solicitations = match I::VERSION {
752 IpVersion::V4 => config
753 .ipv4
754 .and_then(|ipv4| ipv4.arp)
755 .and_then(|arp| arp.nud)
756 .and_then(|nud| nud.max_unicast_solicitations)
757 .ok_or_else(|| {
758 log_error!(
759 "network interface config missing ipv4 arp max_unicast_solicitations"
760 );
761 errno!(EIO)
762 })?,
763 IpVersion::V6 => config
764 .ipv6
765 .and_then(|ipv6| ipv6.ndp)
766 .and_then(|ndp| ndp.nud)
767 .and_then(|nud| nud.max_unicast_solicitations)
768 .ok_or_else(|| {
769 log_error!(
770 "network interface config missing ipv6 ndp max_unicast_solicitations"
771 );
772 errno!(EIO)
773 })?,
774 };
775 Ok(i32::from(max_unicast_solicitations))
776 }
777}
778
779struct McastResolicit<I: Ip> {
780 _marker: core::marker::PhantomData<I>,
781}
782
783impl<I: Ip> InterfaceConfig for McastResolicit<I> {
784 fn try_from_i32(value: i32) -> Result<fnet_interfaces_admin::Configuration, Errno> {
785 let max_multicast_solicitations = u16::try_from(value).map_err(|_| errno!(EINVAL))?;
786 let nud_config = fnet_interfaces_admin::NudConfiguration {
787 max_multicast_solicitations: Some(max_multicast_solicitations),
788 ..Default::default()
789 };
790 let mut config = fnet_interfaces_admin::Configuration::default();
791 match I::VERSION {
792 IpVersion::V4 => {
793 config.ipv4 = Some(fidl_fuchsia_net_interfaces_admin::Ipv4Configuration {
794 arp: Some(fnet_interfaces_admin::ArpConfiguration {
795 nud: Some(nud_config),
796 ..Default::default()
797 }),
798 ..Default::default()
799 })
800 }
801 IpVersion::V6 => {
802 config.ipv6 = Some(fidl_fuchsia_net_interfaces_admin::Ipv6Configuration {
803 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
804 nud: Some(nud_config),
805 ..Default::default()
806 }),
807 ..Default::default()
808 })
809 }
810 }
811 Ok(config)
812 }
813
814 fn try_into_i32(config: fnet_interfaces_admin::Configuration) -> Result<i32, Errno> {
815 let max_multicast_solicitations = match I::VERSION {
816 IpVersion::V4 => config
817 .ipv4
818 .and_then(|ipv4| ipv4.arp)
819 .and_then(|arp| arp.nud)
820 .and_then(|nud| nud.max_multicast_solicitations)
821 .ok_or_else(|| {
822 log_error!(
823 "network interface config missing ipv4 arp max_multicast_solicitations"
824 );
825 errno!(EIO)
826 })?,
827 IpVersion::V6 => config
828 .ipv6
829 .and_then(|ipv6| ipv6.ndp)
830 .and_then(|ndp| ndp.nud)
831 .and_then(|nud| nud.max_multicast_solicitations)
832 .ok_or_else(|| {
833 log_error!(
834 "network interface config missing ipv6 ndp max_multicast_solicitations"
835 );
836 errno!(EIO)
837 })?,
838 };
839 Ok(i32::from(max_multicast_solicitations))
840 }
841}
842
843struct BaseReachableTimeMs<I: Ip> {
844 _marker: core::marker::PhantomData<I>,
845}
846
847impl<I: Ip> InterfaceConfig for BaseReachableTimeMs<I> {
848 fn try_from_i32(value: i32) -> Result<fnet_interfaces_admin::Configuration, Errno> {
849 let base_reachable_time = zx::Duration::<zx::BootTimeline>::from_millis(i64::from(value));
850 let nud_config = fnet_interfaces_admin::NudConfiguration {
851 base_reachable_time: Some(base_reachable_time.into_nanos()),
852 ..Default::default()
853 };
854 let mut config = fnet_interfaces_admin::Configuration::default();
855 match I::VERSION {
856 IpVersion::V4 => {
857 config.ipv4 = Some(fnet_interfaces_admin::Ipv4Configuration {
858 arp: Some(fnet_interfaces_admin::ArpConfiguration {
859 nud: Some(nud_config),
860 ..Default::default()
861 }),
862 ..Default::default()
863 })
864 }
865 IpVersion::V6 => {
866 config.ipv6 = Some(fnet_interfaces_admin::Ipv6Configuration {
867 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
868 nud: Some(nud_config),
869 ..Default::default()
870 }),
871 ..Default::default()
872 })
873 }
874 }
875 Ok(config)
876 }
877
878 fn try_into_i32(config: fnet_interfaces_admin::Configuration) -> Result<i32, Errno> {
879 let base_reachable_time_ns = match I::VERSION {
880 IpVersion::V4 => config
881 .ipv4
882 .and_then(|ipv4| ipv4.arp)
883 .and_then(|arp| arp.nud)
884 .and_then(|nud| nud.base_reachable_time)
885 .ok_or_else(|| {
886 log_error!("network interface config missing ipv4 arp base_reachable_time");
887 errno!(EIO)
888 })?,
889 IpVersion::V6 => config
890 .ipv6
891 .and_then(|ipv6| ipv6.ndp)
892 .and_then(|ndp| ndp.nud)
893 .and_then(|nud| nud.base_reachable_time)
894 .ok_or_else(|| {
895 log_error!("network interface config missing ipv6 ndp base_reachable_time");
896 errno!(EIO)
897 })?,
898 };
899 Ok(i32::try_from(
900 zx::Duration::<zx::BootTimeline>::from_nanos(base_reachable_time_ns).into_millis(),
901 )
902 .map_err(|_| errno!(EIO))?)
903 }
904}
905
906struct Ipv6DadTransmits;
907
908impl InterfaceConfig for Ipv6DadTransmits {
909 fn try_from_i32(value: i32) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
910 let transmits = u16::try_from(value).map_err(|_| errno!(EINVAL))?;
911 Ok(fnet_interfaces_admin::Configuration {
912 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
913 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
914 dad: Some(fnet_interfaces_admin::DadConfiguration {
915 transmits: Some(transmits),
916 ..Default::default()
917 }),
918 ..Default::default()
919 }),
920 ..Default::default()
921 }),
922 ..Default::default()
923 })
924 }
925
926 fn try_into_i32(
927 config: fidl_fuchsia_net_interfaces_admin::Configuration,
928 ) -> Result<i32, Errno> {
929 config
930 .ipv6
931 .and_then(|ipv6| ipv6.ndp)
932 .and_then(|ndp| ndp.dad)
933 .and_then(|dad| dad.transmits)
934 .map(i32::from)
935 .ok_or_else(|| {
936 log_error!("network interface config missing ipv6 ndp dad transmits");
937 errno!(EIO)
938 })
939 }
940}
941
942struct RetransTimeMs<I: Ip> {
947 _marker: core::marker::PhantomData<I>,
948}
949
950impl<I: Ip> InterfaceConfig for RetransTimeMs<I> {
951 fn try_from_i32(value: i32) -> Result<fnet_interfaces_admin::Configuration, Errno> {
952 let retrans_timer = zx::Duration::<zx::BootTimeline>::from_millis(i64::from(value));
953 let nud_config = fnet_interfaces_admin::NudConfiguration {
954 retrans_timer: Some(retrans_timer.into_nanos()),
955 ..Default::default()
956 };
957 let mut config = fnet_interfaces_admin::Configuration::default();
958 match I::VERSION {
959 IpVersion::V4 => {
960 config.ipv4 = Some(fnet_interfaces_admin::Ipv4Configuration {
961 arp: Some(fnet_interfaces_admin::ArpConfiguration {
962 nud: Some(nud_config),
963 ..Default::default()
964 }),
965 ..Default::default()
966 })
967 }
968 IpVersion::V6 => {
969 config.ipv6 = Some(fnet_interfaces_admin::Ipv6Configuration {
970 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
971 nud: Some(nud_config),
972 ..Default::default()
973 }),
974 ..Default::default()
975 })
976 }
977 }
978 Ok(config)
979 }
980
981 fn try_into_i32(config: fnet_interfaces_admin::Configuration) -> Result<i32, Errno> {
982 let retrans_timer_ns = match I::VERSION {
983 IpVersion::V4 => config
984 .ipv4
985 .and_then(|ipv4| ipv4.arp)
986 .and_then(|arp| arp.nud)
987 .and_then(|nud| nud.retrans_timer)
988 .ok_or_else(|| {
989 log_error!("network interface config missing ipv4 arp retrans_timer");
990 errno!(EIO)
991 })?,
992 IpVersion::V6 => config
993 .ipv6
994 .and_then(|ipv6| ipv6.ndp)
995 .and_then(|ndp| ndp.nud)
996 .and_then(|nud| nud.retrans_timer)
997 .ok_or_else(|| {
998 log_error!("network interface config missing ipv6 ndp retrans_timer");
999 errno!(EIO)
1000 })?,
1001 };
1002 Ok(i32::try_from(
1003 zx::Duration::<zx::BootTimeline>::from_nanos(retrans_timer_ns).into_millis(),
1004 )
1005 .map_err(|_| errno!(EIO))?)
1006 }
1007}
1008
1009struct UseTempAddr;
1010
1011impl InterfaceConfig for UseTempAddr {
1012 fn try_from_i32(value: i32) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
1013 if value == 1 {
1025 log_warn!(
1026 "use_tempaddr=1 is not supported, treating it as enabled and we will prefer temporary addresses over public addresses"
1027 );
1028 }
1029 let use_tempaddr = value >= 1;
1030 Ok(fnet_interfaces_admin::Configuration {
1031 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1032 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
1033 slaac: Some(fnet_interfaces_admin::SlaacConfiguration {
1034 temporary_address: Some(use_tempaddr),
1035 ..Default::default()
1036 }),
1037 ..Default::default()
1038 }),
1039 ..Default::default()
1040 }),
1041 ..Default::default()
1042 })
1043 }
1044
1045 fn try_into_i32(
1046 config: fidl_fuchsia_net_interfaces_admin::Configuration,
1047 ) -> Result<i32, Errno> {
1048 config
1049 .ipv6
1050 .and_then(|ipv6| ipv6.ndp)
1051 .and_then(|ndp| ndp.slaac)
1052 .and_then(|slacc| slacc.temporary_address)
1053 .map(|use_tempaddr| if use_tempaddr { 2 } else { 0 })
1057 .ok_or_else(|| {
1058 log_error!("network interface config missing ipv6 ndp slacc temporary_address");
1059 errno!(EIO)
1060 })
1061 }
1062}
1063
1064struct AcceptRaDefrtr;
1065
1066impl InterfaceConfig for AcceptRaDefrtr {
1067 fn try_from_i32(value: i32) -> Result<fidl_fuchsia_net_interfaces_admin::Configuration, Errno> {
1068 Ok(fnet_interfaces_admin::Configuration {
1069 ipv6: Some(fnet_interfaces_admin::Ipv6Configuration {
1070 ndp: Some(fnet_interfaces_admin::NdpConfiguration {
1071 route_discovery: Some(fnet_interfaces_admin::RouteDiscoveryConfiguration {
1072 allow_default_route: Some(value != 0),
1073 ..Default::default()
1074 }),
1075 ..Default::default()
1076 }),
1077 ..Default::default()
1078 }),
1079 ..Default::default()
1080 })
1081 }
1082
1083 fn try_into_i32(
1084 config: fidl_fuchsia_net_interfaces_admin::Configuration,
1085 ) -> Result<i32, Errno> {
1086 config
1087 .ipv6
1088 .and_then(|ipv6| ipv6.ndp)
1089 .and_then(|ndp| ndp.route_discovery)
1090 .and_then(|route_discovery| route_discovery.allow_default_route)
1091 .map(|allow_default_route| i32::from(allow_default_route))
1092 .ok_or_else(|| {
1093 log_error!(
1094 "network interface config missing ipv6 ndp route_discovery allow_default_route"
1095 );
1096 errno!(EIO)
1097 })
1098 }
1099}
1100
1101pub struct TcpRmemFile;
1102
1103impl TcpRmemFile {
1104 pub fn new_node() -> impl FsNodeOps {
1105 BytesFile::new_node(Self)
1106 }
1107}
1108
1109impl BytesFileOps for TcpRmemFile {
1110 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1111 let mut params = std::str::from_utf8(&data)
1112 .map_err(|_| errno!(EINVAL))?
1113 .trim_ascii()
1114 .split_ascii_whitespace();
1115
1116 let min = params
1117 .next()
1118 .ok_or_else(|| errno!(EINVAL))?
1119 .parse::<u32>()
1120 .map_err(|_| errno!(EINVAL))?;
1121 let default = params
1122 .next()
1123 .ok_or_else(|| errno!(EINVAL))?
1124 .parse::<u32>()
1125 .map_err(|_| errno!(EINVAL))?;
1126 let max = params
1127 .next()
1128 .ok_or_else(|| errno!(EINVAL))?
1129 .parse::<u32>()
1130 .map_err(|_| errno!(EINVAL))?;
1131
1132 if params.next().is_some() {
1133 return error!(EINVAL);
1134 }
1135
1136 let control =
1137 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1138 log_error!(
1139 "failed to connect to {}: {:?}",
1140 fnet_settings::ControlMarker::PROTOCOL_NAME,
1141 err
1142 );
1143 errno!(EIO)
1144 })?;
1145
1146 let tcp_settings = fnet_settings::Tcp {
1147 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1148 receive: Some(fnet_settings::SocketBufferSizeRange {
1149 min: Some(min),
1150 default: Some(default),
1151 max: Some(max),
1152 ..Default::default()
1153 }),
1154 ..Default::default()
1155 }),
1156 ..Default::default()
1157 };
1158
1159 control
1160 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1161 .map_err(|err| {
1162 log_error!("failed to update tcp settings: {:?}", err);
1163 errno!(EIO)
1164 })?
1165 .map_err(map_update_error)?;
1166
1167 Ok(())
1168 }
1169
1170 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1171 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1172 log_error!(
1173 "failed to connect to {}: {:?}",
1174 fnet_settings::StateMarker::PROTOCOL_NAME,
1175 err
1176 );
1177 errno!(EIO)
1178 })?;
1179
1180 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1181 log_error!("failed to get tcp settings: {:?}", err);
1182 errno!(EIO)
1183 })?;
1184
1185 let receive_sizes =
1186 tcp_settings.buffer_sizes.and_then(|sizes| sizes.receive).ok_or_else(|| {
1187 log_error!("tcp settings missing receive buffer sizes");
1188 errno!(EIO)
1189 })?;
1190
1191 let min = receive_sizes.min.unwrap_or(0);
1192 let default = receive_sizes.default.unwrap_or(0);
1193 let max = receive_sizes.max.unwrap_or(0);
1194
1195 Ok(format!("{}\t{}\t{}\n", min, default, max).into_bytes().into())
1196 }
1197}
1198
1199pub struct RmemMaxFile;
1200
1201impl RmemMaxFile {
1202 pub fn new_node() -> impl FsNodeOps {
1203 BytesFile::new_node(Self)
1204 }
1205}
1206
1207fn map_update_error(err: fnet_settings::UpdateError) -> Errno {
1208 match err {
1209 fnet_settings::UpdateError::IllegalZeroValue
1210 | fnet_settings::UpdateError::IllegalNegativeValue => errno!(EINVAL),
1211 fnet_settings::UpdateError::OutOfRange => errno!(ERANGE),
1212 fnet_settings::UpdateError::NotSupported => errno!(ENOTSUP),
1213 fnet_settings::UpdateError::__SourceBreaking { unknown_ordinal } => {
1214 log_error!("unknown error with ordinal: {unknown_ordinal}");
1215 errno!(EIO)
1216 }
1217 }
1218}
1219
1220impl BytesFileOps for RmemMaxFile {
1221 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1222 let max = std::str::from_utf8(&data)
1223 .map_err(|_| errno!(EINVAL))?
1224 .trim_ascii()
1225 .parse::<u32>()
1226 .map_err(|_| errno!(EINVAL))?;
1227
1228 let control =
1229 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1230 log_error!(
1231 "failed to connect to {}: {:?}",
1232 fnet_settings::ControlMarker::PROTOCOL_NAME,
1233 err
1234 );
1235 errno!(EIO)
1236 })?;
1237
1238 let tcp_settings = fnet_settings::Tcp {
1239 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1240 receive: Some(fnet_settings::SocketBufferSizeRange {
1241 max: Some(max),
1242 ..Default::default()
1243 }),
1244 ..Default::default()
1245 }),
1246 ..Default::default()
1247 };
1248
1249 control
1250 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1251 .map_err(|err| {
1252 log_error!("failed to update tcp settings: {:?}", err);
1253 errno!(EIO)
1254 })?
1255 .map_err(map_update_error)?;
1256
1257 let udp_settings = fnet_settings::Udp {
1258 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1259 receive: Some(fnet_settings::SocketBufferSizeRange {
1260 max: Some(max),
1261 ..Default::default()
1262 }),
1263 ..Default::default()
1264 }),
1265 ..Default::default()
1266 };
1267
1268 control
1269 .update_udp(&udp_settings, zx::MonotonicInstant::INFINITE)
1270 .map_err(|err| {
1271 log_error!("failed to update udp settings: {:?}", err);
1272 errno!(EIO)
1273 })?
1274 .map_err(map_update_error)?;
1275
1276 let icmp_settings = fnet_settings::Icmp {
1277 echo_buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1278 receive: Some(fnet_settings::SocketBufferSizeRange {
1279 max: Some(max),
1280 ..Default::default()
1281 }),
1282 ..Default::default()
1283 }),
1284 ..Default::default()
1285 };
1286
1287 control
1288 .update_icmp(&icmp_settings, zx::MonotonicInstant::INFINITE)
1289 .map_err(|err| {
1290 log_error!("failed to update icmp settings: {:?}", err);
1291 errno!(EIO)
1292 })?
1293 .map_err(map_update_error)?;
1294
1295 Ok(())
1296 }
1297
1298 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1299 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1300 log_error!(
1301 "failed to connect to {}: {:?}",
1302 fnet_settings::StateMarker::PROTOCOL_NAME,
1303 err
1304 );
1305 errno!(EIO)
1306 })?;
1307
1308 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1311 log_error!("failed to get tcp settings: {:?}", err);
1312 errno!(EIO)
1313 })?;
1314
1315 let max = tcp_settings
1316 .buffer_sizes
1317 .and_then(|sizes| sizes.receive)
1318 .and_then(|sizes| sizes.max)
1319 .ok_or_else(|| {
1320 log_error!("tcp settings missing receive buffer sizes");
1321 errno!(EIO)
1322 })?;
1323
1324 Ok(format!("{}\n", max).into_bytes().into())
1325 }
1326}
1327
1328pub struct WmemMaxFile;
1329
1330impl WmemMaxFile {
1331 pub fn new_node() -> impl FsNodeOps {
1332 BytesFile::new_node(Self)
1333 }
1334}
1335
1336impl BytesFileOps for WmemMaxFile {
1337 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1338 let max = std::str::from_utf8(&data)
1339 .map_err(|_| errno!(EINVAL))?
1340 .trim_ascii()
1341 .parse::<u32>()
1342 .map_err(|_| errno!(EINVAL))?;
1343
1344 let control =
1345 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1346 log_error!(
1347 "failed to connect to {}: {:?}",
1348 fnet_settings::ControlMarker::PROTOCOL_NAME,
1349 err
1350 );
1351 errno!(EIO)
1352 })?;
1353
1354 let tcp_settings = fnet_settings::Tcp {
1355 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1356 send: Some(fnet_settings::SocketBufferSizeRange {
1357 max: Some(max),
1358 ..Default::default()
1359 }),
1360 ..Default::default()
1361 }),
1362 ..Default::default()
1363 };
1364
1365 control
1366 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1367 .map_err(|err| {
1368 log_error!("failed to update tcp settings: {:?}", err);
1369 errno!(EIO)
1370 })?
1371 .map_err(map_update_error)?;
1372
1373 let udp_settings = fnet_settings::Udp {
1374 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1375 send: Some(fnet_settings::SocketBufferSizeRange {
1376 max: Some(max),
1377 ..Default::default()
1378 }),
1379 ..Default::default()
1380 }),
1381 ..Default::default()
1382 };
1383
1384 control
1385 .update_udp(&udp_settings, zx::MonotonicInstant::INFINITE)
1386 .map_err(|err| {
1387 log_error!("failed to update udp settings: {:?}", err);
1388 errno!(EIO)
1389 })?
1390 .map_err(map_update_error)?;
1391
1392 let icmp_settings = fnet_settings::Icmp {
1393 echo_buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1394 send: Some(fnet_settings::SocketBufferSizeRange {
1395 max: Some(max),
1396 ..Default::default()
1397 }),
1398 ..Default::default()
1399 }),
1400 ..Default::default()
1401 };
1402
1403 control
1404 .update_icmp(&icmp_settings, zx::MonotonicInstant::INFINITE)
1405 .map_err(|err| {
1406 log_error!("failed to update icmp settings: {:?}", err);
1407 errno!(EIO)
1408 })?
1409 .map_err(map_update_error)?;
1410
1411 Ok(())
1412 }
1413
1414 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1415 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1416 log_error!(
1417 "failed to connect to {}: {:?}",
1418 fnet_settings::StateMarker::PROTOCOL_NAME,
1419 err
1420 );
1421 errno!(EIO)
1422 })?;
1423
1424 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1427 log_error!("failed to get tcp settings: {:?}", err);
1428 errno!(EIO)
1429 })?;
1430
1431 let max = tcp_settings
1432 .buffer_sizes
1433 .and_then(|sizes| sizes.send)
1434 .and_then(|sizes| sizes.max)
1435 .ok_or_else(|| {
1436 log_error!("tcp settings missing send buffer sizes");
1437 errno!(EIO)
1438 })?;
1439
1440 Ok(format!("{}\n", max).into_bytes().into())
1441 }
1442}