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::neigh::{NdMsg, NdaAttr, NeighborState, ntf, nud},
};
const NLM_F_CREATE: u16 = 0x400;
const NLM_F_EXCL: u16 = 0x200;
const NLM_F_REPLACE: u16 = 0x100;
const AF_INET: u8 = 2;
const AF_INET6: u8 = 10;
pub use super::types::neigh::NeighborState as State;
pub trait NeighborConfig: Send + Sync {
fn interface_ref(&self) -> &InterfaceRef;
fn family(&self) -> u8;
fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()>;
fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()>;
}
#[derive(Debug, Clone)]
pub struct Neighbor {
interface: InterfaceRef,
destination: IpAddr,
lladdr: Option<Vec<u8>>,
state: u16,
flags: u8,
vlan: Option<u16>,
vni: Option<u32>,
master: Option<u32>,
}
impl Neighbor {
pub fn new_v4(interface: impl Into<String>, destination: Ipv4Addr) -> Self {
Self {
interface: InterfaceRef::Name(interface.into()),
destination: IpAddr::V4(destination),
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn with_index_v4(ifindex: u32, destination: Ipv4Addr) -> Self {
Self {
interface: InterfaceRef::Index(ifindex),
destination: IpAddr::V4(destination),
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn new_v6(interface: impl Into<String>, destination: Ipv6Addr) -> Self {
Self {
interface: InterfaceRef::Name(interface.into()),
destination: IpAddr::V6(destination),
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn with_index_v6(ifindex: u32, destination: Ipv6Addr) -> Self {
Self {
interface: InterfaceRef::Index(ifindex),
destination: IpAddr::V6(destination),
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn new(interface: impl Into<String>, destination: IpAddr) -> Self {
Self {
interface: InterfaceRef::Name(interface.into()),
destination,
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn with_index(ifindex: u32, destination: IpAddr) -> Self {
Self {
interface: InterfaceRef::Index(ifindex),
destination,
lladdr: None,
state: nud::PERMANENT,
flags: 0,
vlan: None,
vni: None,
master: None,
}
}
pub fn lladdr(mut self, addr: [u8; 6]) -> Self {
self.lladdr = Some(addr.to_vec());
self
}
pub fn lladdr_bytes(mut self, addr: impl Into<Vec<u8>>) -> Self {
self.lladdr = Some(addr.into());
self
}
pub fn state(mut self, state: NeighborState) -> Self {
self.state = state as u16;
self
}
pub fn permanent(mut self) -> Self {
self.state = nud::PERMANENT;
self
}
pub fn reachable(mut self) -> Self {
self.state = nud::REACHABLE;
self
}
pub fn stale(mut self) -> Self {
self.state = nud::STALE;
self
}
pub fn noarp(mut self) -> Self {
self.state = nud::NOARP;
self
}
pub fn proxy(mut self) -> Self {
self.flags |= ntf::PROXY;
self
}
pub fn router(mut self) -> Self {
self.flags |= ntf::ROUTER;
self
}
pub fn extern_learn(mut self) -> Self {
self.flags |= ntf::EXT_LEARNED;
self
}
pub fn vlan(mut self, vlan_id: u16) -> Self {
self.vlan = Some(vlan_id);
self
}
pub fn vni(mut self, vni: u32) -> Self {
self.vni = Some(vni);
self
}
pub fn master_index(mut self, master_ifindex: u32) -> Self {
self.master = Some(master_ifindex);
self
}
}
impl NeighborConfig for Neighbor {
fn interface_ref(&self) -> &InterfaceRef {
&self.interface
}
fn family(&self) -> u8 {
match self.destination {
IpAddr::V4(_) => AF_INET,
IpAddr::V6(_) => AF_INET6,
}
}
fn write_add(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let mut ndmsg = NdMsg::new()
.with_family(self.family())
.with_ifindex(ifindex as i32);
ndmsg.ndm_state = self.state;
ndmsg.ndm_flags = self.flags;
builder.append(&ndmsg);
match self.destination {
IpAddr::V4(addr) => {
builder.append_attr(NdaAttr::Dst as u16, &addr.octets());
}
IpAddr::V6(addr) => {
builder.append_attr(NdaAttr::Dst as u16, &addr.octets());
}
}
if let Some(ref lladdr) = self.lladdr {
builder.append_attr(NdaAttr::Lladdr as u16, lladdr);
}
if let Some(vlan) = self.vlan {
builder.append_attr_u16(NdaAttr::Vlan as u16, vlan);
}
if let Some(vni) = self.vni {
builder.append_attr_u32(NdaAttr::Vni as u16, vni);
}
if let Some(master) = self.master {
builder.append_attr_u32(NdaAttr::Master as u16, master);
}
Ok(())
}
fn write_delete(&self, builder: &mut MessageBuilder, ifindex: u32) -> Result<()> {
let ndmsg = NdMsg::new()
.with_family(self.family())
.with_ifindex(ifindex as i32);
builder.append(&ndmsg);
match self.destination {
IpAddr::V4(addr) => {
builder.append_attr(NdaAttr::Dst as u16, &addr.octets());
}
IpAddr::V6(addr) => {
builder.append_attr(NdaAttr::Dst as u16, &addr.octets());
}
}
Ok(())
}
}
impl Connection<Route> {
pub async fn add_neighbor<N: NeighborConfig>(&self, config: N) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(
NlMsgType::RTM_NEWNEIGH,
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_neighbor"))
}
pub async fn del_neighbor<N: NeighborConfig>(&self, config: N) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(NlMsgType::RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_ACK);
config.write_delete(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("del_neighbor"))
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_neighbor_v4_by_index"))]
pub async fn add_neighbor_v4_by_index(
&self,
ifindex: u32,
destination: Ipv4Addr,
lladdr: [u8; 6],
) -> Result<()> {
let neigh = Neighbor::with_index_v4(ifindex, destination).lladdr(lladdr);
self.add_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_neighbor_v6_by_index"))]
pub async fn add_neighbor_v6_by_index(
&self,
ifindex: u32,
destination: Ipv6Addr,
lladdr: [u8; 6],
) -> Result<()> {
let neigh = Neighbor::with_index_v6(ifindex, destination).lladdr(lladdr);
self.add_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_neighbor_v4"))]
pub async fn del_neighbor_v4(
&self,
ifname: impl Into<InterfaceRef>,
destination: Ipv4Addr,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let neigh = Neighbor::with_index_v4(ifindex, destination);
self.del_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_neighbor_v4_by_index"))]
pub async fn del_neighbor_v4_by_index(
&self,
ifindex: u32,
destination: Ipv4Addr,
) -> Result<()> {
let neigh = Neighbor::with_index_v4(ifindex, destination);
self.del_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_neighbor_v6"))]
pub async fn del_neighbor_v6(
&self,
ifname: impl Into<InterfaceRef>,
destination: Ipv6Addr,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let neigh = Neighbor::with_index_v6(ifindex, destination);
self.del_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_neighbor_v6_by_index"))]
pub async fn del_neighbor_v6_by_index(
&self,
ifindex: u32,
destination: Ipv6Addr,
) -> Result<()> {
let neigh = Neighbor::with_index_v6(ifindex, destination);
self.del_neighbor(neigh).await
}
pub async fn replace_neighbor<N: NeighborConfig>(&self, config: N) -> Result<()> {
let ifindex = self.resolve_interface(config.interface_ref()).await?;
let mut builder = MessageBuilder::new(
NlMsgType::RTM_NEWNEIGH,
NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE,
);
config.write_add(&mut builder, ifindex)?;
self.send_ack(builder)
.await
.map_err(|e| e.with_context("replace_neighbor"))
}
#[tracing::instrument(
level = "debug",
skip_all,
fields(method = "replace_neighbor_v4_by_index")
)]
pub async fn replace_neighbor_v4_by_index(
&self,
ifindex: u32,
destination: Ipv4Addr,
lladdr: [u8; 6],
) -> Result<()> {
let neigh = Neighbor::with_index_v4(ifindex, destination).lladdr(lladdr);
self.replace_neighbor(neigh).await
}
#[tracing::instrument(
level = "debug",
skip_all,
fields(method = "replace_neighbor_v6_by_index")
)]
pub async fn replace_neighbor_v6_by_index(
&self,
ifindex: u32,
destination: Ipv6Addr,
lladdr: [u8; 6],
) -> Result<()> {
let neigh = Neighbor::with_index_v6(ifindex, destination).lladdr(lladdr);
self.replace_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "flush_neighbors"))]
pub async fn flush_neighbors(&self, ifname: impl Into<InterfaceRef>) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
self.flush_neighbors_by_index(ifindex).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "flush_neighbors_by_index"))]
pub async fn flush_neighbors_by_index(&self, ifindex: u32) -> Result<()> {
let neighbors = self.get_neighbors_by_index(ifindex).await?;
for neigh in neighbors {
if let Some(dest) = neigh.destination {
if neigh.state() == NeighborState::Permanent {
continue;
}
if let Err(e) = self.del_neighbor(Neighbor::with_index(ifindex, dest)).await {
if !e.is_not_found() {
return Err(e);
}
}
}
}
Ok(())
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_proxy_arp"))]
pub async fn add_proxy_arp(
&self,
ifname: impl Into<InterfaceRef>,
destination: Ipv4Addr,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let neigh = Neighbor::with_index_v4(ifindex, destination).proxy();
self.add_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "add_proxy_arp_by_index"))]
pub async fn add_proxy_arp_by_index(&self, ifindex: u32, destination: Ipv4Addr) -> Result<()> {
let neigh = Neighbor::with_index_v4(ifindex, destination).proxy();
self.add_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_proxy_arp"))]
pub async fn del_proxy_arp(
&self,
ifname: impl Into<InterfaceRef>,
destination: Ipv4Addr,
) -> Result<()> {
let ifindex = self.resolve_interface(&ifname.into()).await?;
let neigh = Neighbor::with_index_v4(ifindex, destination).proxy();
self.del_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "del_proxy_arp_by_index"))]
pub async fn del_proxy_arp_by_index(&self, ifindex: u32, destination: Ipv4Addr) -> Result<()> {
let neigh = Neighbor::with_index_v4(ifindex, destination).proxy();
self.del_neighbor(neigh).await
}
#[tracing::instrument(level = "debug", skip_all, fields(method = "get_neighbors_by_index"))]
pub async fn get_neighbors_by_index(
&self,
ifindex: u32,
) -> Result<Vec<super::messages::NeighborMessage>> {
let neighbors = self.get_neighbors().await?;
Ok(neighbors
.into_iter()
.filter(|n| n.ifindex() == ifindex)
.collect())
}
}