| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| anyhow::Error, |
| fidl_fuchsia_net::{ |
| IpAddress::{Ipv4, Ipv6}, |
| Ipv4Address, Ipv6Address, |
| }, |
| fidl_fuchsia_net_stack::{InterfaceAddress, StackProxy}, |
| net_types::ip::{Ipv4Addr, Ipv6Addr}, |
| }; |
| |
| pub async fn netstack_did_get_dhcp( |
| network_svc: &StackProxy, |
| mac_addr: &[u8; 6], |
| ) -> Result<bool, Error> { |
| Ok(network_svc |
| .list_interfaces() |
| .await? |
| .into_iter() |
| .map(|ni| ni.properties) |
| .filter(|p| p.mac.as_ref().map_or(false, |mac| mac.octets == *mac_addr)) |
| .map(|p| p.addresses) |
| .flatten() |
| .any(dhcp_ip_filter)) |
| } |
| |
| fn valid_ip_filter<A: net_types::ip::IpAddress>(addr: &A) -> bool { |
| use net_types::{MulticastAddress, SpecifiedAddress}; |
| !(addr.is_linklocal() || !addr.is_specified() || addr.is_multicast() || addr.is_loopback()) |
| } |
| |
| pub fn dhcp_ip_filter(ip_addr: InterfaceAddress) -> bool { |
| fuchsia_syslog::fx_log_info!("checking validity of ip address: {:?}", ip_addr); |
| match ip_addr.ip_address { |
| Ipv4(Ipv4Address { addr }) => valid_ip_filter(&Ipv4Addr::new(addr)), |
| Ipv6(Ipv6Address { addr }) => valid_ip_filter(&Ipv6Addr::new(addr)), |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use { |
| super::*, |
| fidl_fuchsia_net_stack::StackMarker, |
| fuchsia_async::Executor, |
| futures::{stream::StreamExt, task::Poll}, |
| pin_utils::pin_mut, |
| wlan_common::assert_variant, |
| }; |
| |
| // helper values for tests |
| const TEST_IPV4_ADDR_VALID: [u8; 4] = [1; 4]; |
| const TEST_IPV4_ALL_ZEROS: [u8; 4] = [0; 4]; |
| const TEST_IPV6_ADDR_VALID: [u8; 16] = [0x1; 16]; |
| const TEST_IPV6_ALL_ZEROS: [u8; 16] = [0; 16]; |
| const TEST_IPV6_LINK_LOCAL: [u8; 16] = [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; |
| const TEST_IPV6_MULTICAST: [u8; 16] = [0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; |
| |
| /// Test to verify a valid ipv4 addr is assigned to an interface. In the current |
| /// implementation, only empty vectors or all zeros are considered to be invalid or unset. |
| #[test] |
| fn test_single_ipv4_addr_ok() { |
| let ipv4_addr = InterfaceAddress { |
| ip_address: Ipv4(Ipv4Address { addr: TEST_IPV4_ADDR_VALID }), |
| prefix_len: 0, |
| }; |
| assert!(dhcp_ip_filter(ipv4_addr)); |
| } |
| |
| /// Test to verify a valid ipv6 addr is assigned to an interface. In the current |
| /// implementation, only empty vectors or all zeros are considered to be invalid or unset. |
| #[test] |
| fn test_single_ipv6_addr_ok() { |
| let ipv6_addr = InterfaceAddress { |
| ip_address: Ipv6(Ipv6Address { addr: TEST_IPV6_ADDR_VALID }), |
| prefix_len: 0, |
| }; |
| assert!(dhcp_ip_filter(ipv6_addr)); |
| } |
| |
| /// IPv4 addresses that are all zeros are considered invalid and should return false when |
| /// chacked. |
| #[test] |
| fn test_single_ipv4_addr_all_zeros_invalid() { |
| let ipv4_addr = InterfaceAddress { |
| ip_address: Ipv4(Ipv4Address { addr: TEST_IPV4_ALL_ZEROS }), |
| prefix_len: 0, |
| }; |
| assert_eq!(dhcp_ip_filter(ipv4_addr), false); |
| } |
| |
| /// IPv6 addresses that are all zeros are considered invalid and should return false when |
| /// checked. |
| #[test] |
| fn test_single_ipv6_addr_all_zeros_invalid() { |
| let ipv6_addr = InterfaceAddress { |
| ip_address: Ipv6(Ipv6Address { addr: TEST_IPV6_ALL_ZEROS }), |
| prefix_len: 0, |
| }; |
| assert_eq!(dhcp_ip_filter(ipv6_addr), false); |
| } |
| |
| #[test] |
| fn test_single_ipv6_addr_link_local_invalid() { |
| let ipv6_addr = InterfaceAddress { |
| ip_address: Ipv6(Ipv6Address { addr: TEST_IPV6_LINK_LOCAL }), |
| prefix_len: 0, |
| }; |
| assert_eq!(dhcp_ip_filter(ipv6_addr), false); |
| } |
| |
| #[test] |
| fn test_single_ipv6_addr_multicast_invalid() { |
| let ipv6_addr = InterfaceAddress { |
| ip_address: Ipv6(Ipv6Address { addr: TEST_IPV6_MULTICAST }), |
| prefix_len: 0, |
| }; |
| assert_eq!(dhcp_ip_filter(ipv6_addr), false); |
| } |
| |
| fn send_fake_list_iface_response( |
| exec: &mut Executor, |
| req_stream: &mut fidl_fuchsia_net_stack::StackRequestStream, |
| mut iface_info_list: Vec<fidl_fuchsia_net_stack::InterfaceInfo>, |
| ) { |
| use fidl_fuchsia_net_stack::StackRequest; |
| let req = exec.run_until_stalled(&mut req_stream.next()); |
| let responder = assert_variant!( |
| req, Poll::Ready(Some(Ok(StackRequest::ListInterfaces{responder}))) => responder); |
| responder.send(&mut iface_info_list.iter_mut()).expect("sending response"); |
| } |
| |
| fn fake_interface_info( |
| octets: [u8; 6], |
| ip_list: Vec<fidl_fuchsia_net::IpAddress>, |
| ) -> fidl_fuchsia_net_stack::InterfaceInfo { |
| use fidl_fuchsia_net_stack::*; |
| let addresses = ip_list |
| .into_iter() |
| .map(|ip| InterfaceAddress { prefix_len: 0, ip_address: ip }) |
| .collect(); |
| InterfaceInfo { |
| id: 0, |
| properties: InterfaceProperties { |
| name: String::new(), |
| topopath: String::new(), |
| filepath: String::new(), |
| mac: Some(Box::new(fidl_fuchsia_hardware_ethernet::MacAddress { octets })), |
| mtu: 0, |
| features: 0, |
| administrative_status: AdministrativeStatus::Enabled, |
| physical_status: PhysicalStatus::Up, |
| addresses, |
| }, |
| } |
| } |
| |
| fn run_netstack_did_get_dhcp_test( |
| infos: &[([u8; 6], Vec<fidl_fuchsia_net::IpAddress>)], |
| mac_to_query: &[u8; 6], |
| is_dhcp: bool, |
| ) { |
| let (mut exec, proxy, mut req_stream) = crate::setup_fake_service::<StackMarker>(); |
| let iface_addr_fut = netstack_did_get_dhcp(&proxy, mac_to_query); |
| pin_mut!(iface_addr_fut); |
| |
| assert_variant!(exec.run_until_stalled(&mut iface_addr_fut), Poll::Pending); |
| |
| let infos: Vec<_> = infos |
| .into_iter() |
| .map(|(mac, ips)| fake_interface_info(*mac, (*ips).to_vec())) |
| .collect(); |
| |
| send_fake_list_iface_response(&mut exec, &mut req_stream, infos); |
| |
| let got_dhcp = assert_variant!(exec.run_until_stalled(&mut iface_addr_fut), |
| Poll::Ready(Ok(addrs)) => addrs); |
| assert_eq!(got_dhcp, is_dhcp); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_no_iface_present() { |
| run_netstack_did_get_dhcp_test(&[], &[1, 2, 3, 4, 5, 6], false); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_one_iface_match() { |
| let ip = Ipv4(Ipv4Address { addr: TEST_IPV4_ADDR_VALID }); |
| let infos = [([1, 2, 3, 4, 5, 6], vec![ip])]; |
| |
| run_netstack_did_get_dhcp_test(&infos[..], &[1, 2, 3, 4, 5, 6], true); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_one_iface_mismatch() { |
| let infos = [([0; 6], vec![Ipv4(Ipv4Address { addr: TEST_IPV4_ADDR_VALID })])]; |
| |
| run_netstack_did_get_dhcp_test(&infos[..], &[1, 2, 3, 4, 5, 6], false); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_one_iface_match_one_mismatch() { |
| let ip_match = Ipv4(Ipv4Address { addr: TEST_IPV4_ADDR_VALID }); |
| let ip_mismatch = Ipv4(Ipv4Address { addr: TEST_IPV4_ALL_ZEROS }); |
| let infos = [([1, 2, 3, 4, 5, 6], vec![ip_match]), ([0; 6], vec![ip_mismatch])]; |
| |
| run_netstack_did_get_dhcp_test(&infos[..], &[1, 2, 3, 4, 5, 6], true); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_two_ifaces_match() { |
| let ip1 = Ipv4(Ipv4Address { addr: TEST_IPV4_ADDR_VALID }); |
| let ip2 = Ipv4(Ipv4Address { addr: TEST_IPV4_ALL_ZEROS }); |
| let infos = [([1, 2, 3, 4, 5, 6], vec![ip1]), ([1, 2, 3, 4, 5, 6], vec![ip2])]; |
| |
| run_netstack_did_get_dhcp_test(&infos[..], &[1, 2, 3, 4, 5, 6], true); |
| } |
| |
| #[test] |
| fn test_netstack_did_get_dhcp_all_ips_invalid() { |
| let ip_list = vec![ |
| Ipv4(Ipv4Address { addr: TEST_IPV4_ALL_ZEROS }), |
| Ipv6(Ipv6Address { addr: TEST_IPV6_ALL_ZEROS }), |
| Ipv6(Ipv6Address { addr: TEST_IPV6_LINK_LOCAL }), |
| Ipv6(Ipv6Address { addr: TEST_IPV6_MULTICAST }), |
| ]; |
| |
| let infos = [([1, 2, 3, 4, 5, 6], ip_list)]; |
| |
| run_netstack_did_get_dhcp_test(&infos[..], &[1, 2, 3, 4, 5, 6], false); |
| } |
| } |