use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use super::{
builder::MessageBuilder,
connection::Connection,
error::Result,
interface_ref::InterfaceRef,
message::{NLM_F_ACK, NLM_F_REQUEST, NlMsgType},
protocol::Route,
types::addr::{IfAddrMsg, IfaAttr, Scope, ifa_flags},
};
const NLM_F_CREATE: u16 = 0x400;
const NLM_F_EXCL: u16 = 0x200;
const NLM_F_REPLACE: u16 = 0x100;
pub const AF_INET: u8 = 2;
pub const AF_INET6: u8 = 10;
pub trait AddressConfig: Send + Sync {
fn interface_ref(&self) -> &InterfaceRef;
fn family(&self) -> u8;
fn prefix_len(&self) -> u8;
fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()>;
fn write_replace(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()>;
fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()>;
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
struct IfaCacheinfo {
ifa_prefered: u32,
ifa_valid: u32,
cstamp: u32,
tstamp: u32,
}
pub const INFINITY_LIFE_TIME: u32 = 0xFFFFFFFF;
#[derive(Debug, Clone)]
pub struct Ipv4Address {
interface: InterfaceRef,
address: Ipv4Addr,
prefix_len: u8,
peer: Option<Ipv4Addr>,
broadcast: Option<Ipv4Addr>,
label: Option<String>,
scope: Scope,
flags: u32,
preferred_lft: Option<u32>,
valid_lft: Option<u32>,
metric: Option<u32>,
}
impl Ipv4Address {
pub fn new(interface: impl Into<String>, address: Ipv4Addr, prefix_len: u8) -> Self {
Self {
interface: InterfaceRef::Name(interface.into()),
address,
prefix_len,
peer: None,
broadcast: None,
label: None,
scope: Scope::Universe,
flags: 0,
preferred_lft: None,
valid_lft: None,
metric: None,
}
}
pub fn with_index(ifindex: u32, address: Ipv4Addr, prefix_len: u8) -> Self {
Self {
interface: InterfaceRef::Index(ifindex),
address,
prefix_len,
peer: None,
broadcast: None,
label: None,
scope: Scope::Universe,
flags: 0,
preferred_lft: None,
valid_lft: None,
metric: None,
}
}
pub fn peer(mut self, peer: Ipv4Addr) -> Self {
self.peer = Some(peer);
self
}
pub fn broadcast(mut self, broadcast: Ipv4Addr) -> Self {
self.broadcast = Some(broadcast);
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
let mut l = label.into();
if l.len() > 15 {
l.truncate(15);
}
self.label = Some(l);
self
}
pub fn scope(mut self, scope: Scope) -> Self {
self.scope = scope;
self
}
pub fn secondary(mut self) -> Self {
self.flags |= ifa_flags::SECONDARY;
self
}
pub fn noprefixroute(mut self) -> Self {
self.flags |= ifa_flags::NOPREFIXROUTE;
self
}
pub fn home(mut self) -> Self {
self.flags |= ifa_flags::HOMEADDRESS;
self
}
pub fn preferred_lifetime(mut self, seconds: u32) -> Self {
self.preferred_lft = Some(seconds);
self
}
pub fn valid_lifetime(mut self, seconds: u32) -> Self {
self.valid_lft = Some(seconds);
self
}
pub fn metric(mut self, metric: u32) -> Self {
self.metric = Some(metric);
self
}
fn write_common_attrs(&self, builder: &mut MessageBuilder) {
builder.append_attr(IfaAttr::Local as u16, &self.address.octets());
if let Some(peer) = self.peer {
builder.append_attr(IfaAttr::Address as u16, &peer.octets());
} else {
builder.append_attr(IfaAttr::Address as u16, &self.address.octets());
}
if let Some(brd) = self.broadcast {
builder.append_attr(IfaAttr::Broadcast as u16, &brd.octets());
}
if let Some(ref label) = self.label {
builder.append_attr_str(IfaAttr::Label as u16, label);
}
if self.flags != 0 {
builder.append_attr_u32(IfaAttr::Flags as u16, self.flags);
}
if self.preferred_lft.is_some() || self.valid_lft.is_some() {
let cacheinfo = IfaCacheinfo {
ifa_prefered: self.preferred_lft.unwrap_or(INFINITY_LIFE_TIME),
ifa_valid: self.valid_lft.unwrap_or(INFINITY_LIFE_TIME),
cstamp: 0,
tstamp: 0,
};
let bytes = unsafe {
std::slice::from_raw_parts(
&cacheinfo as *const IfaCacheinfo as *const u8,
std::mem::size_of::<IfaCacheinfo>(),
)
};
builder.append_attr(IfaAttr::Cacheinfo as u16, bytes);
}
if let Some(metric) = self.metric {
builder.append_attr_u32(IfaAttr::RtPriority as u16, metric);
}
}
}
impl AddressConfig for Ipv4Address {
fn interface_ref(&self) -> &InterfaceRef {
&self.interface
}
fn family(&self) -> u8 {
AF_INET
}
fn prefix_len(&self) -> u8 {
self.prefix_len
}
fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET)
.with_prefixlen(self.prefix_len)
.with_index(ifindex)
.with_scope(self.scope as u8);
builder.append(&ifaddr);
self.write_common_attrs(builder);
Ok(())
}
fn write_replace(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET)
.with_prefixlen(self.prefix_len)
.with_index(ifindex)
.with_scope(self.scope as u8);
builder.append(&ifaddr);
self.write_common_attrs(builder);
Ok(())
}
fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET)
.with_prefixlen(self.prefix_len)
.with_index(ifindex);
builder.append(&ifaddr);
builder.append_attr(IfaAttr::Local as u16, &self.address.octets());
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Ipv6Address {
interface: InterfaceRef,
address: Ipv6Addr,
prefix_len: u8,
peer: Option<Ipv6Addr>,
scope: Scope,
flags: u32,
preferred_lft: Option<u32>,
valid_lft: Option<u32>,
metric: Option<u32>,
}
impl Ipv6Address {
pub fn new(interface: impl Into<String>, address: Ipv6Addr, prefix_len: u8) -> Self {
Self {
interface: InterfaceRef::Name(interface.into()),
address,
prefix_len,
peer: None,
scope: Scope::Universe,
flags: 0,
preferred_lft: None,
valid_lft: None,
metric: None,
}
}
pub fn with_index(ifindex: u32, address: Ipv6Addr, prefix_len: u8) -> Self {
Self {
interface: InterfaceRef::Index(ifindex),
address,
prefix_len,
peer: None,
scope: Scope::Universe,
flags: 0,
preferred_lft: None,
valid_lft: None,
metric: None,
}
}
pub fn peer(mut self, peer: Ipv6Addr) -> Self {
self.peer = Some(peer);
self
}
pub fn scope(mut self, scope: Scope) -> Self {
self.scope = scope;
self
}
pub fn nodad(mut self) -> Self {
self.flags |= ifa_flags::NODAD;
self
}
pub fn optimistic(mut self) -> Self {
self.flags |= ifa_flags::OPTIMISTIC;
self
}
pub fn noprefixroute(mut self) -> Self {
self.flags |= ifa_flags::NOPREFIXROUTE;
self
}
pub fn home(mut self) -> Self {
self.flags |= ifa_flags::HOMEADDRESS;
self
}
pub fn mngtmpaddr(mut self) -> Self {
self.flags |= ifa_flags::MANAGETEMPADDR;
self
}
pub fn preferred_lifetime(mut self, seconds: u32) -> Self {
self.preferred_lft = Some(seconds);
self
}
pub fn valid_lifetime(mut self, seconds: u32) -> Self {
self.valid_lft = Some(seconds);
self
}
pub fn metric(mut self, metric: u32) -> Self {
self.metric = Some(metric);
self
}
fn write_common_attrs(&self, builder: &mut MessageBuilder) {
builder.append_attr(IfaAttr::Local as u16, &self.address.octets());
if let Some(peer) = self.peer {
builder.append_attr(IfaAttr::Address as u16, &peer.octets());
} else {
builder.append_attr(IfaAttr::Address as u16, &self.address.octets());
}
if self.flags != 0 {
builder.append_attr_u32(IfaAttr::Flags as u16, self.flags);
}
if self.preferred_lft.is_some() || self.valid_lft.is_some() {
let cacheinfo = IfaCacheinfo {
ifa_prefered: self.preferred_lft.unwrap_or(INFINITY_LIFE_TIME),
ifa_valid: self.valid_lft.unwrap_or(INFINITY_LIFE_TIME),
cstamp: 0,
tstamp: 0,
};
let bytes = unsafe {
std::slice::from_raw_parts(
&cacheinfo as *const IfaCacheinfo as *const u8,
std::mem::size_of::<IfaCacheinfo>(),
)
};
builder.append_attr(IfaAttr::Cacheinfo as u16, bytes);
}
if let Some(metric) = self.metric {
builder.append_attr_u32(IfaAttr::RtPriority as u16, metric);
}
}
}
impl AddressConfig for Ipv6Address {
fn interface_ref(&self) -> &InterfaceRef {
&self.interface
}
fn family(&self) -> u8 {
AF_INET6
}
fn prefix_len(&self) -> u8 {
self.prefix_len
}
fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET6)
.with_prefixlen(self.prefix_len)
.with_index(ifindex)
.with_scope(self.scope as u8);
builder.append(&ifaddr);
self.write_common_attrs(builder);
Ok(())
}
fn write_replace(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET6)
.with_prefixlen(self.prefix_len)
.with_index(ifindex)
.with_scope(self.scope as u8);
builder.append(&ifaddr);
self.write_common_attrs(builder);
Ok(())
}
fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ifaddr = IfAddrMsg::new()
.with_family(AF_INET6)
.with_prefixlen(self.prefix_len)
.with_index(ifindex);
builder.append(&ifaddr);
builder.append_attr(IfaAttr::Local as u16, &self.address.octets());
Ok(())
}
}
impl Connection<Route> {
pub async fn add_address<A: AddressConfig>(&self, config: A) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(
NlMsgType::RTM_NEWADDR,
NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL,
);
config.write_add(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("add_address"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_address"))]
pub async fn del_address(
&self,
ifname: impl Into<InterfaceRef>,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
self.del_address_by_index(ifindex, address, prefix_len)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_address_by_index"))]
pub async fn del_address_by_index(
&self,
ifindex: u32,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
match address {
IpAddr::V4(addr) => {
let config = Ipv4Address::with_index(ifindex, addr, prefix_len);
self.del_address_config(config).await
}
IpAddr::V6(addr) => {
let config = Ipv6Address::with_index(ifindex, addr, prefix_len);
self.del_address_config(config).await
}
}
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_address_by_index"))]
pub async fn add_address_by_index(
&self,
ifindex: u32,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
match address {
IpAddr::V4(addr) => {
let config = Ipv4Address::with_index(ifindex, addr, prefix_len);
self.add_address(config).await
}
IpAddr::V6(addr) => {
let config = Ipv6Address::with_index(ifindex, addr, prefix_len);
self.add_address(config).await
}
}
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "replace_address_by_index"))]
pub async fn replace_address_by_index(
&self,
ifindex: u32,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
match address {
IpAddr::V4(addr) => {
let config = Ipv4Address::with_index(ifindex, addr, prefix_len);
self.replace_address(config).await
}
IpAddr::V6(addr) => {
let config = Ipv6Address::with_index(ifindex, addr, prefix_len);
self.replace_address(config).await
}
}
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_address_by_name"))]
pub async fn add_address_by_name(
&self,
ifname: impl Into<InterfaceRef>,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
self.add_address_by_index(ifindex, address, prefix_len)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "replace_address_by_name"))]
pub async fn replace_address_by_name(
&self,
ifname: impl Into<InterfaceRef>,
address: IpAddr,
prefix_len: u8,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
self.replace_address_by_index(ifindex, address, prefix_len)
.await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_address_v4"))]
pub async fn del_address_v4(
&self,
ifname: impl Into<InterfaceRef>,
address: Ipv4Addr,
prefix_len: u8,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let config = Ipv4Address::with_index(ifindex, address, prefix_len);
self.del_address_config(config).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_address_v6"))]
pub async fn del_address_v6(
&self,
ifname: impl Into<InterfaceRef>,
address: Ipv6Addr,
prefix_len: u8,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let config = Ipv6Address::with_index(ifindex, address, prefix_len);
self.del_address_config(config).await
}
pub async fn del_address_config<A: AddressConfig>(&self, config: A) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK);
config.write_delete(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_address"))
}
pub async fn replace_address<A: AddressConfig>(&self, config: A) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(
NlMsgType::RTM_NEWADDR,
NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE,
);
config.write_replace(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("replace_address"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "flush_addresses"))]
pub async fn flush_addresses(&self, ifname: impl Into<InterfaceRef>) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
self.flush_addresses_by_index(ifindex).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "flush_addresses_by_index"))]
pub async fn flush_addresses_by_index(&self, ifindex: u32) -> Result<()> {
let addresses = self.get_addresses_by_index(ifindex).await?;
for addr in addresses {
if let (Some(address), Some(prefix_len)) = (addr.address, Some(addr.prefix_len())) {
if ifindex == 1
&& (address == IpAddr::V4(Ipv4Addr::LOCALHOST)
|| address == IpAddr::V6(Ipv6Addr::LOCALHOST))
{
continue;
}
if let Err(e) = self
.del_address_by_index(ifindex, address, prefix_len)
.await
{
if !e.is_not_found() {
return Err(e);
}
}
}
}
Ok(())
}
}