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