blob: 186c559bdace26aa3ec55a87561bf4313a1e82cc [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 {
fidl_fuchsia_net as fnet,
fidl_fuchsia_net_stack::{self as stack},
fidl_fuchsia_router_config as netconfig,
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
/// LifIpAddr is an IP address and its prefix.
#[derive(Hash, Eq, PartialEq, Debug, Clone, Copy)]
pub struct LifIpAddr {
pub address: IpAddr,
pub prefix: u8,
}
impl From<&fnet::IpAddress> for LifIpAddr {
fn from(addr: &fnet::IpAddress) -> Self {
match addr {
fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => {
LifIpAddr { address: IpAddr::from(*addr), prefix: 32 }
}
fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => {
LifIpAddr { address: IpAddr::from(*addr), prefix: 128 }
}
}
}
}
impl From<&stack::InterfaceAddress> for LifIpAddr {
fn from(addr: &stack::InterfaceAddress) -> Self {
LifIpAddr { address: to_ip_addr(addr.ip_address), prefix: addr.prefix_len }
}
}
impl From<&fnet::Subnet> for LifIpAddr {
fn from(s: &fnet::Subnet) -> Self {
match *s {
fnet::Subnet {
addr: fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }),
prefix_len: prefix,
} => LifIpAddr { address: addr.into(), prefix },
fnet::Subnet {
addr: fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }),
prefix_len: prefix,
} => LifIpAddr { address: addr.into(), prefix },
}
}
}
impl From<&netconfig::CidrAddress> for LifIpAddr {
fn from(a: &netconfig::CidrAddress) -> Self {
match a.address {
Some(addr) => {
LifIpAddr { address: to_ip_addr(addr), prefix: a.prefix_length.unwrap_or(0) }
}
None => LifIpAddr { address: IpAddr::from([0, 0, 0, 0]), prefix: 0 },
}
}
}
impl From<&LifIpAddr> for netconfig::CidrAddress {
fn from(addr: &LifIpAddr) -> Self {
match addr.address {
IpAddr::V4(a) => netconfig::CidrAddress {
address: Some(fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: a.octets() })),
prefix_length: Some(addr.prefix),
},
IpAddr::V6(a) => netconfig::CidrAddress {
address: Some(fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: a.octets() })),
prefix_length: Some(addr.prefix),
},
}
}
}
impl From<&LifIpAddr> for fnet::Subnet {
fn from(addr: &LifIpAddr) -> Self {
match addr.address {
IpAddr::V4(a) => fnet::Subnet {
addr: fnet::IpAddress::Ipv4(fnet::Ipv4Address {
addr: (u32::from_be_bytes(a.octets()) >> (32 - addr.prefix)
<< (32 - addr.prefix))
.to_be_bytes(),
}),
prefix_len: addr.prefix,
},
IpAddr::V6(a) => fnet::Subnet {
addr: fnet::IpAddress::Ipv6(fnet::Ipv6Address {
addr: (u128::from_be_bytes(a.octets()) >> (128 - addr.prefix)
<< (128 - addr.prefix))
.to_be_bytes(),
}),
prefix_len: addr.prefix,
},
}
}
}
impl From<&LifIpAddr> for stack::InterfaceAddress {
fn from(addr: &LifIpAddr) -> Self {
match addr.address {
IpAddr::V4(a) => stack::InterfaceAddress {
ip_address: fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: a.octets() }),
prefix_len: addr.prefix,
},
IpAddr::V6(a) => stack::InterfaceAddress {
ip_address: fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr: a.octets() }),
prefix_len: addr.prefix,
},
}
}
}
impl LifIpAddr {
/// Returns true if `address` is in the same subnet as `LifIpAddr`.
pub fn is_in_same_subnet(&self, address: &IpAddr) -> bool {
let local_subnet = strip_host(&self.address, self.prefix);
let address_subnet = strip_host(address, self.prefix);
local_subnet == address_subnet
}
/// Returns [`true`] if this address is an [IPv4 address], and [`false`] otherwise.
pub fn is_ipv4(&self) -> bool {
self.address.is_ipv4()
}
/// Returns [`true`] if this address is an [IPv6 address], and [`false`] otherwise.
pub fn is_ipv6(&self) -> bool {
self.address.is_ipv6()
}
}
/// Creates an `std::net::IpAddr` from fuchsia.net.IpAddress.
pub fn to_ip_addr(addr: fnet::IpAddress) -> IpAddr {
match addr {
fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => IpAddr::from(addr),
fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => IpAddr::from(addr),
}
}
/// Converts a subnet mask given as a set of octets to a scalar prefix length.
pub fn subnet_mask_to_prefix_length(addr: fnet::IpAddress) -> u8 {
match addr {
fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr }) => {
(!u32::from_be_bytes(addr)).leading_zeros() as u8
}
fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr }) => {
(!u128::from_be_bytes(addr)).leading_zeros() as u8
}
}
}
/// Strips the host part from a given `address` and `prefix`.
fn strip_host(address: &IpAddr, prefix: u8) -> IpAddr {
match address {
IpAddr::V4(a) => {
if prefix == 0 {
IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))
} else if prefix > 32 {
*address
} else {
IpAddr::V4(Ipv4Addr::from(
(u32::from_be_bytes(a.octets()) >> (32 - prefix) << (32 - prefix))
.to_be_bytes(),
))
}
}
IpAddr::V6(a) => {
if prefix == 0 {
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0))
} else if prefix > 128 {
*address
} else {
IpAddr::V6(Ipv6Addr::from(
(u128::from_be_bytes(a.octets()) >> (128 - prefix) << (128 - prefix))
.to_be_bytes(),
))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn v4(addr: [u8; 4]) -> fnet::IpAddress {
fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr })
}
fn v6(addr: [u8; 16]) -> fnet::IpAddress {
fnet::IpAddress::Ipv6(fnet::Ipv6Address { addr })
}
#[test]
fn test_to_prefix() {
assert_eq!(subnet_mask_to_prefix_length(v4([255, 255, 255, 255])), 32);
assert_eq!(subnet_mask_to_prefix_length(v4([255, 255, 255, 0])), 24);
assert_eq!(subnet_mask_to_prefix_length(v4([255, 128, 0, 0])), 9);
assert_eq!(subnet_mask_to_prefix_length(v4([0, 0, 0, 0])), 0);
assert_eq!(
subnet_mask_to_prefix_length(v6([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
])),
128
);
assert_eq!(
subnet_mask_to_prefix_length(v6([
255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
])),
64
);
}
#[test]
fn test_from_ipaddress_to_lifipaddr() {
assert_eq!(
LifIpAddr::from(&fnet::IpAddress::Ipv4(fnet::Ipv4Address { addr: [1, 2, 3, 4] })),
LifIpAddr { address: "1.2.3.4".parse().unwrap(), prefix: 32 }
);
assert_eq!(
LifIpAddr::from(&fnet::IpAddress::Ipv6(fnet::Ipv6Address {
addr: [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0xfc, 0xb6, 0x5b, 0x27, 0xfd, 0x2c, 0xf, 0x12]
})),
LifIpAddr { address: "fe80::fcb6:5b27:fd2c:f12".parse().unwrap(), prefix: 128 }
);
}
#[test]
fn test_strip_host() {
let got = strip_host(&"85.170.255.170".parse().unwrap(), 23);
let want: IpAddr = "85.170.254.0".parse().unwrap();
assert_eq!(want, got, "valid ipv4 prefix");
let got = strip_host(&"1200:5555:aaaa:aaaa:aaaa:aaaa:5555:aaaa".parse().unwrap(), 57);
let want: IpAddr = "1200:5555:aaaa:aa80:0:0:0:0".parse().unwrap();
assert_eq!(want, got, "valid ipv6 prefix");
let got = strip_host(&"85.170.170.85".parse().unwrap(), 58);
let want: IpAddr = "85.170.170.85".parse().unwrap();
assert_eq!(want, got, "invalid ipv4 prefix");
let got = strip_host(&"1200:0:0:0:aaaa:5555:aaaa:5555".parse().unwrap(), 129);
let want: IpAddr = "1200:0:0:0:aaaa:5555:aaaa:5555".parse().unwrap();
assert_eq!(want, got, "invalid ipv6 prefix");
let got = strip_host(&"85.170.170.85".parse().unwrap(), 0);
let want: IpAddr = "0.0.0.0".parse().unwrap();
assert_eq!(want, got, "ipv4 prefix 0");
let got = strip_host(&"1200:0:0:0:aaaa:5555:aaaa:5555".parse().unwrap(), 0);
let want: IpAddr = "::".parse().unwrap();
assert_eq!(want, got, "ipv6 prefix 0");
}
#[test]
fn test_is_in_same_subnet() {
let address = LifIpAddr { address: "1.2.3.26".parse().unwrap(), prefix: 27 };
assert!(address.is_in_same_subnet(&"1.2.3.26".parse().unwrap()));
assert!(address.is_in_same_subnet(&"1.2.3.30".parse().unwrap()));
assert!(!address.is_in_same_subnet(&"1.2.3.32".parse().unwrap()));
let address = LifIpAddr {
address: "2401:fa00:480:16:1295:6946:837:373a".parse().unwrap(),
prefix: 58,
};
assert!(address.is_in_same_subnet(&"2401:fa00:480:16:1295:6946:837:373a".parse().unwrap()));
assert!(address.is_in_same_subnet(&"2401:fa00:480:16:2345:6946:837:373a".parse().unwrap()));
assert!(address.is_in_same_subnet(&"2401:fa00:480:26:1295:6946:837:373a".parse().unwrap()));
assert!(!address.is_in_same_subnet(&"2401:fa00:480:46:2345:6946:837:373a".parse().unwrap()));
}
}