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 DirEntry, DirectoryEntryType, DirentSink, FileObject, FileOps, FsNode, FsNodeHandle, FsNodeOps,
22 FsStr, emit_dotdot, fileops_impl_directory, fileops_impl_noop_sync,
23 fileops_impl_unbounded_seek, 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 entry: &DirEntry,
152 current_task: &CurrentTask,
153 name: &FsStr,
154 ) -> Result<FsNodeHandle, Errno> {
155 if get_netstack_device(current_task, name).is_some() {
156 let fs = entry.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 entry: &DirEntry,
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 = entry.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 entry: &DirEntry,
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 = entry.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 entry: &DirEntry,
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 = entry.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 mut next_u32 = || {
1117 params.next().ok_or_else(|| errno!(EINVAL))?.parse::<u32>().map_err(|_| errno!(EINVAL))
1118 };
1119
1120 let min = next_u32()?;
1121 let default = next_u32()?;
1122 let max = next_u32()?;
1123
1124 if params.next().is_some() {
1125 return error!(EINVAL);
1126 }
1127
1128 let control =
1129 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1130 log_error!(
1131 "failed to connect to {}: {:?}",
1132 fnet_settings::ControlMarker::PROTOCOL_NAME,
1133 err
1134 );
1135 errno!(EIO)
1136 })?;
1137
1138 let tcp_settings = fnet_settings::Tcp {
1139 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1140 receive: Some(fnet_settings::SocketBufferSizeRange {
1141 min: Some(min),
1142 default: Some(default),
1143 max: Some(max),
1144 ..Default::default()
1145 }),
1146 ..Default::default()
1147 }),
1148 ..Default::default()
1149 };
1150
1151 control
1152 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1153 .map_err(|err| {
1154 log_error!("failed to update tcp settings: {:?}", err);
1155 errno!(EIO)
1156 })?
1157 .map_err(map_update_error)?;
1158
1159 Ok(())
1160 }
1161
1162 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1163 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1164 log_error!(
1165 "failed to connect to {}: {:?}",
1166 fnet_settings::StateMarker::PROTOCOL_NAME,
1167 err
1168 );
1169 errno!(EIO)
1170 })?;
1171
1172 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1173 log_error!("failed to get tcp settings: {:?}", err);
1174 errno!(EIO)
1175 })?;
1176
1177 let receive_sizes =
1178 tcp_settings.buffer_sizes.and_then(|sizes| sizes.receive).ok_or_else(|| {
1179 log_error!("tcp settings missing receive buffer sizes");
1180 errno!(EIO)
1181 })?;
1182
1183 let min = receive_sizes.min.unwrap_or(0);
1184 let default = receive_sizes.default.unwrap_or(0);
1185 let max = receive_sizes.max.unwrap_or(0);
1186
1187 Ok(format!("{}\t{}\t{}\n", min, default, max).into_bytes().into())
1188 }
1189}
1190
1191pub struct TcpWmemFile;
1192
1193impl TcpWmemFile {
1194 pub fn new_node() -> impl FsNodeOps {
1195 BytesFile::new_node(Self)
1196 }
1197}
1198
1199impl BytesFileOps for TcpWmemFile {
1200 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1201 let mut params = std::str::from_utf8(&data)
1202 .map_err(|_| errno!(EINVAL))?
1203 .trim_ascii()
1204 .split_ascii_whitespace();
1205
1206 let mut next_u32 = || {
1207 params.next().ok_or_else(|| errno!(EINVAL))?.parse::<u32>().map_err(|_| errno!(EINVAL))
1208 };
1209
1210 let min = next_u32()?;
1211 let default = next_u32()?;
1212 let max = next_u32()?;
1213
1214 if params.next().is_some() {
1215 return error!(EINVAL);
1216 }
1217
1218 let control =
1219 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1220 log_error!(
1221 "failed to connect to {}: {:?}",
1222 fnet_settings::ControlMarker::PROTOCOL_NAME,
1223 err
1224 );
1225 errno!(EIO)
1226 })?;
1227
1228 let tcp_settings = fnet_settings::Tcp {
1229 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1230 send: Some(fnet_settings::SocketBufferSizeRange {
1231 min: Some(min),
1232 default: Some(default),
1233 max: Some(max),
1234 ..Default::default()
1235 }),
1236 ..Default::default()
1237 }),
1238 ..Default::default()
1239 };
1240
1241 control
1242 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1243 .map_err(|err| {
1244 log_error!("failed to update tcp settings: {:?}", err);
1245 errno!(EIO)
1246 })?
1247 .map_err(map_update_error)?;
1248
1249 Ok(())
1250 }
1251
1252 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1253 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1254 log_error!(
1255 "failed to connect to {}: {:?}",
1256 fnet_settings::StateMarker::PROTOCOL_NAME,
1257 err
1258 );
1259 errno!(EIO)
1260 })?;
1261
1262 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1263 log_error!("failed to get tcp settings: {:?}", err);
1264 errno!(EIO)
1265 })?;
1266
1267 let send_sizes =
1268 tcp_settings.buffer_sizes.and_then(|sizes| sizes.send).ok_or_else(|| {
1269 log_error!("tcp settings missing send buffer sizes");
1270 errno!(EIO)
1271 })?;
1272
1273 let min = send_sizes.min.unwrap_or(0);
1274 let default = send_sizes.default.unwrap_or(0);
1275 let max = send_sizes.max.unwrap_or(0);
1276
1277 Ok(format!("{}\t{}\t{}\n", min, default, max).into_bytes().into())
1278 }
1279}
1280
1281pub struct RmemMaxFile;
1282
1283impl RmemMaxFile {
1284 pub fn new_node() -> impl FsNodeOps {
1285 BytesFile::new_node(Self)
1286 }
1287}
1288
1289fn map_update_error(err: fnet_settings::UpdateError) -> Errno {
1290 match err {
1291 fnet_settings::UpdateError::IllegalZeroValue
1292 | fnet_settings::UpdateError::IllegalNegativeValue => errno!(EINVAL),
1293 fnet_settings::UpdateError::OutOfRange => errno!(ERANGE),
1294 fnet_settings::UpdateError::NotSupported => errno!(ENOTSUP),
1295 fnet_settings::UpdateError::__SourceBreaking { unknown_ordinal } => {
1296 log_error!("unknown error with ordinal: {unknown_ordinal}");
1297 errno!(EIO)
1298 }
1299 }
1300}
1301
1302impl BytesFileOps for RmemMaxFile {
1303 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1304 let max = std::str::from_utf8(&data)
1305 .map_err(|_| errno!(EINVAL))?
1306 .trim_ascii()
1307 .parse::<u32>()
1308 .map_err(|_| errno!(EINVAL))?;
1309
1310 let control =
1311 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1312 log_error!(
1313 "failed to connect to {}: {:?}",
1314 fnet_settings::ControlMarker::PROTOCOL_NAME,
1315 err
1316 );
1317 errno!(EIO)
1318 })?;
1319
1320 let tcp_settings = fnet_settings::Tcp {
1321 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1322 receive: Some(fnet_settings::SocketBufferSizeRange {
1323 max: Some(max),
1324 ..Default::default()
1325 }),
1326 ..Default::default()
1327 }),
1328 ..Default::default()
1329 };
1330
1331 control
1332 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1333 .map_err(|err| {
1334 log_error!("failed to update tcp settings: {:?}", err);
1335 errno!(EIO)
1336 })?
1337 .map_err(map_update_error)?;
1338
1339 let udp_settings = fnet_settings::Udp {
1340 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1341 receive: Some(fnet_settings::SocketBufferSizeRange {
1342 max: Some(max),
1343 ..Default::default()
1344 }),
1345 ..Default::default()
1346 }),
1347 ..Default::default()
1348 };
1349
1350 control
1351 .update_udp(&udp_settings, zx::MonotonicInstant::INFINITE)
1352 .map_err(|err| {
1353 log_error!("failed to update udp settings: {:?}", err);
1354 errno!(EIO)
1355 })?
1356 .map_err(map_update_error)?;
1357
1358 let icmp_settings = fnet_settings::Icmp {
1359 echo_buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1360 receive: Some(fnet_settings::SocketBufferSizeRange {
1361 max: Some(max),
1362 ..Default::default()
1363 }),
1364 ..Default::default()
1365 }),
1366 ..Default::default()
1367 };
1368
1369 control
1370 .update_icmp(&icmp_settings, zx::MonotonicInstant::INFINITE)
1371 .map_err(|err| {
1372 log_error!("failed to update icmp settings: {:?}", err);
1373 errno!(EIO)
1374 })?
1375 .map_err(map_update_error)?;
1376
1377 Ok(())
1378 }
1379
1380 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1381 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1382 log_error!(
1383 "failed to connect to {}: {:?}",
1384 fnet_settings::StateMarker::PROTOCOL_NAME,
1385 err
1386 );
1387 errno!(EIO)
1388 })?;
1389
1390 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1393 log_error!("failed to get tcp settings: {:?}", err);
1394 errno!(EIO)
1395 })?;
1396
1397 let max = tcp_settings
1398 .buffer_sizes
1399 .and_then(|sizes| sizes.receive)
1400 .and_then(|sizes| sizes.max)
1401 .ok_or_else(|| {
1402 log_error!("tcp settings missing receive buffer sizes");
1403 errno!(EIO)
1404 })?;
1405
1406 Ok(format!("{}\n", max).into_bytes().into())
1407 }
1408}
1409
1410pub struct WmemMaxFile;
1411
1412impl WmemMaxFile {
1413 pub fn new_node() -> impl FsNodeOps {
1414 BytesFile::new_node(Self)
1415 }
1416}
1417
1418impl BytesFileOps for WmemMaxFile {
1419 fn write(&self, _current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
1420 let max = std::str::from_utf8(&data)
1421 .map_err(|_| errno!(EINVAL))?
1422 .trim_ascii()
1423 .parse::<u32>()
1424 .map_err(|_| errno!(EINVAL))?;
1425
1426 let control =
1427 connect_to_protocol_sync::<fnet_settings::ControlMarker>().map_err(|err| {
1428 log_error!(
1429 "failed to connect to {}: {:?}",
1430 fnet_settings::ControlMarker::PROTOCOL_NAME,
1431 err
1432 );
1433 errno!(EIO)
1434 })?;
1435
1436 let tcp_settings = fnet_settings::Tcp {
1437 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1438 send: Some(fnet_settings::SocketBufferSizeRange {
1439 max: Some(max),
1440 ..Default::default()
1441 }),
1442 ..Default::default()
1443 }),
1444 ..Default::default()
1445 };
1446
1447 control
1448 .update_tcp(&tcp_settings, zx::MonotonicInstant::INFINITE)
1449 .map_err(|err| {
1450 log_error!("failed to update tcp settings: {:?}", err);
1451 errno!(EIO)
1452 })?
1453 .map_err(map_update_error)?;
1454
1455 let udp_settings = fnet_settings::Udp {
1456 buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1457 send: Some(fnet_settings::SocketBufferSizeRange {
1458 max: Some(max),
1459 ..Default::default()
1460 }),
1461 ..Default::default()
1462 }),
1463 ..Default::default()
1464 };
1465
1466 control
1467 .update_udp(&udp_settings, zx::MonotonicInstant::INFINITE)
1468 .map_err(|err| {
1469 log_error!("failed to update udp settings: {:?}", err);
1470 errno!(EIO)
1471 })?
1472 .map_err(map_update_error)?;
1473
1474 let icmp_settings = fnet_settings::Icmp {
1475 echo_buffer_sizes: Some(fnet_settings::SocketBufferSizes {
1476 send: Some(fnet_settings::SocketBufferSizeRange {
1477 max: Some(max),
1478 ..Default::default()
1479 }),
1480 ..Default::default()
1481 }),
1482 ..Default::default()
1483 };
1484
1485 control
1486 .update_icmp(&icmp_settings, zx::MonotonicInstant::INFINITE)
1487 .map_err(|err| {
1488 log_error!("failed to update icmp settings: {:?}", err);
1489 errno!(EIO)
1490 })?
1491 .map_err(map_update_error)?;
1492
1493 Ok(())
1494 }
1495
1496 fn read(&self, _current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
1497 let state = connect_to_protocol_sync::<fnet_settings::StateMarker>().map_err(|err| {
1498 log_error!(
1499 "failed to connect to {}: {:?}",
1500 fnet_settings::StateMarker::PROTOCOL_NAME,
1501 err
1502 );
1503 errno!(EIO)
1504 })?;
1505
1506 let tcp_settings = state.get_tcp(zx::MonotonicInstant::INFINITE).map_err(|err| {
1509 log_error!("failed to get tcp settings: {:?}", err);
1510 errno!(EIO)
1511 })?;
1512
1513 let max = tcp_settings
1514 .buffer_sizes
1515 .and_then(|sizes| sizes.send)
1516 .and_then(|sizes| sizes.max)
1517 .ok_or_else(|| {
1518 log_error!("tcp settings missing send buffer sizes");
1519 errno!(EIO)
1520 })?;
1521
1522 Ok(format!("{}\n", max).into_bytes().into())
1523 }
1524}