use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use nlink::{
Result,
netlink::{
addr::{Ipv4Address, Ipv6Address},
link::DummyLink,
},
};
use crate::common::TestNamespace;
#[tokio::test]
async fn test_add_ipv4_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("addr4")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let target_ip = Ipv4Addr::new(192, 168, 1, 100);
conn.add_address(Ipv4Address::new("dummy0", target_ip, 24))
.await?;
let addrs = conn.get_addresses().await?;
let addr = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(target_ip))
.unwrap_or(false)
});
assert!(addr.is_some(), "address should exist");
assert_eq!(addr.unwrap().prefix_len(), 24);
Ok(())
}
#[tokio::test]
async fn test_add_ipv6_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("addr6")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ipv6: Ipv6Addr = "2001:db8::1".parse().unwrap();
conn.add_address(Ipv6Address::new("dummy0", ipv6, 64))
.await?;
let addrs = conn.get_addresses().await?;
let addr = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V6(ipv6))
.unwrap_or(false)
});
assert!(addr.is_some(), "IPv6 address should exist");
assert_eq!(addr.unwrap().prefix_len(), 64);
Ok(())
}
#[tokio::test]
async fn test_delete_ipv4_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("deladdr4")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip = Ipv4Addr::new(10, 0, 0, 1);
conn.add_address(Ipv4Address::new("dummy0", ip, 24)).await?;
let addrs = conn.get_addresses().await?;
assert!(addrs.iter().any(|a| a.address() == Some(&IpAddr::V4(ip))));
conn.del_address("dummy0", ip.into(), 24).await?;
let addrs = conn.get_addresses().await?;
assert!(!addrs.iter().any(|a| a.address() == Some(&IpAddr::V4(ip))));
Ok(())
}
#[tokio::test]
async fn test_delete_ipv6_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("deladdr6")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip: Ipv6Addr = "fd00::1".parse().unwrap();
conn.add_address(Ipv6Address::new("dummy0", ip, 64)).await?;
let addrs = conn.get_addresses().await?;
assert!(addrs.iter().any(|a| a.address() == Some(&IpAddr::V6(ip))));
conn.del_address("dummy0", ip.into(), 64).await?;
let addrs = conn.get_addresses().await?;
assert!(!addrs.iter().any(|a| a.address() == Some(&IpAddr::V6(ip))));
Ok(())
}
#[tokio::test]
async fn test_multiple_addresses() -> Result<()> {
require_root!();
let ns = TestNamespace::new("multiaddr")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip1 = Ipv4Addr::new(192, 168, 1, 1);
let ip2 = Ipv4Addr::new(192, 168, 1, 2);
let ip3 = Ipv4Addr::new(10, 0, 0, 1);
conn.add_address(Ipv4Address::new("dummy0", ip1, 24))
.await?;
conn.add_address(Ipv4Address::new("dummy0", ip2, 24))
.await?;
conn.add_address(Ipv4Address::new("dummy0", ip3, 8)).await?;
let addrs = conn.get_addresses().await?;
let dummy_addrs: Vec<_> = addrs
.iter()
.filter(|a| {
a.address()
.map(|ip| {
*ip == IpAddr::V4(ip1) || *ip == IpAddr::V4(ip2) || *ip == IpAddr::V4(ip3)
})
.unwrap_or(false)
})
.collect();
assert_eq!(dummy_addrs.len(), 3);
Ok(())
}
#[tokio::test]
async fn test_get_addresses_for_interface() -> Result<()> {
require_root!();
let ns = TestNamespace::new("getaddr")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.add_link(DummyLink::new("dummy1")).await?;
conn.set_link_up("dummy0").await?;
conn.set_link_up("dummy1").await?;
let target_ip = Ipv4Addr::new(192, 168, 1, 1);
conn.add_address(Ipv4Address::new("dummy0", target_ip, 24))
.await?;
conn.add_address(Ipv4Address::new(
"dummy1",
Ipv4Addr::new(192, 168, 2, 1),
24,
))
.await?;
let addrs = conn.get_addresses_by_name("dummy0").await?;
let ipv4_addrs: Vec<_> = addrs
.iter()
.filter(|a| matches!(a.address(), Some(IpAddr::V4(_))))
.collect();
assert_eq!(ipv4_addrs.len(), 1);
assert_eq!(ipv4_addrs[0].address(), Some(&IpAddr::V4(target_ip)));
Ok(())
}
#[tokio::test]
async fn test_address_with_broadcast() -> Result<()> {
require_root!();
let ns = TestNamespace::new("bcast")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let target_ip = Ipv4Addr::new(192, 168, 1, 100);
let bcast_ip = Ipv4Addr::new(192, 168, 1, 255);
conn.add_address(Ipv4Address::new("dummy0", target_ip, 24).broadcast(bcast_ip))
.await?;
let addrs = conn.get_addresses().await?;
let addr = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(target_ip))
.unwrap_or(false)
});
assert!(addr.is_some());
let addr = addr.unwrap();
assert_eq!(addr.broadcast(), Some(&IpAddr::V4(bcast_ip)));
Ok(())
}
#[tokio::test]
async fn test_address_with_label() -> Result<()> {
require_root!();
let ns = TestNamespace::new("label")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let target_ip = Ipv4Addr::new(192, 168, 1, 100);
conn.add_address(Ipv4Address::new("dummy0", target_ip, 24).label("dummy0:web"))
.await?;
let addrs = conn.get_addresses().await?;
let addr = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(target_ip))
.unwrap_or(false)
});
assert!(addr.is_some());
assert_eq!(addr.unwrap().label(), Some("dummy0:web"));
Ok(())
}
#[tokio::test]
async fn test_replace_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("replace")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip = Ipv4Addr::new(192, 168, 1, 100);
conn.add_address(Ipv4Address::new("dummy0", ip, 24).label("dummy0:first"))
.await?;
conn.replace_address(Ipv4Address::new("dummy0", ip, 24).label("dummy0:second"))
.await?;
let addrs = conn.get_addresses().await?;
let matching: Vec<_> = addrs
.iter()
.filter(|a| a.address() == Some(&IpAddr::V4(ip)))
.collect();
assert_eq!(matching.len(), 1);
assert_eq!(matching[0].label(), Some("dummy0:second"));
Ok(())
}
#[tokio::test]
async fn test_loopback_address() -> Result<()> {
require_root!();
let ns = TestNamespace::new("loaddr")?;
let conn = ns.connection()?;
conn.set_link_up("lo").await?;
let target_ip = Ipv4Addr::new(127, 0, 0, 2);
conn.add_address(Ipv4Address::new("lo", target_ip, 8))
.await?;
let addrs = conn.get_addresses_by_name("lo").await?;
let addr = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(target_ip))
.unwrap_or(false)
});
assert!(addr.is_some());
Ok(())
}
#[tokio::test]
async fn test_address_scope() -> Result<()> {
require_root!();
let ns = TestNamespace::new("scope")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let global_ip = Ipv4Addr::new(192, 168, 1, 1);
let link_ip = Ipv4Addr::new(169, 254, 1, 1);
conn.add_address(Ipv4Address::new("dummy0", global_ip, 24))
.await?;
conn.add_address(Ipv4Address::new("dummy0", link_ip, 16))
.await?;
let addrs = conn.get_addresses_by_name("dummy0").await?;
let global = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(global_ip))
.unwrap_or(false)
});
let link = addrs.iter().find(|a| {
a.address()
.map(|ip| *ip == IpAddr::V4(link_ip))
.unwrap_or(false)
});
assert!(global.is_some());
assert!(link.is_some());
Ok(())
}
#[tokio::test]
async fn test_add_address_by_name() -> Result<()> {
require_root!();
let ns = TestNamespace::new("addr-name")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip: IpAddr = Ipv4Addr::new(10, 0, 0, 1).into();
conn.add_address_by_name("dummy0", ip, 24).await?;
let addrs = conn.get_addresses().await?;
let found = addrs
.iter()
.any(|a| a.address().map(|addr| *addr == ip).unwrap_or(false));
assert!(found, "address 10.0.0.1 should exist on dummy0");
Ok(())
}
#[tokio::test]
async fn test_replace_address_by_name() -> Result<()> {
require_root!();
let ns = TestNamespace::new("addr-repl")?;
let conn = ns.connection()?;
conn.add_link(DummyLink::new("dummy0")).await?;
conn.set_link_up("dummy0").await?;
let ip: IpAddr = Ipv4Addr::new(10, 0, 0, 2).into();
conn.add_address_by_name("dummy0", ip, 24).await?;
conn.replace_address_by_name("dummy0", ip, 24).await?;
let addrs = conn.get_addresses().await?;
let found = addrs
.iter()
.any(|a| a.address().map(|addr| *addr == ip).unwrap_or(false));
assert!(found, "address 10.0.0.2 should exist after replace");
Ok(())
}