From baed9afcddb845d3d61380f53bf9eee93b0cd4c7 Mon Sep 17 00:00:00 2001 From: little-dude Date: Fri, 12 Apr 2024 15:19:15 +0200 Subject: [PATCH] add test for link with IFLA_VRF_PORT_TABLE attribute Note that I had to derive `Default` on a couple structs to make the test manageable. Also, I noticed that the packet we emit does not 100% matches the packet emitted by the kernel. --- src/link/af_spec/inet.rs | 2 +- src/link/af_spec/inet6_devconf.rs | 2 +- src/link/af_spec/inet6_icmp.rs | 2 +- src/link/af_spec/inet6_stats.rs | 2 +- src/link/link_info/mod.rs | 2 +- src/link/map.rs | 2 +- src/link/mod.rs | 6 +- src/link/stats.rs | 2 +- src/link/stats64.rs | 2 +- src/link/tests/vrf.rs | 316 +++++++++++++++++++++++++++++- 10 files changed, 325 insertions(+), 13 deletions(-) diff --git a/src/link/af_spec/inet.rs b/src/link/af_spec/inet.rs index 27a5a0da..b151507f 100644 --- a/src/link/af_spec/inet.rs +++ b/src/link/af_spec/inet.rs @@ -123,7 +123,7 @@ buffer!(InetDevConfBuffer(DEV_CONF_LEN) { arp_evict_nocarrier: (i32, 128..132), }); -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[non_exhaustive] pub struct InetDevConf { pub forwarding: i32, diff --git a/src/link/af_spec/inet6_devconf.rs b/src/link/af_spec/inet6_devconf.rs index 740ce3da..f79f82f3 100644 --- a/src/link/af_spec/inet6_devconf.rs +++ b/src/link/af_spec/inet6_devconf.rs @@ -216,7 +216,7 @@ impl Emitable for Inet6DevConf { } } -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[non_exhaustive] pub struct Inet6DevConf { pub forwarding: i32, diff --git a/src/link/af_spec/inet6_icmp.rs b/src/link/af_spec/inet6_icmp.rs index 238e3245..dc2918eb 100644 --- a/src/link/af_spec/inet6_icmp.rs +++ b/src/link/af_spec/inet6_icmp.rs @@ -7,7 +7,7 @@ use netlink_packet_utils::{ pub(crate) const ICMP6_STATS_LEN: usize = 48; -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[non_exhaustive] pub struct Icmp6Stats { pub num: i64, diff --git a/src/link/af_spec/inet6_stats.rs b/src/link/af_spec/inet6_stats.rs index 685d9693..6f0fd3a7 100644 --- a/src/link/af_spec/inet6_stats.rs +++ b/src/link/af_spec/inet6_stats.rs @@ -46,7 +46,7 @@ buffer!(Inet6StatsBuffer(INET6_STATS_LEN) { in_ce_pkts: (i64, 280..288), }); -#[derive(Clone, Copy, Eq, PartialEq, Debug)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)] #[non_exhaustive] pub struct Inet6Stats { pub num: i64, diff --git a/src/link/link_info/mod.rs b/src/link/link_info/mod.rs index b3145218..628b4fbf 100644 --- a/src/link/link_info/mod.rs +++ b/src/link/link_info/mod.rs @@ -42,7 +42,7 @@ pub use self::gre_tap6::InfoGreTap6; pub use self::gtp::InfoGtp; pub use self::hsr::{HsrProtocol, InfoHsr}; pub use self::info_data::InfoData; -pub use self::info_port::{InfoPortData, InfoPortKind}; +pub use self::info_port::{InfoPortData, InfoPortKind, InfoVrfPort}; pub use self::infos::{InfoKind, LinkInfo}; pub use self::ipoib::InfoIpoib; pub use self::ipvlan::{InfoIpVlan, InfoIpVtap, IpVlanMode, IpVtapMode}; diff --git a/src/link/map.rs b/src/link/map.rs index 425243ae..3a0e761f 100644 --- a/src/link/map.rs +++ b/src/link/map.rs @@ -16,7 +16,7 @@ buffer!(MapBuffer(LINK_MAP_LEN) { port: (u8, 27), }); -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct Map { pub memory_start: u64, diff --git a/src/link/mod.rs b/src/link/mod.rs index ae69f161..eb27982e 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -44,9 +44,9 @@ pub use self::link_info::{ InfoGreTap, InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, InfoSitTun, InfoTun, InfoVeth, - InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm, IpVlanMode, IpVtapMode, - LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, MacSecValidate, - MacVlanMode, MacVtapMode, MiiStatus, VlanQosMapping, + InfoVlan, InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanMode, + IpVtapMode, LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, + MacSecValidate, MacVlanMode, MacVtapMode, MiiStatus, VlanQosMapping, }; pub use self::link_layer_type::LinkLayerType; pub use self::link_state::State; diff --git a/src/link/stats.rs b/src/link/stats.rs index 7ab476d2..0570d639 100644 --- a/src/link/stats.rs +++ b/src/link/stats.rs @@ -7,7 +7,7 @@ use netlink_packet_utils::{ pub(crate) const LINK_STATS_LEN: usize = 96; -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct Stats { /// total packets received diff --git a/src/link/stats64.rs b/src/link/stats64.rs index 5bece142..355556ad 100644 --- a/src/link/stats64.rs +++ b/src/link/stats64.rs @@ -35,7 +35,7 @@ buffer!(Stats64Buffer(LINK_STATS64_LEN) { rx_otherhost_dropped: (u64, 192..200), }); -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub struct Stats64 { /// total packets received diff --git a/src/link/tests/vrf.rs b/src/link/tests/vrf.rs index 34049432..a6fee80d 100644 --- a/src/link/tests/vrf.rs +++ b/src/link/tests/vrf.rs @@ -1,11 +1,18 @@ // SPDX-License-Identifier: MIT +use std::net::Ipv6Addr; + +use netlink_packet_utils::nla::DefaultNla; use netlink_packet_utils::{Emitable, Parseable}; use crate::link::link_flag::LinkFlags; +use crate::link::link_info::InfoVrfPort; use crate::link::{ - InfoData, InfoKind, InfoVrf, LinkAttribute, LinkHeader, LinkInfo, - LinkLayerType, LinkMessage, LinkMessageBuffer, + AfSpecInet, AfSpecInet6, AfSpecUnspec, Icmp6Stats, Inet6CacheInfo, + Inet6DevConf, Inet6IfaceFlags, Inet6Stats, InetDevConf, InfoData, InfoKind, + InfoPortData, InfoPortKind, InfoVrf, LinkAttribute, LinkHeader, LinkInfo, + LinkLayerType, LinkMessage, LinkMessageBuffer, LinkXdp, Map, State, Stats, + Stats64, XdpAttached, }; use crate::AddressFamily; @@ -47,3 +54,308 @@ fn test_parsing_link_vrf() { assert_eq!(buf, raw); } + +/// This link correspond to a veth link attached to a VRF, created as +/// follow: +/// +/// ```no_rust +/// sudo ip link add vrf-blue type vrf table 10 +/// sudo ip link set dev vrf-blue up +/// sudo ip link add veth20 type veth +/// sudo ip link set veth20 master vrf-blue +/// ``` +#[test] +fn test_link_info_with_ifla_vrf_port_table() { + let data = vec![ + 0x00, 0x00, // interface family unspec + 0x01, 0x00, // link layer type ethernet + 0x23, 0x00, 0x00, 0x00, // index 35 + 0x02, 0x10, 0x00, 0x00, // flags: broadcast, multicast + 0x00, 0x00, 0x00, 0x00, // change flags + 0x0b, 0x00, 0x03, 0x00, 0x76, 0x65, 0x74, 0x68, 0x32, 0x30, 0x00, 0x00, + 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, + 0x00, // tx queue len: 1000 + 0x05, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, + 0x00, // oper state: down + 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, // link mode: 0 + 0x08, 0x00, 0x04, 0x00, 0xdc, 0x05, 0x00, 0x00, // mtu: 1500 + 0x08, 0x00, 0x32, 0x00, 0x44, 0x00, 0x00, 0x00, // min mtu: 68 + 0x08, 0x00, 0x33, 0x00, 0xff, 0xff, 0x00, 0x00, // max mtu: 65535 + 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // group: 0 + 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, // promisc: 0 + 0x08, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, // unparsed (all multi count: 0) + 0x08, 0x00, 0x1f, 0x00, 0x1a, 0x00, 0x00, + 0x00, // number of tx queues: 26 + 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, + 0x00, // Max GSO segment count: 65535 + 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, + 0x00, // Max GSO size: 65536 + 0x08, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x01, + 0x00, // unparsed (GRO max size) + 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, + 0x00, // unparsed (TSO max size) + 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, + 0x00, // unparsed (TSO max segments) + 0x08, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x00, + 0x00, // number of rx queues: 26 + 0x08, 0x00, 0x0a, 0x00, 0x21, 0x00, 0x00, + 0x00, // controller 33 (master) + 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, // carrier: not restricted + 0x09, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x6f, 0x70, 0x00, 0x00, 0x00, + 0x00, // queue discipline: noop + 0x08, 0x00, 0x23, 0x00, 0x01, 0x00, 0x00, + 0x00, // carrier changes: 1 + 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, + 0x00, // carrier up count: 0 + 0x08, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, + 0x00, // carrier down count: 1 + 0x05, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, // proto down: 0 + // Map + 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, + // end map + 0x0a, 0x00, 0x01, 0x00, 0x82, 0x84, 0x7b, 0x35, 0x68, 0x99, 0x00, + 0x00, // hw address: 82:84:7b:35:68:99 + 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, // hw broadcast address + // Stats64 + 0xcc, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Stats + 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, // XDP: attached: none + // Link Info + 0x24, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, 0x76, 0x65, 0x74, 0x68, + 0x00, 0x00, 0x00, 0x00, // Kind: veth + 0x08, 0x00, 0x04, 0x00, 0x76, 0x72, 0x66, 0x00, 0x0c, 0x00, 0x05, + 0x00, // Port kind: vrf + 0x08, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00, + 0x00, // port data: table ID 10 + 0x08, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, // Link: 34 + // AF spec header + 0x1c, 0x03, 0x1a, 0x00, // AF spec inet header + 0x8c, 0x00, 0x02, 0x00, // DevConf + 0x88, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, + 0x01, 0x00, 0x00, 0x00, // AF spec inet6 header + 0x8c, 0x02, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, // Flags + // Cache info + 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x59, 0x28, 0x18, 0x00, + 0x00, 0x84, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + // net6 dev conf + 0xf0, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xdc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xa0, 0x0f, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x00, 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, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // inet6 stats + 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, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // icmp6 stats + 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, // token: :: + 0x14, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // addr gen mode: 0 + 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 35, + link_layer_type: LinkLayerType::Ether, + flags: LinkFlags::Broadcast | LinkFlags::Multicast, + change_mask: LinkFlags::empty(), + }, + attributes: vec![ + LinkAttribute::IfName("veth20".to_string()), + LinkAttribute::TxQueueLen(1000), + LinkAttribute::OperState(State::Down), + LinkAttribute::Mode(0), + LinkAttribute::Mtu(1500), + LinkAttribute::MinMtu(68), + LinkAttribute::MaxMtu(65535), + LinkAttribute::Group(0), + LinkAttribute::Promiscuity(0), + LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), + LinkAttribute::NumTxQueues(26), + LinkAttribute::GsoMaxSegs(65535), + LinkAttribute::GsoMaxSize(65536), + LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), + LinkAttribute::Other(DefaultNla::new(59, vec![248, 255, 7, 0])), + LinkAttribute::Other(DefaultNla::new(60, vec![255, 255, 0, 0])), + LinkAttribute::NumRxQueues(26), + LinkAttribute::Controller(33), + LinkAttribute::Carrier(0), + LinkAttribute::Qdisc("noop".to_string()), + LinkAttribute::CarrierChanges(1), + LinkAttribute::CarrierUpCount(0), + LinkAttribute::CarrierDownCount(1), + LinkAttribute::ProtoDown(0), + LinkAttribute::Map(Map::default()), + LinkAttribute::Address(vec![130, 132, 123, 53, 104, 153]), + LinkAttribute::Broadcast(vec![255, 255, 255, 255, 255, 255]), + LinkAttribute::Stats64(Stats64::default()), + LinkAttribute::Stats(Stats::default()), + LinkAttribute::Xdp(vec![LinkXdp::Attached(XdpAttached::None)]), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Veth), + LinkInfo::PortKind(InfoPortKind::Vrf), + LinkInfo::PortData(InfoPortData::VrfPort(vec![ + InfoVrfPort::TableId(10), + ])), + ]), + LinkAttribute::Link(34), + LinkAttribute::AfSpecUnspec(vec![ + AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { + forwarding: 1, + secure_redirects: 1, + send_redirects: 1, + shared_media: 1, + accept_source_route: 1, + igmpv2_unsolicited_report_interval: 10000, + igmpv3_unsolicited_report_interval: 1000, + arp_evict_nocarrier: 1, + ..Default::default() + })]), + AfSpecUnspec::Inet6(vec![ + AfSpecInet6::Flags(Inet6IfaceFlags::empty()), + AfSpecInet6::CacheInfo(Inet6CacheInfo { + max_reasm_len: 65535, + tstamp: 1583193, + reachable_time: 33792, + retrans_time: 1000, + }), + AfSpecInet6::DevConf(Inet6DevConf { + forwarding: 1, + hoplimit: 64, + mtu6: 1500, + accept_ra: 1, + autoconf: 1, + dad_transmits: 1, + rtr_solicits: -1, + rtr_solicit_interval: 4000, + rtr_solicit_delay: 1000, + temp_valid_lft: 604800, + temp_prefered_lft: 86400, + regen_max_retry: 3, + max_desync_factor: 600, + max_addresses: 16, + accept_ra_defrtr: 1, + accept_ra_pinfo: 1, + accept_ra_rtr_pref: 1, + rtr_probe_interval: 60000, + mldv1_unsolicited_report_interval: 10000, + mldv2_unsolicited_report_interval: 1000, + suppress_frag_ndisc: 1, + accept_ra_min_hop_limit: 1, + rtr_solicit_max_interval: 3600000, + enhanced_dad: 1, + ra_defrtr_metric: 1024, + ioam6_id: 65535, + ioam6_id_wide: -1, + ndisc_evict_nocarrier: 1, + accept_ra_mtu: 1, + accept_dad: 1, + ..Default::default() + }), + AfSpecInet6::Stats(Inet6Stats { + num: 37, + ..Default::default() + }), + AfSpecInet6::Icmp6Stats(Icmp6Stats { + num: 6, + ..Default::default() + }), + AfSpecInet6::Token(Ipv6Addr::UNSPECIFIED), + AfSpecInet6::AddrGenMode(0), + ]), + ]), + ], + }; + let link = LinkMessage::parse(&LinkMessageBuffer::new(&data)).unwrap(); + assert_eq!(expected, link); + let mut buf = vec![0; data.len()]; + // FIXME: the packet we write is not a perfect match with the + // packet we received from the kernel. + // + // link.emit(&mut buf[..]); + // assert_eq!(data, buf); +}