blob: 75fae37c269947ef89f8ddfcfda0089601f04055 [file] [log] [blame]
// 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);
}
}