use std::collections::HashMap;
use anyhow::Result;
use crate::{
consts,
message::{InfoMessage, NetlinkRouteAttr},
request::{NetlinkRequest, NetlinkRequestData},
utils::zero_terminated,
};
pub enum Namespace {
Pid(i32),
Fd(i32),
}
pub enum Kind {
Device(LinkAttrs),
Dummy(LinkAttrs),
Bridge {
attrs: LinkAttrs,
hello_time: Option<u32>,
ageing_time: Option<u32>,
multicast_snooping: Option<bool>,
vlan_filtering: Option<bool>,
},
Veth {
attrs: LinkAttrs,
peer_name: String,
peer_hw_addr: Option<Vec<u8>>,
peer_ns: Option<Namespace>,
},
}
pub trait Link {
fn link_type(&self) -> String;
fn attrs(&self) -> &LinkAttrs;
fn attrs_mut(&mut self) -> &mut LinkAttrs;
fn kind(&self) -> &Kind;
}
impl<T: Link + ?Sized> Link for Box<T> {
fn link_type(&self) -> String {
(**self).link_type()
}
fn attrs(&self) -> &LinkAttrs {
(**self).attrs()
}
fn attrs_mut(&mut self) -> &mut LinkAttrs {
(**self).attrs_mut()
}
fn kind(&self) -> &Kind {
(**self).kind()
}
}
#[derive(Debug, Default, Clone)]
pub struct LinkAttrs {
pub link_type: String,
pub index: i32,
pub name: String,
pub hw_addr: Vec<u8>,
pub mtu: u32,
pub flags: u32,
pub raw_flags: u32,
pub parent_index: i32,
pub master_index: i32,
pub tx_queue_len: i32,
pub alias: String,
pub promisc: i32,
pub all_multi: i32,
pub multicast: i32,
pub xdp: LinkXdp,
pub encap_type: String,
pub prot_info: String,
pub oper_state: u8,
pub phys_switch_id: i32,
pub netns_id: i32,
pub gso_max_size: u32,
pub gso_max_segs: u32,
pub gro_max_size: u32,
pub vfs: String,
pub num_tx_queues: i32,
pub num_rx_queues: i32,
pub group: u32,
pub statistics: String,
}
impl LinkAttrs {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
..Default::default()
}
}
fn from(if_info_msg: InfoMessage) -> Self {
let mut attrs = Self::default();
attrs.index = if_info_msg.index;
attrs.raw_flags = if_info_msg.flags;
attrs.flags = {
let mut flags = 0;
if attrs.raw_flags & libc::IFF_UP as u32 != 0 {
flags |= consts::IFF_UP;
}
if attrs.raw_flags & libc::IFF_BROADCAST as u32 != 0 {
flags |= consts::IFF_BROADCAST;
}
if attrs.raw_flags & libc::IFF_LOOPBACK as u32 != 0 {
flags |= consts::IFF_LOOPBACK;
}
if attrs.raw_flags & libc::IFF_POINTOPOINT as u32 != 0 {
flags |= consts::IFF_POINTOPOINT;
}
if attrs.raw_flags & libc::IFF_MULTICAST as u32 != 0 {
flags |= consts::IFF_MULTICAST;
}
flags
};
if if_info_msg.flags & libc::IFF_PROMISC as u32 != 0 {
attrs.promisc = 1;
}
if if_info_msg.flags & libc::IFF_ALLMULTI as u32 != 0 {
attrs.all_multi = 1;
}
if if_info_msg.flags & libc::IFF_MULTICAST as u32 != 0 {
attrs.multicast = 1;
}
attrs.encap_type = match if_info_msg.ifi_type {
0 => "generic".to_string(),
libc::ARPHRD_ETHER => "ether".to_string(),
_ => "unknown".to_string(),
};
attrs
}
}
impl Link for Kind {
fn link_type(&self) -> String {
match self {
Kind::Device(_) => "device".to_string(),
Kind::Dummy(_) => "dummy".to_string(),
Kind::Bridge { .. } => "bridge".to_string(),
Kind::Veth { .. } => "veth".to_string(),
}
}
fn attrs(&self) -> &LinkAttrs {
match self {
Kind::Device(attrs) => attrs,
Kind::Dummy(attrs) => attrs,
Kind::Bridge { attrs, .. } => attrs,
Kind::Veth { attrs, .. } => attrs,
}
}
fn attrs_mut(&mut self) -> &mut LinkAttrs {
match self {
Kind::Device(attrs) => attrs,
Kind::Dummy(attrs) => attrs,
Kind::Bridge { attrs, .. } => attrs,
Kind::Veth { attrs, .. } => attrs,
}
}
fn kind(&self) -> &Kind {
self
}
}
#[derive(Debug, Default, Clone)]
pub struct LinkXdp {
fd: i32,
attached: bool,
attache_mode: u32,
flags: u32,
prog_id: u32,
}
impl LinkXdp {
fn new() -> Self {
Self::default()
}
fn parse(data: &[u8]) -> Result<Self> {
let mut xdp = Self::new();
let rt_attrs = NetlinkRouteAttr::from(data)?;
for attr in rt_attrs {
match attr.rt_attr.rta_type {
consts::IFLA_XDP_FD => {
xdp.fd = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
consts::IFLA_XDP_ATTACHED => {
xdp.attache_mode = attr.value[0].try_into()?;
xdp.attached = attr.value[0] != 0;
}
consts::IFLA_XDP_FLAGS => {
xdp.flags = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
consts::IFLA_XDP_PROG_ID => {
xdp.prog_id = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
_ => {}
}
}
Ok(xdp)
}
}
pub fn link_deserialize(buf: &[u8]) -> Result<Box<dyn Link>> {
let if_info_msg = InfoMessage::deserialize(buf)?;
let rt_attrs = NetlinkRouteAttr::from(&buf[if_info_msg.len()..])?;
let mut base = LinkAttrs::from(if_info_msg);
let mut data = HashMap::new();
for attr in rt_attrs {
match attr.rt_attr.rta_type {
libc::IFLA_LINKINFO => {
data = extract_link_info(&mut base, NetlinkRouteAttr::from(&attr.value)?)?
}
libc::IFLA_ADDRESS => {
base.hw_addr = attr.value;
}
libc::IFLA_IFNAME => {
base.name = String::from_utf8(attr.value[..attr.value.len() - 1].to_vec())?;
}
libc::IFLA_MTU => {
base.mtu = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_LINK => {
base.parent_index = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_MASTER => {
base.master_index = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_TXQLEN => {
base.tx_queue_len = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_IFALIAS => {
base.alias = String::from_utf8(attr.value[..attr.value.len() - 1].to_vec())?;
}
libc::IFLA_STATS => {
}
libc::IFLA_STATS64 => {
}
libc::IFLA_XDP => {
base.xdp = LinkXdp::parse(&attr.value)?;
}
libc::IFLA_PROTINFO | consts::NLA_F_NESTED => {
}
libc::IFLA_OPERSTATE => {
base.oper_state = attr.value[0];
}
libc::IFLA_PHYS_SWITCH_ID => {
base.phys_switch_id = i32::from_be_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_LINK_NETNSID => {
base.netns_id = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_GSO_MAX_SIZE => {
base.gso_max_size = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_GSO_MAX_SEGS => {
base.gso_max_segs = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
consts::IFLA_GRO_MAX_SIZE => {
base.gro_max_size = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_VFINFO_LIST => {
}
libc::IFLA_NUM_TX_QUEUES => {
base.num_tx_queues = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_NUM_RX_QUEUES => {
base.num_rx_queues = i32::from_ne_bytes(attr.value[..4].try_into()?);
}
libc::IFLA_GROUP => {
base.group = u32::from_ne_bytes(attr.value[..4].try_into()?);
}
_ => {}
}
}
Ok(match &base.link_type[..] {
"device" => Box::new(Kind::Device(base)),
"dummy" => Box::new(Kind::Dummy(base)),
"bridge" => Box::new(Kind::Bridge {
attrs: base,
hello_time: data
.get(&consts::IFLA_BR_HELLO_TIME)
.map(|v| u32::from_ne_bytes(v[..4].try_into().unwrap_or([0; 4]))),
ageing_time: data
.get(&consts::IFLA_BR_AGEING_TIME)
.map(|v| u32::from_ne_bytes(v[..4].try_into().unwrap_or([0; 4]))),
multicast_snooping: data.get(&consts::IFLA_BR_MCAST_SNOOPING).map(|v| v[0] == 1),
vlan_filtering: data.get(&consts::IFLA_BR_VLAN_FILTERING).map(|v| v[0] == 1),
}),
"veth" => Box::new(Kind::Veth {
attrs: base,
peer_name: Default::default(),
peer_hw_addr: None,
peer_ns: None,
}),
_ => Box::new(Kind::Device(base)),
})
}
fn extract_link_info(
base: &mut LinkAttrs,
infos: Vec<NetlinkRouteAttr>,
) -> Result<HashMap<u16, Vec<u8>>> {
let mut data = HashMap::new();
for info in infos {
match info.rt_attr.rta_type {
libc::IFLA_INFO_KIND => {
base.link_type = String::from_utf8(info.value[..info.value.len() - 1].to_vec())?;
}
libc::IFLA_INFO_DATA => {
data = NetlinkRouteAttr::map(&info.value)?;
}
libc::IFLA_INFO_SLAVE_KIND => {
}
libc::IFLA_INFO_SLAVE_DATA => {
}
_ => {}
}
}
Ok(data)
}
pub fn link_new(link: &(impl Link + ?Sized), flags: i32) -> Result<NetlinkRequest> {
let base = link.attrs();
let mut req = NetlinkRequest::new(libc::RTM_NEWLINK, flags);
let mut msg = Box::new(InfoMessage::new(libc::AF_UNSPEC));
if base.index != 0 {
msg.index = base.index;
}
if base.flags & consts::IFF_UP != 0 {
msg.flags = libc::IFF_UP as u32;
msg.change = libc::IFF_UP as u32;
}
if base.flags & consts::IFF_BROADCAST != 0 {
msg.flags |= libc::IFF_BROADCAST as u32;
msg.change |= libc::IFF_BROADCAST as u32;
}
if base.flags & consts::IFF_POINTOPOINT != 0 {
msg.flags |= libc::IFF_POINTOPOINT as u32;
msg.change |= libc::IFF_POINTOPOINT as u32;
}
if base.flags & consts::IFF_MULTICAST != 0 {
msg.flags |= libc::IFF_MULTICAST as u32;
msg.change |= libc::IFF_MULTICAST as u32;
}
req.add_data(msg);
let name = Box::new(NetlinkRouteAttr::new(
libc::IFLA_IFNAME,
zero_terminated(&base.name),
));
req.add_data(name);
if base.mtu > 0 {
let mtu = Box::new(NetlinkRouteAttr::new(
libc::IFLA_MTU,
base.mtu.to_ne_bytes().to_vec(),
));
req.add_data(mtu);
}
if base.tx_queue_len > 0 {
let tx_queue_len = Box::new(NetlinkRouteAttr::new(
libc::IFLA_TXQLEN,
base.tx_queue_len.to_ne_bytes().to_vec(),
));
req.add_data(tx_queue_len);
}
if base.num_tx_queues > 0 {
let num_tx_queues = Box::new(NetlinkRouteAttr::new(
libc::IFLA_NUM_TX_QUEUES,
base.num_tx_queues.to_ne_bytes().to_vec(),
));
req.add_data(num_tx_queues);
}
if base.num_rx_queues > 0 {
let num_rx_queues = Box::new(NetlinkRouteAttr::new(
libc::IFLA_NUM_RX_QUEUES,
base.num_rx_queues.to_ne_bytes().to_vec(),
));
req.add_data(num_rx_queues);
}
let mut link_info = Box::new(NetlinkRouteAttr::new(libc::IFLA_LINKINFO, vec![]));
link_info.add_child(libc::IFLA_INFO_KIND, link.link_type().as_bytes().to_vec());
match link.kind() {
Kind::Bridge {
attrs: _,
hello_time,
ageing_time,
multicast_snooping,
vlan_filtering,
} => {
let mut data = Box::new(NetlinkRouteAttr::new(libc::IFLA_INFO_DATA, vec![]));
if let Some(hello_time) = hello_time {
data.add_child(
consts::IFLA_BR_HELLO_TIME,
hello_time.to_ne_bytes().to_vec(),
);
}
if let Some(ageing_time) = ageing_time {
data.add_child(
consts::IFLA_BR_AGEING_TIME,
ageing_time.to_ne_bytes().to_vec(),
);
}
if let Some(multicast_snooping) = multicast_snooping {
data.add_child(
consts::IFLA_BR_MCAST_SNOOPING,
(*multicast_snooping as u8).to_ne_bytes().to_vec(),
);
}
if let Some(vlan_filtering) = vlan_filtering {
data.add_child(
consts::IFLA_BR_VLAN_FILTERING,
(*vlan_filtering as u8).to_ne_bytes().to_vec(),
);
}
link_info.add_child_from_attr(data);
}
Kind::Veth {
attrs: _,
peer_name,
peer_hw_addr,
peer_ns,
} => {
let mut data = Box::new(NetlinkRouteAttr::new(libc::IFLA_INFO_DATA, vec![]));
let mut peer_info = Box::new(NetlinkRouteAttr::new(consts::VETH_INFO_PEER, vec![]));
peer_info.add_child_from_attr(Box::new(InfoMessage::new(libc::AF_UNSPEC)));
peer_info.add_child(libc::IFLA_IFNAME, zero_terminated(peer_name));
if base.mtu > 0 {
peer_info.add_child(libc::IFLA_MTU, base.mtu.to_ne_bytes().to_vec());
}
if base.tx_queue_len >= 0 {
peer_info.add_child(libc::IFLA_TXQLEN, base.tx_queue_len.to_ne_bytes().to_vec());
}
if base.num_tx_queues > 0 {
peer_info.add_child(
libc::IFLA_NUM_TX_QUEUES,
base.num_tx_queues.to_ne_bytes().to_vec(),
);
}
if base.num_rx_queues > 0 {
peer_info.add_child(
libc::IFLA_NUM_RX_QUEUES,
base.num_rx_queues.to_ne_bytes().to_vec(),
);
}
if let Some(hw_addr) = peer_hw_addr {
peer_info.add_child(libc::IFLA_ADDRESS, hw_addr.to_vec());
}
match peer_ns {
Some(ns) => match ns {
Namespace::Pid(pid) => {
peer_info.add_child(libc::IFLA_NET_NS_PID, pid.to_ne_bytes().to_vec());
}
Namespace::Fd(fd) => {
peer_info.add_child(libc::IFLA_NET_NS_FD, fd.to_ne_bytes().to_vec());
}
},
None => {}
}
data.add_child_from_attr(peer_info);
link_info.add_child_from_attr(data);
}
_ => {}
}
req.add_data(link_info);
Ok(req)
}
pub fn link_del(index: i32) -> Result<NetlinkRequest> {
let mut req = NetlinkRequest::new(libc::RTM_DELLINK, libc::NLM_F_ACK);
let mut msg = Box::new(InfoMessage::new(libc::AF_UNSPEC));
msg.index = index;
req.add_data(msg);
Ok(req)
}
pub fn link_get(attr: &LinkAttrs) -> Result<NetlinkRequest> {
let mut req = NetlinkRequest::new(libc::RTM_GETLINK, libc::NLM_F_ACK);
let mut msg = Box::new(InfoMessage::new(libc::AF_UNSPEC));
if attr.index != 0 {
msg.index = attr.index;
}
req.add_data(msg);
if !attr.name.is_empty() {
let name = Box::new(NetlinkRouteAttr::new(
libc::IFLA_IFNAME,
attr.name.as_bytes().to_vec(),
));
req.add_data(name);
}
let ext_mask = Box::new(NetlinkRouteAttr::new(
libc::IFLA_EXT_MASK,
1u32.to_ne_bytes().to_vec(),
));
req.add_data(ext_mask);
Ok(req)
}
pub fn link_setup(index: i32) -> Result<NetlinkRequest> {
let mut req = NetlinkRequest::new(libc::RTM_NEWLINK, libc::NLM_F_ACK);
let mut msg = Box::new(InfoMessage::new(libc::AF_UNSPEC));
msg.index = index;
msg.flags = 1;
msg.change = 1;
req.add_data(msg);
Ok(req)
}
#[cfg(test)]
mod tests {
use super::*;
static NETLINK_MSG: [u8; 1752] = [
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0C, 0x00, 0x03, 0x00, 0x64, 0x6F, 0x63, 0x6B, 0x65, 0x72, 0x30, 0x00, 0x08, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05,
0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xDC, 0x05, 0x00, 0x00,
0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, 0x08, 0x00, 0x33, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0x08, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x00, 0x1F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x28, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x20, 0x00,
0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06,
0x00, 0x6E, 0x6F, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, 0x01, 0x00,
0x00, 0x00, 0x08, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x01,
0x00, 0x00, 0x00, 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0A, 0x00, 0x01, 0x00, 0x02, 0x42, 0x3B, 0x14, 0xA7, 0x98, 0x00, 0x00, 0x0A,
0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xC4, 0x00, 0x17, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x2B, 0x00, 0x05, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0xAC, 0x01, 0x12, 0x00, 0x0B, 0x00, 0x01, 0x00, 0x62, 0x72, 0x69,
0x64, 0x67, 0x65, 0x00, 0x00, 0x9C, 0x01, 0x02, 0x00, 0x0C, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0C, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x13, 0x00, 0x71, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01,
0x00, 0xDC, 0x05, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x08, 0x00,
0x03, 0x00, 0xD0, 0x07, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x30, 0x75, 0x00, 0x00, 0x08,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x80, 0x00, 0x00,
0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0C, 0x00, 0x0B, 0x00, 0x80, 0x00, 0x02, 0x42, 0x3B, 0x14, 0xA7, 0x98, 0x0C, 0x00,
0x0A, 0x00, 0x80, 0x00, 0x02, 0x42, 0x3B, 0x14, 0xA7, 0x98, 0x06, 0x00, 0x0C, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0E, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x14,
0x00, 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x2E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x81, 0x00, 0x00, 0x00, 0x06,
0x00, 0x27, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x01, 0x00, 0x00,
0x00, 0x05, 0x00, 0x17, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2A, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x1A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1B, 0x00,
0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x1C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1D,
0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x2B, 0x00, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00,
0x2C, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x1E, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0C, 0x00, 0x1F, 0x00, 0x90, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x20, 0x00, 0x9C, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x21,
0x00, 0xD4, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x22, 0x00, 0xE8, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x23, 0x00, 0x34, 0x0C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x25, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x1A,
0x00, 0x88, 0x00, 0x02, 0x00, 0x84, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xE8, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x02, 0x0A, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
0x00, 0x05, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xC2, 0xC5, 0x77, 0x00, 0x0C, 0x89, 0x00, 0x00,
0xE8, 0x03, 0x00, 0x00, 0xE4, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
0x00, 0xDC, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x0F, 0x00, 0x00, 0xE8,
0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x80, 0x3A, 0x09, 0x00, 0x80, 0x51, 0x01, 0x00,
0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xEA,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0xE8, 0x03, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0xEE, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x2C, 0x01, 0x03, 0x00, 0x25, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
];
#[test]
fn test_link_deserialize() {
let link = link_deserialize(&NETLINK_MSG).unwrap();
assert_eq!(link.link_type(), "bridge");
let attrs = link.attrs();
assert_eq!(attrs.index, 4);
assert_eq!(attrs.name, "docker0");
assert_eq!(attrs.mtu, 1500);
assert_eq!(attrs.raw_flags, 0x1003);
match link.kind() {
Kind::Bridge {
attrs: _,
hello_time,
ageing_time,
multicast_snooping,
vlan_filtering,
} => {
assert_eq!(hello_time.unwrap(), 200);
assert_eq!(ageing_time.unwrap(), 30000);
assert!(multicast_snooping.unwrap());
assert!(!vlan_filtering.unwrap());
}
_ => panic!("Expected bridge link"),
}
}
}