1use crate::NetworkManager;
10use serde::{Deserialize, Serialize};
11use serde_repr::{Deserialize_repr, Serialize_repr};
12use starnix_core::security::check_task_capable;
13use starnix_core::task::{CurrentTask, Kernel};
14use starnix_core::vfs::fs_args::parse;
15use starnix_core::vfs::pseudo::simple_file::{BytesFile, BytesFileOps};
16use starnix_core::vfs::{
17 CacheMode, FileOps, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions, FsNode,
18 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, MemoryDirectoryFile,
19};
20use starnix_logging::{log_error, log_warn};
21
22use starnix_types::vfs::default_statfs;
23use starnix_uapi::auth::{CAP_NET_ADMIN, FsCred};
24use starnix_uapi::device_id::DeviceId;
25use starnix_uapi::errors::Errno;
26use starnix_uapi::file_mode::{FileMode, mode};
27use starnix_uapi::open_flags::OpenFlags;
28use starnix_uapi::{errno, error, gid_t, statfs, uid_t};
29use std::borrow::Cow;
30use std::net::{Ipv4Addr, Ipv6Addr};
31use std::sync::Arc;
32
33use fidl_fuchsia_net as fnet;
34use fidl_fuchsia_net_policy_socketproxy::{self as fnp_socketproxy, ProtoProperties};
35
36const DEFAULT_NETWORK_FILE_NAME: &str = "default";
37
38#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
39pub(crate) struct NetworkMessage {
40 pub(crate) netid: u32,
41 pub(crate) mark: u32,
42 pub(crate) handle: u64,
43 #[serde(with = "addr_list")]
44 pub(crate) dnsv4: Vec<Ipv4Addr>,
45 #[serde(with = "addr_list")]
46 pub(crate) dnsv6: Vec<Ipv6Addr>,
47 #[serde(flatten)]
48 pub(crate) versioned_properties: VersionedProperties,
49}
50
51#[derive(Clone, Copy, Debug, Deserialize_repr, PartialEq, Serialize_repr)]
52#[repr(u8)]
53pub(crate) enum TransportType {
54 Cellular = 0,
55 Wifi = 1,
56 Bluetooth = 2,
57 Ethernet = 3,
58 Vpn = 4,
59 WifiAware = 5,
60 Lowpan = 6,
61}
62
63#[derive(Clone, Copy, Debug, Deserialize_repr, PartialEq, Serialize_repr)]
64#[repr(u8)]
65pub(crate) enum NetworkCapability {
66 Mms = 0,
67 Supl = 1,
68 Dun = 2,
69 Fota = 3,
70 Ims = 4,
71 Cbs = 5,
72 WifiP2p = 6,
73 Ia = 7,
74 Rcs = 8,
75 Xcap = 9,
76 Eims = 10,
77 NotMetered = 11,
78 Internet = 12,
79 NotRestricted = 13,
80 Trusted = 14,
81 NotVpn = 15,
82 Validated = 16,
83 CaptivePortal = 17,
84 NotRoaming = 18,
85 Foreground = 19,
86 NotCongested = 20,
87 NotSuspended = 21,
88 OemPaid = 22,
89 Mcs = 23,
90 PartialConnectivity = 24,
91 TemporarilyNotMetered = 25,
92 OemPrivate = 26,
93 VehicleInternal = 27,
94 NotVcnManaged = 28,
95 Enterprise = 29,
96 Vsim = 30,
97 Bip = 31,
98 HeadUnit = 32,
99 Mmtel = 33,
100 PrioritizeLatency = 34,
101 PrioritizeBandwidth = 35,
102 LocalNetwork = 36,
103 NotBandwidthConstrained = 37,
104 PrioritizeUnifiedCommunications = 38,
105}
106
107#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
108#[serde(tag = "version")]
109pub(crate) enum VersionedProperties {
110 #[default]
111 V1,
112 V2 {
113 #[serde(with = "transport_list")]
114 transports: Vec<TransportType>,
115 #[serde(with = "capability_list")]
116 capabilities: Vec<NetworkCapability>,
117 name: String,
118 addrv4: bool,
121 addrv6: bool,
122 defaultv4: bool,
125 defaultv6: bool,
126 },
127}
128
129pub struct FuchsiaNetworkMonitorFs;
130impl FuchsiaNetworkMonitorFs {
131 pub fn new_fs(kernel: &Kernel, options: FileSystemOptions) -> Result<FileSystemHandle, Errno> {
132 let mode = if let Some(mode) = options.params.get(b"mode") {
133 FileMode::from_string(mode.as_ref())?.with_type(FileMode::IFDIR)
134 } else {
135 mode!(IFDIR, 0o600)
136 };
137 let uid = options.params.get_as::<uid_t>(b"uid")?.unwrap_or(0);
138 let gid = options.params.get_as::<gid_t>(b"gid")?.unwrap_or(0);
139
140 let fs = FileSystem::new(kernel, CacheMode::Permanent, FuchsiaNetworkMonitorFs, options)?;
141 let root_ino = fs.allocate_ino();
142 let info = FsNodeInfo::new(mode, FsCred { uid, gid });
143 fs.create_root_with_info(root_ino, NetworkDirectoryNode::new(), info);
144 Ok(fs)
145 }
146}
147
148const FUCHSIA_NETWORK_MONITOR_FS_NAME: &[u8; 26] = b"fuchsia_network_monitor_fs";
149const FUCHSIA_NETWORK_MONITOR_FS_MAGIC: u32 = u32::from_be_bytes(*b"nmfs");
150
151impl FileSystemOps for FuchsiaNetworkMonitorFs {
152 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
153 Ok(default_statfs(FUCHSIA_NETWORK_MONITOR_FS_MAGIC))
154 }
155
156 fn name(&self) -> &'static FsStr {
157 FUCHSIA_NETWORK_MONITOR_FS_NAME.into()
158 }
159}
160
161pub fn fuchsia_network_monitor_fs(
162 current_task: &CurrentTask,
163 options: FileSystemOptions,
164) -> Result<FileSystemHandle, Errno> {
165 struct FuchsiaNetworkMonitorFsHandle(FileSystemHandle);
166
167 let kernel = current_task.kernel();
168 Ok(kernel
169 .expando
170 .get_or_try_init(|| {
171 FuchsiaNetworkMonitorFs::new_fs(kernel, options)
172 .map(|fs| FuchsiaNetworkMonitorFsHandle(fs))
173 })?
174 .0
175 .clone())
176}
177
178fn try_acquire_network_manager(current_task: &CurrentTask) -> Result<Arc<NetworkManager>, Errno> {
183 let kernel = current_task.kernel();
184 kernel.expando.peek::<NetworkManager>().ok_or_else(|| errno!(EPERM))
185}
186
187pub struct NetworkDirectoryNode;
188
189impl NetworkDirectoryNode {
190 pub fn new() -> Self {
191 Self
192 }
193}
194
195impl FsNodeOps for NetworkDirectoryNode {
196 fn create_file_ops(
197 &self,
198 _node: &FsNode,
199 _current_task: &CurrentTask,
200 _flags: OpenFlags,
201 ) -> Result<Box<dyn FileOps>, Errno> {
202 Ok(Box::new(MemoryDirectoryFile::new()))
203 }
204
205 fn mkdir(
206 &self,
207 _node: &FsNode,
208 _current_task: &CurrentTask,
209 _name: &FsStr,
210 _mode: FileMode,
211 _owner: FsCred,
212 ) -> Result<FsNodeHandle, Errno> {
213 error!(EPERM)
214 }
215
216 fn mknod(
217 &self,
218 node: &FsNode,
219 current_task: &CurrentTask,
220 name: &FsStr,
221 mode: FileMode,
222 _dev: DeviceId,
223 _owner: FsCred,
224 ) -> Result<FsNodeHandle, Errno> {
225 check_task_capable(current_task, CAP_NET_ADMIN)?;
226 if !mode.is_reg() {
227 return error!(EACCES);
228 }
229
230 let ops: Box<dyn FsNodeOps> = if name == DEFAULT_NETWORK_FILE_NAME {
231 Box::new(DefaultNetworkIdFile::new_node())
234 } else {
235 let id: u32 = parse(name).map_err(|_| errno!(EINVAL))?;
236 let network_manager = try_acquire_network_manager(current_task)?;
238 network_manager.add_empty_network(id)?;
241 Box::new(NetworkFile::new_node(id))
242 };
243
244 let child = node.fs().create_node_and_allocate_node_id(
245 ops,
246 FsNodeInfo::new(mode, current_task.current_fscred()),
247 );
248
249 Ok(child)
250 }
251
252 fn unlink(
253 &self,
254 _node: &FsNode,
255 current_task: &CurrentTask,
256 name: &FsStr,
257 _child: &FsNodeHandle,
258 ) -> Result<(), Errno> {
259 check_task_capable(current_task, CAP_NET_ADMIN)?;
260 let network_manager = try_acquire_network_manager(current_task)?;
261 if name == DEFAULT_NETWORK_FILE_NAME {
264 network_manager.set_default_network_id(None);
267 } else {
268 let id: u32 = parse(name)?;
269 network_manager.remove_network(id)?;
270 }
271
272 Ok(())
273 }
274
275 fn create_symlink(
276 &self,
277 _node: &FsNode,
278 _current_task: &CurrentTask,
279 _name: &FsStr,
280 _target: &FsStr,
281 _owner: FsCred,
282 ) -> Result<FsNodeHandle, Errno> {
283 error!(EPERM)
284 }
285}
286
287pub struct NetworkFile {
288 network_id: u32,
289}
290
291impl NetworkFile {
292 pub fn new_node(network_id: u32) -> impl FsNodeOps {
293 BytesFile::new_node(Self { network_id })
294 }
295}
296
297impl BytesFileOps for NetworkFile {
298 fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
299 check_task_capable(current_task, CAP_NET_ADMIN)?;
300 let network: NetworkMessage = serde_json::from_slice(&data).map_err(|e| {
301 log_error!("failed to deserialize network message: {}", e);
302 errno!(EINVAL)
303 })?;
304
305 let new_netid = network.netid;
306
307 if new_netid != self.network_id {
309 return error!(EINVAL);
310 }
311
312 let network_manager = try_acquire_network_manager(current_task)?;
313 match network_manager.get_network(&new_netid) {
314 None | Some(None) => {
315 network_manager.add_network(network)?;
316 }
317 Some(Some(_old_network)) => {
318 network_manager.update_network(network)?;
319 }
320 }
321
322 Ok(())
323 }
324
325 fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
326 let network_manager = try_acquire_network_manager(current_task)?;
327 if let None = network_manager.get_network(&self.network_id) {
329 return error!(ENOENT);
330 }
331
332 Ok(network_manager.get_network_by_id_as_bytes(self.network_id).to_vec().into())
333 }
334}
335
336pub struct DefaultNetworkIdFile {}
337
338impl DefaultNetworkIdFile {
339 pub fn new_node() -> impl FsNodeOps {
340 BytesFile::new_node(Self {})
341 }
342}
343
344impl BytesFileOps for DefaultNetworkIdFile {
345 fn write(&self, current_task: &CurrentTask, data: Vec<u8>) -> Result<(), Errno> {
346 check_task_capable(current_task, CAP_NET_ADMIN)?;
347 let id_string = std::str::from_utf8(&data).map_err(|_| errno!(EINVAL))?;
348 let id: u32 = id_string.parse().map_err(|_| errno!(EINVAL))?;
349
350 {
351 let network_manager = try_acquire_network_manager(current_task)?;
352 match network_manager.get_network(&id) {
353 Some(Some(_)) => {
356 network_manager.set_default_network_id(Some(id));
357 }
358 Some(None) | None => return error!(ENOENT),
361 };
362 }
363 Ok(())
364 }
365
366 fn read(&self, current_task: &CurrentTask) -> Result<Cow<'_, [u8]>, Errno> {
367 let network_manager = try_acquire_network_manager(current_task)?;
368 if let None = network_manager.get_default_network_id() {
369 return error!(ENOENT);
370 }
371
372 Ok(network_manager.get_default_id_as_bytes().to_vec().into())
373 }
374}
375
376impl From<&NetworkMessage> for fnp_socketproxy::Network {
377 fn from(message: &NetworkMessage) -> Self {
378 let (transports, capabilities, name, addrv4, addrv6, defaultv4, defaultv6) =
379 match &message.versioned_properties {
380 VersionedProperties::V1 => (None, None, None, None, None, None, None),
381 VersionedProperties::V2 {
382 transports,
383 capabilities,
384 name,
385 addrv4,
386 addrv6,
387 defaultv4,
388 defaultv6,
389 } => (
390 Some(transports),
391 Some(capabilities),
392 Some(name),
393 Some(addrv4),
394 Some(addrv6),
395 Some(defaultv4),
396 Some(defaultv6),
397 ),
398 };
399
400 Self {
401 network_id: Some(message.netid),
402 info: Some(fnp_socketproxy::NetworkInfo::Starnix(
403 fnp_socketproxy::StarnixNetworkInfo {
404 mark: Some(message.mark),
405 handle: Some(message.handle),
406 ..Default::default()
407 },
408 )),
409 dns_servers: Some(fnp_socketproxy::NetworkDnsServers {
410 v4: Some(
411 message
412 .dnsv4
413 .clone()
414 .into_iter()
415 .map(|a| fnet::Ipv4Address { addr: a.octets() })
416 .collect::<Vec<_>>(),
417 ),
418 v6: Some(
419 message
420 .dnsv6
421 .clone()
422 .into_iter()
423 .map(|a| fnet::Ipv6Address { addr: a.octets() })
424 .collect::<Vec<_>>(),
425 ),
426 ..Default::default()
427 }),
428 network_type: transports
429 .map(|transport_list| consolidate_transport_types_to_network_type(transport_list)),
430 capabilities: capabilities.map(|capability_list| convert_capabilities(capability_list)),
431 connectivity: capabilities
432 .map(|capability_list| convert_connectivity_state(capability_list)),
433 name: name.cloned(),
434 has_address: Some(proto_properties_from_v4_v6(addrv4.copied(), addrv6.copied())),
435 has_default_route: Some(proto_properties_from_v4_v6(
436 defaultv4.copied(),
437 defaultv6.copied(),
438 )),
439 ..Default::default()
440 }
441 }
442}
443
444fn proto_properties_from_v4_v6(v4: Option<bool>, v6: Option<bool>) -> ProtoProperties {
445 let mut properties = ProtoProperties::empty();
446
447 if Some(true) == v4 {
448 properties |= ProtoProperties::V4;
449 }
450
451 if Some(true) == v6 {
452 properties |= ProtoProperties::V6;
453 }
454 properties
455}
456
457fn consolidate_transport_types_to_network_type(
461 value: &Vec<TransportType>,
462) -> fnp_socketproxy::NetworkType {
463 value
464 .iter()
465 .filter_map(|tt| maybe_network_type_from_transport_type(*tt))
466 .next()
467 .unwrap_or(fnp_socketproxy::NetworkType::Unknown)
468}
469
470fn maybe_network_type_from_transport_type(
471 value: TransportType,
472) -> Option<fnp_socketproxy::NetworkType> {
473 match value {
474 TransportType::Ethernet => Some(fnp_socketproxy::NetworkType::Ethernet),
475 TransportType::Wifi => Some(fnp_socketproxy::NetworkType::Wifi),
476 TransportType::Bluetooth => Some(fnp_socketproxy::NetworkType::Bluetooth),
477 TransportType::Cellular => Some(fnp_socketproxy::NetworkType::Cellular),
478 TransportType::Vpn | TransportType::WifiAware | TransportType::Lowpan => {
479 log_warn!("Known NetworkType {value:?} is not currently supported");
480 None
481 }
482 }
483}
484
485fn convert_capabilities(value: &Vec<NetworkCapability>) -> Vec<fnp_socketproxy::NetworkCapability> {
489 value
490 .iter()
491 .filter_map(|c| match c {
492 NetworkCapability::NotMetered => Some(fnp_socketproxy::NetworkCapability::UNMETERED),
493 NetworkCapability::NotCongested => {
494 Some(fnp_socketproxy::NetworkCapability::UNCONGESTED)
495 }
496 NetworkCapability::NotBandwidthConstrained => {
497 Some(fnp_socketproxy::NetworkCapability::NOT_BANDWIDTH_CONSTRAINED)
498 }
499 _ => None,
500 })
501 .collect::<Vec<_>>()
502}
503
504fn convert_connectivity_state(
505 value: &Vec<NetworkCapability>,
506) -> fnp_socketproxy::ConnectivityState {
507 if value.contains(&NetworkCapability::Validated) && value.contains(&NetworkCapability::Internet)
510 {
511 return fnp_socketproxy::ConnectivityState::FullConnectivity;
512 }
513
514 if value.contains(&NetworkCapability::PartialConnectivity) {
515 return fnp_socketproxy::ConnectivityState::PartialConnectivity;
516 }
517
518 if value.contains(&NetworkCapability::LocalNetwork) {
519 return fnp_socketproxy::ConnectivityState::LocalConnectivity;
520 }
521
522 return fnp_socketproxy::ConnectivityState::NoConnectivity;
523}
524
525mod addr_list {
526 use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
527
528 pub fn serialize<S, Addr>(addr: &Vec<Addr>, serializer: S) -> Result<S::Ok, S::Error>
529 where
530 S: Serializer,
531 Addr: std::fmt::Display,
532 {
533 let strings = addr.iter().map(|x| x.to_string()).collect::<Vec<_>>();
534 strings.serialize(serializer)
535 }
536
537 pub fn deserialize<'de, D, Addr>(deserializer: D) -> Result<Vec<Addr>, D::Error>
538 where
539 D: Deserializer<'de>,
540 Addr: std::str::FromStr,
541 Addr::Err: std::fmt::Display,
542 {
543 let s = Vec::<String>::deserialize(deserializer)?;
544 s.into_iter().map(|s| s.parse().map_err(de::Error::custom)).collect()
545 }
546}
547
548macro_rules! tolerant_repr_serde_impl {
549 ($module_name:ident, $NewType:path) => {
550 mod $module_name {
551 use serde::{Deserialize, Deserializer, Serialize, Serializer};
552 use starnix_logging::log_error;
553
554 pub fn serialize<S>(items: &Vec<$NewType>, serializer: S) -> Result<S::Ok, S::Error>
555 where
556 S: Serializer,
557 {
558 let nums = items.iter().map(|item| *item as u8).collect::<Vec<_>>();
559 nums.serialize(serializer)
560 }
561
562 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<$NewType>, D::Error>
563 where
564 D: Deserializer<'de>,
565 {
566 let vals = Vec::<serde_json::Value>::deserialize(deserializer)?;
567 Ok(vals
568 .into_iter()
569 .filter_map(|val| match serde_json::from_value::<$NewType>(val.clone()) {
570 Ok(s) => Some(s),
571 Err(_) => {
572 log_error!("unknown value: {}", val);
573 None
574 }
575 })
576 .collect())
577 }
578 }
579 };
580}
581
582tolerant_repr_serde_impl!(transport_list, crate::TransportType);
583tolerant_repr_serde_impl!(capability_list, crate::NetworkCapability);
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use net_declare::{std_ip_v4, std_ip_v6};
589 use starnix_core::testing::spawn_kernel_and_run;
590 use starnix_core::vfs::fs_args::MountParams;
591 use test_case::test_case;
592
593 #[::fuchsia::test]
594 fn network_message_serde() {
595 let network = NetworkMessage {
596 netid: 123,
597 mark: 456,
598 handle: 789,
599 dnsv4: vec![std_ip_v4!("192.168.0.1")],
600 dnsv6: vec![std_ip_v6!("2001:db8::1")],
601 versioned_properties: VersionedProperties::V1,
602 };
603 serde_helper(network);
604 }
605
606 #[::fuchsia::test]
607 fn network_message_serde_version2() {
608 let network = NetworkMessage {
609 netid: 123,
610 mark: 456,
611 handle: 789,
612 dnsv4: vec![std_ip_v4!("192.168.0.1")],
613 dnsv6: vec![std_ip_v6!("2001:db8::1")],
614 versioned_properties: VersionedProperties::V2 {
615 transports: vec![
616 TransportType::Cellular,
617 TransportType::Wifi,
618 TransportType::Bluetooth,
619 TransportType::Ethernet,
620 TransportType::Vpn,
621 TransportType::WifiAware,
622 TransportType::Lowpan,
623 ],
624 capabilities: vec![
625 NetworkCapability::Trusted,
626 NetworkCapability::Internet,
627 NetworkCapability::Validated,
628 ],
629 name: "test01".to_string(),
630 addrv4: false,
631 addrv6: true,
632 defaultv4: false,
633 defaultv6: true,
634 },
635 };
636 serde_helper(network);
637 }
638
639 #[::fuchsia::test]
640 fn network_message_serde_unknown_transports_and_capabilities() {
641 let json = r#"{
642 "netid": 123,
643 "mark": 456,
644 "handle": 789,
645 "dnsv4": ["192.168.0.1"],
646 "dnsv6": ["2001:db8::1"],
647 "version": "V2",
648 "transports": [1, 99, 3],
649 "capabilities": [11, 99],
650 "name": "test01",
651 "addrv4": false,
652 "addrv6": false,
653 "defaultv4": false,
654 "defaultv6": false
655 }"#;
656
657 let expected = NetworkMessage {
658 netid: 123,
659 mark: 456,
660 handle: 789,
661 dnsv4: vec![std_ip_v4!("192.168.0.1")],
662 dnsv6: vec![std_ip_v6!("2001:db8::1")],
663 versioned_properties: VersionedProperties::V2 {
664 transports: vec![TransportType::Wifi, TransportType::Ethernet],
665 capabilities: vec![NetworkCapability::NotMetered],
666 name: "test01".to_string(),
667 addrv4: false,
668 addrv6: false,
669 defaultv4: false,
670 defaultv6: false,
671 },
672 };
673
674 let deserialized: NetworkMessage = serde_json::from_str(json).unwrap();
675 assert_eq!(expected, deserialized);
676 }
677
678 fn serde_helper(network: NetworkMessage) {
681 let serialized = serde_json::to_string(&network).unwrap_or_else(|_| "{}".to_string());
682 let deserialized = serde_json::from_str(&serialized).unwrap();
683 assert_eq!(network, deserialized);
684 }
685
686 #[test_case(vec![TransportType::Cellular], fnp_socketproxy::NetworkType::Cellular;
687 "cellular")]
688 #[test_case(vec![TransportType::Wifi], fnp_socketproxy::NetworkType::Wifi;
689 "wifi")]
690 #[test_case(vec![TransportType::Bluetooth], fnp_socketproxy::NetworkType::Bluetooth;
691 "bluetooth")]
692 #[test_case(vec![TransportType::Ethernet], fnp_socketproxy::NetworkType::Ethernet;
693 "ethernet")]
694 #[test_case(
695 vec![
696 TransportType::Wifi,
697 TransportType::Cellular
698 ],
699 fnp_socketproxy::NetworkType::Wifi; "first_transport_prioritized")]
700 #[test_case(vec![], fnp_socketproxy::NetworkType::Unknown; "empty_fallback")]
701 #[test_case(
702 vec![
703 TransportType::Lowpan,
704 TransportType::WifiAware,
705 TransportType::Vpn
706 ],
707 fnp_socketproxy::NetworkType::Unknown; "none_applicable")]
708 #[::fuchsia::test]
709 fn test_consolidate_transport_types_to_network_type(
710 transports: Vec<TransportType>,
711 expected: fnp_socketproxy::NetworkType,
712 ) {
713 assert_eq!(consolidate_transport_types_to_network_type(&transports), expected);
714 }
715
716 #[test_case(
717 vec![
718 NetworkCapability::NotMetered,
719 NetworkCapability::NotCongested,
720 NetworkCapability::NotBandwidthConstrained,
721 ],
722 vec![
723 fnp_socketproxy::NetworkCapability::UNMETERED,
724 fnp_socketproxy::NetworkCapability::UNCONGESTED,
725 fnp_socketproxy::NetworkCapability::NOT_BANDWIDTH_CONSTRAINED,
726 ];
727 "all_capabilities"
728 )]
729 #[test_case(
730 vec![NetworkCapability::NotMetered],
731 vec![fnp_socketproxy::NetworkCapability::UNMETERED];
732 "one_capability"
733 )]
734 #[test_case(
735 vec![],
736 vec![];
737 "no_capabilities"
738 )]
739 #[test_case(
740 vec![
741 NetworkCapability::NotMetered,
742 NetworkCapability::Trusted, ],
744 vec![fnp_socketproxy::NetworkCapability::UNMETERED];
745 "ignore_unrelated"
746 )]
747 #[::fuchsia::test]
748 fn test_convert_capabilities(
749 capabilities: Vec<NetworkCapability>,
750 expected: Vec<fnp_socketproxy::NetworkCapability>,
751 ) {
752 let mut result = convert_capabilities(&capabilities);
753 result.sort();
754 let mut expected_sorted = expected;
755 expected_sorted.sort();
756 assert_eq!(result, expected_sorted);
757 }
758
759 #[test_case(
760 vec![
761 NetworkCapability::Validated,
762 NetworkCapability::Internet,
763 ],
764 fnp_socketproxy::ConnectivityState::FullConnectivity;
765 "full_connectivity"
766 )]
767 #[test_case(
768 vec![
769 NetworkCapability::Validated,
770 NetworkCapability::Internet,
771 NetworkCapability::LocalNetwork,
772 ],
773 fnp_socketproxy::ConnectivityState::FullConnectivity;
774 "full_connectivity_prioritized"
775 )]
776 #[test_case(
777 vec![NetworkCapability::PartialConnectivity],
778 fnp_socketproxy::ConnectivityState::PartialConnectivity;
779 "partial_connectivity"
780 )]
781 #[test_case(
782 vec![NetworkCapability::LocalNetwork],
783 fnp_socketproxy::ConnectivityState::LocalConnectivity;
784 "local_connectivity"
785 )]
786 #[test_case(
787 vec![],
788 fnp_socketproxy::ConnectivityState::NoConnectivity;
789 "no_connectivity"
790 )]
791 #[test_case(
792 vec![NetworkCapability::Trusted], fnp_socketproxy::ConnectivityState::NoConnectivity;
794 "ignore_unrelated"
795 )]
796 #[::fuchsia::test]
797 fn test_convert_connectivity_state(
798 capabilities: Vec<NetworkCapability>,
799 expected: fnp_socketproxy::ConnectivityState,
800 ) {
801 assert_eq!(convert_connectivity_state(&capabilities), expected);
802 }
803
804 #[::fuchsia::test]
805 async fn test_mode_option() {
806 spawn_kernel_and_run(async |current_task| {
807 let kernel = current_task.kernel();
808 let fs = FuchsiaNetworkMonitorFs::new_fs(
809 &kernel,
810 FileSystemOptions {
811 params: MountParams::parse(b"mode=0123,uid=1000,gid=2000".into())
812 .expect("parsed correctly"),
813 ..Default::default()
814 },
815 )
816 .expect("new_fs");
817 {
818 let info = fs.root().node.info();
819 assert_eq!(info.mode, mode!(IFDIR, 0o123));
820 assert_eq!(info.uid, 1000);
821 assert_eq!(info.gid, 2000);
822 }
823
824 let fs = FuchsiaNetworkMonitorFs::new_fs(&kernel, FileSystemOptions::default())
826 .expect("new_fs");
827 {
828 let info = fs.root().node.info();
829 assert_eq!(info.mode, mode!(IFDIR, 0o600));
830 assert_eq!(info.uid, 0);
831 assert_eq!(info.gid, 0);
832 }
833 })
834 .await;
835 }
836}