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