diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..fe7e7f10 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +name: Build + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - main + +jobs: + build: + name: Build + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + include: + - rust_target: "x86_64-unknown-linux-gnu" + - rust_target: "x86_64-unknown-freebsd" + - rust_target: "x86_64-unknown-fuchsia" + - rust_target: "x86_64-apple-darwin" + - rust_target: "x86_64-linux-android" + + steps: + - uses: actions/checkout@v3 + + - name: Install Rust Stable + run: | + rustup override set stable + rustup update stable + rustup target add ${{ matrix.rust_target }} + + - name: Build test for ${{ matrix.rust_target }} + run: cargo build --target ${{ matrix.rust_target }} diff --git a/Cargo.toml b/Cargo.toml index 8097eced..5686956f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,26 +16,17 @@ rich_nlas = [] [dependencies] anyhow = "1.0.31" +bitflags = "1.2.1" byteorder = "1.3.2" libc = "0.2.66" +log = { version = "0.4.20", features = ["std"] } netlink-packet-core = { version = "0.7.0" } netlink-packet-utils = { version = "0.5.2" } -bitflags = "1.2.1" [[example]] name = "dump_packet_links" [dev-dependencies] -criterion = "0.3.0" pcap-file = "1.1.1" -lazy_static = "1.4.0" netlink-sys = { version = "0.8.5" } pretty_assertions = "0.7.2" - -[[bench]] -name = "link_message" -harness = false - -[[bench]] -name = "rtnetlink_dump" -harness = false diff --git a/README.md b/README.md index b6903856..f4732fe9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,59 @@ -# netlink-packet-route +# Rust crate for Netlink Route Protocol -Rust crate providing netlink message parsing and generating support for the -[netlink route protocol][1]. +The `netlink-packet-route` crate is designed to abstract Netlink route +protocol([`rtnetlink`][rtnetlink_man]) packet into Rust data types. The goal of +this crate is saving netlink user from reading Kernel Netlink codes. -Rust crate document could be found at [docs.rs][2]. +This crate grouped Netlink route protocol into these modules: + * `link`: NIC interface, similar to to `ip link` command. + * `address`: IP address, similar to `ip address` command. + * `route`: Route, similar to `ip route` command. + * `rule`: Route rule, similar to `ip rule` command. + * `tc`: Traffic control, similar to `tc` command. + * `neighbour`: Neighbour, similar to `ip neighbour` command. + * `neighbour_table`: Neighbour table, similar to `ip ntable` command. + * `nsid`: Namespace, similar to `ip netns` command. -[1]: https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html -[2]: https://docs.rs/netlink-packet-route/ +Normally, you should use [`rtnetlink`][rtnetlink_url] instead of using this +crate directly. + +## Development + * Please use `git commit --signoff` to append Signed-off-by trailer to commit + message. + + * For new source file, please append `// SPDX-License-Identifier: MIT` at + the beginning with content of license new code to MIT license. + + * No panic is allowed, please use `Result<>` instead of `unwrap()` or + `expect()`. + + * Please run `cargo fmt` and `cargo clippy` before creating pull request. + + * All struct/enum should be decorated by + [`#[non_exhaustive]`][non_exhaustive_doc] to preserve + API backwards compatibility unless explicit approval from maintainer. + + * Unknown netlink attribute should be stored into `Other(DefaultNla)` instead + of breaking decoding. + + * Please use unit test case to cover the serialize and deserialize of the + changed netlink attribute. To capture the netlink raw bytes, you may use + tcpdump/wireshark again the `nlmon` interface. For example: + + * The integration test(play with linux kernel netlink interface) should be + placed into `rtnetlink` crate. Current(netlink-packet-route) crate should + only unit test case only. + +```bash +modprobe nlmon +ip link add nl0 type nlmon +ip link set nl0 up +tcpdump -i nl0 -w netlink_capture_file.cap +``` + + * For certain netlink message which cannot captured by nlmon, please use + Rust Debug and explain every bits in comment. + +[rtnetlink_man]: https://www.man7.org/linux/man-pages/man7/rtnetlink.7.html +[rtnetlink_url]: https://docs.rs/rtnetlink +[non_exhaustive_doc]: https://doc.rust-lang.org/stable/reference/attributes/type_system.html#the-non_exhaustive-attribute diff --git a/benches/link_message.rs b/benches/link_message.rs deleted file mode 100644 index cfd22537..00000000 --- a/benches/link_message.rs +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: MIT - -use criterion::{criterion_group, criterion_main, Criterion}; - -use netlink_packet_route::{ - nlas::link::Nla, LinkHeader, LinkMessage, LinkMessageBuffer, -}; -use netlink_packet_utils::traits::{Parseable, ParseableParametrized}; - -const LINKMSG1: [u8; 96] = [ - 0x00, // address family - 0x00, // reserved - 0x04, 0x03, // link layer type 772 = loopback - 0x01, 0x00, 0x00, 0x00, // interface index = 1 - // Note: in the wireshark capture, the thrid byte is 0x01 - // but that does not correpond to any of the IFF_ flags... - 0x49, 0x00, 0x00, 0x00, // device flags: UP, LOOPBACK, RUNNING, LOWERUP - 0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag) - // nlas - 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, // device name L=7,T=3,V=lo - 0x00, // padding - 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, - 0x00, // TxQueue length L=8,T=13,V=1000 - 0x05, 0x00, 0x10, 0x00, 0x00, // OperState L=5,T=16,V=0 (unknown) - 0x00, 0x00, 0x00, // padding - 0x05, 0x00, 0x11, 0x00, 0x00, // Link mode L=5,T=17,V=0 - 0x00, 0x00, 0x00, // padding - 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, // MTU L=8,T=4,V=65536 - 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 - 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, // Promiscuity L=8,T=30,V=0 - 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, - 0x00, // Number of Tx Queues L=8,T=31,V=1 - 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, - 0x00, // Maximum GSO segment count L=8,T=40,V=65536 - 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, - 0x00, // Maximum GSO size L=8,T=41,V=65536 -]; - -fn b1(c: &mut Criterion) { - c.bench_function("parse LinkMessage header", |b| { - b.iter(|| { - LinkHeader::parse(&LinkMessageBuffer::new(&LINKMSG1[..])).unwrap(); - }) - }); - - c.bench_function("parse LinkMessage nlas", |b| { - b.iter(|| { - Vec::::parse_with_param( - &LinkMessageBuffer::new(&&LINKMSG1[..]), - 0_u8, - ) - .unwrap(); - }) - }); - - c.bench_function("parse LinkMessage", |b| { - b.iter(|| { - LinkMessage::parse(&LinkMessageBuffer::new(&&LINKMSG1[..])) - .unwrap(); - }) - }); -} - -criterion_group!(benches, b1); -criterion_main!(benches); diff --git a/benches/rtnetlink_dump.rs b/benches/rtnetlink_dump.rs deleted file mode 100644 index d2cb2491..00000000 --- a/benches/rtnetlink_dump.rs +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT - -use std::fs::File; - -use criterion::{criterion_group, criterion_main, Criterion}; -use pcap_file::PcapReader; - -use netlink_packet_core::NetlinkMessage; -use netlink_packet_route::RtnlMessage; - -fn bench(c: &mut Criterion) { - let pcap_reader = - PcapReader::new(File::open("data/rtnetlink.pcap").unwrap()).unwrap(); - let packets: Vec> = pcap_reader - .map(|pkt| pkt.unwrap().data.into_owned().to_vec()) - .collect(); - - c.bench_function("parse", move |b| { - b.iter(|| { - for (i, buf) in packets.iter().enumerate() { - NetlinkMessage::::deserialize(&buf[16..]) - .unwrap_or_else(|_| panic!("message {} failed", i)); - } - }) - }); -} - -criterion_group!(benches, bench); -criterion_main!(benches); diff --git a/data/README.md b/data/README.md deleted file mode 100644 index 36b93fe4..00000000 --- a/data/README.md +++ /dev/null @@ -1,23 +0,0 @@ -The rtnetlink dump was generated with: - -``` -sudo ip link add name qemu-br1 type bridge -sudo ip link set qemu-br1 up -sudo ip address add 192.168.10.1/24 dev qemu-br1 - -docker run -d -it busybox /bin/sh - -sudo ip netns add blue -sudo ip link add veth0 type veth peer name veth1 -sudo ip netns list -sudo ip link set veth1 netns blue -sudo ip -6 link add vxlan100 type vxlan id 100 dstport 4789 local 2001:db8:1::1 group ff05::100 dev veth0 ttl 5 -sudo brctl addbr br100 -sudo brctl addif br100 vxlan100 -sudo ip link show -sudo ip address show -sudo ip neigh show -sudo ip route show - -tc qdisc show -``` diff --git a/data/rtnetlink.pcap b/data/rtnetlink.pcap deleted file mode 100644 index de1dda12..00000000 Binary files a/data/rtnetlink.pcap and /dev/null differ diff --git a/examples/dump_neighbours.rs b/examples/dump_neighbours.rs index 2e941914..d2beb72d 100644 --- a/examples/dump_neighbours.rs +++ b/examples/dump_neighbours.rs @@ -11,7 +11,7 @@ use netlink_packet_route::{ NUD_PERMANENT, NUD_PROBE, NUD_REACHABLE, NUD_STALE, }, nlas::neighbour::Nla, - NeighbourMessage, RtnlMessage, AF_INET, AF_INET6, + AddressFamily, NeighbourMessage, RtnlMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; @@ -58,7 +58,10 @@ fn main() { entry, )) => { let address_family = entry.header.family as u16; - if address_family == AF_INET || address_family == AF_INET6 { + if address_family == u8::from(AddressFamily::Inet) as u16 + || address_family + == u8::from(AddressFamily::Inet6) as u16 + { print_entry(entry); } } diff --git a/examples/dump_packet_link_bridge_vlan.rs b/examples/dump_packet_link_bridge_vlan.rs index 7e2dd2cd..279cf09f 100644 --- a/examples/dump_packet_link_bridge_vlan.rs +++ b/examples/dump_packet_link_bridge_vlan.rs @@ -4,8 +4,8 @@ use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; use netlink_packet_route::{ - nlas::link::Nla, LinkMessage, RtnlMessage, AF_BRIDGE, - RTEXT_FILTER_BRVLAN_COMPRESSED, + link::{LinkAttribute, LinkMessage}, + AddressFamily, RtnlMessage, RTEXT_FILTER_BRVLAN_COMPRESSED, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; @@ -15,10 +15,10 @@ fn main() { socket.connect(&SocketAddr::new(0, 0)).unwrap(); let mut message = LinkMessage::default(); - message.header.interface_family = AF_BRIDGE as u8; + message.header.interface_family = AddressFamily::Bridge; message - .nlas - .push(Nla::ExtMask(RTEXT_FILTER_BRVLAN_COMPRESSED)); + .attributes + .push(LinkAttribute::ExtMask(RTEXT_FILTER_BRVLAN_COMPRESSED)); let mut packet = NetlinkMessage::new( NetlinkHeader::default(), NetlinkPayload::from(RtnlMessage::GetLink(message)), diff --git a/examples/dump_packet_links.rs b/examples/dump_packet_links.rs index c7a7884a..4d0beca5 100644 --- a/examples/dump_packet_links.rs +++ b/examples/dump_packet_links.rs @@ -3,7 +3,7 @@ use netlink_packet_core::{ NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST, }; -use netlink_packet_route::{LinkMessage, RtnlMessage}; +use netlink_packet_route::{link::LinkMessage, RtnlMessage}; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; fn main() { diff --git a/examples/new_rule.rs b/examples/new_rule.rs index ff99e99f..7adba171 100644 --- a/examples/new_rule.rs +++ b/examples/new_rule.rs @@ -5,8 +5,8 @@ use netlink_packet_core::{ NLM_F_EXCL, NLM_F_REQUEST, }; use netlink_packet_route::{ - constants::{AF_INET, FR_ACT_TO_TBL, RT_TABLE_DEFAULT}, - rule, RtnlMessage, RuleHeader, RuleMessage, + constants::{FR_ACT_TO_TBL, RT_TABLE_DEFAULT}, + rule, AddressFamily, RtnlMessage, RuleHeader, RuleMessage, }; use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr}; @@ -16,7 +16,7 @@ fn main() { socket.connect(&SocketAddr::new(0, 0)).unwrap(); let rule_msg_hdr = RuleHeader { - family: AF_INET as u8, + family: u8::from(AddressFamily::Inet), table: RT_TABLE_DEFAULT, action: FR_ACT_TO_TBL, ..Default::default() diff --git a/src/address_family_fallback.rs b/src/address_family_fallback.rs new file mode 100644 index 00000000..b241dc6a --- /dev/null +++ b/src/address_family_fallback.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +// We are using not using #[repr(u8)] here as we have duplicate(e.g. AF_ROUTE vs +// AF_NETLINK) here +pub enum AddressFamily { + #[default] + Unspec, + Local, + Unix, + Inet, + Inet6, + Other(u8), +} + +impl From for AddressFamily { + fn from(d: u8) -> Self { + match d { + d if d == libc::AF_UNSPEC as u8 => Self::Unspec, + d if d == libc::AF_LOCAL as u8 => Self::Local, + d if d == libc::AF_UNIX as u8 => Self::Unix, + d if d == libc::AF_INET as u8 => Self::Inet, + d if d == libc::AF_INET6 as u8 => Self::Inet6, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(v: AddressFamily) -> u8 { + match v { + AddressFamily::Unspec => libc::AF_UNSPEC as u8, + AddressFamily::Local => libc::AF_LOCAL as u8, + AddressFamily::Unix => libc::AF_UNIX as u8, + AddressFamily::Inet => libc::AF_INET as u8, + AddressFamily::Inet6 => libc::AF_INET6 as u8, + AddressFamily::Other(d) => d, + } + } +} diff --git a/src/address_family_freebsd.rs b/src/address_family_freebsd.rs new file mode 100644 index 00000000..22dc7d3b --- /dev/null +++ b/src/address_family_freebsd.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MIT + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +pub enum AddressFamily { + #[default] + Unspec, + Local, + Unix, + Inet, + Implink, + Pup, + Chaos, + Netbios, + Iso, + Osi, + Ecma, + Datakit, + Ccitt, + Sna, + Decnet, + Dli, + Lat, + Hylink, + Appletalk, + Route, + Link, + Coip, + Cnt, + Ipx, + Sip, + Isdn, + E164, + Inet6, + Natm, + Atm, + Netgraph, + Other(u8), +} + +impl From for AddressFamily { + fn from(d: u8) -> Self { + match d { + d if d == libc::AF_UNSPEC as u8 => Self::Unspec, + d if d == libc::AF_LOCAL as u8 => Self::Local, + d if d == libc::AF_UNIX as u8 => Self::Unix, + d if d == libc::AF_INET as u8 => Self::Inet, + d if d == libc::AF_IMPLINK as u8 => Self::Implink, + d if d == libc::AF_PUP as u8 => Self::Pup, + d if d == libc::AF_CHAOS as u8 => Self::Chaos, + d if d == libc::AF_NETBIOS as u8 => Self::Netbios, + d if d == libc::AF_ISO as u8 => Self::Iso, + d if d == libc::AF_OSI as u8 => Self::Osi, + d if d == libc::AF_ECMA as u8 => Self::Ecma, + d if d == libc::AF_DATAKIT as u8 => Self::Datakit, + d if d == libc::AF_CCITT as u8 => Self::Ccitt, + d if d == libc::AF_SNA as u8 => Self::Sna, + d if d == libc::AF_DECnet as u8 => Self::Decnet, + d if d == libc::AF_DLI as u8 => Self::Dli, + d if d == libc::AF_LAT as u8 => Self::Lat, + d if d == libc::AF_HYLINK as u8 => Self::Hylink, + d if d == libc::AF_APPLETALK as u8 => Self::Appletalk, + d if d == libc::AF_ROUTE as u8 => Self::Route, + d if d == libc::AF_LINK as u8 => Self::Link, + d if d == libc::AF_COIP as u8 => Self::Coip, + d if d == libc::AF_CNT as u8 => Self::Cnt, + d if d == libc::AF_IPX as u8 => Self::Ipx, + d if d == libc::AF_SIP as u8 => Self::Sip, + d if d == libc::AF_ISDN as u8 => Self::Isdn, + d if d == libc::AF_E164 as u8 => Self::E164, + d if d == libc::AF_INET6 as u8 => Self::Inet6, + d if d == libc::AF_NATM as u8 => Self::Natm, + d if d == libc::AF_ATM as u8 => Self::Atm, + d if d == libc::AF_NETGRAPH as u8 => Self::Netgraph, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(v: AddressFamily) -> u8 { + match v { + AddressFamily::Unspec => libc::AF_UNSPEC as u8, + AddressFamily::Local => libc::AF_LOCAL as u8, + AddressFamily::Unix => libc::AF_UNIX as u8, + AddressFamily::Inet => libc::AF_INET as u8, + AddressFamily::Implink => libc::AF_IMPLINK as u8, + AddressFamily::Pup => libc::AF_PUP as u8, + AddressFamily::Chaos => libc::AF_CHAOS as u8, + AddressFamily::Netbios => libc::AF_NETBIOS as u8, + AddressFamily::Iso => libc::AF_ISO as u8, + AddressFamily::Osi => libc::AF_OSI as u8, + AddressFamily::Ecma => libc::AF_ECMA as u8, + AddressFamily::Datakit => libc::AF_DATAKIT as u8, + AddressFamily::Ccitt => libc::AF_CCITT as u8, + AddressFamily::Sna => libc::AF_SNA as u8, + AddressFamily::Decnet => libc::AF_DECnet as u8, + AddressFamily::Dli => libc::AF_DLI as u8, + AddressFamily::Lat => libc::AF_LAT as u8, + AddressFamily::Hylink => libc::AF_HYLINK as u8, + AddressFamily::Appletalk => libc::AF_APPLETALK as u8, + AddressFamily::Route => libc::AF_ROUTE as u8, + AddressFamily::Link => libc::AF_LINK as u8, + AddressFamily::Coip => libc::AF_COIP as u8, + AddressFamily::Cnt => libc::AF_CNT as u8, + AddressFamily::Ipx => libc::AF_IPX as u8, + AddressFamily::Sip => libc::AF_SIP as u8, + AddressFamily::Isdn => libc::AF_ISDN as u8, + AddressFamily::E164 => libc::AF_E164 as u8, + AddressFamily::Inet6 => libc::AF_INET6 as u8, + AddressFamily::Natm => libc::AF_NATM as u8, + AddressFamily::Atm => libc::AF_ATM as u8, + AddressFamily::Netgraph => libc::AF_NETGRAPH as u8, + AddressFamily::Other(d) => d, + } + } +} diff --git a/src/address_family_linux.rs b/src/address_family_linux.rs new file mode 100644 index 00000000..3a6e3e03 --- /dev/null +++ b/src/address_family_linux.rs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT + +const AF_KCM: u8 = 41; +const AF_QIPCRTR: u8 = 42; +const AF_SMC: u8 = 43; +const AF_XDP: u8 = 44; +const AF_MCTP: u8 = 45; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +pub enum AddressFamily { + #[default] + Unspec, + Local, + Unix, + Inet, + Ax25, + Ipx, + Appletalk, + Netrom, + Bridge, + Atmpvc, + X25, + Inet6, + Rose, + Decnet, + Netbeui, + Security, + Key, + Route, + Netlink, + Packet, + Ash, + Econet, + Atmsvc, + Rds, + Sna, + Irda, + Pppox, + Wanpipe, + Llc, + Ib, + Mpls, + Can, + Tipc, + Bluetooth, + Iucv, + Rxrpc, + Isdn, + Phonet, + Ieee802154, + Caif, + Alg, + Nfc, + Vsock, + Kcm, + Qipcrtr, + Smc, + Xdp, + Mctp, + Other(u8), +} + +impl From for AddressFamily { + fn from(d: u8) -> Self { + match d { + d if d == libc::AF_UNSPEC as u8 => Self::Unspec, + d if d == libc::AF_UNIX as u8 => Self::Unix, + d if d == libc::AF_LOCAL as u8 => Self::Local, + d if d == libc::AF_INET as u8 => Self::Inet, + d if d == libc::AF_AX25 as u8 => Self::Ax25, + d if d == libc::AF_IPX as u8 => Self::Ipx, + d if d == libc::AF_APPLETALK as u8 => Self::Appletalk, + d if d == libc::AF_NETROM as u8 => Self::Netrom, + d if d == libc::AF_BRIDGE as u8 => Self::Bridge, + d if d == libc::AF_ATMPVC as u8 => Self::Atmpvc, + d if d == libc::AF_X25 as u8 => Self::X25, + d if d == libc::AF_INET6 as u8 => Self::Inet6, + d if d == libc::AF_ROSE as u8 => Self::Rose, + d if d == libc::AF_DECnet as u8 => Self::Decnet, + d if d == libc::AF_NETBEUI as u8 => Self::Netbeui, + d if d == libc::AF_SECURITY as u8 => Self::Security, + d if d == libc::AF_KEY as u8 => Self::Key, + d if d == libc::AF_NETLINK as u8 => Self::Netlink, + d if d == libc::AF_ROUTE as u8 => Self::Route, + d if d == libc::AF_PACKET as u8 => Self::Packet, + d if d == libc::AF_ASH as u8 => Self::Ash, + d if d == libc::AF_ECONET as u8 => Self::Econet, + d if d == libc::AF_ATMSVC as u8 => Self::Atmsvc, + d if d == libc::AF_RDS as u8 => Self::Rds, + d if d == libc::AF_SNA as u8 => Self::Sna, + d if d == libc::AF_IRDA as u8 => Self::Irda, + d if d == libc::AF_PPPOX as u8 => Self::Pppox, + d if d == libc::AF_WANPIPE as u8 => Self::Wanpipe, + d if d == libc::AF_LLC as u8 => Self::Llc, + d if d == libc::AF_IB as u8 => Self::Ib, + d if d == libc::AF_MPLS as u8 => Self::Mpls, + d if d == libc::AF_CAN as u8 => Self::Can, + d if d == libc::AF_TIPC as u8 => Self::Tipc, + d if d == libc::AF_BLUETOOTH as u8 => Self::Bluetooth, + d if d == libc::AF_IUCV as u8 => Self::Iucv, + d if d == libc::AF_RXRPC as u8 => Self::Rxrpc, + d if d == libc::AF_ISDN as u8 => Self::Isdn, + d if d == libc::AF_PHONET as u8 => Self::Phonet, + d if d == libc::AF_IEEE802154 as u8 => Self::Ieee802154, + d if d == libc::AF_CAIF as u8 => Self::Caif, + d if d == libc::AF_ALG as u8 => Self::Alg, + d if d == libc::AF_NFC as u8 => Self::Nfc, + d if d == libc::AF_VSOCK as u8 => Self::Vsock, + d if d == AF_KCM => Self::Kcm, + d if d == AF_QIPCRTR => Self::Qipcrtr, + d if d == AF_SMC => Self::Smc, + d if d == AF_XDP => Self::Xdp, + d if d == AF_MCTP => Self::Mctp, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(v: AddressFamily) -> u8 { + match v { + AddressFamily::Unspec => libc::AF_UNSPEC as u8, + AddressFamily::Local => libc::AF_LOCAL as u8, + AddressFamily::Unix => libc::AF_UNIX as u8, + AddressFamily::Inet => libc::AF_INET as u8, + AddressFamily::Ax25 => libc::AF_AX25 as u8, + AddressFamily::Ipx => libc::AF_IPX as u8, + AddressFamily::Appletalk => libc::AF_APPLETALK as u8, + AddressFamily::Netrom => libc::AF_NETROM as u8, + AddressFamily::Bridge => libc::AF_BRIDGE as u8, + AddressFamily::Atmpvc => libc::AF_ATMPVC as u8, + AddressFamily::X25 => libc::AF_X25 as u8, + AddressFamily::Inet6 => libc::AF_INET6 as u8, + AddressFamily::Rose => libc::AF_ROSE as u8, + AddressFamily::Decnet => libc::AF_DECnet as u8, + AddressFamily::Netbeui => libc::AF_NETBEUI as u8, + AddressFamily::Security => libc::AF_SECURITY as u8, + AddressFamily::Key => libc::AF_KEY as u8, + AddressFamily::Route => libc::AF_ROUTE as u8, + AddressFamily::Netlink => libc::AF_NETLINK as u8, + AddressFamily::Packet => libc::AF_PACKET as u8, + AddressFamily::Ash => libc::AF_ASH as u8, + AddressFamily::Econet => libc::AF_ECONET as u8, + AddressFamily::Atmsvc => libc::AF_ATMSVC as u8, + AddressFamily::Rds => libc::AF_RDS as u8, + AddressFamily::Sna => libc::AF_SNA as u8, + AddressFamily::Irda => libc::AF_IRDA as u8, + AddressFamily::Pppox => libc::AF_PPPOX as u8, + AddressFamily::Wanpipe => libc::AF_WANPIPE as u8, + AddressFamily::Llc => libc::AF_LLC as u8, + AddressFamily::Ib => libc::AF_IB as u8, + AddressFamily::Mpls => libc::AF_MPLS as u8, + AddressFamily::Can => libc::AF_CAN as u8, + AddressFamily::Tipc => libc::AF_TIPC as u8, + AddressFamily::Bluetooth => libc::AF_BLUETOOTH as u8, + AddressFamily::Iucv => libc::AF_IUCV as u8, + AddressFamily::Rxrpc => libc::AF_RXRPC as u8, + AddressFamily::Isdn => libc::AF_ISDN as u8, + AddressFamily::Phonet => libc::AF_PHONET as u8, + AddressFamily::Ieee802154 => libc::AF_IEEE802154 as u8, + AddressFamily::Caif => libc::AF_CAIF as u8, + AddressFamily::Alg => libc::AF_ALG as u8, + AddressFamily::Nfc => libc::AF_NFC as u8, + AddressFamily::Vsock => libc::AF_VSOCK as u8, + AddressFamily::Kcm => AF_KCM, + AddressFamily::Qipcrtr => AF_QIPCRTR, + AddressFamily::Smc => AF_SMC, + AddressFamily::Xdp => AF_XDP, + AddressFamily::Mctp => AF_MCTP, + AddressFamily::Other(d) => d, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 56c7f716..d59af7f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,59 @@ // SPDX-License-Identifier: MIT -#[macro_use] -extern crate bitflags; - pub mod rtnl; pub use self::rtnl::*; -#[cfg(test)] +pub mod link; + +#[cfg(any(target_os = "linux", target_os = "fuchsia"))] +mod address_family_linux; +#[cfg(any(target_os = "linux", target_os = "fuchsia"))] +pub use self::address_family_linux::AddressFamily; + +#[cfg(target_os = "freebsd")] +mod address_family_freebsd; +#[cfg(target_os = "freebsd")] +pub use self::address_family_freebsd::AddressFamily; + +#[cfg(not(any( + target_os = "linux", + target_os = "fuchsia", + target_os = "freebsd", +)))] +mod address_family_fallback; +#[cfg(not(any( + target_os = "linux", + target_os = "fuchsia", + target_os = "freebsd", +)))] +pub use self::address_family_fallback::AddressFamily; + +/// The `netlink-packet-route` crate is designed to abstract Netlink route +/// protocol(`rtnetlink`) packet into Rust data types. The goal of this crate is +/// saving netlink user from reading Kernel Netlink codes. +/// +/// This crate grouped Netlink route protocol into these modules: +/// * `link`: NIC interface, similar to to `ip link` command. +/// * `address`: IP address, similar to `ip address` command. +/// * `route`: Route, similar to `ip route` command. +/// * `rule`: Route rule, similar to `ip rule` command. +/// * `tc`: Traffic control, similar to `tc` command. +/// * `neighbour`: Neighbour, similar to `ip neighbour` command. +/// * `neighbour_table`: Neighbour table, similar to `ip ntable` command. +/// * `nsid`: Namespace, similar to `ip netns` command. +/// +/// At the top level of this crate, we also provide: +/// * [AddressFamily] +/// +/// Normally, you should use [`rtnetlink`][rtnetlink_url] instead of using this +/// crate directly. +/// +/// [rtnetlink_url]: https://docs.rs/rtnetlink +#[macro_use] +extern crate bitflags; #[macro_use] -extern crate lazy_static; +extern crate netlink_packet_utils; #[cfg(test)] #[macro_use] extern crate pretty_assertions; - -#[macro_use] -extern crate netlink_packet_utils; diff --git a/src/rtnl/link/nlas/af_spec_bridge.rs b/src/link/af_spec/bridge.rs similarity index 79% rename from src/rtnl/link/nlas/af_spec_bridge.rs rename to src/link/af_spec/bridge.rs index e8b36bb8..0a622427 100644 --- a/src/rtnl/link/nlas/af_spec_bridge.rs +++ b/src/link/af_spec/bridge.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ nla::{self, DefaultNla, NlaBuffer}, parsers::parse_u16, @@ -10,9 +11,8 @@ use netlink_packet_utils::{ DecodeError, }; -use crate::constants::*; - -use byteorder::{ByteOrder, NativeEndian}; +const IFLA_BRIDGE_FLAGS: u16 = 0; +const IFLA_BRIDGE_VLAN_INFO: u16 = 2; #[derive(Clone, Eq, PartialEq, Debug)] #[non_exhaustive] @@ -75,6 +75,26 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecBridge { } } +#[cfg(any(target_os = "linux", target_os = "fuchsia"))] +pub(crate) struct VecAfSpecBridge(pub(crate) Vec); + +#[cfg(any(target_os = "linux", target_os = "fuchsia"))] +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for VecAfSpecBridge +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut nlas = vec![]; + let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_BRIDGE)"; + for nla in + netlink_packet_utils::nla::NlasIterator::new(buf.into_inner()) + { + let nla = nla.context(err)?; + nlas.push(AfSpecBridge::parse(&nla).context(err)?); + } + Ok(Self(nlas)) + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[non_exhaustive] pub struct BridgeVlanInfo { @@ -105,7 +125,8 @@ impl TryFrom<&[u8]> for BridgeVlanInfo { }) } else { Err(DecodeError::from(format!( - "Invalid IFLA_BRIDGE_VLAN_INFO value, expecting [u8;4], but got {raw:?}" + "Invalid IFLA_BRIDGE_VLAN_INFO value, expecting [u8;4], \ + but got {raw:?}" ))) } } diff --git a/src/rtnl/link/nlas/inet/dev_conf.rs b/src/link/af_spec/inet.rs similarity index 68% rename from src/rtnl/link/nlas/inet/dev_conf.rs rename to src/link/af_spec/inet.rs index fc91fa70..27a5a0da 100644 --- a/src/rtnl/link/nlas/inet/dev_conf.rs +++ b/src/link/af_spec/inet.rs @@ -1,11 +1,91 @@ // SPDX-License-Identifier: MIT +use anyhow::Context; use netlink_packet_utils::{ + nla::{self, DefaultNla, NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -pub const DEV_CONF_LEN: usize = 124; +use super::super::buffer_tool::expand_buffer_if_small; + +const IFLA_INET_CONF: u16 = 1; +// This number might change when kernel add more IPV4_DEV_CONF +const __IPV4_DEVCONF_MAX: usize = 34; +const IPV4_DEVCONF_MAX: usize = __IPV4_DEVCONF_MAX - 1; +const DEV_CONF_LEN: usize = IPV4_DEVCONF_MAX * 4; + +#[derive(Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum AfSpecInet { + DevConf(InetDevConf), + Other(DefaultNla), +} + +pub(crate) struct VecAfSpecInet(pub(crate) Vec); + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for VecAfSpecInet +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut nlas = vec![]; + let err = "Invalid AF_INET NLA for IFLA_AF_SPEC(AF_UNSPEC)"; + for nla in NlasIterator::new(buf.into_inner()) { + let nla = nla.context(err)?; + nlas.push(AfSpecInet::parse(&nla)?); + } + Ok(Self(nlas)) + } +} + +impl nla::Nla for AfSpecInet { + fn value_len(&self) -> usize { + use self::AfSpecInet::*; + match *self { + DevConf(ref c) => c.buffer_len(), + Other(ref nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::AfSpecInet::*; + match *self { + DevConf(ref c) => c.emit(buffer), + Other(ref nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::AfSpecInet::*; + match *self { + DevConf(_) => IFLA_INET_CONF, + Other(ref nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::AfSpecInet::*; + + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_INET_CONF => { + DevConf(InetDevConf::parse(&InetDevConfBuffer::new( + expand_buffer_if_small( + payload, + DEV_CONF_LEN, + "IFLA_INET_CONF", + ) + .as_slice(), + ))?) + } + kind => Other(DefaultNla::parse(buf).context(format!( + "Unknown NLA type {kind} for IFLA_AF_SPEC(inet)" + ))?), + }) + } +} buffer!(InetDevConfBuffer(DEV_CONF_LEN) { forwarding: (i32, 0..4), @@ -39,6 +119,8 @@ buffer!(InetDevConfBuffer(DEV_CONF_LEN) { ignore_routes_with_linkdown: (i32, 112..116), drop_unicast_in_l2_multicast: (i32, 116..120), drop_gratuitous_arp: (i32, 120..124), + bc_forwarding: (i32, 124..128), + arp_evict_nocarrier: (i32, 128..132), }); #[derive(Clone, Copy, Eq, PartialEq, Debug)] @@ -75,6 +157,8 @@ pub struct InetDevConf { pub ignore_routes_with_linkdown: i32, pub drop_unicast_in_l2_multicast: i32, pub drop_gratuitous_arp: i32, + pub bc_forwarding: i32, + pub arp_evict_nocarrier: i32, } impl> Parseable> for InetDevConf { @@ -113,6 +197,8 @@ impl> Parseable> for InetDevConf { ignore_routes_with_linkdown: buf.ignore_routes_with_linkdown(), drop_unicast_in_l2_multicast: buf.drop_unicast_in_l2_multicast(), drop_gratuitous_arp: buf.drop_gratuitous_arp(), + bc_forwarding: buf.bc_forwarding(), + arp_evict_nocarrier: buf.arp_evict_nocarrier(), }) } } @@ -162,5 +248,7 @@ impl Emitable for InetDevConf { self.drop_unicast_in_l2_multicast, ); buffer.set_drop_gratuitous_arp(self.drop_gratuitous_arp); + buffer.set_bc_forwarding(self.bc_forwarding); + buffer.set_arp_evict_nocarrier(self.arp_evict_nocarrier); } } diff --git a/src/link/af_spec/inet6.rs b/src/link/af_spec/inet6.rs new file mode 100644 index 00000000..85911591 --- /dev/null +++ b/src/link/af_spec/inet6.rs @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::{parse_ipv6, parse_u32, parse_u8}, + traits::{Emitable, Parseable}, + DecodeError, +}; + +use super::super::{ + buffer_tool::expand_buffer_if_small, Icmp6Stats, Icmp6StatsBuffer, + Inet6CacheInfo, Inet6CacheInfoBuffer, Inet6DevConf, Inet6DevConfBuffer, + Inet6IfaceFlags, Inet6Stats, Inet6StatsBuffer, +}; +use super::inet6_devconf::LINK_INET6_DEV_CONF_LEN; + +const IFLA_INET6_FLAGS: u16 = 1; +const IFLA_INET6_CONF: u16 = 2; +const IFLA_INET6_STATS: u16 = 3; +// No kernel code used IFLA_INET6_MCAST +// const IFLA_INET6_MCAST: u16 = 4; +const IFLA_INET6_CACHEINFO: u16 = 5; +const IFLA_INET6_ICMP6STATS: u16 = 6; +const IFLA_INET6_TOKEN: u16 = 7; +const IFLA_INET6_ADDR_GEN_MODE: u16 = 8; +const IFLA_INET6_RA_MTU: u16 = 9; + +#[derive(Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum AfSpecInet6 { + //TODO(Gris Ge): Use Vec for `IFF_UP` and etc + Flags(Inet6IfaceFlags), + CacheInfo(Inet6CacheInfo), + DevConf(Inet6DevConf), + Stats(Inet6Stats), + Icmp6Stats(Icmp6Stats), + Token([u8; 16]), + AddrGenMode(u8), + RaMtu(u32), + Other(DefaultNla), +} + +pub(crate) struct VecAfSpecInet6(pub(crate) Vec); + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for VecAfSpecInet6 +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut nlas = vec![]; + let err = "Invalid AF_INET6 NLA for IFLA_AF_SPEC(AF_UNSPEC)"; + for nla in NlasIterator::new(buf.into_inner()) { + let nla = nla.context(err)?; + nlas.push(AfSpecInet6::parse(&nla).context(err)?); + } + Ok(Self(nlas)) + } +} + +impl Nla for AfSpecInet6 { + fn value_len(&self) -> usize { + use self::AfSpecInet6::*; + match *self { + CacheInfo(ref cache_info) => cache_info.buffer_len(), + DevConf(ref dev_conf) => dev_conf.buffer_len(), + Stats(ref stats) => stats.buffer_len(), + Icmp6Stats(ref icmp_stats) => icmp_stats.buffer_len(), + Flags(_) | RaMtu(_) => 4, + Token(_) => 16, + AddrGenMode(_) => 1, + Other(ref nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::AfSpecInet6::*; + match *self { + Flags(ref value) => { + NativeEndian::write_u32(buffer, u32::from(value)) + } + RaMtu(ref value) => NativeEndian::write_u32(buffer, *value), + CacheInfo(ref v) => v.emit(buffer), + DevConf(ref v) => v.emit(buffer), + Stats(ref v) => v.emit(buffer), + Icmp6Stats(ref v) => v.emit(buffer), + Token(ref ipv6) => buffer.copy_from_slice(&ipv6[..]), + AddrGenMode(value) => buffer[0] = value, + Other(ref nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::AfSpecInet6::*; + match *self { + Flags(_) => IFLA_INET6_FLAGS, + CacheInfo(_) => IFLA_INET6_CACHEINFO, + DevConf(_) => IFLA_INET6_CONF, + Stats(_) => IFLA_INET6_STATS, + Icmp6Stats(_) => IFLA_INET6_ICMP6STATS, + Token(_) => IFLA_INET6_TOKEN, + AddrGenMode(_) => IFLA_INET6_ADDR_GEN_MODE, + RaMtu(_) => IFLA_INET6_RA_MTU, + Other(ref nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet6 { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::AfSpecInet6::*; + + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_INET6_FLAGS => Flags(Inet6IfaceFlags::from( + parse_u32(payload).context("invalid IFLA_INET6_FLAGS value")?, + )), + IFLA_INET6_CACHEINFO => CacheInfo( + Inet6CacheInfo::parse(&Inet6CacheInfoBuffer::new(payload)) + .context(format!( + "invalid IFLA_INET6_CACHEINFO value {:?}", + payload + ))?, + ), + IFLA_INET6_CONF => DevConf( + Inet6DevConf::parse(&Inet6DevConfBuffer::new( + expand_buffer_if_small( + payload, + LINK_INET6_DEV_CONF_LEN, + "IFLA_INET6_CONF", + ) + .as_slice(), + )) + .context(format!( + "invalid IFLA_INET6_CONF value {:?}", + payload + ))?, + ), + IFLA_INET6_STATS => Stats( + Inet6Stats::parse(&Inet6StatsBuffer::new(payload)).context( + format!("invalid IFLA_INET6_STATS value {:?}", payload), + )?, + ), + IFLA_INET6_ICMP6STATS => Icmp6Stats( + super::super::Icmp6Stats::parse(&Icmp6StatsBuffer::new( + payload, + )) + .context(format!( + "invalid IFLA_INET6_ICMP6STATS value {:?}", + payload + ))?, + ), + IFLA_INET6_TOKEN => Token( + parse_ipv6(payload) + .context("invalid IFLA_INET6_TOKEN value")?, + ), + IFLA_INET6_ADDR_GEN_MODE => AddrGenMode( + parse_u8(payload) + .context("invalid IFLA_INET6_ADDR_GEN_MODE value")?, + ), + IFLA_INET6_RA_MTU => RaMtu( + parse_u32(payload) + .context("invalid IFLA_INET6_RA_MTU value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown AF_INET6 NLA type {kind} for IFLA_AF_SPEC(AF_UNSPEC)" + ))?), + }) + } +} diff --git a/src/rtnl/link/nlas/inet6/cache.rs b/src/link/af_spec/inet6_cache.rs similarity index 96% rename from src/rtnl/link/nlas/inet6/cache.rs rename to src/link/af_spec/inet6_cache.rs index 1ad8d2a6..8d28710d 100644 --- a/src/rtnl/link/nlas/inet6/cache.rs +++ b/src/link/af_spec/inet6_cache.rs @@ -14,7 +14,8 @@ pub struct Inet6CacheInfo { pub retrans_time: i32, } -pub const LINK_INET6_CACHE_INFO_LEN: usize = 16; +const LINK_INET6_CACHE_INFO_LEN: usize = 16; + buffer!(Inet6CacheInfoBuffer(LINK_INET6_CACHE_INFO_LEN) { max_reasm_len: (i32, 0..4), tstamp: (i32, 4..8), diff --git a/src/rtnl/link/nlas/inet6/dev_conf.rs b/src/link/af_spec/inet6_devconf.rs similarity index 86% rename from src/rtnl/link/nlas/inet6/dev_conf.rs rename to src/link/af_spec/inet6_devconf.rs index eea8d80a..740ce3da 100644 --- a/src/rtnl/link/nlas/inet6/dev_conf.rs +++ b/src/link/af_spec/inet6_devconf.rs @@ -5,7 +5,10 @@ use netlink_packet_utils::{ DecodeError, }; -pub const LINK_INET6_DEV_CONF_LEN: usize = 204; +// The DEVCONF_MAX will increase when kernel add more DEVCONF +const DEVCONF_MAX: usize = 59; +pub(crate) const LINK_INET6_DEV_CONF_LEN: usize = DEVCONF_MAX * 4; + buffer!(Inet6DevConfBuffer(LINK_INET6_DEV_CONF_LEN) { forwarding: (i32, 0..4), hoplimit: (i32, 4..8), @@ -58,6 +61,14 @@ buffer!(Inet6DevConfBuffer(LINK_INET6_DEV_CONF_LEN) { disable_policy: (i32, 192..196), accept_ra_rt_info_min_plen: (i32, 196..200), ndisc_tclass: (i32, 200..204), + rpl_seg_enabled: (i32, 204..208), + ra_defrtr_metric: (i32, 208..212), + ioam6_enabled: (i32, 212..216), + ioam6_id: (i32, 216..220), + ioam6_id_wide: (i32, 220..224), + ndisc_evict_nocarrier: (i32, 224..228), + accept_untracked_na: (i32, 228..232), + accept_ra_min_lft: (i32, 232..236), }); impl> Parseable> for Inet6DevConf { @@ -116,6 +127,14 @@ impl> Parseable> for Inet6DevConf { disable_policy: buf.disable_policy(), accept_ra_rt_info_min_plen: buf.accept_ra_rt_info_min_plen(), ndisc_tclass: buf.ndisc_tclass(), + rpl_seg_enabled: buf.rpl_seg_enabled(), + ra_defrtr_metric: buf.ra_defrtr_metric(), + ioam6_enabled: buf.ioam6_enabled(), + ioam6_id: buf.ioam6_id(), + ioam6_id_wide: buf.ioam6_id_wide(), + ndisc_evict_nocarrier: buf.ndisc_evict_nocarrier(), + accept_untracked_na: buf.accept_untracked_na(), + accept_ra_min_lft: buf.accept_ra_min_lft(), }) } } @@ -185,6 +204,15 @@ impl Emitable for Inet6DevConf { buffer.set_disable_policy(self.disable_policy); buffer.set_accept_ra_rt_info_min_plen(self.accept_ra_rt_info_min_plen); buffer.set_ndisc_tclass(self.ndisc_tclass); + buffer.set_ndisc_tclass(self.ndisc_tclass); + buffer.set_rpl_seg_enabled(self.rpl_seg_enabled); + buffer.set_ra_defrtr_metric(self.ra_defrtr_metric); + buffer.set_ioam6_enabled(self.ioam6_enabled); + buffer.set_ioam6_id(self.ioam6_id); + buffer.set_ioam6_id_wide(self.ioam6_id_wide); + buffer.set_ndisc_evict_nocarrier(self.ndisc_evict_nocarrier); + buffer.set_accept_untracked_na(self.accept_untracked_na); + buffer.set_accept_ra_min_lft(self.accept_ra_min_lft); } } @@ -242,4 +270,12 @@ pub struct Inet6DevConf { pub disable_policy: i32, pub accept_ra_rt_info_min_plen: i32, pub ndisc_tclass: i32, + pub rpl_seg_enabled: i32, + pub ra_defrtr_metric: i32, + pub ioam6_enabled: i32, + pub ioam6_id: i32, + pub ioam6_id_wide: i32, + pub ndisc_evict_nocarrier: i32, + pub accept_untracked_na: i32, + pub accept_ra_min_lft: i32, } diff --git a/src/rtnl/link/nlas/inet6/icmp6_stats.rs b/src/link/af_spec/inet6_icmp.rs similarity index 97% rename from src/rtnl/link/nlas/inet6/icmp6_stats.rs rename to src/link/af_spec/inet6_icmp.rs index 498cc187..1329a2e9 100644 --- a/src/rtnl/link/nlas/inet6/icmp6_stats.rs +++ b/src/link/af_spec/inet6_icmp.rs @@ -5,6 +5,8 @@ use netlink_packet_utils::{ DecodeError, }; +const ICMP6_STATS_LEN: usize = 48; + #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] pub struct Icmp6Stats { @@ -16,7 +18,6 @@ pub struct Icmp6Stats { pub csum_errors: i64, } -pub const ICMP6_STATS_LEN: usize = 48; buffer!(Icmp6StatsBuffer(ICMP6_STATS_LEN) { num: (i64, 0..8), in_msgs: (i64, 8..16), diff --git a/src/link/af_spec/inet6_iface_flag.rs b/src/link/af_spec/inet6_iface_flag.rs new file mode 100644 index 00000000..0d2cc318 --- /dev/null +++ b/src/link/af_spec/inet6_iface_flag.rs @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT + +const IF_RA_OTHERCONF: u32 = 0x80; +const IF_RA_MANAGED: u32 = 0x40; +const IF_RA_RCVD: u32 = 0x20; +const IF_RS_SENT: u32 = 0x10; +const IF_READY: u32 = 0x80000000; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct Inet6IfaceFlags(pub Vec); + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +#[repr(u32)] +pub enum Inet6IfaceFlag { + Otherconf = IF_RA_OTHERCONF, + RaManaged = IF_RA_MANAGED, + RaRcvd = IF_RA_RCVD, + RsSent = IF_RS_SENT, + Ready = IF_READY, + Other(u32), +} + +impl From for Inet6IfaceFlag { + fn from(d: u32) -> Self { + match d { + d if (d & IF_RA_OTHERCONF) > 0 => Self::Otherconf, + d if (d & IF_RA_MANAGED) > 0 => Self::RaManaged, + d if (d & IF_RA_RCVD) > 0 => Self::RaRcvd, + d if (d & IF_RS_SENT) > 0 => Self::RsSent, + d if (d & IF_READY) > 0 => Self::Ready, + _ => Self::Other(d), + } + } +} + +impl From for u32 { + fn from(v: Inet6IfaceFlag) -> u32 { + match v { + Inet6IfaceFlag::Otherconf => IF_RA_OTHERCONF, + Inet6IfaceFlag::RaManaged => IF_RA_MANAGED, + Inet6IfaceFlag::RaRcvd => IF_RA_RCVD, + Inet6IfaceFlag::RsSent => IF_RS_SENT, + Inet6IfaceFlag::Ready => IF_READY, + Inet6IfaceFlag::Other(i) => i, + } + } +} + +impl std::fmt::Display for Inet6IfaceFlag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Otherconf => write!(f, "OTHERCONF"), + Self::RaManaged => write!(f, "RA_MANAGED"), + Self::RaRcvd => write!(f, "RA_RCVD"), + Self::RsSent => write!(f, "RS_SENT"), + Self::Ready => write!(f, "READY"), + Self::Other(i) => write!(f, "Other({})", i), + } + } +} + +const ALL_INET_IFACE_FLAGS: [Inet6IfaceFlag; 5] = [ + Inet6IfaceFlag::Otherconf, + Inet6IfaceFlag::RaManaged, + Inet6IfaceFlag::RaRcvd, + Inet6IfaceFlag::RsSent, + Inet6IfaceFlag::Ready, +]; + +impl From for Inet6IfaceFlags { + fn from(d: u32) -> Self { + let mut got: u32 = 0; + let mut ret = Vec::new(); + for flag in ALL_INET_IFACE_FLAGS { + if (d & u32::from(flag)) > 0 { + ret.push(flag); + got += u32::from(flag); + } + } + if got != d { + ret.push(Inet6IfaceFlag::Other(d - got)); + } + Self(ret) + } +} + +impl From<&Inet6IfaceFlags> for u32 { + fn from(v: &Inet6IfaceFlags) -> u32 { + let mut d: u32 = 0; + for flag in &v.0 { + d += u32::from(*flag); + } + d + } +} diff --git a/src/rtnl/link/nlas/inet6/stats.rs b/src/link/af_spec/inet6_stats.rs similarity index 99% rename from src/rtnl/link/nlas/inet6/stats.rs rename to src/link/af_spec/inet6_stats.rs index 234872ad..766f6912 100644 --- a/src/rtnl/link/nlas/inet6/stats.rs +++ b/src/link/af_spec/inet6_stats.rs @@ -5,7 +5,8 @@ use netlink_packet_utils::{ DecodeError, }; -pub const INET6_STATS_LEN: usize = 288; +const INET6_STATS_LEN: usize = 288; + buffer!(Inet6StatsBuffer(INET6_STATS_LEN) { num: (i64, 0..8), in_pkts: (i64, 8..16), diff --git a/src/link/af_spec/mod.rs b/src/link/af_spec/mod.rs new file mode 100644 index 00000000..45715060 --- /dev/null +++ b/src/link/af_spec/mod.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT + +mod bridge; +mod inet; +mod inet6; +mod inet6_cache; +mod inet6_devconf; +mod inet6_icmp; +mod inet6_iface_flag; +mod inet6_stats; +mod unspec; + +pub use self::bridge::{AfSpecBridge, BridgeVlanInfo}; +pub use self::inet::{AfSpecInet, InetDevConf}; +pub use self::inet6::AfSpecInet6; +pub use self::inet6_cache::{Inet6CacheInfo, Inet6CacheInfoBuffer}; +pub use self::inet6_devconf::{Inet6DevConf, Inet6DevConfBuffer}; +pub use self::inet6_icmp::{Icmp6Stats, Icmp6StatsBuffer}; +pub use self::inet6_iface_flag::{Inet6IfaceFlag, Inet6IfaceFlags}; +pub use self::inet6_stats::{Inet6Stats, Inet6StatsBuffer}; +pub use self::unspec::AfSpecUnspec; + +#[cfg(any(target_os = "linux", target_os = "fuchsia"))] +pub(crate) use self::bridge::VecAfSpecBridge; +pub(crate) use self::inet::VecAfSpecInet; +pub(crate) use self::inet6::VecAfSpecInet6; +pub(crate) use self::unspec::VecAfSpecUnspec; diff --git a/src/link/af_spec/unspec.rs b/src/link/af_spec/unspec.rs new file mode 100644 index 00000000..439d3626 --- /dev/null +++ b/src/link/af_spec/unspec.rs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + traits::{Emitable, Parseable}, + DecodeError, +}; + +use crate::link::{ + af_spec::{VecAfSpecInet, VecAfSpecInet6}, + AfSpecInet, AfSpecInet6, +}; +use crate::AddressFamily; + +// For `AF_UNSPEC`, the `IFLA_AF_SPEC` is two layer array: +// +// [{nla_len=408, nla_type=IFLA_AF_SPEC}, +// [ +// [{nla_len=140, nla_type=AF_INET}, [ +// {nla_len=136, nla_type=IFLA_INET_CONF}, [ +// [IPV4_DEVCONF_FORWARDING-1] = 0, +// +// [IPV4_DEVCONF_ARP_EVICT_NOCARRIER-1] = 1]]], +// [{nla_len=264, nla_type=AF_INET6}, [ +// [{nla_len=8, nla_type=IFLA_INET6_FLAGS}, IF_READY], +// [{nla_len=20, nla_type=IFLA_INET6_CACHEINFO}, +// { +// max_reasm_len=65535, +// tstamp=3794, +// reachable_time=37584, +// retrans_time=1000}], +// [{nla_len=232, nla_type=IFLA_INET6_CONF}, +// [[DEVCONF_FORWARDING] = 0, +// +// [DEVCONF_NDISC_EVICT_NOCARRIER] = 1]]]]]] + +#[derive(Clone, Eq, PartialEq, Debug)] +#[non_exhaustive] +pub enum AfSpecUnspec { + Inet(Vec), + Inet6(Vec), + Other(DefaultNla), +} + +pub(crate) struct VecAfSpecUnspec(pub(crate) Vec); + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for VecAfSpecUnspec +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut nlas = vec![]; + let err = "Invalid NLA for IFLA_AF_SPEC(AF_UNSPEC)"; + for nla in NlasIterator::new(buf.into_inner()) { + let nla = nla.context(err)?; + nlas.push(match nla.kind() { + k if k == u8::from(AddressFamily::Inet) as u16 => { + AfSpecUnspec::Inet( + VecAfSpecInet::parse(&NlaBuffer::new(&nla.value())) + .context(err)? + .0, + ) + } + k if k == u8::from(AddressFamily::Inet6) as u16 => { + AfSpecUnspec::Inet6( + VecAfSpecInet6::parse(&NlaBuffer::new(&nla.value())) + .context(err)? + .0, + ) + } + kind => AfSpecUnspec::Other(DefaultNla::parse(&nla).context( + format!( + "Unknown AF_XXX type {kind} for IFLA_AF_SPEC(AF_UNSPEC)" + ), + )?), + }) + } + Ok(Self(nlas)) + } +} + +impl Nla for AfSpecUnspec { + fn value_len(&self) -> usize { + match *self { + Self::Inet(ref nlas) => nlas.as_slice().buffer_len(), + Self::Inet6(ref nlas) => nlas.as_slice().buffer_len(), + Self::Other(ref nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match *self { + Self::Inet(ref nlas) => nlas.as_slice().emit(buffer), + Self::Inet6(ref nlas) => nlas.as_slice().emit(buffer), + Self::Other(ref nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + match *self { + Self::Inet(_) => u8::from(AddressFamily::Inet) as u16, + Self::Inet6(_) => u8::from(AddressFamily::Inet6) as u16, + Self::Other(ref nla) => nla.kind(), + } + } +} diff --git a/src/rtnl/link/nlas/mod.rs b/src/link/attribute.rs similarity index 64% rename from src/rtnl/link/nlas/mod.rs rename to src/link/attribute.rs index 148559e5..977f3e5c 100644 --- a/src/rtnl/link/nlas/mod.rs +++ b/src/link/attribute.rs @@ -1,72 +1,95 @@ // SPDX-License-Identifier: MIT -mod inet; -pub use self::inet::*; - -mod inet6; -pub use self::inet6::*; - -mod af_spec_inet; -pub use self::af_spec_inet::*; - -mod af_spec_bridge; -pub use self::af_spec_bridge::*; - -mod link_infos; -pub use self::link_infos::*; - -mod bond; -pub use self::bond::*; - -mod bond_port; -pub use self::bond_port::*; - -mod bridge; -pub use self::bridge::*; - -mod prop_list; -pub use self::prop_list::*; - -mod map; -pub use self::map::*; - -mod stats; -pub use self::stats::*; - -mod stats64; -pub use self::stats64::*; - -mod link_state; -pub use self::link_state::*; - -mod link_xdp; -pub use self::link_xdp::*; - -mod vxlan; -pub use self::vxlan::InfoVxlan; - -#[cfg(test)] -mod tests; - use std::os::unix::io::RawFd; use anyhow::Context; use byteorder::{ByteOrder, NativeEndian}; use netlink_packet_utils::{ - nla::{self, DefaultNla, NlaBuffer, NlasIterator, NLA_F_NESTED}, + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator, NLA_F_NESTED}, parsers::{parse_i32, parse_string, parse_u32, parse_u8}, traits::{Emitable, Parseable, ParseableParametrized}, DecodeError, }; -use crate::constants::*; +#[cfg(any(target_os = "linux", target_os = "fuchsia",))] +use super::af_spec::VecAfSpecBridge; +use super::{ + af_spec::VecAfSpecUnspec, buffer_tool::expand_buffer_if_small, + link_info::VecLinkInfo, stats::LINK_STATS_LEN, stats64::LINK_STATS64_LEN, + xdp::VecXdp, LinkInfo, Map, MapBuffer, Prop, State, Stats, Stats64, + Stats64Buffer, StatsBuffer, Xdp, +}; +use crate::AddressFamily; + +const IFLA_ADDRESS: u16 = 1; +const IFLA_BROADCAST: u16 = 2; +const IFLA_IFNAME: u16 = 3; +const IFLA_MTU: u16 = 4; +const IFLA_LINK: u16 = 5; +const IFLA_QDISC: u16 = 6; +const IFLA_STATS: u16 = 7; +// No kernel is using IFLA_COST +// const IFLA_COST: u16 = 8; +const IFLA_PRIORITY: u16 = 9; +const IFLA_MASTER: u16 = 10; +const IFLA_WIRELESS: u16 = 11; +const IFLA_PROTINFO: u16 = 12; +const IFLA_TXQLEN: u16 = 13; +const IFLA_MAP: u16 = 14; +const IFLA_WEIGHT: u16 = 15; +const IFLA_OPERSTATE: u16 = 16; +const IFLA_LINKMODE: u16 = 17; +const IFLA_LINKINFO: u16 = 18; +const IFLA_NET_NS_PID: u16 = 19; +const IFLA_IFALIAS: u16 = 20; +const IFLA_NUM_VF: u16 = 21; +const IFLA_VFINFO_LIST: u16 = 22; +const IFLA_STATS64: u16 = 23; +const IFLA_VF_PORTS: u16 = 24; +const IFLA_PORT_SELF: u16 = 25; +const IFLA_AF_SPEC: u16 = 26; +const IFLA_GROUP: u16 = 27; +const IFLA_NET_NS_FD: u16 = 28; +const IFLA_EXT_MASK: u16 = 29; +const IFLA_PROMISCUITY: u16 = 30; +const IFLA_NUM_TX_QUEUES: u16 = 31; +const IFLA_NUM_RX_QUEUES: u16 = 32; +const IFLA_CARRIER: u16 = 33; +const IFLA_PHYS_PORT_ID: u16 = 34; +const IFLA_CARRIER_CHANGES: u16 = 35; +const IFLA_PHYS_SWITCH_ID: u16 = 36; +const IFLA_LINK_NETNSID: u16 = 37; +const IFLA_PHYS_PORT_NAME: u16 = 38; +const IFLA_PROTO_DOWN: u16 = 39; +const IFLA_GSO_MAX_SEGS: u16 = 40; +const IFLA_GSO_MAX_SIZE: u16 = 41; +const IFLA_PAD: u16 = 42; +const IFLA_XDP: u16 = 43; +const IFLA_EVENT: u16 = 44; +const IFLA_NEW_NETNSID: u16 = 45; +const IFLA_IF_NETNSID: u16 = 46; +const IFLA_CARRIER_UP_COUNT: u16 = 47; +const IFLA_CARRIER_DOWN_COUNT: u16 = 48; +const IFLA_NEW_IFINDEX: u16 = 49; +const IFLA_MIN_MTU: u16 = 50; +const IFLA_MAX_MTU: u16 = 51; +const IFLA_PROP_LIST: u16 = 52; +const IFLA_PERM_ADDRESS: u16 = 54; +const IFLA_PROTO_DOWN_REASON: u16 = 55; + +/* TODO:(Gris Ge) +const IFLA_PARENT_DEV_NAME: u16 = 56; +const IFLA_PARENT_DEV_BUS_NAME: u16 = 57; +const IFLA_GRO_MAX_SIZE: u16 = 58; +const IFLA_TSO_MAX_SIZE: u16 = 59; +const IFLA_TSO_MAX_SEGS: u16 = 60; +const IFLA_ALLMULTI: u16 = 61; +const IFLA_DEVLINK_PORT: u16 = 62; +*/ #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] -pub enum Nla { - // Vec - Unspec(Vec), - Cost(Vec), +pub enum LinkAttribute { Priority(Vec), Weight(Vec), VfInfoList(Vec), @@ -82,34 +105,16 @@ pub enum Nla { CarrierUpCount(Vec), CarrierDownCount(Vec), NewIfIndex(Vec), - Info(Vec), + LinkInfo(Vec), Wireless(Vec), ProtoInfo(Vec), - /// A list of properties for the device. For additional context see the - /// related linux kernel threads[1][1],[2][2]. In particular - /// see [this message][defining message] from the first thread - /// describing the design. - /// - /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/ - /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/ - /// [defining message]: https://lwn.net/ml/netdev/20190913145012.GB2276@nanopsycho.orion/ PropList(Vec), - /// `protodown` is a mechanism that allows protocols to hold an interface - /// down. This field is used to specify the reason why it is held down. - /// For additional context see the related linux kernel - /// threads[1][1],[2][2]. - /// - /// [1]: https://lwn.net/ml/netdev/1595877677-45849-1-git-send-email-roopa%40cumulusnetworks.com/ - /// [2]: https://lwn.net/ml/netdev/1596242041-14347-1-git-send-email-roopa%40cumulusnetworks.com/ ProtoDownReason(Vec), - // mac address (use to be [u8; 6] but it turns out MAC != HW address, for - // instance for IP over GRE where it's an IPv4!) Address(Vec), Broadcast(Vec), /// Permanent hardware address of the device. The provides the same /// information as the ethtool ioctl interface. PermAddress(Vec), - // string // FIXME: for empty string, should we encode the NLA as \0 or should we // not set a payload? It seems that for certain attriutes, this @@ -118,13 +123,6 @@ pub enum Nla { Qdisc(String), IfAlias(String), PhysPortName(String), - /// Alternate name for the device. - /// For additional context see the related linux kernel - /// threads[1][1],[2][2]. - /// - /// [1]: https://lwn.net/ml/netdev/20190719110029.29466-1-jiri@resnulli.us/ - /// [2]: https://lwn.net/ml/netdev/20190930094820.11281-1-jiri@resnulli.us/ - AltIfName(String), // byte Mode(u8), Carrier(u8), @@ -146,118 +144,83 @@ pub enum Nla { GsoMaxSegs(u32), GsoMaxSize(u32), /// The minimum MTU for the device. - /// For additional context see the related [linux kernel message][1]. - /// - /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/ MinMtu(u32), /// The maximum MTU for the device. - /// For additional context see the related [linux kernel message][1]. - /// - /// [1]: https://lwn.net/ml/netdev/20180727204323.19408-3-sthemmin%40microsoft.com/ MaxMtu(u32), // i32 NetnsId(i32), - // custom OperState(State), - Stats(Vec), - Stats64(Vec), - Map(Vec), + Stats(Stats), + Stats64(Stats64), + Map(Map), // AF_SPEC (the type of af_spec depends on the interface family of the // message) - AfSpecInet(Vec), - AfSpecBridge(Vec), - //AfSpecBridge(Vec), + AfSpecUnspec(Vec), + AfSpecBridge(Vec), AfSpecUnknown(Vec), Other(DefaultNla), } -impl nla::Nla for Nla { - #[rustfmt::skip] +impl Nla for LinkAttribute { fn value_len(&self) -> usize { - use self::Nla::*; + use self::LinkAttribute::*; match *self { - // Vec - Unspec(ref bytes) - | Cost(ref bytes) - | Priority(ref bytes) - | Weight(ref bytes) - | VfInfoList(ref bytes) - | VfPorts(ref bytes) - | PortSelf(ref bytes) - | PhysPortId(ref bytes) - | PhysSwitchId(ref bytes) - | Pad(ref bytes) - | Event(ref bytes) - | NewNetnsId(ref bytes) - | IfNetnsId(ref bytes) - | Wireless(ref bytes) - | ProtoInfo(ref bytes) - | CarrierUpCount(ref bytes) - | CarrierDownCount(ref bytes) - | NewIfIndex(ref bytes) - | Address(ref bytes) - | Broadcast(ref bytes) - | PermAddress(ref bytes) - | AfSpecUnknown(ref bytes) - | Map(ref bytes) - | ProtoDownReason(ref bytes) - => bytes.len(), + Priority(ref bytes) + | Weight(ref bytes) + | VfInfoList(ref bytes) + | VfPorts(ref bytes) + | PortSelf(ref bytes) + | PhysPortId(ref bytes) + | PhysSwitchId(ref bytes) + | Pad(ref bytes) + | Event(ref bytes) + | NewNetnsId(ref bytes) + | IfNetnsId(ref bytes) + | Wireless(ref bytes) + | ProtoInfo(ref bytes) + | CarrierUpCount(ref bytes) + | CarrierDownCount(ref bytes) + | NewIfIndex(ref bytes) + | Address(ref bytes) + | Broadcast(ref bytes) + | PermAddress(ref bytes) + | AfSpecUnknown(ref bytes) + | ProtoDownReason(ref bytes) => bytes.len(), // strings: +1 because we need to append a nul byte IfName(ref string) - | Qdisc(ref string) - | IfAlias(ref string) - | PhysPortName(ref string) - | AltIfName(ref string) - => string.as_bytes().len() + 1, + | Qdisc(ref string) + | IfAlias(ref string) + | PhysPortName(ref string) => string.as_bytes().len() + 1, // u8 - Mode(_) - | Carrier(_) - | ProtoDown(_) - => 1, + Mode(_) | Carrier(_) | ProtoDown(_) => 1, // u32 and i32 - Mtu(_) - | Link(_) - | Master(_) - | TxQueueLen(_) - | NetNsPid(_) - | NumVf(_) - | Group(_) - | NetNsFd(_) - | ExtMask(_) - | Promiscuity(_) - | NumTxQueues(_) - | NumRxQueues(_) - | CarrierChanges(_) - | GsoMaxSegs(_) - | GsoMaxSize(_) - | NetnsId(_) - | MinMtu(_) - | MaxMtu(_) => 4, + Mtu(_) | Link(_) | Master(_) | TxQueueLen(_) | NetNsPid(_) + | NumVf(_) | Group(_) | NetNsFd(_) | ExtMask(_) + | Promiscuity(_) | NumTxQueues(_) | NumRxQueues(_) + | CarrierChanges(_) | GsoMaxSegs(_) | GsoMaxSize(_) + | NetnsId(_) | MinMtu(_) | MaxMtu(_) => 4, - // Defaults OperState(_) => 1, Stats(_) => LINK_STATS_LEN, Stats64(_) => LINK_STATS64_LEN, - Info(ref nlas) => nlas.as_slice().buffer_len(), + Map(ref nla) => nla.buffer_len(), + LinkInfo(ref nlas) => nlas.as_slice().buffer_len(), Xdp(ref nlas) => nlas.as_slice().buffer_len(), PropList(ref nlas) => nlas.as_slice().buffer_len(), - AfSpecInet(ref nlas) => nlas.as_slice().buffer_len(), + AfSpecUnspec(ref nlas) => nlas.as_slice().buffer_len(), AfSpecBridge(ref nlas) => nlas.as_slice().buffer_len(), - Other(ref attr) => attr.value_len(), + Other(ref attr) => attr.value_len(), } } - #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { - use self::Nla::*; + use self::LinkAttribute::*; match *self { // Vec - Unspec(ref bytes) - | Cost(ref bytes) - | Priority(ref bytes) + Priority(ref bytes) | Weight(ref bytes) | VfInfoList(ref bytes) | VfPorts(ref bytes) @@ -273,15 +236,12 @@ impl nla::Nla for Nla { | CarrierUpCount(ref bytes) | CarrierDownCount(ref bytes) | NewIfIndex(ref bytes) - // mac address (could be [u8; 6] or [u8; 4] for example. Not sure if we should have - // a separate type for them + // mac address (could be [u8; 6] or [u8; 4] for example. Not + // sure if we should have a separate type for them | Address(ref bytes) | Broadcast(ref bytes) | PermAddress(ref bytes) | AfSpecUnknown(ref bytes) - | Stats(ref bytes) - | Stats64(ref bytes) - | Map(ref bytes) | ProtoDownReason(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), @@ -290,7 +250,6 @@ impl nla::Nla for Nla { | Qdisc(ref string) | IfAlias(ref string) | PhysPortName(ref string) - | AltIfName(ref string) => { buffer[..string.len()].copy_from_slice(string.as_bytes()); buffer[string.len()] = 0; @@ -324,12 +283,14 @@ impl nla::Nla for Nla { NetnsId(ref value) | NetNsFd(ref value) => NativeEndian::write_i32(buffer, *value), - + Stats(ref nla) => nla.emit(buffer), + Map(ref nla) => nla.emit(buffer), + Stats64(ref nla) => nla.emit(buffer), OperState(state) => buffer[0] = state.into(), - Info(ref nlas) => nlas.as_slice().emit(buffer), + LinkInfo(ref nlas) => nlas.as_slice().emit(buffer), Xdp(ref nlas) => nlas.as_slice().emit(buffer), PropList(ref nlas) => nlas.as_slice().emit(buffer), - AfSpecInet(ref nlas) => nlas.as_slice().emit(buffer), + AfSpecUnspec(ref nlas) => nlas.as_slice().emit(buffer), AfSpecBridge(ref nlas) => nlas.as_slice().emit(buffer), // default nlas Other(ref attr) => attr.emit_value(buffer), @@ -337,11 +298,8 @@ impl nla::Nla for Nla { } fn kind(&self) -> u16 { - use self::Nla::*; + use self::LinkAttribute::*; match *self { - // Vec - Unspec(_) => IFLA_UNSPEC, - Cost(_) => IFLA_COST, Priority(_) => IFLA_PRIORITY, Weight(_) => IFLA_WEIGHT, VfInfoList(_) => IFLA_VFINFO_LIST, @@ -349,7 +307,7 @@ impl nla::Nla for Nla { PortSelf(_) => IFLA_PORT_SELF, PhysPortId(_) => IFLA_PHYS_PORT_ID, PhysSwitchId(_) => IFLA_PHYS_SWITCH_ID, - Info(_) => IFLA_LINKINFO, + LinkInfo(_) => IFLA_LINKINFO, Wireless(_) => IFLA_WIRELESS, ProtoInfo(_) => IFLA_PROTINFO, Pad(_) => IFLA_PAD, @@ -371,7 +329,6 @@ impl nla::Nla for Nla { Qdisc(_) => IFLA_QDISC, IfAlias(_) => IFLA_IFALIAS, PhysPortName(_) => IFLA_PHYS_PORT_NAME, - AltIfName(_) => IFLA_ALT_IFNAME, // u8 Mode(_) => IFLA_LINKMODE, Carrier(_) => IFLA_CARRIER, @@ -401,25 +358,24 @@ impl nla::Nla for Nla { Map(_) => IFLA_MAP, Stats(_) => IFLA_STATS, Stats64(_) => IFLA_STATS64, - AfSpecInet(_) | AfSpecBridge(_) | AfSpecUnknown(_) => IFLA_AF_SPEC, + AfSpecUnspec(_) | AfSpecBridge(_) | AfSpecUnknown(_) => { + IFLA_AF_SPEC + } Other(ref attr) => attr.kind(), } } } -impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> - for Nla +impl<'a, T: AsRef<[u8]> + ?Sized> + ParseableParametrized, AddressFamily> for LinkAttribute { fn parse_with_param( buf: &NlaBuffer<&'a T>, - interface_family: u16, + interface_family: AddressFamily, ) -> Result { - use Nla::*; + use self::LinkAttribute::*; let payload = buf.value(); Ok(match buf.kind() { - // Vec - IFLA_UNSPEC => Unspec(payload.to_vec()), - IFLA_COST => Cost(payload.to_vec()), IFLA_PRIORITY => Priority(payload.to_vec()), IFLA_WEIGHT => Weight(payload.to_vec()), IFLA_VFINFO_LIST => VfInfoList(payload.to_vec()), @@ -467,11 +423,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> parse_string(payload) .context("invalid IFLA_PHYS_PORT_NAME value")?, ), - IFLA_ALT_IFNAME => AltIfName( - parse_string(payload) - .context("invalid IFLA_ALT_IFNAME value")?, - ), - // u8 IFLA_LINKMODE => { Mode(parse_u8(payload).context("invalid IFLA_LINKMODE value")?) @@ -548,47 +499,53 @@ impl<'a, T: AsRef<[u8]> + ?Sized> ParseableParametrized, u16> .context("invalid IFLA_OPERSTATE value")? .into(), ), - IFLA_MAP => Map(payload.to_vec()), - IFLA_STATS => Stats(payload.to_vec()), - IFLA_STATS64 => Stats64(payload.to_vec()), + IFLA_MAP => Map(super::Map::parse(&MapBuffer::new(payload)) + .context(format!("Invalid IFLA_MAP value {:?}", payload))?), + IFLA_STATS => { + Stats(super::Stats::parse(&StatsBuffer::new(payload)).context( + format!("Invalid IFLA_STATS value {:?}", payload), + )?) + } + IFLA_STATS64 => { + let payload = expand_buffer_if_small( + payload, + LINK_STATS64_LEN, + "IFLA_STATS64", + ); + Stats64( + super::Stats64::parse(&Stats64Buffer::new( + payload.as_slice(), + )) + .context(format!( + "Invalid IFLA_STATS64 value {:?}", + payload + ))?, + ) + } IFLA_AF_SPEC => match interface_family { - AF_INET | AF_INET6 | AF_UNSPEC => { - let mut nlas = vec![]; - let err = "invalid IFLA_AF_SPEC value"; - for nla in NlasIterator::new(payload) { - let nla = nla.context(err)?; - nlas.push( - af_spec_inet::AfSpecInet::parse(&nla) - .context(err)?, - ); - } - AfSpecInet(nlas) - } - AF_BRIDGE => { - let mut nlas = vec![]; - let err = "invalid IFLA_AF_SPEC value for AF_BRIDGE"; - for nla in NlasIterator::new(payload) { - let nla = nla.context(err)?; - nlas.push( - af_spec_bridge::AfSpecBridge::parse(&nla) - .context(err)?, - ); - } - AfSpecBridge(nlas) - } + AddressFamily::Unspec => AfSpecUnspec( + VecAfSpecUnspec::parse(&NlaBuffer::new(&buf.value())) + .context("invalid IFLA_AF_SPEC value for AF_UNSPEC")? + .0, + ), + #[cfg(any(target_os = "linux", target_os = "fuchsia",))] + AddressFamily::Bridge => AfSpecBridge( + VecAfSpecBridge::parse(&NlaBuffer::new(&buf.value())) + .context("invalid IFLA_AF_SPEC value for AF_BRIDGE")? + .0, + ), _ => AfSpecUnknown(payload.to_vec()), }, - IFLA_LINKINFO => { - let err = "invalid IFLA_LINKINFO value"; - let buf = NlaBuffer::new_checked(payload).context(err)?; - Info(VecInfo::parse(&buf).context(err)?.0) - } + IFLA_LINKINFO => LinkInfo( + VecLinkInfo::parse(&NlaBuffer::new(&buf.value())) + .context("invalid IFLA_LINKINFO value")? + .0, + ), IFLA_XDP => { let err = "invalid IFLA_XDP value"; let buf = NlaBuffer::new_checked(payload).context(err)?; Xdp(VecXdp::parse(&buf).context(err)?.0) } - kind => Other( DefaultNla::parse(buf) .context(format!("unknown NLA type {kind}"))?, diff --git a/src/link/buffer_tool.rs b/src/link/buffer_tool.rs new file mode 100644 index 00000000..6412fc67 --- /dev/null +++ b/src/link/buffer_tool.rs @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT + +pub(crate) fn expand_buffer_if_small( + got: &[u8], + expected_size: usize, + nla_name: &str, +) -> Vec { + let mut payload = got.to_vec(); + match payload.len() { + l if l > expected_size => { + log::warn!( + "Specified {nla_name} NLA attribute holds \ + more(most likely new kernel) data which is unknown to \ + netlink-packet-route crate, expecting \ + {expected_size}, got {}", + got.len() + ); + } + l if l < expected_size => { + payload.extend_from_slice(&vec![0; expected_size - got.len()]); + } + _ => (), + } + payload +} diff --git a/src/rtnl/link/header.rs b/src/link/header.rs similarity index 54% rename from src/rtnl/link/header.rs rename to src/link/header.rs index 9b1f956d..5109da06 100644 --- a/src/rtnl/link/header.rs +++ b/src/link/header.rs @@ -1,11 +1,35 @@ // SPDX-License-Identifier: MIT use netlink_packet_utils::{ + nla::{NlaBuffer, NlasIterator}, traits::{Emitable, Parseable}, DecodeError, }; -use crate::{LinkMessageBuffer, LINK_HEADER_LEN}; +use crate::{ + link::{LinkFlag, LinkFlags, LinkLayerType}, + AddressFamily, +}; + +const LINK_HEADER_LEN: usize = 16; + +buffer!(LinkMessageBuffer(LINK_HEADER_LEN) { + interface_family: (u8, 0), + reserved_1: (u8, 1), + link_layer_type: (u16, 2..4), + link_index: (u32, 4..8), + flags: (u32, 8..12), + change_mask: (u32, 12..LINK_HEADER_LEN), + payload: (slice, LINK_HEADER_LEN..), +}); + +impl<'a, T: AsRef<[u8]> + ?Sized> LinkMessageBuffer<&'a T> { + pub fn attributes( + &self, + ) -> impl Iterator, DecodeError>> { + NlasIterator::new(self.payload()) + } +} /// High level representation of `RTM_GETLINK`, `RTM_SETLINK`, `RTM_NEWLINK` and /// `RTM_DELLINK` messages headers. @@ -29,18 +53,26 @@ use crate::{LinkMessageBuffer, LINK_HEADER_LEN}; #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct LinkHeader { /// Address family: one of the `AF_*` constants. - pub interface_family: u8, + /// The [AddressFamily] has `From` and `From for u8` + /// implemented. + pub interface_family: AddressFamily, /// Link index. pub index: u32, /// Link type. It should be set to one of the `ARPHRD_*` - /// constants. The most common value is `ARPHRD_ETHER` for + /// constants. The most common value is [ALinkLayerType::ETHER] for /// Ethernet. - pub link_layer_type: u16, + /// The LinkLayerType has `From` and `From for u16` + /// implemented. + pub link_layer_type: LinkLayerType, /// State of the link, described by a combinations of `IFF_*` - /// constants, for instance `IFF_UP | IFF_LOWER_UP`. - pub flags: u32, + /// constants, for instance `vec![LinkFlag::Up, LinkFlag::LowerUp]`. + /// To convert `Vec` into `u32`, you may: + /// `u32::from(&LinkFlags(Vec)` + /// To convert `u32` to `Vec`, you may: + /// `LinkFlags::from(u32).0` + pub flags: Vec, /// Change mask for the `flags` field. Reserved, it should be set - /// to `0xffff_ffff`. + /// to u32::MAX or 0(equal to u32::MAX for backwards compatibility). pub change_mask: u32, } @@ -51,22 +83,22 @@ impl Emitable for LinkHeader { fn emit(&self, buffer: &mut [u8]) { let mut packet = LinkMessageBuffer::new(buffer); - packet.set_interface_family(self.interface_family); + packet.set_interface_family(u8::from(self.interface_family)); packet.set_link_index(self.index); packet.set_change_mask(self.change_mask); - packet.set_link_layer_type(self.link_layer_type); - packet.set_flags(self.flags); + packet.set_link_layer_type(u16::from(self.link_layer_type)); + packet.set_flags(u32::from(&LinkFlags(self.flags.to_vec()))); } } impl> Parseable> for LinkHeader { fn parse(buf: &LinkMessageBuffer) -> Result { Ok(Self { - interface_family: buf.interface_family(), - link_layer_type: buf.link_layer_type(), + interface_family: buf.interface_family().into(), + link_layer_type: buf.link_layer_type().into(), index: buf.link_index(), change_mask: buf.change_mask(), - flags: buf.flags(), + flags: LinkFlags::from(buf.flags()).0, }) } } diff --git a/src/link/link_flag.rs b/src/link/link_flag.rs new file mode 100644 index 00000000..a9053705 --- /dev/null +++ b/src/link/link_flag.rs @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT + +const IFF_UP: u32 = 1 << 0; +const IFF_BROADCAST: u32 = 1 << 1; +const IFF_DEBUG: u32 = 1 << 2; +const IFF_LOOPBACK: u32 = 1 << 3; +const IFF_POINTOPOINT: u32 = 1 << 4; +const IFF_NOTRAILERS: u32 = 1 << 5; +const IFF_RUNNING: u32 = 1 << 6; +const IFF_NOARP: u32 = 1 << 7; +const IFF_PROMISC: u32 = 1 << 8; +const IFF_ALLMULTI: u32 = 1 << 9; +// Kernel constant name is IFF_MASTER +const IFF_CONTROLLER: u32 = 1 << 10; +// Kernel constant name is IFF_SLAVE +const IFF_PORT: u32 = 1 << 11; +const IFF_MULTICAST: u32 = 1 << 12; +const IFF_PORTSEL: u32 = 1 << 13; +const IFF_AUTOMEDIA: u32 = 1 << 14; +const IFF_DYNAMIC: u32 = 1 << 15; +const IFF_LOWER_UP: u32 = 1 << 16; +const IFF_DORMANT: u32 = 1 << 17; +const IFF_ECHO: u32 = 1 << 18; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct LinkFlags(pub Vec); + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +#[repr(u32)] +pub enum LinkFlag { + Up = IFF_UP, + Broadcast = IFF_BROADCAST, + Debug = IFF_DEBUG, + Loopback = IFF_LOOPBACK, + Pointopoint = IFF_POINTOPOINT, + Notrailers = IFF_NOTRAILERS, + Running = IFF_RUNNING, + Noarp = IFF_NOARP, + Promisc = IFF_PROMISC, + Allmulti = IFF_ALLMULTI, + Controller = IFF_CONTROLLER, + Port = IFF_PORT, + Multicast = IFF_MULTICAST, + Portsel = IFF_PORTSEL, + Automedia = IFF_AUTOMEDIA, + Dynamic = IFF_DYNAMIC, + LowerUp = IFF_LOWER_UP, + Dormant = IFF_DORMANT, + Echo = IFF_ECHO, + Other(u32), +} + +impl From for LinkFlag { + fn from(d: u32) -> Self { + match d { + d if (d & IFF_UP) > 0 => Self::Up, + d if (d & IFF_BROADCAST) > 0 => Self::Broadcast, + d if (d & IFF_DEBUG) > 0 => Self::Debug, + d if (d & IFF_LOOPBACK) > 0 => Self::Loopback, + d if (d & IFF_POINTOPOINT) > 0 => Self::Pointopoint, + d if (d & IFF_NOTRAILERS) > 0 => Self::Notrailers, + d if (d & IFF_RUNNING) > 0 => Self::Running, + d if (d & IFF_NOARP) > 0 => Self::Noarp, + d if (d & IFF_PROMISC) > 0 => Self::Promisc, + d if (d & IFF_ALLMULTI) > 0 => Self::Allmulti, + d if (d & IFF_CONTROLLER) > 0 => Self::Controller, + d if (d & IFF_PORT) > 0 => Self::Port, + d if (d & IFF_MULTICAST) > 0 => Self::Multicast, + d if (d & IFF_PORTSEL) > 0 => Self::Portsel, + d if (d & IFF_AUTOMEDIA) > 0 => Self::Automedia, + d if (d & IFF_DYNAMIC) > 0 => Self::Dynamic, + d if (d & IFF_LOWER_UP) > 0 => Self::LowerUp, + d if (d & IFF_DORMANT) > 0 => Self::Dormant, + d if (d & IFF_ECHO) > 0 => Self::Echo, + _ => Self::Other(d), + } + } +} + +impl From for u32 { + fn from(v: LinkFlag) -> u32 { + match v { + LinkFlag::Up => IFF_UP, + LinkFlag::Broadcast => IFF_BROADCAST, + LinkFlag::Debug => IFF_DEBUG, + LinkFlag::Loopback => IFF_LOOPBACK, + LinkFlag::Pointopoint => IFF_POINTOPOINT, + LinkFlag::Notrailers => IFF_NOTRAILERS, + LinkFlag::Running => IFF_RUNNING, + LinkFlag::Noarp => IFF_NOARP, + LinkFlag::Promisc => IFF_PROMISC, + LinkFlag::Allmulti => IFF_ALLMULTI, + LinkFlag::Controller => IFF_CONTROLLER, + LinkFlag::Port => IFF_PORT, + LinkFlag::Multicast => IFF_MULTICAST, + LinkFlag::Portsel => IFF_PORTSEL, + LinkFlag::Automedia => IFF_AUTOMEDIA, + LinkFlag::Dynamic => IFF_DYNAMIC, + LinkFlag::LowerUp => IFF_LOWER_UP, + LinkFlag::Dormant => IFF_DORMANT, + LinkFlag::Echo => IFF_ECHO, + LinkFlag::Other(i) => i, + } + } +} + +impl std::fmt::Display for LinkFlag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Up => write!(f, "UP"), + Self::Broadcast => write!(f, "BROADCAST"), + Self::Debug => write!(f, "DEBUG"), + Self::Loopback => write!(f, "LOOPBACK"), + Self::Pointopoint => write!(f, "POINTOPOINT"), + Self::Notrailers => write!(f, "NOTRAILERS"), + Self::Running => write!(f, "RUNNING"), + Self::Noarp => write!(f, "NOARP"), + Self::Promisc => write!(f, "PROMISC"), + Self::Allmulti => write!(f, "ALLMULTI"), + Self::Controller => write!(f, "CONTROLLER"), + Self::Port => write!(f, "PORT"), + Self::Multicast => write!(f, "MULTICAST"), + Self::Portsel => write!(f, "PORTSEL"), + Self::Automedia => write!(f, "AUTOMEDIA"), + Self::Dynamic => write!(f, "DYNAMIC"), + Self::LowerUp => write!(f, "LOWER_UP"), + Self::Dormant => write!(f, "DORMANT"), + Self::Echo => write!(f, "ECHO"), + Self::Other(i) => write!(f, "Other({})", i), + } + } +} + +// Please sort this list. +const ALL_LINK_FLAGS: [LinkFlag; 19] = [ + LinkFlag::Allmulti, + LinkFlag::Automedia, + LinkFlag::Broadcast, + LinkFlag::Controller, + LinkFlag::Debug, + LinkFlag::Dormant, + LinkFlag::Dynamic, + LinkFlag::Echo, + LinkFlag::Loopback, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Noarp, + LinkFlag::Notrailers, + LinkFlag::Pointopoint, + LinkFlag::Port, + LinkFlag::Portsel, + LinkFlag::Promisc, + LinkFlag::Running, + LinkFlag::Up, +]; + +impl From for LinkFlags { + fn from(d: u32) -> Self { + let mut got: u32 = 0; + let mut ret = Vec::new(); + for flag in ALL_LINK_FLAGS { + if (d & u32::from(flag)) > 0 { + ret.push(flag); + got += u32::from(flag); + } + } + if got != d { + ret.push(LinkFlag::Other(d - got)); + } + Self(ret) + } +} + +impl From<&LinkFlags> for u32 { + fn from(v: &LinkFlags) -> u32 { + let mut d: u32 = 0; + for flag in &v.0 { + d += u32::from(*flag); + } + d + } +} diff --git a/src/rtnl/link/nlas/bond.rs b/src/link/link_info/bond.rs similarity index 91% rename from src/rtnl/link/nlas/bond.rs rename to src/link/link_info/bond.rs index fb111385..494e9245 100644 --- a/src/rtnl/link/nlas/bond.rs +++ b/src/link/link_info/bond.rs @@ -14,7 +14,43 @@ use netlink_packet_utils::{ DecodeError, }; -use crate::constants::*; +const IFLA_BOND_AD_INFO_AGGREGATOR: u16 = 1; +const IFLA_BOND_AD_INFO_NUM_PORTS: u16 = 2; +const IFLA_BOND_AD_INFO_ACTOR_KEY: u16 = 3; +const IFLA_BOND_AD_INFO_PARTNER_KEY: u16 = 4; +const IFLA_BOND_AD_INFO_PARTNER_MAC: u16 = 5; + +const IFLA_BOND_MODE: u16 = 1; +const IFLA_BOND_ACTIVE_PORT: u16 = 2; +const IFLA_BOND_MIIMON: u16 = 3; +const IFLA_BOND_UPDELAY: u16 = 4; +const IFLA_BOND_DOWNDELAY: u16 = 5; +const IFLA_BOND_USE_CARRIER: u16 = 6; +const IFLA_BOND_ARP_INTERVAL: u16 = 7; +const IFLA_BOND_ARP_IP_TARGET: u16 = 8; +const IFLA_BOND_ARP_VALIDATE: u16 = 9; +const IFLA_BOND_ARP_ALL_TARGETS: u16 = 10; +const IFLA_BOND_PRIMARY: u16 = 11; +const IFLA_BOND_PRIMARY_RESELECT: u16 = 12; +const IFLA_BOND_FAIL_OVER_MAC: u16 = 13; +const IFLA_BOND_XMIT_HASH_POLICY: u16 = 14; +const IFLA_BOND_RESEND_IGMP: u16 = 15; +const IFLA_BOND_NUM_PEER_NOTIF: u16 = 16; +const IFLA_BOND_ALL_PORTS_ACTIVE: u16 = 17; +const IFLA_BOND_MIN_LINKS: u16 = 18; +const IFLA_BOND_LP_INTERVAL: u16 = 19; +const IFLA_BOND_PACKETS_PER_PORT: u16 = 20; +const IFLA_BOND_AD_LACP_RATE: u16 = 21; +const IFLA_BOND_AD_SELECT: u16 = 22; +const IFLA_BOND_AD_INFO: u16 = 23; +const IFLA_BOND_AD_ACTOR_SYS_PRIO: u16 = 24; +const IFLA_BOND_AD_USER_PORT_KEY: u16 = 25; +const IFLA_BOND_AD_ACTOR_SYSTEM: u16 = 26; +const IFLA_BOND_TLB_DYNAMIC_LB: u16 = 27; +const IFLA_BOND_PEER_NOTIF_DELAY: u16 = 28; +const IFLA_BOND_AD_LACP_ACTIVE: u16 = 29; +const IFLA_BOND_MISSED_MAX: u16 = 30; +const IFLA_BOND_NS_IP6_TARGET: u16 = 31; #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] diff --git a/src/rtnl/link/nlas/bond_port.rs b/src/link/link_info/bond_port.rs similarity index 88% rename from src/rtnl/link/nlas/bond_port.rs rename to src/link/link_info/bond_port.rs index 451bd5a0..62206818 100644 --- a/src/rtnl/link/nlas/bond_port.rs +++ b/src/link/link_info/bond_port.rs @@ -8,7 +8,23 @@ use netlink_packet_utils::{ DecodeError, }; -use crate::constants::*; +const IFLA_BOND_PORT_STATE_ACTIVE: u8 = 0; +const IFLA_BOND_PORT_STATE_BACKUP: u8 = 1; + +const IFLA_BOND_PORT_MII_STATUS_UP: u8 = 0; +const IFLA_BOND_PORT_MII_STATUS_GOING_DOWN: u8 = 1; +const IFLA_BOND_PORT_MII_STATUS_DOWN: u8 = 2; +const IFLA_BOND_PORT_MII_STATUS_GOING_BACK: u8 = 3; + +const IFLA_BOND_PORT_STATE: u16 = 1; +const IFLA_BOND_PORT_MII_STATUS: u16 = 2; +const IFLA_BOND_PORT_LINK_FAILURE_COUNT: u16 = 3; +const IFLA_BOND_PORT_PERM_HWADDR: u16 = 4; +const IFLA_BOND_PORT_QUEUE_ID: u16 = 5; +// const IFLA_BOND_PORT_AD_AGGREGATOR_ID: u16 = 6; +// const IFLA_BOND_PORT_AD_ACTOR_OPER_PORT_STATE: u16 = 7; +// const IFLA_BOND_PORT_AD_PARTNER_OPER_PORT_STATE: u16 = 8; +const IFLA_BOND_PORT_PRIO: u16 = 9; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] diff --git a/src/rtnl/link/nlas/bridge.rs b/src/link/link_info/bridge.rs similarity index 74% rename from src/rtnl/link/nlas/bridge.rs rename to src/link/link_info/bridge.rs index 2a4baaec..cfd5f70d 100644 --- a/src/rtnl/link/nlas/bridge.rs +++ b/src/link/link_info/bridge.rs @@ -14,8 +14,6 @@ use netlink_packet_utils::{ DecodeError, }; -use crate::constants::*; - const BRIDGE_QUERIER_IP_ADDRESS: u16 = 1; const BRIDGE_QUERIER_IP_PORT: u16 = 2; const BRIDGE_QUERIER_IP_OTHER_TIMER: u16 = 3; @@ -24,10 +22,57 @@ const BRIDGE_QUERIER_IPV6_ADDRESS: u16 = 5; const BRIDGE_QUERIER_IPV6_PORT: u16 = 6; const BRIDGE_QUERIER_IPV6_OTHER_TIMER: u16 = 7; +const IFLA_BR_FORWARD_DELAY: u16 = 1; +const IFLA_BR_HELLO_TIME: u16 = 2; +const IFLA_BR_MAX_AGE: u16 = 3; +const IFLA_BR_AGEING_TIME: u16 = 4; +const IFLA_BR_STP_STATE: u16 = 5; +const IFLA_BR_PRIORITY: u16 = 6; +const IFLA_BR_VLAN_FILTERING: u16 = 7; +const IFLA_BR_VLAN_PROTOCOL: u16 = 8; +const IFLA_BR_GROUP_FWD_MASK: u16 = 9; +const IFLA_BR_ROOT_ID: u16 = 10; +const IFLA_BR_BRIDGE_ID: u16 = 11; +const IFLA_BR_ROOT_PORT: u16 = 12; +const IFLA_BR_ROOT_PATH_COST: u16 = 13; +const IFLA_BR_TOPOLOGY_CHANGE: u16 = 14; +const IFLA_BR_TOPOLOGY_CHANGE_DETECTED: u16 = 15; +const IFLA_BR_HELLO_TIMER: u16 = 16; +const IFLA_BR_TCN_TIMER: u16 = 17; +const IFLA_BR_TOPOLOGY_CHANGE_TIMER: u16 = 18; +const IFLA_BR_GC_TIMER: u16 = 19; +const IFLA_BR_GROUP_ADDR: u16 = 20; +const IFLA_BR_FDB_FLUSH: u16 = 21; +const IFLA_BR_MCAST_ROUTER: u16 = 22; +const IFLA_BR_MCAST_SNOOPING: u16 = 23; +const IFLA_BR_MCAST_QUERY_USE_IFADDR: u16 = 24; +const IFLA_BR_MCAST_QUERIER: u16 = 25; +const IFLA_BR_MCAST_HASH_ELASTICITY: u16 = 26; +const IFLA_BR_MCAST_HASH_MAX: u16 = 27; +const IFLA_BR_MCAST_LAST_MEMBER_CNT: u16 = 28; +const IFLA_BR_MCAST_STARTUP_QUERY_CNT: u16 = 29; +const IFLA_BR_MCAST_LAST_MEMBER_INTVL: u16 = 30; +const IFLA_BR_MCAST_MEMBERSHIP_INTVL: u16 = 31; +const IFLA_BR_MCAST_QUERIER_INTVL: u16 = 32; +const IFLA_BR_MCAST_QUERY_INTVL: u16 = 33; +const IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: u16 = 34; +const IFLA_BR_MCAST_STARTUP_QUERY_INTVL: u16 = 35; +const IFLA_BR_NF_CALL_IPTABLES: u16 = 36; +const IFLA_BR_NF_CALL_IP6TABLES: u16 = 37; +const IFLA_BR_NF_CALL_ARPTABLES: u16 = 38; +const IFLA_BR_VLAN_DEFAULT_PVID: u16 = 39; +const IFLA_BR_PAD: u16 = 40; +const IFLA_BR_VLAN_STATS_ENABLED: u16 = 41; +const IFLA_BR_MCAST_STATS_ENABLED: u16 = 42; +const IFLA_BR_MCAST_IGMP_VERSION: u16 = 43; +const IFLA_BR_MCAST_MLD_VERSION: u16 = 44; +const IFLA_BR_VLAN_STATS_PER_PORT: u16 = 45; +const IFLA_BR_MULTI_BOOLOPT: u16 = 46; +const IFLA_BR_MCAST_QUERIER_STATE: u16 = 47; + #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBridge { - Unspec(Vec), GroupAddr([u8; 6]), // FIXME: what type is this? putting Vec for now but it might // be a boolean actually @@ -81,154 +126,135 @@ pub enum InfoBridge { } impl Nla for InfoBridge { - #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoBridge::*; match self { - Unspec(bytes) - | FdbFlush(bytes) - | Pad(bytes) - => bytes.len(), + FdbFlush(bytes) | Pad(bytes) => bytes.len(), HelloTimer(_) - | TcnTimer(_) - | TopologyChangeTimer(_) - | GcTimer(_) - | MulticastMembershipInterval(_) - | MulticastQuerierInterval(_) - | MulticastQueryInterval(_) - | MulticastQueryResponseInterval(_) - | MulticastLastMemberInterval(_) - | MulticastStartupQueryInterval(_) - => 8, + | TcnTimer(_) + | TopologyChangeTimer(_) + | GcTimer(_) + | MulticastMembershipInterval(_) + | MulticastQuerierInterval(_) + | MulticastQueryInterval(_) + | MulticastQueryResponseInterval(_) + | MulticastLastMemberInterval(_) + | MulticastStartupQueryInterval(_) => 8, ForwardDelay(_) - | HelloTime(_) - | MaxAge(_) - | AgeingTime(_) - | StpState(_) - | MulticastHashElasticity(_) - | MulticastHashMax(_) - | MulticastLastMemberCount(_) - | MulticastStartupQueryCount(_) - | RootPathCost(_) - => 4, - Priority(_) - | VlanProtocol(_) - | GroupFwdMask(_) - | RootPort(_) - | VlanDefaultPvid(_) - => 2, - - RootId(_) - | BridgeId(_) - | MultiBoolOpt(_) - => 8, + | HelloTime(_) + | MaxAge(_) + | AgeingTime(_) + | StpState(_) + | MulticastHashElasticity(_) + | MulticastHashMax(_) + | MulticastLastMemberCount(_) + | MulticastStartupQueryCount(_) + | RootPathCost(_) => 4, + Priority(_) | VlanProtocol(_) | GroupFwdMask(_) | RootPort(_) + | VlanDefaultPvid(_) => 2, + + RootId(_) | BridgeId(_) | MultiBoolOpt(_) => 8, GroupAddr(_) => 6, VlanFiltering(_) - | TopologyChange(_) - | TopologyChangeDetected(_) - | MulticastRouter(_) - | MulticastSnooping(_) - | MulticastQueryUseIfaddr(_) - | MulticastQuerier(_) - | NfCallIpTables(_) - | NfCallIp6Tables(_) - | NfCallArpTables(_) - | VlanStatsEnabled(_) - | MulticastStatsEnabled(_) - | MulticastIgmpVersion(_) - | MulticastMldVersion(_) - | VlanStatsPerHost(_) - => 1, + | TopologyChange(_) + | TopologyChangeDetected(_) + | MulticastRouter(_) + | MulticastSnooping(_) + | MulticastQueryUseIfaddr(_) + | MulticastQuerier(_) + | NfCallIpTables(_) + | NfCallIp6Tables(_) + | NfCallArpTables(_) + | VlanStatsEnabled(_) + | MulticastStatsEnabled(_) + | MulticastIgmpVersion(_) + | MulticastMldVersion(_) + | VlanStatsPerHost(_) => 1, MulticastQuerierState(ref nlas) => nlas.as_slice().buffer_len(), - Other(nla) - => nla.value_len(), + Other(nla) => nla.value_len(), } } - #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoBridge::*; match self { - Unspec(ref bytes) - | FdbFlush(ref bytes) - | Pad(ref bytes) - => buffer.copy_from_slice(bytes), + FdbFlush(ref bytes) | Pad(ref bytes) => { + buffer.copy_from_slice(bytes) + } HelloTimer(ref value) - | TcnTimer(ref value) - | TopologyChangeTimer(ref value) - | GcTimer(ref value) - | MulticastMembershipInterval(ref value) - | MulticastQuerierInterval(ref value) - | MulticastQueryInterval(ref value) - | MulticastQueryResponseInterval(ref value) - | MulticastLastMemberInterval(ref value) - | MulticastStartupQueryInterval(ref value) - | MultiBoolOpt(ref value) - => NativeEndian::write_u64(buffer, *value), + | TcnTimer(ref value) + | TopologyChangeTimer(ref value) + | GcTimer(ref value) + | MulticastMembershipInterval(ref value) + | MulticastQuerierInterval(ref value) + | MulticastQueryInterval(ref value) + | MulticastQueryResponseInterval(ref value) + | MulticastLastMemberInterval(ref value) + | MulticastStartupQueryInterval(ref value) + | MultiBoolOpt(ref value) => { + NativeEndian::write_u64(buffer, *value) + } ForwardDelay(ref value) - | HelloTime(ref value) - | MaxAge(ref value) - | AgeingTime(ref value) - | StpState(ref value) - | MulticastHashElasticity(ref value) - | MulticastHashMax(ref value) - | MulticastLastMemberCount(ref value) - | MulticastStartupQueryCount(ref value) - | RootPathCost(ref value) - => NativeEndian::write_u32(buffer, *value), + | HelloTime(ref value) + | MaxAge(ref value) + | AgeingTime(ref value) + | StpState(ref value) + | MulticastHashElasticity(ref value) + | MulticastHashMax(ref value) + | MulticastLastMemberCount(ref value) + | MulticastStartupQueryCount(ref value) + | RootPathCost(ref value) => { + NativeEndian::write_u32(buffer, *value) + } Priority(ref value) - | GroupFwdMask(ref value) - | RootPort(ref value) - | VlanDefaultPvid(ref value) - => NativeEndian::write_u16(buffer, *value), + | GroupFwdMask(ref value) + | RootPort(ref value) + | VlanDefaultPvid(ref value) => { + NativeEndian::write_u16(buffer, *value) + } - VlanProtocol(ref value) - => BigEndian::write_u16(buffer, *value), + VlanProtocol(ref value) => BigEndian::write_u16(buffer, *value), RootId((ref priority, ref address)) - | BridgeId((ref priority, ref address)) - => { - NativeEndian::write_u16(buffer, *priority); - buffer[2..].copy_from_slice(&address[..]); - } + | BridgeId((ref priority, ref address)) => { + NativeEndian::write_u16(buffer, *priority); + buffer[2..].copy_from_slice(&address[..]); + } GroupAddr(ref value) => buffer.copy_from_slice(&value[..]), VlanFiltering(ref value) - | TopologyChange(ref value) - | TopologyChangeDetected(ref value) - | MulticastRouter(ref value) - | MulticastSnooping(ref value) - | MulticastQueryUseIfaddr(ref value) - | MulticastQuerier(ref value) - | NfCallIpTables(ref value) - | NfCallIp6Tables(ref value) - | NfCallArpTables(ref value) - | VlanStatsEnabled(ref value) - | MulticastStatsEnabled(ref value) - | MulticastIgmpVersion(ref value) - | MulticastMldVersion(ref value) - | VlanStatsPerHost(ref value) - => buffer[0] = *value, + | TopologyChange(ref value) + | TopologyChangeDetected(ref value) + | MulticastRouter(ref value) + | MulticastSnooping(ref value) + | MulticastQueryUseIfaddr(ref value) + | MulticastQuerier(ref value) + | NfCallIpTables(ref value) + | NfCallIp6Tables(ref value) + | NfCallArpTables(ref value) + | VlanStatsEnabled(ref value) + | MulticastStatsEnabled(ref value) + | MulticastIgmpVersion(ref value) + | MulticastMldVersion(ref value) + | VlanStatsPerHost(ref value) => buffer[0] = *value, MulticastQuerierState(ref nlas) => nlas.as_slice().emit(buffer), - Other(nla) - => nla.emit_value(buffer), + Other(nla) => nla.emit_value(buffer), } } fn kind(&self) -> u16 { use self::InfoBridge::*; match self { - Unspec(_) => IFLA_BR_UNSPEC, GroupAddr(_) => IFLA_BR_GROUP_ADDR, FdbFlush(_) => IFLA_BR_FDB_FLUSH, Pad(_) => IFLA_BR_PAD, @@ -292,7 +318,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoBridge { use self::InfoBridge::*; let payload = buf.value(); Ok(match buf.kind() { - IFLA_BR_UNSPEC => Unspec(payload.to_vec()), IFLA_BR_FDB_FLUSH => FdbFlush(payload.to_vec()), IFLA_BR_PAD => Pad(payload.to_vec()), IFLA_BR_HELLO_TIMER => HelloTimer( @@ -609,64 +634,3 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> }) } } - -#[cfg(test)] -mod tests { - use netlink_packet_utils::{ - nla::NlaBuffer, - traits::{Emitable, Parseable}, - }; - - use super::{BridgeQuerierState, InfoBridge}; - - #[rustfmt::skip] - // This is capture of nlmon of `ip -d link show br0` after: - // ip link set br0 type bridge mcast_snooping 1 - // ip link set br0 type bridge mcast_querier 1 - // ip link set br0 type bridge mcast_stats_enabled 1 - const BR_MCAST_QUERIER_STATE_DUMP: [u8; 32] = [ - 0x20, 0x00, // len: 32 - 0x2f, 0x80, // IFLA_BR_MCAST_QUERIER_STATE | NLA_F_NESTED - 0x08, 0x00, // len: 8 - 0x01, 0x00, // BRIDGE_QUERIER_IP_ADDRESS - 0x00, 0x00, 0x00, 0x00, // 0.0.0.0 - 0x14, 0x00, // len: 20 - 0x05, 0x00, // BRIDGE_QUERIER_IPV6_ADDRESS - 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x23, 0x45, 0xff, 0xfe, 0x67, 0x89, 0x1c, // fe80::223:45ff:fe67:891c - ]; - - #[test] - fn test_br_multicast_querier_state_parse() { - let expected = vec![ - BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()), - BridgeQuerierState::Ipv6Address( - "fe80::223:45ff:fe67:891c".parse().unwrap(), - ), - ]; - let nla = - NlaBuffer::new_checked(&BR_MCAST_QUERIER_STATE_DUMP[..]).unwrap(); - let parsed = if let InfoBridge::MulticastQuerierState(s) = - InfoBridge::parse(&nla).unwrap() - { - s - } else { - panic!("Failed for parse IFLA_BR_MCAST_QUERIER_STATE") - }; - assert_eq!(parsed, expected); - } - - #[test] - fn test_br_multicast_querier_state_emit() { - let mut expected = [0u8; 32]; - InfoBridge::MulticastQuerierState(vec![ - BridgeQuerierState::Ipv4Address("0.0.0.0".parse().unwrap()), - BridgeQuerierState::Ipv6Address( - "fe80::223:45ff:fe67:891c".parse().unwrap(), - ), - ]) - .emit(&mut expected); - - assert_eq!(expected, BR_MCAST_QUERIER_STATE_DUMP); - } -} diff --git a/src/link/link_info/hsr.rs b/src/link/link_info/hsr.rs new file mode 100644 index 00000000..52884ac5 --- /dev/null +++ b/src/link/link_info/hsr.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::{parse_mac, parse_u16, parse_u32, parse_u8}, + traits::Parseable, + DecodeError, +}; + +// Kernel constant name is IFLA_HSR_SLAVE1 +const IFLA_HSR_PORT1: u16 = 1; +// Kernel constant name is IFLA_HSR_SLAVE2 +const IFLA_HSR_PORT2: u16 = 2; +const IFLA_HSR_MULTICAST_SPEC: u16 = 3; +const IFLA_HSR_SUPERVISION_ADDR: u16 = 4; +const IFLA_HSR_SEQ_NR: u16 = 5; +const IFLA_HSR_VERSION: u16 = 6; +const IFLA_HSR_PROTOCOL: u16 = 7; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoHsr { + Port1(u32), + Port2(u32), + MulticastSpec(u8), + SupervisionAddr([u8; 6]), + Version(u8), + SeqNr(u16), + Protocol(HsrProtocol), + Other(DefaultNla), +} + +impl Nla for InfoHsr { + fn value_len(&self) -> usize { + use self::InfoHsr::*; + match self { + SupervisionAddr(_) => 6, + Port1(_) | Port2(_) => 4, + SeqNr(_) => 2, + MulticastSpec(_) | Version(_) | Protocol(_) => 1, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoHsr::*; + match self { + Port1(value) | Port2(value) => { + NativeEndian::write_u32(buffer, *value) + } + MulticastSpec(value) | Version(value) => buffer[0] = *value, + SeqNr(value) => NativeEndian::write_u16(buffer, *value), + Protocol(value) => buffer[0] = (*value).into(), + SupervisionAddr(ref value) => buffer.copy_from_slice(&value[..]), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoHsr::*; + match self { + Port1(_) => IFLA_HSR_PORT1, + Port2(_) => IFLA_HSR_PORT2, + MulticastSpec(_) => IFLA_HSR_MULTICAST_SPEC, + SupervisionAddr(_) => IFLA_HSR_SUPERVISION_ADDR, + SeqNr(_) => IFLA_HSR_SEQ_NR, + Version(_) => IFLA_HSR_VERSION, + Protocol(_) => IFLA_HSR_PROTOCOL, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoHsr { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoHsr::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_HSR_PORT1 => Port1( + parse_u32(payload).context("invalid IFLA_HSR_PORT1 value")?, + ), + IFLA_HSR_PORT2 => Port2( + parse_u32(payload).context("invalid IFLA_HSR_PORT2 value")?, + ), + IFLA_HSR_MULTICAST_SPEC => MulticastSpec( + parse_u8(payload) + .context("invalid IFLA_HSR_MULTICAST_SPEC value")?, + ), + IFLA_HSR_SUPERVISION_ADDR => SupervisionAddr( + parse_mac(payload) + .context("invalid IFLA_HSR_SUPERVISION_ADDR value")?, + ), + IFLA_HSR_SEQ_NR => SeqNr( + parse_u16(payload).context("invalid IFLA_HSR_SEQ_NR value")?, + ), + IFLA_HSR_VERSION => Version( + parse_u8(payload).context("invalid IFLA_HSR_VERSION value")?, + ), + IFLA_HSR_PROTOCOL => Protocol( + parse_u8(payload) + .context("invalid IFLA_HSR_PROTOCOL value")? + .into(), + ), + kind => Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind}"))?, + ), + }) + } +} + +const HSR_PROTOCOL_HSR: u8 = 0; +const HSR_PROTOCOL_PRP: u8 = 1; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +#[repr(u8)] +pub enum HsrProtocol { + Hsr = HSR_PROTOCOL_HSR, + Prp = HSR_PROTOCOL_PRP, + Other(u8), +} + +impl From for HsrProtocol { + fn from(d: u8) -> Self { + match d { + HSR_PROTOCOL_HSR => Self::Hsr, + HSR_PROTOCOL_PRP => Self::Prp, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(d: HsrProtocol) -> Self { + match d { + HsrProtocol::Hsr => HSR_PROTOCOL_HSR, + HsrProtocol::Prp => HSR_PROTOCOL_PRP, + HsrProtocol::Other(value) => value, + } + } +} + +impl std::fmt::Display for HsrProtocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Hsr => write!(f, "hsr"), + Self::Prp => write!(f, "prp"), + Self::Other(d) => write!(f, "{}", d), + } + } +} diff --git a/src/link/link_info/infos.rs b/src/link/link_info/infos.rs new file mode 100644 index 00000000..ea545732 --- /dev/null +++ b/src/link/link_info/infos.rs @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; + +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::parse_string, + traits::{Emitable, Parseable}, + DecodeError, +}; + +use super::super::{ + InfoBond, InfoBondPort, InfoBridge, InfoHsr, InfoIpVlan, InfoIpoib, + InfoMacSec, InfoMacVlan, InfoMacVtap, InfoVeth, InfoVlan, InfoVrf, + InfoVxlan, InfoXfrm, +}; + +const IFLA_INFO_KIND: u16 = 1; +const IFLA_INFO_DATA: u16 = 2; +const IFLA_INFO_XSTATS: u16 = 3; +const IFLA_INFO_PORT_KIND: u16 = 4; +const IFLA_INFO_PORT_DATA: u16 = 5; + +const DUMMY: &str = "dummy"; +const IFB: &str = "ifb"; +const BRIDGE: &str = "bridge"; +const TUN: &str = "tun"; +const NLMON: &str = "nlmon"; +const VLAN: &str = "vlan"; +const VETH: &str = "veth"; +const VXLAN: &str = "vxlan"; +const BOND: &str = "bond"; +const IPVLAN: &str = "ipvlan"; +const MACVLAN: &str = "macvlan"; +const MACVTAP: &str = "macvtap"; +const GRETAP: &str = "gretap"; +const IP6GRETAP: &str = "ip6gretap"; +const IPIP: &str = "ipip"; +const SIT: &str = "sit"; +const GRE: &str = "gre"; +const IP6GRE: &str = "ip6gre"; +const VTI: &str = "vti"; +const VRF: &str = "vrf"; +const GTP: &str = "gtp"; +const IPOIB: &str = "ipoib"; +const WIREGUARD: &str = "wireguard"; +const XFRM: &str = "xfrm"; +const MACSEC: &str = "macsec"; +const HSR: &str = "hsr"; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum LinkInfo { + Xstats(Vec), + Kind(InfoKind), + Data(InfoData), + PortKind(InfoPortKind), + PortData(InfoPortData), + Other(DefaultNla), +} + +impl Nla for LinkInfo { + fn value_len(&self) -> usize { + use self::LinkInfo::*; + match self { + Xstats(ref bytes) => bytes.len(), + Kind(ref nla) => nla.value_len(), + Data(ref nla) => nla.value_len(), + PortKind(ref nla) => nla.value_len(), + PortData(ref nla) => nla.value_len(), + Other(ref nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::LinkInfo::*; + match self { + Xstats(ref bytes) => buffer.copy_from_slice(bytes), + Kind(ref nla) => nla.emit_value(buffer), + Data(ref nla) => nla.emit_value(buffer), + PortKind(ref nla) => nla.emit_value(buffer), + PortData(ref nla) => nla.emit_value(buffer), + Other(ref nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::LinkInfo::*; + match self { + Xstats(_) => IFLA_INFO_XSTATS, + PortKind(_) => IFLA_INFO_PORT_KIND, + PortData(_) => IFLA_INFO_PORT_DATA, + Kind(_) => IFLA_INFO_KIND, + Data(_) => IFLA_INFO_DATA, + Other(ref nla) => nla.kind(), + } + } +} + +pub(crate) struct VecLinkInfo(pub(crate) Vec); + +// We cannot `impl Parseable<_> for Info` because some attributes +// depend on each other. To parse IFLA_INFO_DATA we first need to +// parse the preceding IFLA_INFO_KIND for example. +// +// Moreover, with cannot `impl Parseable for Vec` due to the +// orphan rule: `Parseable` and `Vec<_>` are both defined outside of +// this crate. Thus, we create this internal VecLinkInfo struct that wraps +// `Vec` and allows us to circumvent the orphan rule. +// +// The downside is that this impl will not be exposed. + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecLinkInfo { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let mut nlas = Vec::new(); + let mut link_info_kind: Option = None; + let mut link_info_port_kind: Option = None; + for nla in NlasIterator::new(buf.into_inner()) { + let nla = nla?; + match nla.kind() { + IFLA_INFO_XSTATS => { + nlas.push(LinkInfo::Xstats(nla.value().to_vec())) + } + IFLA_INFO_PORT_KIND => { + let parsed = InfoPortKind::parse(&nla)?; + nlas.push(LinkInfo::PortKind(parsed.clone())); + link_info_port_kind = Some(parsed); + } + IFLA_INFO_PORT_DATA => { + if let Some(link_info_port_kind) = link_info_port_kind { + nlas.push(LinkInfo::PortData( + parse_info_port_data_with_kind( + nla.value(), + link_info_port_kind, + )?, + )); + } else { + return Err("IFLA_INFO_PORT_DATA is not preceded by \ + an IFLA_INFO_PORT_KIND" + .into()); + } + link_info_port_kind = None; + } + IFLA_INFO_KIND => { + let parsed = InfoKind::parse(&nla)?; + nlas.push(LinkInfo::Kind(parsed.clone())); + link_info_kind = Some(parsed); + } + IFLA_INFO_DATA => { + if let Some(link_info_kind) = link_info_kind { + nlas.push(LinkInfo::Data(parse_info_data_with_kind( + nla.value(), + link_info_kind, + )?)); + } else { + return Err("IFLA_INFO_DATA is not preceded by an \ + IFLA_INFO_KIND" + .into()); + } + link_info_kind = None; + } + _kind => nlas.push(LinkInfo::Other( + DefaultNla::parse(&nla).context(format!( + "Unknown NLA type for IFLA_INFO_DATA {:?}", + nla + ))?, + )), + } + } + Ok(Self(nlas)) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoData { + Bridge(Vec), + Tun(Vec), + Nlmon(Vec), + Vlan(Vec), + Dummy(Vec), + Ifb(Vec), + Veth(InfoVeth), + Vxlan(Vec), + Bond(Vec), + IpVlan(Vec), + MacVlan(Vec), + MacVtap(Vec), + GreTap(Vec), + GreTap6(Vec), + IpTun(Vec), + SitTun(Vec), + GreTun(Vec), + GreTun6(Vec), + Vti(Vec), + Vrf(Vec), + Gtp(Vec), + Ipoib(Vec), + Wireguard(Vec), + Xfrm(Vec), + MacSec(Vec), + Hsr(Vec), + Other(Vec), +} + +impl Nla for InfoData { + #[rustfmt::skip] + fn value_len(&self) -> usize { + use self::InfoData::*; + match self { + Bond(ref nlas) => nlas.as_slice().buffer_len(), + Bridge(ref nlas) => nlas.as_slice().buffer_len(), + Vlan(ref nlas) => nlas.as_slice().buffer_len(), + Veth(ref msg) => msg.buffer_len(), + IpVlan(ref nlas) => nlas.as_slice().buffer_len(), + Ipoib(ref nlas) => nlas.as_slice().buffer_len(), + MacVlan(ref nlas) => nlas.as_slice().buffer_len(), + MacVtap(ref nlas) => nlas.as_slice().buffer_len(), + Vrf(ref nlas) => nlas.as_slice().buffer_len(), + Vxlan(ref nlas) => nlas.as_slice().buffer_len(), + Xfrm(ref nlas) => nlas.as_slice().buffer_len(), + MacSec(ref nlas) => nlas.as_slice().buffer_len(), + Hsr(ref nlas) => nlas.as_slice().buffer_len(), + Dummy(ref bytes) + | Tun(ref bytes) + | Nlmon(ref bytes) + | Ifb(ref bytes) + | GreTap(ref bytes) + | GreTap6(ref bytes) + | IpTun(ref bytes) + | SitTun(ref bytes) + | GreTun(ref bytes) + | GreTun6(ref bytes) + | Vti(ref bytes) + | Gtp(ref bytes) + | Wireguard(ref bytes) + | Other(ref bytes) + => bytes.len(), + } + } + + #[rustfmt::skip] + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoData::*; + match self { + Bond(ref nlas) => nlas.as_slice().emit(buffer), + Bridge(ref nlas) => nlas.as_slice().emit(buffer), + Vlan(ref nlas) => nlas.as_slice().emit(buffer), + Veth(ref msg) => msg.emit(buffer), + IpVlan(ref nlas) => nlas.as_slice().emit(buffer), + Ipoib(ref nlas) => nlas.as_slice().emit(buffer), + MacVlan(ref nlas) => nlas.as_slice().emit(buffer), + MacVtap(ref nlas) => nlas.as_slice().emit(buffer), + Vrf(ref nlas) => nlas.as_slice().emit(buffer), + Vxlan(ref nlas) => nlas.as_slice().emit(buffer), + Xfrm(ref nlas) => nlas.as_slice().emit(buffer), + MacSec(ref nlas) => nlas.as_slice().emit(buffer), + Hsr(ref nlas) => nlas.as_slice().emit(buffer), + Dummy(ref bytes) + | Tun(ref bytes) + | Nlmon(ref bytes) + | Ifb(ref bytes) + | GreTap(ref bytes) + | GreTap6(ref bytes) + | IpTun(ref bytes) + | SitTun(ref bytes) + | GreTun(ref bytes) + | GreTun6(ref bytes) + | Vti(ref bytes) + | Gtp(ref bytes) + | Wireguard(ref bytes) + | Other(ref bytes) + => buffer.copy_from_slice(bytes), + } + } + + fn kind(&self) -> u16 { + IFLA_INFO_DATA + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoPortData { + BondPort(Vec), + Other(Vec), +} + +impl Nla for InfoPortData { + #[rustfmt::skip] + fn value_len(&self) -> usize { + use self::InfoPortData::*; + match self { + BondPort(ref nlas) => nlas.as_slice().buffer_len(), + Other(ref bytes) => bytes.len(), + } + } + + #[rustfmt::skip] + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoPortData::*; + match self { + BondPort(ref nlas) => nlas.as_slice().emit(buffer), + Other(ref bytes) => buffer.copy_from_slice(bytes), + } + } + + fn kind(&self) -> u16 { + IFLA_INFO_PORT_DATA + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoKind { + Dummy, + Ifb, + Bridge, + Tun, + Nlmon, + Vlan, + Veth, + Vxlan, + Bond, + IpVlan, + MacVlan, + MacVtap, + GreTap, + GreTap6, + IpTun, + SitTun, + GreTun, + GreTun6, + Vti, + Vrf, + Gtp, + Ipoib, + Wireguard, + Xfrm, + MacSec, + Hsr, + Other(String), +} + +impl std::fmt::Display for InfoKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Dummy => DUMMY, + Self::Ifb => IFB, + Self::Bridge => BRIDGE, + Self::Tun => TUN, + Self::Nlmon => NLMON, + Self::Vlan => VLAN, + Self::Veth => VETH, + Self::Vxlan => VXLAN, + Self::Bond => BOND, + Self::IpVlan => IPVLAN, + Self::MacVlan => MACVLAN, + Self::MacVtap => MACVTAP, + Self::GreTap => GRETAP, + Self::GreTap6 => IP6GRETAP, + Self::IpTun => IPIP, + Self::SitTun => SIT, + Self::GreTun => GRE, + Self::GreTun6 => IP6GRE, + Self::Vti => VTI, + Self::Vrf => VRF, + Self::Gtp => GTP, + Self::Ipoib => IPOIB, + Self::Wireguard => WIREGUARD, + Self::Xfrm => XFRM, + Self::MacSec => MACSEC, + Self::Hsr => HSR, + Self::Other(s) => s.as_str(), + } + ) + } +} + +impl Nla for InfoKind { + fn value_len(&self) -> usize { + use self::InfoKind::*; + let len = match *self { + Dummy => DUMMY.len(), + Ifb => IFB.len(), + Bridge => BRIDGE.len(), + Tun => TUN.len(), + Nlmon => NLMON.len(), + Vlan => VLAN.len(), + Veth => VETH.len(), + Vxlan => VXLAN.len(), + Bond => BOND.len(), + IpVlan => IPVLAN.len(), + MacVlan => MACVLAN.len(), + MacVtap => MACVTAP.len(), + GreTap => GRETAP.len(), + GreTap6 => IP6GRETAP.len(), + IpTun => IPIP.len(), + SitTun => SIT.len(), + GreTun => GRE.len(), + GreTun6 => IP6GRE.len(), + Vti => VTI.len(), + Vrf => VRF.len(), + Gtp => GTP.len(), + Ipoib => IPOIB.len(), + Wireguard => WIREGUARD.len(), + Xfrm => XFRM.len(), + MacSec => MACSEC.len(), + Hsr => HSR.len(), + Other(ref s) => s.len(), + }; + len + 1 + } + + fn emit_value(&self, buffer: &mut [u8]) { + let kind = self.to_string(); + let s = kind.as_str(); + buffer[..s.len()].copy_from_slice(s.to_string().as_bytes()); + buffer[s.len()] = 0; + } + + fn kind(&self) -> u16 { + IFLA_INFO_KIND + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoKind { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoKind::*; + if buf.kind() != IFLA_INFO_KIND { + return Err(format!( + "failed to parse IFLA_INFO_KIND: NLA type is {}", + buf.kind() + ) + .into()); + } + let s = parse_string(buf.value()) + .context("invalid IFLA_INFO_KIND value")?; + Ok(match s.as_str() { + DUMMY => Dummy, + IFB => Ifb, + BRIDGE => Bridge, + TUN => Tun, + NLMON => Nlmon, + VLAN => Vlan, + VETH => Veth, + VXLAN => Vxlan, + BOND => Bond, + IPVLAN => IpVlan, + MACVLAN => MacVlan, + MACVTAP => MacVtap, + GRETAP => GreTap, + IP6GRETAP => GreTap6, + IPIP => IpTun, + SIT => SitTun, + GRE => GreTun, + IP6GRE => GreTun6, + VTI => Vti, + VRF => Vrf, + GTP => Gtp, + IPOIB => Ipoib, + WIREGUARD => Wireguard, + MACSEC => MacSec, + XFRM => Xfrm, + HSR => Hsr, + _ => Other(s), + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoPortKind { + Bond, + Other(String), +} + +impl std::fmt::Display for InfoPortKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Bond => BOND, + Self::Other(s) => s.as_str(), + } + ) + } +} + +impl Nla for InfoPortKind { + fn value_len(&self) -> usize { + use self::InfoPortKind::*; + let len = match *self { + Bond => BOND.len(), + Other(ref s) => s.len(), + }; + len + 1 + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoPortKind::*; + let s = match *self { + Bond => BOND, + Other(ref s) => s.as_str(), + }; + buffer[..s.len()].copy_from_slice(s.as_bytes()); + buffer[s.len()] = 0; + } + + fn kind(&self) -> u16 { + IFLA_INFO_PORT_KIND + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoPortKind { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoPortKind::*; + if buf.kind() != IFLA_INFO_PORT_KIND { + return Err(format!( + "failed to parse IFLA_INFO_PORT_KIND: NLA type is {}", + buf.kind() + ) + .into()); + } + let s = parse_string(buf.value()) + .context("invalid IFLA_INFO_PORT_KIND value")?; + Ok(match s.as_str() { + BOND => Bond, + _ => Other(s), + }) + } +} + +fn parse_info_data_with_kind( + payload: &[u8], + kind: InfoKind, +) -> Result { + Ok(match kind { + InfoKind::Dummy => InfoData::Dummy(payload.to_vec()), + InfoKind::Ifb => InfoData::Ifb(payload.to_vec()), + InfoKind::Bridge => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bridge')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoBridge::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Bridge(v) + } + InfoKind::Vlan => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vlan')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoVlan::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Vlan(v) + } + InfoKind::Tun => InfoData::Tun(payload.to_vec()), + InfoKind::Nlmon => InfoData::Nlmon(payload.to_vec()), + InfoKind::Veth => { + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'veth')"; + let nla_buf = NlaBuffer::new_checked(&payload).context(err)?; + let parsed = InfoVeth::parse(&nla_buf).context(err)?; + InfoData::Veth(parsed) + } + InfoKind::Vxlan => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vxlan')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoVxlan::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Vxlan(v) + } + InfoKind::Bond => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bond')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoBond::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Bond(v) + } + InfoKind::IpVlan => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipvlan')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoIpVlan::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::IpVlan(v) + } + InfoKind::MacVlan => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvlan')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoMacVlan::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::MacVlan(v) + } + InfoKind::MacVtap => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvtap')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoMacVtap::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::MacVtap(v) + } + InfoKind::GreTap => InfoData::GreTap(payload.to_vec()), + InfoKind::GreTap6 => InfoData::GreTap6(payload.to_vec()), + InfoKind::IpTun => InfoData::IpTun(payload.to_vec()), + InfoKind::SitTun => InfoData::SitTun(payload.to_vec()), + InfoKind::GreTun => InfoData::GreTun(payload.to_vec()), + InfoKind::GreTun6 => InfoData::GreTun6(payload.to_vec()), + InfoKind::Vti => InfoData::Vti(payload.to_vec()), + InfoKind::Vrf => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vrf')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoVrf::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Vrf(v) + } + InfoKind::Gtp => InfoData::Gtp(payload.to_vec()), + InfoKind::Ipoib => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipoib')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoIpoib::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Ipoib(v) + } + InfoKind::Wireguard => InfoData::Wireguard(payload.to_vec()), + InfoKind::Other(_) => InfoData::Other(payload.to_vec()), + InfoKind::Xfrm => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'Xfrm')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoXfrm::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Xfrm(v) + } + InfoKind::MacSec => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macsec')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoMacSec::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::MacSec(v) + } + InfoKind::Hsr => { + let mut v = Vec::new(); + let err = + "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'hsr')"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoHsr::parse(nla).context(err)?; + v.push(parsed); + } + InfoData::Hsr(v) + } + }) +} + +fn parse_info_port_data_with_kind( + payload: &[u8], + kind: InfoPortKind, +) -> Result { + Ok(match kind { + InfoPortKind::Bond => { + let mut v = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(format!( + "failed to parse IFLA_INFO_PORT_DATA \ + (IFLA_INFO_PORT_KIND is '{kind}')" + ))?; + let parsed = InfoBondPort::parse(nla).context(format!( + "failed to parse IFLA_INFO_PORT_DATA \ + (IFLA_INFO_PORT_KIND is '{kind}')" + ))?; + v.push(parsed); + } + InfoPortData::BondPort(v) + } + InfoPortKind::Other(_) => InfoPortData::Other(payload.to_vec()), + }) +} diff --git a/src/link/link_info/ipoib.rs b/src/link/link_info/ipoib.rs new file mode 100644 index 00000000..e68c57b6 --- /dev/null +++ b/src/link/link_info/ipoib.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::parse_u16, + traits::Parseable, + DecodeError, +}; + +const IFLA_IPOIB_PKEY: u16 = 1; +const IFLA_IPOIB_MODE: u16 = 2; +const IFLA_IPOIB_UMCAST: u16 = 3; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoIpoib { + Pkey(u16), + Mode(u16), + UmCast(u16), + Other(DefaultNla), +} + +impl Nla for InfoIpoib { + fn value_len(&self) -> usize { + use self::InfoIpoib::*; + match self { + Pkey(_) | Mode(_) | UmCast(_) => 2, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoIpoib::*; + match self { + Pkey(value) => NativeEndian::write_u16(buffer, *value), + Mode(value) => NativeEndian::write_u16(buffer, *value), + UmCast(value) => NativeEndian::write_u16(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoIpoib::*; + match self { + Pkey(_) => IFLA_IPOIB_PKEY, + Mode(_) => IFLA_IPOIB_MODE, + UmCast(_) => IFLA_IPOIB_UMCAST, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpoib { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoIpoib::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_IPOIB_PKEY => Pkey( + parse_u16(payload).context("invalid IFLA_IPOIB_PKEY value")?, + ), + IFLA_IPOIB_MODE => Mode( + parse_u16(payload).context("invalid IFLA_IPOIB_MODE value")?, + ), + IFLA_IPOIB_UMCAST => UmCast( + parse_u16(payload) + .context("invalid IFLA_IPOIB_UMCAST value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(ipoib)" + ))?), + }) + } +} diff --git a/src/link/link_info/ipvlan.rs b/src/link/link_info/ipvlan.rs new file mode 100644 index 00000000..384427e5 --- /dev/null +++ b/src/link/link_info/ipvlan.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::parse_u16, + traits::Parseable, + DecodeError, +}; + +const IFLA_IPVLAN_MODE: u16 = 1; +const IFLA_IPVLAN_FLAGS: u16 = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoIpVlan { + Mode(u16), + Flags(u16), + Other(DefaultNla), +} + +impl Nla for InfoIpVlan { + fn value_len(&self) -> usize { + use self::InfoIpVlan::*; + match self { + Mode(_) | Flags(_) => 2, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoIpVlan::*; + match self { + Mode(value) => NativeEndian::write_u16(buffer, *value), + Flags(value) => NativeEndian::write_u16(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoIpVlan::*; + match self { + Mode(_) => IFLA_IPVLAN_MODE, + Flags(_) => IFLA_IPVLAN_FLAGS, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVlan { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoIpVlan::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_IPVLAN_MODE => Mode( + parse_u16(payload).context("invalid IFLA_IPVLAN_MODE value")?, + ), + IFLA_IPVLAN_FLAGS => Flags( + parse_u16(payload) + .context("invalid IFLA_IPVLAN_FLAGS value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(ipvlan)" + ))?), + }) + } +} diff --git a/src/link/link_info/mac_vlan.rs b/src/link/link_info/mac_vlan.rs new file mode 100644 index 00000000..4c98488a --- /dev/null +++ b/src/link/link_info/mac_vlan.rs @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::{parse_i32, parse_mac, parse_u16, parse_u32}, + traits::{Emitable, Parseable}, + DecodeError, +}; + +const IFLA_MACVLAN_MODE: u16 = 1; +const IFLA_MACVLAN_FLAGS: u16 = 2; +const IFLA_MACVLAN_MACADDR_MODE: u16 = 3; +const IFLA_MACVLAN_MACADDR: u16 = 4; +const IFLA_MACVLAN_MACADDR_DATA: u16 = 5; +const IFLA_MACVLAN_MACADDR_COUNT: u16 = 6; +const IFLA_MACVLAN_BC_QUEUE_LEN: u16 = 7; +const IFLA_MACVLAN_BC_QUEUE_LEN_USED: u16 = 8; +const IFLA_MACVLAN_BC_CUTOFF: u16 = 9; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoMacVlan { + Mode(u32), + Flags(u16), + MacAddrMode(u32), + MacAddr([u8; 6]), + /// A list of InfoMacVlan::MacAddr + MacAddrData(Vec), + MacAddrCount(u32), + BcQueueLen(u32), + BcQueueLenUsed(u32), + BcCutoff(i32), + Other(DefaultNla), +} + +impl Nla for InfoMacVlan { + fn value_len(&self) -> usize { + use self::InfoMacVlan::*; + match self { + Mode(_) => 4, + Flags(_) => 2, + MacAddrMode(_) => 4, + MacAddr(_) => 6, + MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), + MacAddrCount(_) => 4, + BcQueueLen(_) => 4, + BcQueueLenUsed(_) => 4, + BcCutoff(_) => 4, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoMacVlan::*; + match self { + Mode(value) => NativeEndian::write_u32(buffer, *value), + Flags(value) => NativeEndian::write_u16(buffer, *value), + MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), + MacAddr(bytes) => buffer.copy_from_slice(bytes), + MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), + MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), + BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), + BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), + BcCutoff(value) => NativeEndian::write_i32(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoMacVlan::*; + match self { + Mode(_) => IFLA_MACVLAN_MODE, + Flags(_) => IFLA_MACVLAN_FLAGS, + MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, + MacAddr(_) => IFLA_MACVLAN_MACADDR, + MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, + MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, + BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, + BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, + BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVlan { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoMacVlan::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_MACVLAN_MODE => Mode( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MODE value")?, + ), + IFLA_MACVLAN_FLAGS => Flags( + parse_u16(payload) + .context("invalid IFLA_MACVLAN_FLAGS value")?, + ), + IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, + ), + IFLA_MACVLAN_MACADDR => MacAddr( + parse_mac(payload) + .context("invalid IFLA_MACVLAN_MACADDR value")?, + ), + IFLA_MACVLAN_MACADDR_DATA => { + let mut mac_data = Vec::new(); + let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoMacVlan::parse(nla).context(err)?; + mac_data.push(parsed); + } + MacAddrData(mac_data) + } + IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, + ), + IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, + ), + IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, + ), + IFLA_MACVLAN_BC_CUTOFF => BcCutoff( + parse_i32(payload) + .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(mac_vlan)" + ))?), + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoMacVtap { + Mode(u32), + Flags(u16), + MacAddrMode(u32), + MacAddr([u8; 6]), + MacAddrData(Vec), + MacAddrCount(u32), + BcQueueLen(u32), + BcQueueLenUsed(u32), + BcCutoff(i32), + Other(DefaultNla), +} + +impl Nla for InfoMacVtap { + fn value_len(&self) -> usize { + use self::InfoMacVtap::*; + match self { + Mode(_) => 4, + Flags(_) => 2, + MacAddrMode(_) => 4, + MacAddr(_) => 6, + MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), + MacAddrCount(_) => 4, + BcQueueLen(_) => 4, + BcQueueLenUsed(_) => 4, + BcCutoff(_) => 4, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoMacVtap::*; + match self { + Mode(value) => NativeEndian::write_u32(buffer, *value), + Flags(value) => NativeEndian::write_u16(buffer, *value), + MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), + MacAddr(bytes) => buffer.copy_from_slice(bytes), + MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), + MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), + BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), + BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), + BcCutoff(value) => NativeEndian::write_i32(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoMacVtap::*; + match self { + Mode(_) => IFLA_MACVLAN_MODE, + Flags(_) => IFLA_MACVLAN_FLAGS, + MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, + MacAddr(_) => IFLA_MACVLAN_MACADDR, + MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, + MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, + BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, + BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, + BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVtap { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoMacVtap::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_MACVLAN_MODE => Mode( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MODE value")?, + ), + IFLA_MACVLAN_FLAGS => Flags( + parse_u16(payload) + .context("invalid IFLA_MACVLAN_FLAGS value")?, + ), + IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, + ), + IFLA_MACVLAN_MACADDR => MacAddr( + parse_mac(payload) + .context("invalid IFLA_MACVLAN_MACADDR value")?, + ), + IFLA_MACVLAN_MACADDR_DATA => { + let mut mac_data = Vec::new(); + let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; + for nla in NlasIterator::new(payload) { + let nla = &nla.context(err)?; + let parsed = InfoMacVtap::parse(nla).context(err)?; + mac_data.push(parsed); + } + MacAddrData(mac_data) + } + IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, + ), + IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, + ), + IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( + parse_u32(payload) + .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, + ), + IFLA_MACVLAN_BC_CUTOFF => BcCutoff( + parse_i32(payload) + .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(mac_vtap)" + ))?), + }) + } +} diff --git a/src/link/link_info/macsec.rs b/src/link/link_info/macsec.rs new file mode 100644 index 00000000..3382363d --- /dev/null +++ b/src/link/link_info/macsec.rs @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::{parse_u16, parse_u32, parse_u64, parse_u8}, + traits::Parseable, + DecodeError, +}; + +const IFLA_MACSEC_SCI: u16 = 1; +const IFLA_MACSEC_PORT: u16 = 2; +const IFLA_MACSEC_ICV_LEN: u16 = 3; +const IFLA_MACSEC_CIPHER_SUITE: u16 = 4; +const IFLA_MACSEC_WINDOW: u16 = 5; +const IFLA_MACSEC_ENCODING_SA: u16 = 6; +const IFLA_MACSEC_ENCRYPT: u16 = 7; +const IFLA_MACSEC_PROTECT: u16 = 8; +const IFLA_MACSEC_INC_SCI: u16 = 9; +const IFLA_MACSEC_ES: u16 = 10; +const IFLA_MACSEC_SCB: u16 = 11; +const IFLA_MACSEC_REPLAY_PROTECT: u16 = 12; +const IFLA_MACSEC_VALIDATION: u16 = 13; +// const IFLA_MACSEC_PAD: u16 = 14; +const IFLA_MACSEC_OFFLOAD: u16 = 15; +const MACSEC_VALIDATE_DISABLED: u8 = 0; +const MACSEC_VALIDATE_CHECK: u8 = 1; +const MACSEC_VALIDATE_STRICT: u8 = 2; +const MACSEC_OFFLOAD_OFF: u8 = 0; +const MACSEC_OFFLOAD_PHY: u8 = 1; +const MACSEC_OFFLOAD_MAC: u8 = 2; +const MACSEC_CIPHER_ID_GCM_AES_128: u64 = 0x0080C20001000001; +const MACSEC_CIPHER_ID_GCM_AES_256: u64 = 0x0080C20001000002; +const MACSEC_CIPHER_ID_GCM_AES_XPN_128: u64 = 0x0080C20001000003; +const MACSEC_CIPHER_ID_GCM_AES_XPN_256: u64 = 0x0080C20001000004; +const MACSEC_DEFAULT_CIPHER_ID: u64 = 0x0080020001000001; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MacSecCipherId { + #[deprecated] + DefaultGcmAes128, + GcmAes128, + GcmAes256, + GcmAesXpn128, + GcmAesXpn256, + Other(u64), +} + +impl From for MacSecCipherId { + fn from(d: u64) -> Self { + match d { + #[allow(deprecated)] + MACSEC_DEFAULT_CIPHER_ID => Self::DefaultGcmAes128, + MACSEC_CIPHER_ID_GCM_AES_128 => Self::GcmAes128, + MACSEC_CIPHER_ID_GCM_AES_256 => Self::GcmAes256, + MACSEC_CIPHER_ID_GCM_AES_XPN_128 => Self::GcmAesXpn128, + MACSEC_CIPHER_ID_GCM_AES_XPN_256 => Self::GcmAesXpn256, + _ => Self::Other(d), + } + } +} + +impl From for u64 { + fn from(d: MacSecCipherId) -> Self { + match d { + #[allow(deprecated)] + MacSecCipherId::DefaultGcmAes128 => MACSEC_DEFAULT_CIPHER_ID, + MacSecCipherId::GcmAes128 => MACSEC_CIPHER_ID_GCM_AES_128, + MacSecCipherId::GcmAes256 => MACSEC_CIPHER_ID_GCM_AES_256, + MacSecCipherId::GcmAesXpn128 => MACSEC_CIPHER_ID_GCM_AES_XPN_128, + MacSecCipherId::GcmAesXpn256 => MACSEC_CIPHER_ID_GCM_AES_XPN_256, + MacSecCipherId::Other(value) => value, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MacSecValidation { + Disabled, + Check, + Strict, + Other(u8), +} + +impl From for MacSecValidation { + fn from(d: u8) -> Self { + match d { + MACSEC_VALIDATE_DISABLED => Self::Disabled, + MACSEC_VALIDATE_CHECK => Self::Check, + MACSEC_VALIDATE_STRICT => Self::Strict, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(d: MacSecValidation) -> Self { + match d { + MacSecValidation::Disabled => MACSEC_VALIDATE_DISABLED, + MacSecValidation::Check => MACSEC_VALIDATE_CHECK, + MacSecValidation::Strict => MACSEC_VALIDATE_STRICT, + MacSecValidation::Other(value) => value, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MacSecOffload { + Off, + Phy, + Mac, + Other(u8), +} + +impl From for MacSecOffload { + fn from(d: u8) -> Self { + match d { + MACSEC_OFFLOAD_OFF => Self::Off, + MACSEC_OFFLOAD_PHY => Self::Phy, + MACSEC_OFFLOAD_MAC => Self::Mac, + _ => Self::Other(d), + } + } +} + +impl From for u8 { + fn from(d: MacSecOffload) -> Self { + match d { + MacSecOffload::Off => MACSEC_OFFLOAD_OFF, + MacSecOffload::Phy => MACSEC_OFFLOAD_PHY, + MacSecOffload::Mac => MACSEC_OFFLOAD_MAC, + MacSecOffload::Other(value) => value, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoMacSec { + Sci(u64), + Port(u16), + IcvLen(u8), + CipherSuite(MacSecCipherId), + Window(u32), + EncodingSa(u8), + Encrypt(u8), + Protect(u8), + IncSci(u8), + Es(u8), + Scb(u8), + ReplayProtect(u8), + Validation(MacSecValidation), + Offload(MacSecOffload), + Other(DefaultNla), +} + +impl Nla for InfoMacSec { + fn value_len(&self) -> usize { + use self::InfoMacSec::*; + match self { + Sci(_) | CipherSuite(_) => 8, + Window(_) => 4, + Port(_) => 2, + IcvLen(_) | EncodingSa(_) | Encrypt(_) | Protect(_) | IncSci(_) + | Es(_) | Scb(_) | ReplayProtect(_) | Validation(_) + | Offload(_) => 1, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoMacSec::*; + match self { + Sci(value) => NativeEndian::write_u64(buffer, *value), + CipherSuite(value) => { + NativeEndian::write_u64(buffer, (*value).into()) + } + Window(value) => NativeEndian::write_u32(buffer, *value), + Port(value) => NativeEndian::write_u16(buffer, *value), + IcvLen(value) | EncodingSa(value) | Encrypt(value) + | Protect(value) | IncSci(value) | Es(value) | Scb(value) + | ReplayProtect(value) => buffer[0] = *value, + Offload(value) => buffer[0] = (*value).into(), + Validation(value) => buffer[0] = (*value).into(), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoMacSec::*; + match self { + Sci(_) => IFLA_MACSEC_SCI, + Port(_) => IFLA_MACSEC_PORT, + IcvLen(_) => IFLA_MACSEC_ICV_LEN, + CipherSuite(_) => IFLA_MACSEC_CIPHER_SUITE, + Window(_) => IFLA_MACSEC_WINDOW, + EncodingSa(_) => IFLA_MACSEC_ENCODING_SA, + Encrypt(_) => IFLA_MACSEC_ENCRYPT, + Protect(_) => IFLA_MACSEC_PROTECT, + IncSci(_) => IFLA_MACSEC_INC_SCI, + Es(_) => IFLA_MACSEC_ES, + Scb(_) => IFLA_MACSEC_SCB, + ReplayProtect(_) => IFLA_MACSEC_REPLAY_PROTECT, + Validation(_) => IFLA_MACSEC_VALIDATION, + Offload(_) => IFLA_MACSEC_OFFLOAD, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacSec { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoMacSec::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_MACSEC_SCI => { + Sci(parse_u64(payload) + .context("invalid IFLA_MACSEC_SCI value")?) + } + IFLA_MACSEC_PORT => Port( + parse_u16(payload).context("invalid IFLA_MACSEC_PORT value")?, + ), + IFLA_MACSEC_ICV_LEN => IcvLen( + parse_u8(payload) + .context("invalid IFLA_MACSEC_ICV_LEN value")?, + ), + IFLA_MACSEC_CIPHER_SUITE => CipherSuite( + parse_u64(payload) + .context("invalid IFLA_MACSEC_CIPHER_SUITE value")? + .into(), + ), + IFLA_MACSEC_WINDOW => Window( + parse_u32(payload) + .context("invalid IFLA_MACSEC_WINDOW value")?, + ), + IFLA_MACSEC_ENCODING_SA => EncodingSa( + parse_u8(payload) + .context("invalid IFLA_MACSEC_ENCODING_SA value")?, + ), + IFLA_MACSEC_ENCRYPT => Encrypt( + parse_u8(payload) + .context("invalid IFLA_MACSEC_ENCRYPT value")?, + ), + IFLA_MACSEC_PROTECT => Protect( + parse_u8(payload) + .context("invalid IFLA_MACSEC_PROTECT value")?, + ), + IFLA_MACSEC_INC_SCI => IncSci( + parse_u8(payload) + .context("invalid IFLA_MACSEC_INC_SCI value")?, + ), + IFLA_MACSEC_ES => { + Es(parse_u8(payload).context("invalid IFLA_MACSEC_ES value")?) + } + IFLA_MACSEC_SCB => { + Scb(parse_u8(payload) + .context("invalid IFLA_MACSEC_SCB value")?) + } + IFLA_MACSEC_REPLAY_PROTECT => ReplayProtect( + parse_u8(payload) + .context("invalid IFLA_MACSEC_REPLAY_PROTECT value")?, + ), + IFLA_MACSEC_VALIDATION => Validation( + parse_u8(payload) + .context("invalid IFLA_MACSEC_VALIDATION value")? + .into(), + ), + IFLA_MACSEC_OFFLOAD => Offload( + parse_u8(payload) + .context("invalid IFLA_MACSEC_OFFLOAD value")? + .into(), + ), + kind => Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind}"))?, + ), + }) + } +} diff --git a/src/link/link_info/mod.rs b/src/link/link_info/mod.rs new file mode 100644 index 00000000..c0b7e3e0 --- /dev/null +++ b/src/link/link_info/mod.rs @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT + +mod bond; +mod bond_port; +mod bridge; +mod hsr; +mod infos; +mod ipoib; +mod ipvlan; +mod mac_vlan; +mod macsec; +mod veth; +mod vlan; +mod vrf; +mod vxlan; +mod xfrm; + +pub use self::bond::{BondAdInfo, InfoBond}; +pub use self::bond_port::{BondPortState, InfoBondPort, MiiStatus}; +pub use self::bridge::{BridgeQuerierState, InfoBridge}; +pub use self::hsr::{HsrProtocol, InfoHsr}; +pub use self::infos::{ + InfoData, InfoKind, InfoPortData, InfoPortKind, LinkInfo, +}; +pub use self::ipoib::InfoIpoib; +pub use self::ipvlan::InfoIpVlan; +pub use self::mac_vlan::{InfoMacVlan, InfoMacVtap}; +pub use self::macsec::{ + InfoMacSec, MacSecCipherId, MacSecOffload, MacSecValidation, +}; +pub use self::veth::InfoVeth; +pub use self::vlan::{InfoVlan, VlanProtocol, VlanQosMapping}; +pub use self::vrf::InfoVrf; +pub use self::vxlan::InfoVxlan; +pub use self::xfrm::InfoXfrm; + +pub(crate) use self::infos::VecLinkInfo; diff --git a/src/link/link_info/veth.rs b/src/link/link_info/veth.rs new file mode 100644 index 00000000..e36f698d --- /dev/null +++ b/src/link/link_info/veth.rs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + traits::{Emitable, Parseable}, + DecodeError, +}; + +use super::super::{LinkMessage, LinkMessageBuffer}; + +const VETH_INFO_PEER: u16 = 1; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +// This data is not for querying/dumping as in kernel 6.5.8, +// because the `struct rtnl_link_ops veth_link_ops` does not have `fill_info`. +// Only for create veth +pub enum InfoVeth { + Peer(LinkMessage), + Other(DefaultNla), +} + +impl Nla for InfoVeth { + fn value_len(&self) -> usize { + use self::InfoVeth::*; + match *self { + Peer(ref message) => message.buffer_len(), + Other(ref attr) => attr.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoVeth::*; + match *self { + Peer(ref message) => message.emit(buffer), + Other(ref attr) => attr.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoVeth::*; + match *self { + Peer(_) => VETH_INFO_PEER, + Other(ref attr) => attr.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVeth { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoVeth::*; + let payload = buf.value(); + Ok(match buf.kind() { + VETH_INFO_PEER => { + let err = "failed to parse veth link info"; + let buffer = + LinkMessageBuffer::new_checked(&payload).context(err)?; + Peer(LinkMessage::parse(&buffer).context(err)?) + } + kind => Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind}"))?, + ), + }) + } +} diff --git a/src/link/link_info/vlan.rs b/src/link/link_info/vlan.rs new file mode 100644 index 00000000..31ea4a02 --- /dev/null +++ b/src/link/link_info/vlan.rs @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{BigEndian, ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, + parsers::{parse_u16, parse_u16_be, parse_u32}, + traits::{Emitable, Parseable}, + DecodeError, +}; + +const IFLA_VLAN_ID: u16 = 1; +const IFLA_VLAN_FLAGS: u16 = 2; +const IFLA_VLAN_EGRESS_QOS: u16 = 3; +const IFLA_VLAN_INGRESS_QOS: u16 = 4; +const IFLA_VLAN_PROTOCOL: u16 = 5; + +const IFLA_VLAN_QOS_MAPPING: u16 = 1; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoVlan { + Id(u16), + Flags((u32, u32)), + EgressQos(Vec), + IngressQos(Vec), + Protocol(VlanProtocol), +} + +impl Nla for InfoVlan { + fn value_len(&self) -> usize { + use self::InfoVlan::*; + match self { + Id(_) | Protocol(_) => 2, + Flags(_) => 8, + EgressQos(mappings) | IngressQos(mappings) => { + mappings.as_slice().buffer_len() + } + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoVlan::*; + match self { + EgressQos(ref mappings) | IngressQos(ref mappings) => { + mappings.as_slice().emit(buffer) + } + Id(ref value) => NativeEndian::write_u16(buffer, *value), + Protocol(value) => BigEndian::write_u16(buffer, (*value).into()), + Flags(ref flags) => { + NativeEndian::write_u32(&mut buffer[0..4], flags.0); + NativeEndian::write_u32(&mut buffer[4..8], flags.1) + } + } + } + + fn kind(&self) -> u16 { + use self::InfoVlan::*; + match self { + Id(_) => IFLA_VLAN_ID, + Flags(_) => IFLA_VLAN_FLAGS, + EgressQos(_) => IFLA_VLAN_EGRESS_QOS, + IngressQos(_) => IFLA_VLAN_INGRESS_QOS, + Protocol(_) => IFLA_VLAN_PROTOCOL, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum VlanQosMapping { + /// Tuple (from, to) + Mapping(u32, u32), + Other(DefaultNla), +} + +impl Nla for VlanQosMapping { + fn value_len(&self) -> usize { + match self { + VlanQosMapping::Mapping { .. } => 8, + VlanQosMapping::Other(nla) => nla.value_len(), + } + } + + fn kind(&self) -> u16 { + match self { + VlanQosMapping::Mapping { .. } => IFLA_VLAN_QOS_MAPPING, + VlanQosMapping::Other(nla) => nla.kind(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use VlanQosMapping::*; + match self { + Mapping(from, to) => { + NativeEndian::write_u32(buffer, *from); + NativeEndian::write_u32(&mut buffer[4..], *to); + } + Other(nla) => nla.emit_value(buffer), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> + for VlanQosMapping +{ + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use VlanQosMapping::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_VLAN_QOS_MAPPING => Mapping( + parse_u32(&payload[..4]).context("expected u32 from value")?, + parse_u32(&payload[4..]).context("expected u32 to value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for VLAN QoS mapping" + ))?), + }) + } +} + +fn parse_mappings(payload: &[u8]) -> Result, DecodeError> { + let mut mappings = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = nla?; + let parsed = VlanQosMapping::parse(&nla)?; + mappings.push(parsed); + } + Ok(mappings) +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVlan { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoVlan::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_VLAN_ID => { + Id(parse_u16(payload).context("invalid IFLA_VLAN_ID value")?) + } + IFLA_VLAN_FLAGS => { + let err = "invalid IFLA_VLAN_FLAGS value"; + if payload.len() != 8 { + return Err(err.into()); + } + let flags = parse_u32(&payload[0..4]).context(err)?; + let mask = parse_u32(&payload[4..]).context(err)?; + Flags((flags, mask)) + } + IFLA_VLAN_EGRESS_QOS => EgressQos( + parse_mappings(payload) + .context("failed to parse IFLA_VLAN_EGRESS_QOS")?, + ), + IFLA_VLAN_INGRESS_QOS => IngressQos( + parse_mappings(payload) + .context("failed to parse IFLA_VLAN_INGRESS_QOS")?, + ), + IFLA_VLAN_PROTOCOL => Protocol( + parse_u16_be(payload) + .context("invalid IFLA_VLAN_PROTOCOL value")? + .into(), + ), + _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), + }) + } +} + +const ETH_P_8021Q: u16 = 0x8100; +const ETH_P_8021AD: u16 = 0x88A8; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +#[repr(u16)] +// VLAN protocol seldom add new, so no Other for this enum. +pub enum VlanProtocol { + #[default] + Ieee8021Q = ETH_P_8021Q, + Ieee8021Ad = ETH_P_8021AD, +} + +impl From for VlanProtocol { + fn from(d: u16) -> Self { + match d { + ETH_P_8021Q => Self::Ieee8021Q, + ETH_P_8021AD => Self::Ieee8021Ad, + _ => { + log::warn!( + "BUG: Got unknown VLAN protocol {}, treating as {}", + d, + Self::Ieee8021Q + ); + Self::Ieee8021Q + } + } + } +} + +impl From for u16 { + fn from(v: VlanProtocol) -> u16 { + match v { + VlanProtocol::Ieee8021Q => ETH_P_8021Q, + VlanProtocol::Ieee8021Ad => ETH_P_8021AD, + } + } +} + +impl std::fmt::Display for VlanProtocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + VlanProtocol::Ieee8021Q => "802.1q", + VlanProtocol::Ieee8021Ad => "802.1ad", + } + ) + } +} diff --git a/src/link/link_info/vrf.rs b/src/link/link_info/vrf.rs new file mode 100644 index 00000000..a8524e12 --- /dev/null +++ b/src/link/link_info/vrf.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::parse_u32, + traits::Parseable, + DecodeError, +}; + +const IFLA_VRF_TABLE: u16 = 1; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoVrf { + TableId(u32), + Other(DefaultNla), +} + +impl Nla for InfoVrf { + fn value_len(&self) -> usize { + use self::InfoVrf::*; + match self { + TableId(_) => 4, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoVrf::*; + match self { + TableId(value) => NativeEndian::write_u32(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoVrf::*; + match self { + TableId(_) => IFLA_VRF_TABLE, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVrf { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoVrf::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_VRF_TABLE => TableId( + parse_u32(payload).context("invalid IFLA_VRF_TABLE value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(vrf)" + ))?), + }) + } +} diff --git a/src/rtnl/link/nlas/vxlan.rs b/src/link/link_info/vxlan.rs similarity index 75% rename from src/rtnl/link/nlas/vxlan.rs rename to src/link/link_info/vxlan.rs index 40ca8643..79a6e446 100644 --- a/src/rtnl/link/nlas/vxlan.rs +++ b/src/link/link_info/vxlan.rs @@ -9,7 +9,6 @@ use netlink_packet_utils::{ DecodeError, }; -const IFLA_VXLAN_UNSPEC: u16 = 0; const IFLA_VXLAN_ID: u16 = 1; const IFLA_VXLAN_GROUP: u16 = 2; const IFLA_VXLAN_LINK: u16 = 3; @@ -39,12 +38,12 @@ const IFLA_VXLAN_LABEL: u16 = 26; const IFLA_VXLAN_GPE: u16 = 27; const IFLA_VXLAN_TTL_INHERIT: u16 = 28; const IFLA_VXLAN_DF: u16 = 29; -const __IFLA_VXLAN_MAX: u16 = 30; +const IFLA_VXLAN_VNIFILTER: u16 = 30; +const IFLA_VXLAN_LOCALBYPASS: u16 = 31; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoVxlan { - Unspec(Vec), Id(u32), Group(Vec), Group6(Vec), @@ -54,106 +53,78 @@ pub enum InfoVxlan { Tos(u8), Ttl(u8), Label(u32), - Learning(u8), + Learning(bool), Ageing(u32), Limit(u32), PortRange((u16, u16)), - Proxy(u8), - Rsc(u8), - L2Miss(u8), - L3Miss(u8), - CollectMetadata(u8), + Proxy(bool), + Rsc(bool), + L2Miss(bool), + L3Miss(bool), + CollectMetadata(bool), Port(u16), - UDPCsum(u8), - UDPZeroCsumTX(u8), - UDPZeroCsumRX(u8), - RemCsumTX(u8), - RemCsumRX(u8), + UDPCsum(bool), + UDPZeroCsumTX(bool), + UDPZeroCsumRX(bool), + RemCsumTX(bool), + RemCsumRX(bool), Gbp(u8), Gpe(u8), RemCsumNoPartial(u8), - TtlInherit(u8), + TtlInherit(bool), Df(u8), + Vnifilter(bool), + Localbypass(bool), Other(DefaultNla), } impl Nla for InfoVxlan { - #[rustfmt::skip] fn value_len(&self) -> usize { use self::InfoVxlan::*; match *self { - Tos(_) - | Ttl(_) - | Learning(_) - | Proxy(_) - | Rsc(_) - | L2Miss(_) - | L3Miss(_) - | CollectMetadata(_) - | UDPCsum(_) - | UDPZeroCsumTX(_) - | UDPZeroCsumRX(_) - | RemCsumTX(_) - | RemCsumRX(_) - | Gbp(_) - | Gpe(_) - | RemCsumNoPartial(_) - | TtlInherit(_) - | Df(_) - => 1, + Tos(_) | Ttl(_) | Learning(_) | Proxy(_) | Rsc(_) | L2Miss(_) + | L3Miss(_) | CollectMetadata(_) | UDPCsum(_) + | UDPZeroCsumTX(_) | UDPZeroCsumRX(_) | RemCsumTX(_) + | RemCsumRX(_) | Gbp(_) | Gpe(_) | RemCsumNoPartial(_) + | TtlInherit(_) | Df(_) | Vnifilter(_) | Localbypass(_) => 1, Port(_) => 2, - Id(_) - | Label(_) - | Link(_) - | Ageing(_) - | Limit(_) - | PortRange(_) - => 4, - Local(ref bytes) - | Local6(ref bytes) - | Group(ref bytes) - | Group6(ref bytes) - | Unspec(ref bytes) - => bytes.len(), + Id(_) | Label(_) | Link(_) | Ageing(_) | Limit(_) + | PortRange(_) => 4, + Local(ref bytes) | Local6(ref bytes) | Group(ref bytes) + | Group6(ref bytes) => bytes.len(), Other(ref nla) => nla.value_len(), } } - #[rustfmt::skip] fn emit_value(&self, buffer: &mut [u8]) { use self::InfoVxlan::*; match self { - Unspec(ref bytes) => buffer.copy_from_slice(bytes), - Id(ref value) - | Label(ref value) - | Link(ref value) - | Ageing(ref value) - | Limit(ref value) - => NativeEndian::write_u32(buffer, *value), + Id(ref value) | Label(ref value) | Link(ref value) + | Ageing(ref value) | Limit(ref value) => { + NativeEndian::write_u32(buffer, *value) + } Tos(ref value) - | Ttl(ref value) - | Learning (ref value) - | Proxy(ref value) - | Rsc(ref value) - | L2Miss(ref value) - | L3Miss(ref value) - | CollectMetadata(ref value) - | UDPCsum(ref value) - | UDPZeroCsumTX(ref value) - | UDPZeroCsumRX(ref value) - | RemCsumTX(ref value) - | RemCsumRX(ref value) - | Gbp(ref value) - | Gpe(ref value) - | RemCsumNoPartial(ref value) - | TtlInherit(ref value) - | Df(ref value) - => buffer[0] = *value, - Local(ref value) - | Group(ref value) - | Group6(ref value) - | Local6(ref value) - => buffer.copy_from_slice(value.as_slice()), + | Gbp(ref value) + | Gpe(ref value) + | RemCsumNoPartial(ref value) + | Ttl(ref value) + | Df(ref value) => buffer[0] = *value, + Vnifilter(ref value) + | Localbypass(ref value) + | Learning(ref value) + | Proxy(ref value) + | Rsc(ref value) + | L2Miss(ref value) + | L3Miss(ref value) + | CollectMetadata(ref value) + | UDPCsum(ref value) + | UDPZeroCsumTX(ref value) + | UDPZeroCsumRX(ref value) + | RemCsumTX(ref value) + | RemCsumRX(ref value) + | TtlInherit(ref value) => buffer[0] = *value as u8, + Local(ref value) | Group(ref value) | Group6(ref value) + | Local6(ref value) => buffer.copy_from_slice(value.as_slice()), Port(ref value) => BigEndian::write_u16(buffer, *value), PortRange(ref range) => { BigEndian::write_u16(buffer, range.0); @@ -196,7 +167,8 @@ impl Nla for InfoVxlan { RemCsumNoPartial(_) => IFLA_VXLAN_REMCSUM_NOPARTIAL, TtlInherit(_) => IFLA_VXLAN_TTL_INHERIT, Df(_) => IFLA_VXLAN_DF, - Unspec(_) => IFLA_VXLAN_UNSPEC, + Vnifilter(_) => IFLA_VXLAN_VNIFILTER, + Localbypass(_) => IFLA_VXLAN_LOCALBYPASS, Other(nla) => nla.kind(), } } @@ -207,7 +179,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { use self::InfoVxlan::*; let payload = buf.value(); Ok(match buf.kind() { - IFLA_VXLAN_UNSPEC => Unspec(payload.to_vec()), IFLA_VXLAN_ID => { Id(parse_u32(payload).context("invalid IFLA_VXLAN_ID value")?) } @@ -231,7 +202,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { ), IFLA_VXLAN_LEARNING => Learning( parse_u8(payload) - .context("invalid IFLA_VXLAN_LEARNING value")?, + .context("invalid IFLA_VXLAN_LEARNING value")? > 0, ), IFLA_VXLAN_AGEING => Ageing( parse_u32(payload) @@ -241,21 +212,21 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { parse_u32(payload).context("invalid IFLA_VXLAN_LIMIT value")?, ), IFLA_VXLAN_PROXY => Proxy( - parse_u8(payload).context("invalid IFLA_VXLAN_PROXY value")?, + parse_u8(payload).context("invalid IFLA_VXLAN_PROXY value")? > 0, ), IFLA_VXLAN_RSC => { Rsc(parse_u8(payload) - .context("invalid IFLA_VXLAN_RSC value")?) + .context("invalid IFLA_VXLAN_RSC value")?> 0) } IFLA_VXLAN_L2MISS => L2Miss( - parse_u8(payload).context("invalid IFLA_VXLAN_L2MISS value")?, + parse_u8(payload).context("invalid IFLA_VXLAN_L2MISS value")? > 0, ), IFLA_VXLAN_L3MISS => L3Miss( - parse_u8(payload).context("invalid IFLA_VXLAN_L3MISS value")?, + parse_u8(payload).context("invalid IFLA_VXLAN_L3MISS value")? > 0, ), IFLA_VXLAN_COLLECT_METADATA => CollectMetadata( parse_u8(payload) - .context("invalid IFLA_VXLAN_COLLECT_METADATA value")?, + .context("invalid IFLA_VXLAN_COLLECT_METADATA value")? >0, ), IFLA_VXLAN_PORT_RANGE => { let err = "invalid IFLA_VXLAN_PORT value"; @@ -272,23 +243,23 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { ), IFLA_VXLAN_UDP_CSUM => UDPCsum( parse_u8(payload) - .context("invalid IFLA_VXLAN_UDP_CSUM value")?, + .context("invalid IFLA_VXLAN_UDP_CSUM value")? > 0, ), IFLA_VXLAN_UDP_ZERO_CSUM6_TX => UDPZeroCsumTX( parse_u8(payload) - .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_TX value")?, + .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_TX value")? > 0, ), IFLA_VXLAN_UDP_ZERO_CSUM6_RX => UDPZeroCsumRX( parse_u8(payload) - .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_RX value")?, + .context("invalid IFLA_VXLAN_UDP_ZERO_CSUM6_RX value")? > 0, ), IFLA_VXLAN_REMCSUM_TX => RemCsumTX( parse_u8(payload) - .context("invalid IFLA_VXLAN_REMCSUM_TX value")?, + .context("invalid IFLA_VXLAN_REMCSUM_TX value")? > 0, ), IFLA_VXLAN_REMCSUM_RX => RemCsumRX( parse_u8(payload) - .context("invalid IFLA_VXLAN_REMCSUM_RX value")?, + .context("invalid IFLA_VXLAN_REMCSUM_RX value")? > 0, ), IFLA_VXLAN_DF => { Df(parse_u8(payload).context("invalid IFLA_VXLAN_DF value")?) @@ -307,9 +278,16 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVxlan { ), IFLA_VXLAN_TTL_INHERIT => TtlInherit( parse_u8(payload) - .context("invalid IFLA_VXLAN_TTL_INHERIT value")?, + .context("invalid IFLA_VXLAN_TTL_INHERIT value")? > 0, + ), + IFLA_VXLAN_VNIFILTER => Vnifilter( + parse_u8(payload) + .context("invalid IFLA_VXLAN_VNIFILTER value")? > 0, + ), + IFLA_VXLAN_LOCALBYPASS => Localbypass( + parse_u8(payload) + .context("invalid IFLA_VXLAN_LOCALBYPASS value")? > 0, ), - __IFLA_VXLAN_MAX => Unspec(payload.to_vec()), unknown_kind => Other(DefaultNla::parse(buf).context(format!( "Failed to parse IFLA_INFO_DATA(vxlan) NLA type: {unknown_kind} as DefaultNla" ))?), diff --git a/src/link/link_info/xfrm.rs b/src/link/link_info/xfrm.rs new file mode 100644 index 00000000..ba1df48c --- /dev/null +++ b/src/link/link_info/xfrm.rs @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::parse_u32, + traits::Parseable, + DecodeError, +}; + +const IFLA_XFRM_LINK: u16 = 1; +const IFLA_XFRM_IF_ID: u16 = 2; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoXfrm { + Link(u32), + IfId(u32), + Other(DefaultNla), +} + +impl Nla for InfoXfrm { + fn value_len(&self) -> usize { + use self::InfoXfrm::*; + match self { + Link(_) => 4, + IfId(_) => 4, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoXfrm::*; + match self { + Link(value) => NativeEndian::write_u32(buffer, *value), + IfId(value) => NativeEndian::write_u32(buffer, *value), + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoXfrm::*; + match self { + Link(_) => IFLA_XFRM_LINK, + IfId(_) => IFLA_XFRM_IF_ID, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoXfrm { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + use self::InfoXfrm::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_XFRM_LINK => Link( + parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, + ), + IFLA_XFRM_IF_ID => IfId( + parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, + ), + kind => Other(DefaultNla::parse(buf).context(format!( + "unknown NLA type {kind} for IFLA_INFO_DATA(xfrm)" + ))?), + }) + } +} diff --git a/src/link/link_layer_type.rs b/src/link/link_layer_type.rs new file mode 100644 index 00000000..f78c76e9 --- /dev/null +++ b/src/link/link_layer_type.rs @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: MIT + +const ARPHRD_NETROM: u16 = 0; +const ARPHRD_ETHER: u16 = 1; +const ARPHRD_EETHER: u16 = 2; +const ARPHRD_AX25: u16 = 3; +const ARPHRD_PRONET: u16 = 4; +const ARPHRD_CHAOS: u16 = 5; +const ARPHRD_IEEE802: u16 = 6; +const ARPHRD_ARCNET: u16 = 7; +const ARPHRD_APPLETLK: u16 = 8; +const ARPHRD_DLCI: u16 = 15; +const ARPHRD_ATM: u16 = 19; +const ARPHRD_METRICOM: u16 = 23; +const ARPHRD_IEEE1394: u16 = 24; +const ARPHRD_EUI64: u16 = 27; +const ARPHRD_INFINIBAND: u16 = 32; +const ARPHRD_SLIP: u16 = 256; +const ARPHRD_CSLIP: u16 = 257; +const ARPHRD_SLIP6: u16 = 258; +const ARPHRD_CSLIP6: u16 = 259; +const ARPHRD_RSRVD: u16 = 260; +const ARPHRD_ADAPT: u16 = 264; +const ARPHRD_ROSE: u16 = 270; +const ARPHRD_X25: u16 = 271; +const ARPHRD_HWX25: u16 = 272; +const ARPHRD_CAN: u16 = 280; +const ARPHRD_PPP: u16 = 512; +const ARPHRD_CISCO: u16 = 513; +const ARPHRD_HDLC: u16 = ARPHRD_CISCO; +const ARPHRD_LAPB: u16 = 516; +const ARPHRD_DDCMP: u16 = 517; +const ARPHRD_RAWHDLC: u16 = 518; +const ARPHRD_RAWIP: u16 = 519; +const ARPHRD_TUNNEL: u16 = 768; +const ARPHRD_TUNNEL6: u16 = 769; +const ARPHRD_FRAD: u16 = 770; +const ARPHRD_SKIP: u16 = 771; +const ARPHRD_LOOPBACK: u16 = 772; +const ARPHRD_LOCALTLK: u16 = 773; +const ARPHRD_FDDI: u16 = 774; +const ARPHRD_BIF: u16 = 775; +const ARPHRD_SIT: u16 = 776; +const ARPHRD_IPDDP: u16 = 777; +const ARPHRD_IPGRE: u16 = 778; +const ARPHRD_PIMREG: u16 = 779; +const ARPHRD_HIPPI: u16 = 780; +const ARPHRD_ASH: u16 = 781; +const ARPHRD_ECONET: u16 = 782; +const ARPHRD_IRDA: u16 = 783; +const ARPHRD_FCPP: u16 = 784; +const ARPHRD_FCAL: u16 = 785; +const ARPHRD_FCPL: u16 = 786; +const ARPHRD_FCFABRIC: u16 = 787; +const ARPHRD_IEEE802_TR: u16 = 800; +const ARPHRD_IEEE80211: u16 = 801; +const ARPHRD_IEEE80211_PRISM: u16 = 802; +const ARPHRD_IEEE80211_RADIOTAP: u16 = 803; +const ARPHRD_IEEE802154: u16 = 804; +const ARPHRD_IEEE802154_MONITOR: u16 = 805; +const ARPHRD_PHONET: u16 = 820; +const ARPHRD_PHONET_PIPE: u16 = 821; +const ARPHRD_CAIF: u16 = 822; +const ARPHRD_IP6GRE: u16 = 823; +const ARPHRD_NETLINK: u16 = 824; +const ARPHRD_6LOWPAN: u16 = 825; +const ARPHRD_VSOCKMON: u16 = 826; +const ARPHRD_VOID: u16 = 0xffff; +const ARPHRD_NONE: u16 = 0xfffe; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +#[repr(u16)] +// Since this list seldom changes, we do not add `Other(u16)` for unknown data. +// For unknown value, we log a warning message +pub enum LinkLayerType { + #[default] + Netrom = ARPHRD_NETROM, + Ether = ARPHRD_ETHER, + Eether = ARPHRD_EETHER, + Ax25 = ARPHRD_AX25, + Pronet = ARPHRD_PRONET, + Chaos = ARPHRD_CHAOS, + Ieee802 = ARPHRD_IEEE802, + Arcnet = ARPHRD_ARCNET, + Appletlk = ARPHRD_APPLETLK, + Dlci = ARPHRD_DLCI, + Atm = ARPHRD_ATM, + Metricom = ARPHRD_METRICOM, + Ieee1394 = ARPHRD_IEEE1394, + Eui64 = ARPHRD_EUI64, + Infiniband = ARPHRD_INFINIBAND, + Slip = ARPHRD_SLIP, + Cslip = ARPHRD_CSLIP, + Slip6 = ARPHRD_SLIP6, + Cslip6 = ARPHRD_CSLIP6, + Rsrvd = ARPHRD_RSRVD, + Adapt = ARPHRD_ADAPT, + Rose = ARPHRD_ROSE, + X25 = ARPHRD_X25, + Hwx25 = ARPHRD_HWX25, + Can = ARPHRD_CAN, + Ppp = ARPHRD_PPP, + Hdlc = ARPHRD_HDLC, + Lapb = ARPHRD_LAPB, + Ddcmp = ARPHRD_DDCMP, + Rawhdlc = ARPHRD_RAWHDLC, + Rawip = ARPHRD_RAWIP, + Tunnel = ARPHRD_TUNNEL, + Tunnel6 = ARPHRD_TUNNEL6, + Frad = ARPHRD_FRAD, + Skip = ARPHRD_SKIP, + Loopback = ARPHRD_LOOPBACK, + Localtlk = ARPHRD_LOCALTLK, + Fddi = ARPHRD_FDDI, + Bif = ARPHRD_BIF, + Sit = ARPHRD_SIT, + Ipddp = ARPHRD_IPDDP, + Ipgre = ARPHRD_IPGRE, + Pimreg = ARPHRD_PIMREG, + Hippi = ARPHRD_HIPPI, + Ash = ARPHRD_ASH, + Econet = ARPHRD_ECONET, + Irda = ARPHRD_IRDA, + Fcpp = ARPHRD_FCPP, + Fcal = ARPHRD_FCAL, + Fcpl = ARPHRD_FCPL, + Fcfabric = ARPHRD_FCFABRIC, + Ieee802Tr = ARPHRD_IEEE802_TR, + Ieee80211 = ARPHRD_IEEE80211, + Ieee80211Prism = ARPHRD_IEEE80211_PRISM, + Ieee80211Radiotap = ARPHRD_IEEE80211_RADIOTAP, + Ieee802154 = ARPHRD_IEEE802154, + Ieee802154Monitor = ARPHRD_IEEE802154_MONITOR, + Phonet = ARPHRD_PHONET, + PhonetPipe = ARPHRD_PHONET_PIPE, + Caif = ARPHRD_CAIF, + Ip6gre = ARPHRD_IP6GRE, + Netlink = ARPHRD_NETLINK, + Sixlowpan = ARPHRD_6LOWPAN, + Vsockmon = ARPHRD_VSOCKMON, + /// Void type, nothing is known + Void = ARPHRD_VOID, + /// zero header length + None = ARPHRD_NONE, +} + +impl From for LinkLayerType { + fn from(d: u16) -> Self { + match d { + d if d == ARPHRD_NETROM => Self::Netrom, + d if d == ARPHRD_ETHER => Self::Ether, + d if d == ARPHRD_EETHER => Self::Eether, + d if d == ARPHRD_AX25 => Self::Ax25, + d if d == ARPHRD_PRONET => Self::Pronet, + d if d == ARPHRD_CHAOS => Self::Chaos, + d if d == ARPHRD_IEEE802 => Self::Ieee802, + d if d == ARPHRD_ARCNET => Self::Arcnet, + d if d == ARPHRD_APPLETLK => Self::Appletlk, + d if d == ARPHRD_DLCI => Self::Dlci, + d if d == ARPHRD_ATM => Self::Atm, + d if d == ARPHRD_METRICOM => Self::Metricom, + d if d == ARPHRD_IEEE1394 => Self::Ieee1394, + d if d == ARPHRD_EUI64 => Self::Eui64, + d if d == ARPHRD_INFINIBAND => Self::Infiniband, + d if d == ARPHRD_SLIP => Self::Slip, + d if d == ARPHRD_CSLIP => Self::Cslip, + d if d == ARPHRD_SLIP6 => Self::Slip6, + d if d == ARPHRD_CSLIP6 => Self::Cslip6, + d if d == ARPHRD_RSRVD => Self::Rsrvd, + d if d == ARPHRD_ADAPT => Self::Adapt, + d if d == ARPHRD_ROSE => Self::Rose, + d if d == ARPHRD_X25 => Self::X25, + d if d == ARPHRD_HWX25 => Self::Hwx25, + d if d == ARPHRD_CAN => Self::Can, + d if d == ARPHRD_PPP => Self::Ppp, + d if d == ARPHRD_HDLC => Self::Hdlc, + d if d == ARPHRD_LAPB => Self::Lapb, + d if d == ARPHRD_DDCMP => Self::Ddcmp, + d if d == ARPHRD_RAWHDLC => Self::Rawhdlc, + d if d == ARPHRD_RAWIP => Self::Rawip, + d if d == ARPHRD_TUNNEL => Self::Tunnel, + d if d == ARPHRD_TUNNEL6 => Self::Tunnel6, + d if d == ARPHRD_FRAD => Self::Frad, + d if d == ARPHRD_SKIP => Self::Skip, + d if d == ARPHRD_LOOPBACK => Self::Loopback, + d if d == ARPHRD_LOCALTLK => Self::Localtlk, + d if d == ARPHRD_FDDI => Self::Fddi, + d if d == ARPHRD_BIF => Self::Bif, + d if d == ARPHRD_SIT => Self::Sit, + d if d == ARPHRD_IPDDP => Self::Ipddp, + d if d == ARPHRD_IPGRE => Self::Ipgre, + d if d == ARPHRD_PIMREG => Self::Pimreg, + d if d == ARPHRD_HIPPI => Self::Hippi, + d if d == ARPHRD_ASH => Self::Ash, + d if d == ARPHRD_ECONET => Self::Econet, + d if d == ARPHRD_IRDA => Self::Irda, + d if d == ARPHRD_FCPP => Self::Fcpp, + d if d == ARPHRD_FCAL => Self::Fcal, + d if d == ARPHRD_FCPL => Self::Fcpl, + d if d == ARPHRD_FCFABRIC => Self::Fcfabric, + d if d == ARPHRD_IEEE802_TR => Self::Ieee802Tr, + d if d == ARPHRD_IEEE80211 => Self::Ieee80211, + d if d == ARPHRD_IEEE80211_PRISM => Self::Ieee80211Prism, + d if d == ARPHRD_IEEE80211_RADIOTAP => Self::Ieee80211Radiotap, + d if d == ARPHRD_IEEE802154 => Self::Ieee802154, + d if d == ARPHRD_IEEE802154_MONITOR => Self::Ieee802154Monitor, + d if d == ARPHRD_PHONET => Self::Phonet, + d if d == ARPHRD_PHONET_PIPE => Self::PhonetPipe, + d if d == ARPHRD_CAIF => Self::Caif, + d if d == ARPHRD_IP6GRE => Self::Ip6gre, + d if d == ARPHRD_NETLINK => Self::Netlink, + d if d == ARPHRD_6LOWPAN => Self::Sixlowpan, + d if d == ARPHRD_VSOCKMON => Self::Vsockmon, + d if d == ARPHRD_VOID => Self::Void, + d if d == ARPHRD_NONE => Self::None, + _ => { + log::warn!( + "BUG: Got unknown ARPHRD_XXX {d} for LinkLayerType, \ + treating it as LinkLayerType::Void" + ); + Self::Void + } + } + } +} + +impl From for u16 { + fn from(v: LinkLayerType) -> u16 { + v as u16 + } +} + +impl std::fmt::Display for LinkLayerType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::Netrom => "NETROM", + Self::Ether => "ETHER", + Self::Eether => "EETHER", + Self::Ax25 => "AX25", + Self::Pronet => "PRONET", + Self::Chaos => "CHAOS", + Self::Ieee802 => "IEEE802", + Self::Arcnet => "ARCNET", + Self::Appletlk => "APPLETLK", + Self::Dlci => "DLCI", + Self::Atm => "ATM", + Self::Metricom => "METRICOM", + Self::Ieee1394 => "IEEE1394", + Self::Eui64 => "EUI64", + Self::Infiniband => "INFINIBAND", + Self::Slip => "SLIP", + Self::Cslip => "CSLIP", + Self::Slip6 => "SLIP6", + Self::Cslip6 => "CSLIP6", + Self::Rsrvd => "RSRVD", + Self::Adapt => "ADAPT", + Self::Rose => "ROSE", + Self::X25 => "X25", + Self::Hwx25 => "HWX25", + Self::Can => "CAN", + Self::Ppp => "PPP", + Self::Hdlc => "HDLC", + Self::Lapb => "LAPB", + Self::Ddcmp => "DDCMP", + Self::Rawhdlc => "RAWHDLC", + Self::Rawip => "RAWIP", + Self::Tunnel => "TUNNEL", + Self::Tunnel6 => "TUNNEL6", + Self::Frad => "FRAD", + Self::Skip => "SKIP", + Self::Loopback => "LOOPBACK", + Self::Localtlk => "LOCALTLK", + Self::Fddi => "FDDI", + Self::Bif => "BIF", + Self::Sit => "SIT", + Self::Ipddp => "IPDDP", + Self::Ipgre => "IPGRE", + Self::Pimreg => "PIMREG", + Self::Hippi => "HIPPI", + Self::Ash => "ASH", + Self::Econet => "ECONET", + Self::Irda => "IRDA", + Self::Fcpp => "FCPP", + Self::Fcal => "FCAL", + Self::Fcpl => "FCPL", + Self::Fcfabric => "FCFABRIC", + Self::Ieee802Tr => "IEEE802_TR", + Self::Ieee80211 => "IEEE80211", + Self::Ieee80211Prism => "IEEE80211_PRISM", + Self::Ieee80211Radiotap => "IEEE80211_RADIOTAP", + Self::Ieee802154 => "IEEE802154", + Self::Ieee802154Monitor => "IEEE802154_MONITOR", + Self::Phonet => "PHONET", + Self::PhonetPipe => "PHONET_PIPE", + Self::Caif => "CAIF", + Self::Ip6gre => "IP6GRE", + Self::Netlink => "NETLINK", + Self::Sixlowpan => "6LOWPAN", + Self::Vsockmon => "VSOCKMON", + Self::Void => "VOID", + Self::None => "NONE", + } + ) + } +} + +impl LinkLayerType { + #[allow(non_upper_case_globals)] + pub const Cisco: LinkLayerType = LinkLayerType::Hdlc; +} diff --git a/src/rtnl/link/nlas/link_state.rs b/src/link/link_state.rs similarity index 80% rename from src/rtnl/link/nlas/link_state.rs rename to src/link/link_state.rs index 55d7dca6..1183cd12 100644 --- a/src/rtnl/link/nlas/link_state.rs +++ b/src/link/link_state.rs @@ -1,6 +1,12 @@ // SPDX-License-Identifier: MIT -use crate::constants::*; +const IF_OPER_UNKNOWN: u8 = 0; +const IF_OPER_NOTPRESENT: u8 = 1; +const IF_OPER_DOWN: u8 = 2; +const IF_OPER_LOWERLAYERDOWN: u8 = 3; +const IF_OPER_TESTING: u8 = 4; +const IF_OPER_DORMANT: u8 = 5; +const IF_OPER_UP: u8 = 6; #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] @@ -19,10 +25,8 @@ pub enum State { Dormant, /// Up, ready to send packets Up, - /// Unrecognized value. This should go away when `TryFrom` is stable in - /// Rust - // FIXME: there's not point in having this. When TryFrom is stable we'll - // remove it + /// Place holder for new state introduced by kernel when current crate does + /// not support so. Other(u8), } diff --git a/src/rtnl/link/nlas/map.rs b/src/link/map.rs similarity index 97% rename from src/rtnl/link/nlas/map.rs rename to src/link/map.rs index 0bfd1666..425243ae 100644 --- a/src/rtnl/link/nlas/map.rs +++ b/src/link/map.rs @@ -5,7 +5,8 @@ use netlink_packet_utils::{ DecodeError, }; -pub const LINK_MAP_LEN: usize = 28; +const LINK_MAP_LEN: usize = 32; + buffer!(MapBuffer(LINK_MAP_LEN) { memory_start: (u64, 0..8), memory_end: (u64, 8..16), diff --git a/src/link/message.rs b/src/link/message.rs new file mode 100644 index 00000000..b6922a01 --- /dev/null +++ b/src/link/message.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +use anyhow::Context; +use netlink_packet_utils::{ + traits::{Emitable, Parseable, ParseableParametrized}, + DecodeError, +}; + +use crate::link::{LinkAttribute, LinkHeader, LinkMessageBuffer}; +use crate::AddressFamily; + +#[derive(Debug, PartialEq, Eq, Clone, Default)] +#[non_exhaustive] +pub struct LinkMessage { + pub header: LinkHeader, + pub attributes: Vec, +} + +impl Emitable for LinkMessage { + fn buffer_len(&self) -> usize { + self.header.buffer_len() + self.attributes.as_slice().buffer_len() + } + + fn emit(&self, buffer: &mut [u8]) { + self.header.emit(buffer); + self.attributes + .as_slice() + .emit(&mut buffer[self.header.buffer_len()..]); + } +} + +impl<'a, T: AsRef<[u8]> + 'a> Parseable> + for LinkMessage +{ + fn parse(buf: &LinkMessageBuffer<&'a T>) -> Result { + let header = LinkHeader::parse(buf) + .context("failed to parse link message header")?; + let interface_family = header.interface_family; + let attributes = + Vec::::parse_with_param(buf, interface_family) + .context("failed to parse link message NLAs")?; + Ok(LinkMessage { header, attributes }) + } +} + +impl<'a, T: AsRef<[u8]> + 'a> + ParseableParametrized, AddressFamily> + for Vec +{ + fn parse_with_param( + buf: &LinkMessageBuffer<&'a T>, + family: AddressFamily, + ) -> Result { + let mut attributes = vec![]; + for nla_buf in buf.attributes() { + attributes + .push(LinkAttribute::parse_with_param(&nla_buf?, family)?); + } + Ok(attributes) + } +} diff --git a/src/link/mod.rs b/src/link/mod.rs new file mode 100644 index 00000000..93086c60 --- /dev/null +++ b/src/link/mod.rs @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +mod af_spec; +mod attribute; +mod buffer_tool; +mod header; +mod link_flag; +mod link_info; +mod link_layer_type; +mod link_state; +mod map; +mod message; +mod prop_list; +mod stats; +mod stats64; +mod xdp; + +mod tests; + +pub use self::af_spec::{ + AfSpecBridge, AfSpecInet, AfSpecInet6, AfSpecUnspec, BridgeVlanInfo, + Icmp6Stats, Icmp6StatsBuffer, Inet6CacheInfo, Inet6CacheInfoBuffer, + Inet6DevConf, Inet6DevConfBuffer, Inet6IfaceFlag, Inet6IfaceFlags, + Inet6Stats, Inet6StatsBuffer, InetDevConf, +}; +pub use self::attribute::LinkAttribute; +pub use self::header::{LinkHeader, LinkMessageBuffer}; +pub use self::link_flag::{LinkFlag, LinkFlags}; +pub use self::link_info::{ + BondAdInfo, BondPortState, BridgeQuerierState, HsrProtocol, InfoBond, + InfoBondPort, InfoBridge, InfoData, InfoHsr, InfoIpVlan, InfoIpoib, + InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, + InfoVeth, InfoVlan, InfoVrf, InfoVxlan, InfoXfrm, LinkInfo, MacSecCipherId, + MacSecOffload, MacSecValidation, MiiStatus, VlanProtocol, VlanQosMapping, +}; +pub use self::link_layer_type::LinkLayerType; +pub use self::link_state::State; +pub use self::map::{Map, MapBuffer}; +pub use self::message::LinkMessage; +pub use self::prop_list::Prop; +pub use self::stats::{Stats, StatsBuffer}; +pub use self::stats64::{Stats64, Stats64Buffer}; +pub use self::xdp::{Xdp, XdpAttached}; diff --git a/src/rtnl/link/nlas/prop_list.rs b/src/link/prop_list.rs similarity index 98% rename from src/rtnl/link/nlas/prop_list.rs rename to src/link/prop_list.rs index c0ec48ab..7ba4061b 100644 --- a/src/rtnl/link/nlas/prop_list.rs +++ b/src/link/prop_list.rs @@ -8,7 +8,7 @@ use netlink_packet_utils::{ DecodeError, }; -use crate::constants::*; +const IFLA_ALT_IFNAME: u16 = 53; #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] diff --git a/src/rtnl/link/nlas/stats.rs b/src/link/stats.rs similarity index 99% rename from src/rtnl/link/nlas/stats.rs rename to src/link/stats.rs index fda03422..7ab476d2 100644 --- a/src/rtnl/link/nlas/stats.rs +++ b/src/link/stats.rs @@ -5,6 +5,8 @@ use netlink_packet_utils::{ DecodeError, }; +pub(crate) const LINK_STATS_LEN: usize = 96; + #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[non_exhaustive] pub struct Stats { @@ -56,8 +58,6 @@ pub struct Stats { pub rx_nohandler: u32, } -pub const LINK_STATS_LEN: usize = 96; - buffer!(StatsBuffer(LINK_STATS_LEN) { rx_packets: (u32, 0..4), tx_packets: (u32, 4..8), diff --git a/src/rtnl/link/nlas/stats64.rs b/src/link/stats64.rs similarity index 95% rename from src/rtnl/link/nlas/stats64.rs rename to src/link/stats64.rs index 9ebcb692..5bece142 100644 --- a/src/rtnl/link/nlas/stats64.rs +++ b/src/link/stats64.rs @@ -5,7 +5,8 @@ use netlink_packet_utils::{ DecodeError, }; -pub const LINK_STATS64_LEN: usize = 192; +pub(crate) const LINK_STATS64_LEN: usize = 200; + buffer!(Stats64Buffer(LINK_STATS64_LEN) { rx_packets: (u64, 0..8), tx_packets: (u64, 8..16), @@ -31,6 +32,7 @@ buffer!(Stats64Buffer(LINK_STATS64_LEN) { rx_compressed: (u64, 168..176), tx_compressed: (u64, 176..184), rx_nohandler: (u64, 184..192), + rx_otherhost_dropped: (u64, 192..200), }); #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -82,6 +84,8 @@ pub struct Stats64 { /// dropped, no handler found pub rx_nohandler: u64, + + pub rx_otherhost_dropped: u64, } impl> Parseable> for Stats64 { @@ -111,6 +115,7 @@ impl> Parseable> for Stats64 { rx_compressed: buf.rx_compressed(), tx_compressed: buf.tx_compressed(), rx_nohandler: buf.rx_nohandler(), + rx_otherhost_dropped: buf.rx_otherhost_dropped(), }) } } @@ -146,5 +151,6 @@ impl Emitable for Stats64 { buffer.set_rx_compressed(self.rx_compressed); buffer.set_tx_compressed(self.tx_compressed); buffer.set_rx_nohandler(self.rx_nohandler); + buffer.set_rx_otherhost_dropped(self.rx_otherhost_dropped); } } diff --git a/src/link/tests/bond.rs b/src/link/tests/bond.rs new file mode 100644 index 00000000..5c4a5250 --- /dev/null +++ b/src/link/tests/bond.rs @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + BondPortState, InfoBond, InfoBondPort, InfoData, InfoKind, InfoPortData, + InfoPortKind, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, + LinkMessage, LinkMessageBuffer, MiiStatus, +}; +use crate::AddressFamily; + +#[test] +fn test_bond_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x43, 0x14, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, // length 204 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x09, 0x00, // length 9 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x62, 0x6f, 0x6e, 0x64, 0x00, // 'bond\0' + 0x00, 0x00, 0x00, // padding + 0xbc, 0x00, // length 188 + 0x02, 0x00, // IFLA_INFO_DATA + 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x0f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x13, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1e, 0x00, + 0x02, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 24, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::Controller, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Bond), + LinkInfo::Data(InfoData::Bond(vec![ + InfoBond::Mode(0), + InfoBond::MiiMon(0), + InfoBond::UpDelay(0), + InfoBond::DownDelay(0), + InfoBond::PeerNotifDelay(0), + InfoBond::UseCarrier(1), + InfoBond::ArpInterval(0), + InfoBond::ArpValidate(0), + InfoBond::ArpAllTargets(0), + InfoBond::PrimaryReselect(0), + InfoBond::FailOverMac(0), + InfoBond::XmitHashPolicy(0), + InfoBond::ResendIgmp(1), + InfoBond::NumPeerNotif(1), + InfoBond::AllPortsActive(0), + InfoBond::MinLinks(0), + InfoBond::LpInterval(1), + InfoBond::PacketsPerPort(1), + InfoBond::AdLacpActive(1), + InfoBond::AdLacpRate(0), + InfoBond::AdSelect(0), + InfoBond::TlbDynamicLb(1), + InfoBond::MissedMax(2), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} + +#[test] +fn test_bond_port_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x43, 0x18, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, + 0x76, 0x65, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x04, 0x00, + 0x62, 0x6f, 0x6e, 0x64, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x05, 0x00, + 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1a, 0x00, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 21, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Port, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Veth), + LinkInfo::PortKind(InfoPortKind::Bond), + LinkInfo::PortData(InfoPortData::BondPort(vec![ + InfoBondPort::BondPortState(BondPortState::Active), + InfoBondPort::MiiStatus(MiiStatus::Up), + InfoBondPort::LinkFailureCount(0), + InfoBondPort::PermHwaddr(vec![ + 0x00, 0x23, 0x45, 0x67, 0x89, 0x1a, + ]), + InfoBondPort::QueueId(0), + InfoBondPort::Prio(0), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/bridge.rs b/src/link/tests/bridge.rs new file mode 100644 index 00000000..368c9388 --- /dev/null +++ b/src/link/tests/bridge.rs @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{ + nla::{DefaultNla, NlaBuffer}, + Emitable, Parseable, +}; + +use crate::link::{ + af_spec::VecAfSpecBridge, AfSpecBridge, AfSpecInet, AfSpecInet6, + AfSpecUnspec, BridgeVlanInfo, Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlag, + Inet6IfaceFlags, InetDevConf, InfoBridge, InfoData, InfoKind, + LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, LinkMessage, + LinkMessageBuffer, Map, State, Stats, Stats64, Xdp, XdpAttached, +}; +use crate::AddressFamily; + +#[test] +fn test_parse_link_bridge_no_extention_mask() { + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x35, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, 0x62, 0x72, 0x30, 0x00, + 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, + 0x06, 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, 0x3d, 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, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, 0x00, 0x08, 0x00, 0x3c, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, + 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, 0x01, 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, 0x00, 0x23, 0x45, 0x67, + 0x89, 0x1c, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xcc, 0x00, 0x17, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 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, 0x36, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x88, 0x14, 0x00, 0x00, 0x42, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0xa6, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0xdb, 0x05, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, + 0xcf, 0x07, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x2f, 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, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x0c, 0x00, 0x0a, 0x00, + 0x80, 0x00, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 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, + 0x07, 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, 0x63, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x8f, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x20, 0x00, 0x9b, 0x63, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x21, 0x00, 0xd3, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x22, 0x00, 0xe7, 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, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, + 0x88, 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, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xe7, 0xc4, 0x92, 0x01, 0x18, 0x92, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xdc, 0x05, 0x00, 0x00, 0x01, 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, 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, + 0x04, 0x00, 0x3e, 0x80, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 53, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![ + LinkAttribute::IfName("br0".into()), + LinkAttribute::TxQueueLen(1000), + LinkAttribute::OperState(State::Up), + 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(1), + LinkAttribute::GsoMaxSegs(65535), + LinkAttribute::GsoMaxSize(65536), + LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), + LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), + LinkAttribute::Other(DefaultNla::new(64, 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(1), + LinkAttribute::Carrier(1), + LinkAttribute::Qdisc("noqueue".to_string()), + LinkAttribute::CarrierChanges(2), + LinkAttribute::CarrierUpCount(vec![1, 0, 0, 0]), + LinkAttribute::CarrierDownCount(vec![1, 0, 0, 0]), + LinkAttribute::ProtoDown(0), + LinkAttribute::Map(Map { + memory_start: 0, + memory_end: 0, + base_address: 0, + irq: 0, + dma: 0, + port: 0, + }), + LinkAttribute::Address(vec![0x00, 0x23, 0x45, 0x67, 0x89, 0x1c]), + LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + LinkAttribute::Stats64(Stats64 { + rx_packets: 54, + tx_packets: 31, + rx_bytes: 5256, + tx_bytes: 3394, + rx_errors: 0, + tx_errors: 0, + rx_dropped: 0, + tx_dropped: 0, + multicast: 54, + collisions: 0, + rx_length_errors: 0, + rx_over_errors: 0, + rx_crc_errors: 0, + rx_frame_errors: 0, + rx_fifo_errors: 0, + rx_missed_errors: 0, + tx_aborted_errors: 0, + tx_carrier_errors: 0, + tx_fifo_errors: 0, + tx_heartbeat_errors: 0, + tx_window_errors: 0, + rx_compressed: 0, + tx_compressed: 0, + rx_nohandler: 0, + rx_otherhost_dropped: 0, + }), + LinkAttribute::Stats(Stats { + rx_packets: 54, + tx_packets: 31, + rx_bytes: 5256, + tx_bytes: 3394, + rx_errors: 0, + tx_errors: 0, + rx_dropped: 0, + tx_dropped: 0, + multicast: 54, + collisions: 0, + rx_length_errors: 0, + rx_over_errors: 0, + rx_crc_errors: 0, + rx_frame_errors: 0, + rx_fifo_errors: 0, + rx_missed_errors: 0, + tx_aborted_errors: 0, + tx_carrier_errors: 0, + tx_fifo_errors: 0, + tx_heartbeat_errors: 0, + tx_window_errors: 0, + rx_compressed: 0, + tx_compressed: 0, + rx_nohandler: 0, + }), + LinkAttribute::Xdp(vec![Xdp::Attached(XdpAttached::None)]), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Bridge), + LinkInfo::Data(InfoData::Bridge(vec![ + InfoBridge::HelloTimer(0), + InfoBridge::TcnTimer(0), + InfoBridge::TopologyChangeTimer(0), + InfoBridge::GcTimer(14246), + InfoBridge::ForwardDelay(1499), + InfoBridge::HelloTime(199), + InfoBridge::MaxAge(1999), + InfoBridge::AgeingTime(29999), + InfoBridge::StpState(0), + InfoBridge::Priority(32768), + InfoBridge::VlanFiltering(0), + InfoBridge::GroupFwdMask(0), + InfoBridge::BridgeId(( + 0x0080, // iproute is showing it as 0x8000 + [0x00, 0x23, 0x45, 0x67, 0x89, 0x1c], + )), + InfoBridge::RootId(( + 0x0080, // iproute is showing it as 0x8000 + [0x00, 0x23, 0x45, 0x67, 0x89, 0x1c], + )), + InfoBridge::RootPort(0), + InfoBridge::RootPathCost(0), + InfoBridge::TopologyChange(0), + InfoBridge::TopologyChangeDetected(0), + InfoBridge::GroupAddr([0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]), + InfoBridge::MultiBoolOpt(30064771072), + InfoBridge::VlanProtocol(33024), + InfoBridge::VlanDefaultPvid(1), + InfoBridge::VlanStatsEnabled(0), + InfoBridge::VlanStatsPerHost(0), + InfoBridge::MulticastRouter(1), + InfoBridge::MulticastSnooping(1), + InfoBridge::MulticastQueryUseIfaddr(0), + InfoBridge::MulticastQuerier(0), + InfoBridge::MulticastStatsEnabled(0), + InfoBridge::MulticastHashElasticity(16), + InfoBridge::MulticastHashMax(4096), + InfoBridge::MulticastLastMemberCount(2), + InfoBridge::MulticastStartupQueryCount(2), + InfoBridge::MulticastIgmpVersion(2), + InfoBridge::MulticastMldVersion(1), + InfoBridge::MulticastLastMemberInterval(99), + InfoBridge::MulticastMembershipInterval(25999), + InfoBridge::MulticastQuerierInterval(25499), + InfoBridge::MulticastQueryInterval(12499), + InfoBridge::MulticastQueryResponseInterval(999), + InfoBridge::MulticastStartupQueryInterval(3124), + InfoBridge::NfCallIpTables(0), + InfoBridge::NfCallIp6Tables(0), + InfoBridge::NfCallArpTables(0), + ])), + ]), + LinkAttribute::AfSpecUnspec(vec![ + AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { + forwarding: 1, + mc_forwarding: 0, + proxy_arp: 0, + accept_redirects: 1, + secure_redirects: 1, + send_redirects: 1, + shared_media: 1, + rp_filter: 2, + accept_source_route: 0, + bootp_relay: 0, + log_martians: 0, + tag: 0, + arpfilter: 0, + medium_id: 0, + noxfrm: 0, + nopolicy: 0, + force_igmp_version: 0, + arp_announce: 0, + arp_ignore: 0, + promote_secondaries: 1, + arp_accept: 0, + arp_notify: 0, + accept_local: 0, + src_vmark: 0, + proxy_arp_pvlan: 0, + route_localnet: 0, + igmpv2_unsolicited_report_interval: 10000, + igmpv3_unsolicited_report_interval: 1000, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_gratuitous_arp: 0, + bc_forwarding: 0, + arp_evict_nocarrier: 1, + })]), + AfSpecUnspec::Inet6(vec![ + AfSpecInet6::Flags(Inet6IfaceFlags(vec![ + Inet6IfaceFlag::RsSent, + Inet6IfaceFlag::Ready, + ])), + AfSpecInet6::CacheInfo(Inet6CacheInfo { + max_reasm_len: 65535, + tstamp: 26395879, + reachable_time: 37400, + retrans_time: 1000, + }), + AfSpecInet6::DevConf(Inet6DevConf { + forwarding: 0, + hoplimit: 64, + mtu6: 1500, + accept_ra: 1, + accept_redirects: 1, + autoconf: 1, + dad_transmits: 1, + rtr_solicits: -1, + rtr_solicit_interval: 4000, + rtr_solicit_delay: 1000, + use_tempaddr: 0, + temp_valid_lft: 604800, + temp_prefered_lft: 86400, + regen_max_retry: 3, + max_desync_factor: 600, + max_addresses: 16, + force_mld_version: 0, + accept_ra_defrtr: 1, + accept_ra_pinfo: 1, + accept_ra_rtr_pref: 1, + rtr_probe_interval: 60000, + accept_ra_rt_info_max_plen: 0, + proxy_ndp: 0, + optimistic_dad: 0, + accept_source_route: 0, + mc_forwarding: 0, + disable_ipv6: 0, + accept_dad: 1, + force_tllao: 0, + ndisc_notify: 0, + mldv1_unsolicited_report_interval: 10000, + mldv2_unsolicited_report_interval: 1000, + suppress_frag_ndisc: 1, + accept_ra_from_local: 0, + use_optimistic: 0, + accept_ra_mtu: 1, + stable_secret: 0, + use_oif_addrs_only: 0, + accept_ra_min_hop_limit: 1, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_unsolicited_na: 0, + keep_addr_on_down: 0, + rtr_solicit_max_interval: 3600000, + seg6_enabled: 0, + seg6_require_hmac: 0, + enhanced_dad: 1, + addr_gen_mode: 0, + disable_policy: 0, + accept_ra_rt_info_min_plen: 0, + ndisc_tclass: 0, + rpl_seg_enabled: 0, + ra_defrtr_metric: 1024, + ioam6_enabled: 0, + ioam6_id: 65535, + ioam6_id_wide: -1, + ndisc_evict_nocarrier: 1, + accept_untracked_na: 0, + accept_ra_min_lft: 0, + }), + ]), + ]), + LinkAttribute::Other(DefaultNla::new(32830, vec![])), + ], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} + +#[test] +fn test_af_spec_bridge() { + // The nlmon cannot capture AF_BRIDGE data, this is debug print of + // example `dump_packet_link_bridge_vlan` after command: + // `bridge vlan add vid 2-4094 dev eth2` + let raw: Vec = vec![ + 0x08, 0x00, 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0xfe, 0x0f, + ]; + + let expected = vec![ + AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: 6, vid: 1 }), + AfSpecBridge::VlanInfo(BridgeVlanInfo { flags: 8, vid: 2 }), + AfSpecBridge::VlanInfo(BridgeVlanInfo { + flags: 16, + vid: 4094, + }), + ]; + + assert_eq!( + VecAfSpecBridge::parse(&NlaBuffer::new(&raw)).unwrap().0, + expected + ); +} diff --git a/src/link/tests/hsr.rs b/src/link/tests/hsr.rs new file mode 100644 index 00000000..ac838ec3 --- /dev/null +++ b/src/link/tests/hsr.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + HsrProtocol, InfoData, InfoHsr, InfoKind, LinkAttribute, LinkFlag, + LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_parsing_link_hsr() { + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x68, 0x73, 0x72, 0x00, 0x30, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x04, 0x00, 0x01, 0x15, 0x4e, 0x00, 0x01, 0x90, 0x00, 0x00, + 0x06, 0x00, 0x05, 0x00, 0x09, 0xfc, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 45, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Hsr), + LinkInfo::Data(InfoData::Hsr(vec![ + InfoHsr::Port1(44), + InfoHsr::Port2(42), + InfoHsr::SupervisionAddr([0x01, 0x15, 0x4e, 0x00, 0x01, 0x90]), + InfoHsr::SeqNr(64521), + InfoHsr::Protocol(HsrProtocol::Hsr), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/ipvlan.rs b/src/link/tests/ipvlan.rs new file mode 100644 index 00000000..96055be4 --- /dev/null +++ b/src/link/tests/ipvlan.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoIpVlan, InfoKind, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_ipvlan_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, + 0x69, 0x70, 0x76, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 18, + link_layer_type: LinkLayerType::Ether, + flags: vec![LinkFlag::Broadcast, LinkFlag::Multicast], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::IpVlan), + LinkInfo::Data(InfoData::IpVlan(vec![ + InfoIpVlan::Mode(0), + InfoIpVlan::Flags(2), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/loopback.rs b/src/link/tests/loopback.rs new file mode 100644 index 00000000..53703ff9 --- /dev/null +++ b/src/link/tests/loopback.rs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{ + nla::{DefaultNla, NlaBuffer}, + Emitable, Parseable, +}; + +use crate::link::{ + af_spec::VecAfSpecUnspec, AfSpecInet, AfSpecInet6, AfSpecUnspec, + Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlag, Inet6IfaceFlags, InetDevConf, +}; + +#[test] +fn test_link_loopback() { + // This is nlmon capture for `ip link show lo` on Linux kernel 6.5.8 + let raw: Vec = vec![ + 0x0c, 0x00, // length 12 + 0x2d, 0x00, // AF_MCTP 45 + 0x08, 0x00, // length 8 + 0x01, 0x00, // IFLA_MCTP_NET 1 + 0x01, 0x00, 0x00, 0x00, // u32 value 1 + 0x8c, 0x00, // length 140 + 0x02, 0x00, // AF_INET 2 + 0x88, 0x00, // length 136 + 0x01, 0x00, // IFLA_INET_CONF + 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, 0x01, 0x00, 0x00, 0x00, + 0x01, 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, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x01, // length 272 + 0x0a, 0x00, // AF_INET 6 + 0x08, 0x00, // length 8 + 0x01, 0x00, // IFLA_INET6_FLAGS 1 + 0x00, 0x00, 0x00, 0x80, // IF_READY: 0x80000000 + 0x14, 0x00, // length 20 + 0x05, 0x00, // IFLA_INET6_CACHEINFO 5 + 0xff, 0xff, 0x00, 0x00, // max_reasm_len: 65535 + 0xb2, 0x00, 0x00, 0x00, // tstamp: 178 + 0x05, 0x75, 0x00, 0x00, // reachable_time: 29957 + 0xe8, 0x03, 0x00, 0x00, // retrans_time: 1000 + 0xf0, 0x00, // length 240 + 0x02, 0x00, // IFLA_INET6_CONF 2 + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 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, 0xff, 0xff, 0xff, 0xff, 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, + 0xff, 0xff, 0xff, 0xff, 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, // End of IFLA_INET6_CONF + ]; + + let expected = vec![ + AfSpecUnspec::Other(DefaultNla::new( + 45, + vec![8u8, 0, 1, 0, 1, 0, 0, 0], + )), + AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { + forwarding: 1, + mc_forwarding: 0, + proxy_arp: 0, + accept_redirects: 1, + secure_redirects: 1, + send_redirects: 1, + shared_media: 1, + rp_filter: 2, + accept_source_route: 0, + bootp_relay: 0, + log_martians: 0, + tag: 0, + arpfilter: 0, + medium_id: 0, + noxfrm: 1, + nopolicy: 1, + force_igmp_version: 0, + arp_announce: 0, + arp_ignore: 0, + promote_secondaries: 1, + arp_accept: 0, + arp_notify: 0, + accept_local: 0, + src_vmark: 0, + proxy_arp_pvlan: 0, + route_localnet: 0, + igmpv2_unsolicited_report_interval: 10000, + igmpv3_unsolicited_report_interval: 1000, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_gratuitous_arp: 0, + bc_forwarding: 0, + arp_evict_nocarrier: 1, + })]), + AfSpecUnspec::Inet6(vec![ + AfSpecInet6::Flags(Inet6IfaceFlags(vec![Inet6IfaceFlag::Ready])), + AfSpecInet6::CacheInfo(Inet6CacheInfo { + max_reasm_len: 65535, + tstamp: 178, + reachable_time: 29957, + retrans_time: 1000, + }), + AfSpecInet6::DevConf(Inet6DevConf { + forwarding: 0, + hoplimit: 64, + mtu6: 65536, + accept_ra: 1, + accept_redirects: 1, + autoconf: 1, + dad_transmits: 1, + rtr_solicits: -1, + rtr_solicit_interval: 4000, + rtr_solicit_delay: 1000, + use_tempaddr: -1, + temp_valid_lft: 604800, + temp_prefered_lft: 86400, + regen_max_retry: 3, + max_desync_factor: 600, + max_addresses: 16, + force_mld_version: 0, + accept_ra_defrtr: 1, + accept_ra_pinfo: 1, + accept_ra_rtr_pref: 1, + rtr_probe_interval: 60000, + accept_ra_rt_info_max_plen: 0, + proxy_ndp: 0, + optimistic_dad: 0, + accept_source_route: 0, + mc_forwarding: 0, + disable_ipv6: 0, + accept_dad: -1, + force_tllao: 0, + ndisc_notify: 0, + mldv1_unsolicited_report_interval: 10000, + mldv2_unsolicited_report_interval: 1000, + suppress_frag_ndisc: 1, + accept_ra_from_local: 0, + use_optimistic: 0, + accept_ra_mtu: 1, + stable_secret: 0, + use_oif_addrs_only: 0, + accept_ra_min_hop_limit: 1, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_unsolicited_na: 0, + keep_addr_on_down: 0, + rtr_solicit_max_interval: 3600000, + seg6_enabled: 0, + seg6_require_hmac: 0, + enhanced_dad: 1, + addr_gen_mode: 0, + disable_policy: 0, + accept_ra_rt_info_min_plen: 0, + ndisc_tclass: 0, + rpl_seg_enabled: 0, + ra_defrtr_metric: 1024, + ioam6_enabled: 0, + ioam6_id: 65535, + ioam6_id_wide: -1, + ndisc_evict_nocarrier: 1, + accept_untracked_na: 0, + accept_ra_min_lft: 0, + }), + ]), + ]; + + assert_eq!( + VecAfSpecUnspec::parse(&NlaBuffer::new(&raw)).unwrap().0, + expected + ); + let mut buffer = vec![0; expected.as_slice().buffer_len()]; + expected.as_slice().emit(&mut buffer); + assert_eq!(buffer.as_slice(), raw); +} diff --git a/src/link/tests/macsec.rs b/src/link/tests/macsec.rs new file mode 100644 index 00000000..a59c22c7 --- /dev/null +++ b/src/link/tests/macsec.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoMacSec, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, MacSecCipherId, + MacSecOffload, MacSecValidation, +}; +use crate::AddressFamily; + +#[test] +fn test_macsec_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x12, 0x00, 0x0b, 0x00, 0x01, 0x00, + 0x6d, 0x61, 0x63, 0x73, 0x65, 0x63, 0x00, 0x00, 0x6c, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x01, 0x00, 0xf2, 0xca, 0xda, 0x49, 0x34, 0x51, 0x00, 0x01, + 0x05, 0x00, 0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x80, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 9, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::MacSec), + LinkInfo::Data(InfoData::MacSec(vec![ + InfoMacSec::Sci(72146879057152754), + InfoMacSec::IcvLen(16), + #[allow(deprecated)] + InfoMacSec::CipherSuite(MacSecCipherId::DefaultGcmAes128), + InfoMacSec::EncodingSa(0), + InfoMacSec::Encrypt(1), + InfoMacSec::Protect(1), + InfoMacSec::IncSci(1), + InfoMacSec::Es(0), + InfoMacSec::Scb(0), + InfoMacSec::ReplayProtect(0), + InfoMacSec::Validation(MacSecValidation::Strict), + InfoMacSec::Offload(MacSecOffload::Off), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/macvlan.rs b/src/link/tests/macvlan.rs new file mode 100644 index 00000000..755cec35 --- /dev/null +++ b/src/link/tests/macvlan.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoMacVlan, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_macvlan_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x01, 0x00, + 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, 0x48, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, + 0x89, 0x1d, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x23, 0x45, 0x67, + 0x89, 0x1c, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0x08, 0x00, 0x08, 0x00, 0xe8, 0x03, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 23, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::MacVlan), + LinkInfo::Data(InfoData::MacVlan(vec![ + InfoMacVlan::Mode(16), + InfoMacVlan::Flags(0), + InfoMacVlan::MacAddrCount(2), + InfoMacVlan::MacAddrData(vec![ + InfoMacVlan::MacAddr([0, 35, 69, 103, 137, 29]), + InfoMacVlan::MacAddr([0, 35, 69, 103, 137, 28]), + ]), + InfoMacVlan::BcQueueLen(1000), + InfoMacVlan::BcQueueLenUsed(1000), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/message.rs b/src/link/tests/message.rs new file mode 100644 index 00000000..9bb561bb --- /dev/null +++ b/src/link/tests/message.rs @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::traits::{Emitable, ParseableParametrized}; + +use crate::link::{ + LinkAttribute, LinkFlag, LinkFlags, LinkHeader, LinkLayerType, LinkMessage, + LinkMessageBuffer, State, +}; +use crate::AddressFamily; + +static LINK_MSG: [u8; 96] = [ + 0x00, // interface family AF_UNSPEC + 0x00, // reserved + 0x04, 0x03, // link layer type 772 = loopback + 0x01, 0x00, 0x00, 0x00, // interface index = 1 + 0x49, 0x00, 0x01, 0x00, // flags: UP|LOOPBACK|RUNNING|LOWERUP + 0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag) + // attributes + 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, // device name L=7,T=3,V=lo + 0x00, // padding + 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, + 0x00, // TxQueue length L=8,T=13,V=1000 + 0x05, 0x00, 0x10, 0x00, 0x00, // OperState L=5,T=16,V=0 (unknown) + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, 0x11, 0x00, 0x00, // Link mode L=5,T=17,V=0 + 0x00, 0x00, 0x00, // padding + 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, // MTU L=8,T=4,V=65536 + 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 + 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, // Promiscuity L=8,T=30,V=0 + 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, + 0x00, // Number of Tx Queues L=8,T=31,V=1 + 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, + 0x00, // Maximum GSO segment count L=8,T=40,V=65536 + 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, + 0x00, // Maximum GSO size L=8,T=41,V=65536 +]; + +#[test] +fn link_message_packet_header_read() { + let packet = LinkMessageBuffer::new(&LINK_MSG[0..16]); + assert_eq!(packet.interface_family(), AddressFamily::Unspec.into()); + assert_eq!(packet.reserved_1(), 0); + assert_eq!(packet.link_layer_type(), LinkLayerType::Loopback.into()); + assert_eq!(packet.link_index(), 1); + assert_eq!( + LinkFlags::from(packet.flags()).0, + vec![ + LinkFlag::Loopback, + LinkFlag::LowerUp, + LinkFlag::Running, + LinkFlag::Up, + ] + ); + assert_eq!(packet.change_mask(), 0); +} + +#[test] +fn link_message_packet_header_build() { + let mut buf = vec![0xff; 16]; + { + let mut packet = LinkMessageBuffer::new(&mut buf); + packet.set_interface_family(AddressFamily::Unspec.into()); + packet.set_reserved_1(0); + packet.set_link_layer_type(LinkLayerType::Loopback.into()); + packet.set_link_index(1); + packet.set_flags(u32::from(&LinkFlags(vec![ + LinkFlag::Loopback, + LinkFlag::LowerUp, + LinkFlag::Running, + LinkFlag::Up, + ]))); + packet.set_change_mask(0); + } + assert_eq!(&buf[..], &LINK_MSG[0..16]); +} + +#[test] +fn link_mssage_packet_attributes_read() { + let packet = LinkMessageBuffer::new(&LINK_MSG[..]); + assert_eq!(packet.attributes().count(), 10); + let mut attributes = packet.attributes(); + + // device name L=7,T=3,V=lo + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 7); + assert_eq!(nla.kind(), 3); + assert_eq!(nla.value(), &[0x6c, 0x6f, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::IfName(String::from("lo"))); + + // TxQueue length L=8,T=13,V=1000 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 8); + assert_eq!(nla.kind(), 13); + assert_eq!(nla.value(), &[0xe8, 0x03, 0x00, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::TxQueueLen(1000)); + + // OperState L=5,T=16,V=0 (unknown) + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 5); + assert_eq!(nla.kind(), 16); + assert_eq!(nla.value(), &[0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::OperState(State::Unknown)); + + // Link mode L=5,T=17,V=0 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 5); + assert_eq!(nla.kind(), 17); + assert_eq!(nla.value(), &[0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::Mode(0)); + + // MTU L=8,T=4,V=65536 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 8); + assert_eq!(nla.kind(), 4); + assert_eq!(nla.value(), &[0x00, 0x00, 0x01, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::Mtu(65_536)); + + // 0x00, 0x00, 0x00, 0x00, + // Group L=8,T=27,V=9 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 8); + assert_eq!(nla.kind(), 27); + assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::Group(0)); + + // Promiscuity L=8,T=30,V=0 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 8); + assert_eq!(nla.kind(), 30); + assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::Promiscuity(0)); + + // Number of Tx Queues L=8,T=31,V=1 + // 0x01, 0x00, 0x00, 0x00 + let nla = attributes.next().unwrap().unwrap(); + nla.check_buffer_length().unwrap(); + assert_eq!(nla.length(), 8); + assert_eq!(nla.kind(), 31); + assert_eq!(nla.value(), &[0x01, 0x00, 0x00, 0x00]); + let parsed = + LinkAttribute::parse_with_param(&nla, AddressFamily::Inet).unwrap(); + assert_eq!(parsed, LinkAttribute::NumTxQueues(1)); +} + +#[test] +fn link_message_emit() { + let header = LinkHeader { + link_layer_type: LinkLayerType::Loopback, + index: 1, + flags: vec![ + LinkFlag::Loopback, + LinkFlag::LowerUp, + LinkFlag::Running, + LinkFlag::Up, + ], + interface_family: AddressFamily::Unspec, + ..Default::default() + }; + + let attributes = vec![ + LinkAttribute::IfName("lo".into()), + LinkAttribute::TxQueueLen(1000), + LinkAttribute::OperState(State::Unknown), + LinkAttribute::Mode(0), + LinkAttribute::Mtu(0x1_0000), + LinkAttribute::Group(0), + LinkAttribute::Promiscuity(0), + LinkAttribute::NumTxQueues(1), + LinkAttribute::GsoMaxSegs(0xffff), + LinkAttribute::GsoMaxSize(0x1_0000), + ]; + + let packet = LinkMessage { header, attributes }; + + let mut buf = [0; 96]; + + assert_eq!(packet.buffer_len(), 96); + packet.emit(&mut buf[..]); + + assert_eq!(buf, &LINK_MSG[..96]); +} diff --git a/src/link/tests/mod.rs b/src/link/tests/mod.rs new file mode 100644 index 00000000..01701f04 --- /dev/null +++ b/src/link/tests/mod.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +#[cfg(test)] +mod bond; +#[cfg(test)] +mod bridge; +#[cfg(test)] +mod hsr; +#[cfg(test)] +mod ipvlan; +#[cfg(test)] +mod loopback; +#[cfg(test)] +mod macsec; +#[cfg(test)] +mod macvlan; +#[cfg(test)] +mod message; +#[cfg(test)] +mod prop_list; +#[cfg(test)] +mod veth; +#[cfg(test)] +mod vlan; +#[cfg(test)] +mod vrf; +#[cfg(test)] +mod vxlan; +#[cfg(test)] +mod xfrm; + +//TODO(Gris Ge): capture netlink message for ipoib +//TODO(Gris Ge): need test for Icmp6Stats and Inet6Stats diff --git a/src/link/tests/prop_list.rs b/src/link/tests/prop_list.rs new file mode 100644 index 00000000..ef0bcf39 --- /dev/null +++ b/src/link/tests/prop_list.rs @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + LinkAttribute, LinkFlag, LinkHeader, LinkLayerType, LinkMessage, + LinkMessageBuffer, Prop, +}; +use crate::AddressFamily; + +#[test] +fn test_wlan0_with_prop_altname() { + // nlmon dump of `ip link show wlan0` with two alt_name for wlan0 with + // IFLA_PROP_LIST only + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x34, 0x80, 0x0e, 0x00, 0x35, 0x00, + 0x77, 0x6c, 0x70, 0x30, 0x73, 0x32, 0x30, 0x66, 0x33, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x35, 0x00, 0x77, 0x69, 0x66, 0x69, 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 2, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::PropList(vec![ + Prop::AltIfName("wlp0s20f3".to_string()), + Prop::AltIfName("wifi".to_string()), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/veth.rs b/src/link/tests/veth.rs new file mode 100644 index 00000000..d36db583 --- /dev/null +++ b/src/link/tests/veth.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoVeth, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_veth_get_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, // AF_UNSPEC and reserved + 0x01, 0x00, // Link layer type ethernet(1) + 0x19, 0x00, 0x00, 0x00, // iface index 25 + 0x43, 0x10, 0x01, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // changed flags0 + 0x10, 0x00, // length 16 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x09, 0x00, // length 09 + 0x01, 0x00, // IFLA_INFO_KIND + 0x76, 0x65, 0x74, 0x68, 0x00, // 'bond\0' + 0x00, 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 25, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![LinkInfo::Kind( + InfoKind::Veth, + )])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} + +#[test] +fn test_crate_veth() { + // With `iproute 6.5.0`, the IFLA_INFO_KIND will not use NULL terminated + // string. + // This is bug of iproute: https://issues.redhat.com/browse/RHEL-14964 + // The correct way is NULL terminated `IFLA_INFO_KIND`, hence below packet + // is different from what iproute generate. + let raw: Vec = vec![ + 0x00, // interface family AF_UNSPEC + 0x00, // reserved + 0x00, 0x00, // link layer type 0 + 0x00, 0x00, 0x00, 0x00, // iface index 0 + 0x00, 0x00, 0x00, 0x00, // device flags 0 + 0x00, 0x00, 0x00, 0x00, // change flags 0 + 0x0a, 0x00, // length 10 + 0x03, 0x00, // IFLA_IFNAME + 0x76, 0x65, 0x74, 0x68, 0x31, 0x00, 0x00, 0x00, // "veth0\0" + 0x38, 0x00, // length 56 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x09, 0x00, // length 9 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x76, 0x65, 0x74, 0x68, 0x00, // 'veth\0' + 0x00, 0x00, 0x00, // padding + 0x28, 0x00, // length 40 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x24, 0x00, // length 36 + 0x01, 0x00, // VETH_INFO_PEER 1 + 0x00, // Netlink Message header: family AF_UNSPEC + 0x00, // reserved + 0x00, 0x00, // link layer type 0 + 0x00, 0x00, 0x00, 0x00, // iface index 0 + 0x00, 0x00, 0x00, 0x00, // device flags 0 + 0x00, 0x00, 0x00, 0x00, // change flags 0 + 0x0d, 0x00, // length 16 + 0x03, 0x00, // IFLA_IFNAME 3 + 0x76, 0x65, 0x74, 0x68, 0x31, 0x2d, 0x65, 0x70, + 0x00, // "veth1-ep\0" + 0x00, 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("veth1".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Veth), + LinkInfo::Data(InfoData::Veth(InfoVeth::Peer(LinkMessage { + attributes: vec![LinkAttribute::IfName( + "veth1-ep".to_string(), + )], + ..Default::default() + }))), + ]), + ], + ..Default::default() + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/vlan.rs b/src/link/tests/vlan.rs new file mode 100644 index 00000000..ab9242c3 --- /dev/null +++ b/src/link/tests/vlan.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoVlan, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, VlanProtocol, + VlanQosMapping, +}; +use crate::AddressFamily; + +#[test] +fn test_parsing_link_vlan() { + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, // change flags 0 + 0x50, 0x00, // length 80 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x09, 0x00, // length + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x76, 0x6c, 0x61, 0x6e, 0x00, // 'vlan\0' + 0x00, 0x00, 0x00, // padding + 0x40, 0x00, // length 64 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x06, 0x00, // length 06 + 0x05, 0x00, // IFLA_VLAN_PROTOCOL 5 + 0x81, 0x00, // big endian 0x8100 ETH_P_8021Q + 0x00, 0x00, // padding + 0x06, 0x00, // length 06 + 0x01, 0x00, // IFLA_VLAN_ID 1 + 0x65, 0x00, // VLAN ID 101 + 0x00, 0x00, // padding + 0x0c, 0x00, // length 12 + 0x02, 0x00, // IFLA_VLAN_FLAGS 2 + 0x01, 0x00, 0x00, 0x00, // flags VLAN_FLAG_REORDER_HDR(1) + 0xff, 0xff, 0xff, 0xff, // mask + 0x10, 0x00, // length 16 + 0x04, 0x00, // IFLA_VLAN_INGRESS_QOS 4 + 0x0c, 0x00, // length 12 + 0x01, 0x00, // IFLA_VLAN_QOS_MAPPING 1 + 0x06, 0x00, 0x00, 0x00, // from 6 + 0x07, 0x00, 0x00, 0x00, // to 7 + 0x10, 0x00, // length 16 + 0x03, 0x00, // IFLA_VLAN_EGRESS_QOS 3 + 0x0c, 0x00, // length 12 + 0x01, 0x00, // IFLA_VLAN_QOS_MAPPING 1 + 0x04, 0x00, 0x00, 0x00, // from 4 + 0x05, 0x00, 0x00, 0x00, // to 5 + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 34, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Vlan), + LinkInfo::Data(InfoData::Vlan(vec![ + InfoVlan::Protocol(VlanProtocol::Ieee8021Q), + InfoVlan::Id(101), + InfoVlan::Flags((1, 4294967295)), + InfoVlan::IngressQos(vec![VlanQosMapping::Mapping(6, 7)]), + InfoVlan::EgressQos(vec![VlanQosMapping::Mapping(4, 5)]), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/vrf.rs b/src/link/tests/vrf.rs new file mode 100644 index 00000000..fab93d45 --- /dev/null +++ b/src/link/tests/vrf.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoVrf, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, + LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_parsing_link_vrf() { + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x00, 0x00, 0xc1, 0x04, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x76, 0x72, 0x66, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x0a, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 34, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Controller, + LinkFlag::LowerUp, + LinkFlag::Noarp, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Vrf), + LinkInfo::Data(InfoData::Vrf(vec![InfoVrf::TableId(10)])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/vxlan.rs b/src/link/tests/vxlan.rs new file mode 100644 index 00000000..3ac41f55 --- /dev/null +++ b/src/link/tests/vxlan.rs @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; + +use crate::link::{ + AfSpecInet, AfSpecInet6, AfSpecUnspec, Inet6CacheInfo, Inet6DevConf, + Inet6IfaceFlag, Inet6IfaceFlags, InetDevConf, InfoData, InfoKind, + InfoVxlan, LinkAttribute, LinkFlag, LinkHeader, LinkInfo, LinkLayerType, + LinkMessage, LinkMessageBuffer, Map, State, Stats, Stats64, Xdp, + XdpAttached, +}; +use crate::AddressFamily; + +#[test] +fn test_parsing_link_vxlan() { + let raw = vec![ + 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x10, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x76, 0x78, 0x6c, 0x61, + 0x6e, 0x30, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0xaa, 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, 0x3d, 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, 0x3a, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, 0xf8, 0xff, 0x07, 0x00, + 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x00, + 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, 0x00, 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, + 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x00, 0x00, 0x0a, 0x00, 0x02, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 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, 0xba, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 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, 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, 0xba, 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, 0xba, 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, + 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x76, 0x78, 0x6c, 0x61, + 0x6e, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x2c, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x0f, 0x00, 0x12, 0xb5, 0x00, 0x00, 0x05, 0x00, 0x12, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, 0x8c, 0x00, 0x02, 0x00, + 0x88, 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, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, 0xff, 0xff, 0x00, 0x00, + 0xb0, 0x1c, 0x00, 0x00, 0x0e, 0x8c, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0xaa, 0x05, 0x00, 0x00, 0x01, 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, 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, + 0x04, 0x00, 0x3e, 0x80, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 16, + link_layer_type: LinkLayerType::Ether, + flags: vec![ + LinkFlag::Broadcast, + LinkFlag::LowerUp, + LinkFlag::Multicast, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![ + LinkAttribute::IfName("vxlan0".into()), + LinkAttribute::TxQueueLen(1000), + LinkAttribute::OperState(State::Unknown), + LinkAttribute::Mode(0), + LinkAttribute::Mtu(1450), + LinkAttribute::MinMtu(68), + LinkAttribute::MaxMtu(65535), + LinkAttribute::Group(0), + LinkAttribute::Promiscuity(0), + LinkAttribute::Other(DefaultNla::new(61, vec![0, 0, 0, 0])), + LinkAttribute::NumTxQueues(1), + LinkAttribute::GsoMaxSegs(65535), + LinkAttribute::GsoMaxSize(65536), + LinkAttribute::Other(DefaultNla::new(58, vec![0, 0, 1, 0])), + LinkAttribute::Other(DefaultNla::new(63, vec![0, 0, 1, 0])), + LinkAttribute::Other(DefaultNla::new(64, 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(1), + LinkAttribute::Carrier(1), + LinkAttribute::Qdisc("noqueue".to_string()), + LinkAttribute::CarrierChanges(0), + LinkAttribute::CarrierUpCount(vec![0, 0, 0, 0]), + LinkAttribute::CarrierDownCount(vec![0, 0, 0, 0]), + LinkAttribute::ProtoDown(0), + LinkAttribute::Map(Map { + memory_start: 0, + memory_end: 0, + base_address: 0, + irq: 0, + dma: 0, + port: 0, + }), + LinkAttribute::Address(vec![0x00, 0x23, 0x45, 0x67, 0x89, 0x1c]), + LinkAttribute::Broadcast(vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + LinkAttribute::Stats64(Stats64 { + rx_packets: 0, + tx_packets: 0, + rx_bytes: 0, + tx_bytes: 0, + rx_errors: 0, + tx_errors: 698, + rx_dropped: 0, + tx_dropped: 0, + multicast: 0, + collisions: 0, + rx_length_errors: 0, + rx_over_errors: 0, + rx_crc_errors: 0, + rx_frame_errors: 0, + rx_fifo_errors: 0, + rx_missed_errors: 0, + tx_aborted_errors: 0, + tx_carrier_errors: 698, + tx_fifo_errors: 0, + tx_heartbeat_errors: 0, + tx_window_errors: 0, + rx_compressed: 0, + tx_compressed: 0, + rx_nohandler: 0, + rx_otherhost_dropped: 0, + }), + LinkAttribute::Stats(Stats { + rx_packets: 0, + tx_packets: 0, + rx_bytes: 0, + tx_bytes: 0, + rx_errors: 0, + tx_errors: 698, + rx_dropped: 0, + tx_dropped: 0, + multicast: 0, + collisions: 0, + rx_length_errors: 0, + rx_over_errors: 0, + rx_crc_errors: 0, + rx_frame_errors: 0, + rx_fifo_errors: 0, + rx_missed_errors: 0, + tx_aborted_errors: 0, + tx_carrier_errors: 698, + tx_fifo_errors: 0, + tx_heartbeat_errors: 0, + tx_window_errors: 0, + rx_compressed: 0, + tx_compressed: 0, + rx_nohandler: 0, + }), + LinkAttribute::Xdp(vec![Xdp::Attached(XdpAttached::None)]), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Vxlan), + LinkInfo::Data(InfoData::Vxlan(vec![ + InfoVxlan::Id(101), + InfoVxlan::Group(vec![8, 8, 8, 8]), + InfoVxlan::Link(13), + InfoVxlan::Local(vec![1, 1, 1, 1]), + InfoVxlan::Ttl(0), + InfoVxlan::TtlInherit(false), + InfoVxlan::Tos(0), + InfoVxlan::Df(0), + InfoVxlan::Label(0), + InfoVxlan::Learning(true), + InfoVxlan::Proxy(false), + InfoVxlan::Rsc(false), + InfoVxlan::L2Miss(false), + InfoVxlan::L3Miss(false), + InfoVxlan::CollectMetadata(false), + InfoVxlan::Ageing(300), + InfoVxlan::Limit(0), + InfoVxlan::Port(4789), + InfoVxlan::UDPCsum(true), + InfoVxlan::UDPZeroCsumTX(false), + InfoVxlan::UDPZeroCsumRX(false), + InfoVxlan::RemCsumTX(false), + InfoVxlan::RemCsumRX(false), + InfoVxlan::Localbypass(true), + InfoVxlan::PortRange((0, 0)), + ])), + ]), + LinkAttribute::AfSpecUnspec(vec![ + AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf { + forwarding: 1, + mc_forwarding: 0, + proxy_arp: 0, + accept_redirects: 1, + secure_redirects: 1, + send_redirects: 1, + shared_media: 1, + rp_filter: 2, + accept_source_route: 0, + bootp_relay: 0, + log_martians: 0, + tag: 0, + arpfilter: 0, + medium_id: 0, + noxfrm: 0, + nopolicy: 0, + force_igmp_version: 0, + arp_announce: 0, + arp_ignore: 0, + promote_secondaries: 1, + arp_accept: 0, + arp_notify: 0, + accept_local: 0, + src_vmark: 0, + proxy_arp_pvlan: 0, + route_localnet: 0, + igmpv2_unsolicited_report_interval: 10000, + igmpv3_unsolicited_report_interval: 1000, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_gratuitous_arp: 0, + bc_forwarding: 0, + arp_evict_nocarrier: 1, + })]), + AfSpecUnspec::Inet6(vec![ + AfSpecInet6::Flags(Inet6IfaceFlags(vec![ + Inet6IfaceFlag::RsSent, + Inet6IfaceFlag::Ready, + ])), + AfSpecInet6::CacheInfo(Inet6CacheInfo { + max_reasm_len: 65535, + tstamp: 7344, + reachable_time: 35854, + retrans_time: 1000, + }), + AfSpecInet6::DevConf(Inet6DevConf { + forwarding: 0, + hoplimit: 64, + mtu6: 1450, + accept_ra: 1, + accept_redirects: 1, + autoconf: 1, + dad_transmits: 1, + rtr_solicits: -1, + rtr_solicit_interval: 4000, + rtr_solicit_delay: 1000, + use_tempaddr: 0, + temp_valid_lft: 604800, + temp_prefered_lft: 86400, + regen_max_retry: 3, + max_desync_factor: 600, + max_addresses: 16, + force_mld_version: 0, + accept_ra_defrtr: 1, + accept_ra_pinfo: 1, + accept_ra_rtr_pref: 1, + rtr_probe_interval: 60000, + accept_ra_rt_info_max_plen: 0, + proxy_ndp: 0, + optimistic_dad: 0, + accept_source_route: 0, + mc_forwarding: 0, + disable_ipv6: 0, + accept_dad: 1, + force_tllao: 0, + ndisc_notify: 0, + mldv1_unsolicited_report_interval: 10000, + mldv2_unsolicited_report_interval: 1000, + suppress_frag_ndisc: 1, + accept_ra_from_local: 0, + use_optimistic: 0, + accept_ra_mtu: 1, + stable_secret: 0, + use_oif_addrs_only: 0, + accept_ra_min_hop_limit: 1, + ignore_routes_with_linkdown: 0, + drop_unicast_in_l2_multicast: 0, + drop_unsolicited_na: 0, + keep_addr_on_down: 0, + rtr_solicit_max_interval: 3600000, + seg6_enabled: 0, + seg6_require_hmac: 0, + enhanced_dad: 1, + addr_gen_mode: 0, + disable_policy: 0, + accept_ra_rt_info_min_plen: 0, + ndisc_tclass: 0, + rpl_seg_enabled: 0, + ra_defrtr_metric: 1024, + ioam6_enabled: 0, + ioam6_id: 65535, + ioam6_id_wide: -1, + ndisc_evict_nocarrier: 1, + accept_untracked_na: 0, + accept_ra_min_lft: 0, + }), + ]), + ]), + LinkAttribute::Other(DefaultNla::new(32830, vec![])), + ], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/link/tests/xfrm.rs b/src/link/tests/xfrm.rs new file mode 100644 index 00000000..8f84d46e --- /dev/null +++ b/src/link/tests/xfrm.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +use netlink_packet_utils::{Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoKind, InfoXfrm, LinkAttribute, LinkFlag, LinkHeader, + LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, +}; +use crate::AddressFamily; + +#[test] +fn test_parsing_link_xfrm() { + let raw = vec![ + 0x00, 0x00, 0xfe, 0xff, 0x28, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x12, 0x00, 0x09, 0x00, 0x01, 0x00, + 0x78, 0x66, 0x72, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x08, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0x0a, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 40, + link_layer_type: LinkLayerType::None, + flags: vec![ + LinkFlag::LowerUp, + LinkFlag::Noarp, + LinkFlag::Running, + LinkFlag::Up, + ], + change_mask: 0, + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Xfrm), + LinkInfo::Data(InfoData::Xfrm(vec![ + InfoXfrm::Link(2), + InfoXfrm::IfId(10), + ])), + ])], + }; + + assert_eq!( + expected, + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap() + ); + + let mut buf = vec![0; expected.buffer_len()]; + + expected.emit(&mut buf); + + assert_eq!(buf, raw); +} diff --git a/src/rtnl/link/nlas/link_xdp.rs b/src/link/xdp.rs similarity index 96% rename from src/rtnl/link/nlas/link_xdp.rs rename to src/link/xdp.rs index eabf06e5..37d13f8f 100644 --- a/src/rtnl/link/nlas/link_xdp.rs +++ b/src/link/xdp.rs @@ -10,7 +10,20 @@ use netlink_packet_utils::{ DecodeError, Parseable, }; -use crate::constants::*; +const IFLA_XDP_FD: u32 = 1; +const IFLA_XDP_ATTACHED: u32 = 2; +const IFLA_XDP_FLAGS: u32 = 3; +const IFLA_XDP_PROG_ID: u32 = 4; +const IFLA_XDP_DRV_PROG_ID: u32 = 5; +const IFLA_XDP_SKB_PROG_ID: u32 = 6; +const IFLA_XDP_HW_PROG_ID: u32 = 7; +const IFLA_XDP_EXPECTED_FD: u32 = 8; + +const XDP_ATTACHED_NONE: u8 = 0; +const XDP_ATTACHED_DRV: u8 = 1; +const XDP_ATTACHED_SKB: u8 = 2; +const XDP_ATTACHED_HW: u8 = 3; +const XDP_ATTACHED_MULTI: u8 = 4; #[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone)] @@ -132,8 +145,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecXdp { } } -#[non_exhaustive] #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] pub enum XdpAttached { /// XDP_ATTACHED_NONE None, diff --git a/src/rtnl/buffer.rs b/src/rtnl/buffer.rs index fe823841..76ece9f0 100644 --- a/src/rtnl/buffer.rs +++ b/src/rtnl/buffer.rs @@ -7,11 +7,13 @@ use netlink_packet_utils::{ }; use crate::{ - constants::*, AddressHeader, AddressMessage, AddressMessageBuffer, - LinkMessage, LinkMessageBuffer, NeighbourMessage, NeighbourMessageBuffer, - NeighbourTableMessage, NeighbourTableMessageBuffer, NsidMessage, - NsidMessageBuffer, RouteHeader, RouteMessage, RouteMessageBuffer, - RtnlMessage, RuleMessage, RuleMessageBuffer, TcMessage, TcMessageBuffer, + constants::*, + link::{LinkMessage, LinkMessageBuffer}, + AddressHeader, AddressMessage, AddressMessageBuffer, NeighbourMessage, + NeighbourMessageBuffer, NeighbourTableMessage, NeighbourTableMessageBuffer, + NsidMessage, NsidMessageBuffer, RouteHeader, RouteMessage, + RouteMessageBuffer, RtnlMessage, RuleMessage, RuleMessageBuffer, TcMessage, + TcMessageBuffer, }; buffer!(RtnlMessageBuffer); @@ -33,7 +35,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Err(e) => { if buf.inner().len() == 4 && message_type == RTM_GETLINK { let mut msg = LinkMessage::default(); - msg.header.interface_family = buf.inner()[0]; + msg.header.interface_family = buf.inner()[0].into(); msg } else { return Err(e); diff --git a/src/rtnl/constants.rs b/src/rtnl/constants.rs index 1c87fa6a..7abe5b52 100644 --- a/src/rtnl/constants.rs +++ b/src/rtnl/constants.rs @@ -233,46 +233,6 @@ pub const RTM_F_LOOKUP_TABLE: u32 = 4096; /// `b61798130f1be5bff08712308126c2d7ebe390ef`) pub const RTM_F_FIB_MATCH: u32 = 8192; -pub const AF_UNSPEC: u16 = libc::AF_UNSPEC as u16; -pub const AF_UNIX: u16 = libc::AF_UNIX as u16; -// pub const AF_LOCAL: u16 = libc::AF_LOCAL as u16; -pub const AF_INET: u16 = libc::AF_INET as u16; -pub const AF_AX25: u16 = libc::AF_AX25 as u16; -pub const AF_IPX: u16 = libc::AF_IPX as u16; -pub const AF_APPLETALK: u16 = libc::AF_APPLETALK as u16; -pub const AF_NETROM: u16 = libc::AF_NETROM as u16; -pub const AF_BRIDGE: u16 = libc::AF_BRIDGE as u16; -pub const AF_ATMPVC: u16 = libc::AF_ATMPVC as u16; -pub const AF_X25: u16 = libc::AF_X25 as u16; -pub const AF_INET6: u16 = libc::AF_INET6 as u16; -pub const AF_ROSE: u16 = libc::AF_ROSE as u16; -pub const AF_DECNET: u16 = libc::AF_DECnet as u16; -pub const AF_NETBEUI: u16 = libc::AF_NETBEUI as u16; -pub const AF_SECURITY: u16 = libc::AF_SECURITY as u16; -pub const AF_KEY: u16 = libc::AF_KEY as u16; -pub const AF_NETLINK: u16 = libc::AF_NETLINK as u16; -// pub const AF_ROUTE: u16 = libc::AF_ROUTE as u16; -pub const AF_PACKET: u16 = libc::AF_PACKET as u16; -pub const AF_ASH: u16 = libc::AF_ASH as u16; -pub const AF_ECONET: u16 = libc::AF_ECONET as u16; -pub const AF_ATMSVC: u16 = libc::AF_ATMSVC as u16; -pub const AF_RDS: u16 = libc::AF_RDS as u16; -pub const AF_SNA: u16 = libc::AF_SNA as u16; -pub const AF_IRDA: u16 = libc::AF_IRDA as u16; -pub const AF_PPPOX: u16 = libc::AF_PPPOX as u16; -pub const AF_WANPIPE: u16 = libc::AF_WANPIPE as u16; -pub const AF_LLC: u16 = libc::AF_LLC as u16; -pub const AF_CAN: u16 = libc::AF_CAN as u16; -pub const AF_TIPC: u16 = libc::AF_TIPC as u16; -pub const AF_BLUETOOTH: u16 = libc::AF_BLUETOOTH as u16; -pub const AF_IUCV: u16 = libc::AF_IUCV as u16; -pub const AF_RXRPC: u16 = libc::AF_RXRPC as u16; -pub const AF_ISDN: u16 = libc::AF_ISDN as u16; -pub const AF_PHONET: u16 = libc::AF_PHONET as u16; -pub const AF_IEEE802154: u16 = libc::AF_IEEE802154 as u16; -pub const AF_CAIF: u16 = libc::AF_CAIF as u16; -pub const AF_ALG: u16 = libc::AF_ALG as u16; - pub const NETNSA_NONE: u16 = 0; pub const NETNSA_NSID: u16 = 1; pub const NETNSA_PID: u16 = 2; @@ -680,43 +640,6 @@ pub const IFLA_INET6_ICMP6STATS: u16 = 6; pub const IFLA_INET6_TOKEN: u16 = 7; pub const IFLA_INET6_ADDR_GEN_MODE: u16 = 8; -/// Link is up (administratively). -pub const IFF_UP: u32 = libc::IFF_UP as u32; -/// Link is up and carrier is OK (RFC2863 OPER_UP) -pub const IFF_RUNNING: u32 = libc::IFF_RUNNING as u32; -/// Link layer is operational -pub const IFF_LOWER_UP: u32 = libc::IFF_LOWER_UP as u32; -/// Driver signals IFF_DORMANT -pub const IFF_DORMANT: u32 = libc::IFF_DORMANT as u32; -/// Link supports broadcasting -pub const IFF_BROADCAST: u32 = libc::IFF_BROADCAST as u32; -/// Link supports multicasting -pub const IFF_MULTICAST: u32 = libc::IFF_MULTICAST as u32; -/// Link supports multicast routing -pub const IFF_ALLMULTI: u32 = libc::IFF_ALLMULTI as u32; -/// Tell driver to do debugging (currently unused) -pub const IFF_DEBUG: u32 = libc::IFF_DEBUG as u32; -/// Link loopback network -pub const IFF_LOOPBACK: u32 = libc::IFF_LOOPBACK as u32; -/// u32erface is point-to-point link -pub const IFF_POINTOPOINT: u32 = libc::IFF_POINTOPOINT as u32; -/// ARP is not supported -pub const IFF_NOARP: u32 = libc::IFF_NOARP as u32; -/// Receive all packets. -pub const IFF_PROMISC: u32 = libc::IFF_PROMISC as u32; -/// Master of a load balancer (bonding) -pub const IFF_MASTER: u32 = libc::IFF_MASTER as u32; -/// Link selects port automatically (only used by ARM ethernet) -pub const IFF_PORTSEL: u32 = libc::IFF_PORTSEL as u32; -/// Driver supports setting media type (only used by ARM ethernet) -pub const IFF_AUTOMEDIA: u32 = libc::IFF_AUTOMEDIA as u32; -// /// Echo sent packets (testing feature, CAN only) -// pub const IFF_ECHO: u32 = libc::IFF_ECHO as u32; -// /// Dialup device with changing addresses (unused, BSD compatibility) -// pub const IFF_DYNAMIC: u32 = libc::IFF_DYNAMIC as u32; -// /// Avoid use of trailers (unused, BSD compatibility) -// pub const IFF_NOTRAILERS: u32 = libc::IFF_NOTRAILERS as u32; - pub const IF_OPER_UNKNOWN: u8 = 0; pub const IF_OPER_NOTPRESENT: u8 = 1; pub const IF_OPER_DOWN: u8 = 2; diff --git a/src/rtnl/link/buffer.rs b/src/rtnl/link/buffer.rs deleted file mode 100644 index e0e93fc4..00000000 --- a/src/rtnl/link/buffer.rs +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_utils::DecodeError; - -use netlink_packet_utils::nla::{NlaBuffer, NlasIterator}; - -pub const LINK_HEADER_LEN: usize = 16; - -buffer!(LinkMessageBuffer(LINK_HEADER_LEN) { - interface_family: (u8, 0), - reserved_1: (u8, 1), - link_layer_type: (u16, 2..4), - link_index: (u32, 4..8), - flags: (u32, 8..12), - change_mask: (u32, 12..LINK_HEADER_LEN), - payload: (slice, LINK_HEADER_LEN..), -}); - -impl<'a, T: AsRef<[u8]> + ?Sized> LinkMessageBuffer<&'a T> { - pub fn nlas( - &self, - ) -> impl Iterator, DecodeError>> { - NlasIterator::new(self.payload()) - } -} diff --git a/src/rtnl/link/message.rs b/src/rtnl/link/message.rs deleted file mode 100644 index 07cb93dd..00000000 --- a/src/rtnl/link/message.rs +++ /dev/null @@ -1,243 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; -use netlink_packet_utils::{ - traits::{Emitable, Parseable, ParseableParametrized}, - DecodeError, -}; - -use crate::{nlas::link::Nla, LinkHeader, LinkMessageBuffer}; - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -#[non_exhaustive] -pub struct LinkMessage { - pub header: LinkHeader, - pub nlas: Vec, -} - -impl Emitable for LinkMessage { - fn buffer_len(&self) -> usize { - self.header.buffer_len() + self.nlas.as_slice().buffer_len() - } - - fn emit(&self, buffer: &mut [u8]) { - self.header.emit(buffer); - self.nlas - .as_slice() - .emit(&mut buffer[self.header.buffer_len()..]); - } -} - -impl<'a, T: AsRef<[u8]> + 'a> Parseable> - for LinkMessage -{ - fn parse(buf: &LinkMessageBuffer<&'a T>) -> Result { - let header = LinkHeader::parse(buf) - .context("failed to parse link message header")?; - let interface_family = header.interface_family; - let nlas = Vec::::parse_with_param(buf, interface_family) - .context("failed to parse link message NLAs")?; - Ok(LinkMessage { header, nlas }) - } -} - -impl<'a, T: AsRef<[u8]> + 'a> - ParseableParametrized, u16> for Vec -{ - fn parse_with_param( - buf: &LinkMessageBuffer<&'a T>, - family: u16, - ) -> Result { - let mut nlas = vec![]; - for nla_buf in buf.nlas() { - nlas.push(Nla::parse_with_param(&nla_buf?, family)?); - } - Ok(nlas) - } -} - -impl<'a, T: AsRef<[u8]> + 'a> - ParseableParametrized, u8> for Vec -{ - fn parse_with_param( - buf: &LinkMessageBuffer<&'a T>, - family: u8, - ) -> Result { - Vec::::parse_with_param(buf, u16::from(family)) - } -} - -#[cfg(test)] -mod test { - use crate::{ - constants::*, - nlas::link::{Nla, State}, - LinkHeader, LinkMessage, LinkMessageBuffer, - }; - - use netlink_packet_utils::traits::{Emitable, ParseableParametrized}; - - #[rustfmt::skip] - static HEADER: [u8; 96] = [ - 0x00, // interface family - 0x00, // reserved - 0x04, 0x03, // link layer type 772 = loopback - 0x01, 0x00, 0x00, 0x00, // interface index = 1 - // Note: in the wireshark capture, the thrid byte is 0x01 - // but that does not correpond to any of the IFF_ flags... - 0x49, 0x00, 0x00, 0x00, // device flags: UP, LOOPBACK, RUNNING, LOWERUP - 0x00, 0x00, 0x00, 0x00, // reserved 2 (aka device change flag) - - // nlas - 0x07, 0x00, 0x03, 0x00, 0x6c, 0x6f, 0x00, // device name L=7,T=3,V=lo - 0x00, // padding - 0x08, 0x00, 0x0d, 0x00, 0xe8, 0x03, 0x00, 0x00, // TxQueue length L=8,T=13,V=1000 - 0x05, 0x00, 0x10, 0x00, 0x00, // OperState L=5,T=16,V=0 (unknown) - 0x00, 0x00, 0x00, // padding - 0x05, 0x00, 0x11, 0x00, 0x00, // Link mode L=5,T=17,V=0 - 0x00, 0x00, 0x00, // padding - 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, // MTU L=8,T=4,V=65536 - 0x08, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, // Group L=8,T=27,V=9 - 0x08, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, // Promiscuity L=8,T=30,V=0 - 0x08, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, // Number of Tx Queues L=8,T=31,V=1 - 0x08, 0x00, 0x28, 0x00, 0xff, 0xff, 0x00, 0x00, // Maximum GSO segment count L=8,T=40,V=65536 - 0x08, 0x00, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, // Maximum GSO size L=8,T=41,V=65536 - ]; - - #[test] - fn packet_header_read() { - let packet = LinkMessageBuffer::new(&HEADER[0..16]); - assert_eq!(packet.interface_family(), 0); - assert_eq!(packet.reserved_1(), 0); - assert_eq!(packet.link_layer_type(), ARPHRD_LOOPBACK); - assert_eq!(packet.link_index(), 1); - assert_eq!(packet.flags(), IFF_UP | IFF_LOOPBACK | IFF_RUNNING); - assert_eq!(packet.change_mask(), 0); - } - - #[test] - fn packet_header_build() { - let mut buf = vec![0xff; 16]; - { - let mut packet = LinkMessageBuffer::new(&mut buf); - packet.set_interface_family(0); - packet.set_reserved_1(0); - packet.set_link_layer_type(ARPHRD_LOOPBACK); - packet.set_link_index(1); - packet.set_flags(IFF_UP | IFF_LOOPBACK | IFF_RUNNING); - packet.set_change_mask(0); - } - assert_eq!(&buf[..], &HEADER[0..16]); - } - - #[test] - fn packet_nlas_read() { - let packet = LinkMessageBuffer::new(&HEADER[..]); - assert_eq!(packet.nlas().count(), 10); - let mut nlas = packet.nlas(); - - // device name L=7,T=3,V=lo - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 7); - assert_eq!(nla.kind(), 3); - assert_eq!(nla.value(), &[0x6c, 0x6f, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::IfName(String::from("lo"))); - - // TxQueue length L=8,T=13,V=1000 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 8); - assert_eq!(nla.kind(), 13); - assert_eq!(nla.value(), &[0xe8, 0x03, 0x00, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::TxQueueLen(1000)); - - // OperState L=5,T=16,V=0 (unknown) - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 5); - assert_eq!(nla.kind(), 16); - assert_eq!(nla.value(), &[0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::OperState(State::Unknown)); - - // Link mode L=5,T=17,V=0 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 5); - assert_eq!(nla.kind(), 17); - assert_eq!(nla.value(), &[0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::Mode(0)); - - // MTU L=8,T=4,V=65536 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 8); - assert_eq!(nla.kind(), 4); - assert_eq!(nla.value(), &[0x00, 0x00, 0x01, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::Mtu(65_536)); - - // 0x00, 0x00, 0x00, 0x00, - // Group L=8,T=27,V=9 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 8); - assert_eq!(nla.kind(), 27); - assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::Group(0)); - - // Promiscuity L=8,T=30,V=0 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 8); - assert_eq!(nla.kind(), 30); - assert_eq!(nla.value(), &[0x00, 0x00, 0x00, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::Promiscuity(0)); - - // Number of Tx Queues L=8,T=31,V=1 - // 0x01, 0x00, 0x00, 0x00 - let nla = nlas.next().unwrap().unwrap(); - nla.check_buffer_length().unwrap(); - assert_eq!(nla.length(), 8); - assert_eq!(nla.kind(), 31); - assert_eq!(nla.value(), &[0x01, 0x00, 0x00, 0x00]); - let parsed = Nla::parse_with_param(&nla, AF_INET).unwrap(); - assert_eq!(parsed, Nla::NumTxQueues(1)); - } - - #[test] - fn emit() { - let header = LinkHeader { - link_layer_type: ARPHRD_LOOPBACK, - index: 1, - flags: IFF_UP | IFF_LOOPBACK | IFF_RUNNING | IFF_LOWER_UP, - ..Default::default() - }; - - let nlas = vec![ - Nla::IfName("lo".into()), - Nla::TxQueueLen(1000), - Nla::OperState(State::Unknown), - Nla::Mode(0), - Nla::Mtu(0x1_0000), - Nla::Group(0), - Nla::Promiscuity(0), - Nla::NumTxQueues(1), - Nla::GsoMaxSegs(0xffff), - Nla::GsoMaxSize(0x1_0000), - ]; - - let packet = LinkMessage { header, nlas }; - - let mut buf = [0; 96]; - - assert_eq!(packet.buffer_len(), 96); - packet.emit(&mut buf[..]); - } -} diff --git a/src/rtnl/link/mod.rs b/src/rtnl/link/mod.rs deleted file mode 100644 index 71505920..00000000 --- a/src/rtnl/link/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT - -mod buffer; -mod header; -mod message; -pub mod nlas; - -mod tests; - -pub use self::{buffer::*, header::*, message::*}; diff --git a/src/rtnl/link/nlas/af_spec_inet.rs b/src/rtnl/link/nlas/af_spec_inet.rs deleted file mode 100644 index 55d603d6..00000000 --- a/src/rtnl/link/nlas/af_spec_inet.rs +++ /dev/null @@ -1,263 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; -use netlink_packet_utils::{ - nla::{self, DefaultNla, NlaBuffer, NlasIterator}, - traits::{Emitable, Parseable}, - DecodeError, -}; - -use super::{inet, inet6}; -use crate::constants::*; - -#[derive(Clone, Eq, PartialEq, Debug)] -#[non_exhaustive] -pub enum AfSpecInet { - Unspec(Vec), - Unix(Vec), - Ax25(Vec), - Ipx(Vec), - AppleTalk(Vec), - Netrom(Vec), - Bridge(Vec), - AtmPvc(Vec), - X25(Vec), - Inet(Vec), - Inet6(Vec), - Rose(Vec), - DecNet(Vec), - NetbEui(Vec), - Security(Vec), - Key(Vec), - Netlink(Vec), - Packet(Vec), - Ash(Vec), - EcoNet(Vec), - AtmSvc(Vec), - Rds(Vec), - Sna(Vec), - Irda(Vec), - Pppox(Vec), - WanPipe(Vec), - Llc(Vec), - Can(Vec), - Tipc(Vec), - Bluetooth(Vec), - Iucv(Vec), - RxRpc(Vec), - Isdn(Vec), - Phonet(Vec), - Ieee802154(Vec), - Caif(Vec), - Alg(Vec), - Other(DefaultNla), -} - -impl nla::Nla for AfSpecInet { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::AfSpecInet::*; - match *self { - Unspec(ref bytes) - | Unix(ref bytes) - | Ax25(ref bytes) - | Ipx(ref bytes) - | AppleTalk(ref bytes) - | Netrom(ref bytes) - | Bridge(ref bytes) - | AtmPvc(ref bytes) - | X25(ref bytes) - | Rose(ref bytes) - | DecNet(ref bytes) - | NetbEui(ref bytes) - | Security(ref bytes) - | Key(ref bytes) - | Netlink(ref bytes) - | Packet(ref bytes) - | Ash(ref bytes) - | EcoNet(ref bytes) - | AtmSvc(ref bytes) - | Rds(ref bytes) - | Sna(ref bytes) - | Irda(ref bytes) - | Pppox(ref bytes) - | WanPipe(ref bytes) - | Llc(ref bytes) - | Can(ref bytes) - | Tipc(ref bytes) - | Bluetooth(ref bytes) - | Iucv(ref bytes) - | RxRpc(ref bytes) - | Isdn(ref bytes) - | Phonet(ref bytes) - | Ieee802154(ref bytes) - | Caif(ref bytes) - | Alg(ref bytes) - => bytes.len(), - Inet6(ref nlas) => nlas.as_slice().buffer_len(), - Inet(ref nlas) => nlas.as_slice().buffer_len(), - Other(ref nla) => nla.value_len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::AfSpecInet::*; - match *self { - Unspec(ref bytes) - | Unix(ref bytes) - | Ax25(ref bytes) - | Ipx(ref bytes) - | AppleTalk(ref bytes) - | Netrom(ref bytes) - | Bridge(ref bytes) - | AtmPvc(ref bytes) - | X25(ref bytes) - | Rose(ref bytes) - | DecNet(ref bytes) - | NetbEui(ref bytes) - | Security(ref bytes) - | Key(ref bytes) - | Netlink(ref bytes) - | Packet(ref bytes) - | Ash(ref bytes) - | EcoNet(ref bytes) - | AtmSvc(ref bytes) - | Rds(ref bytes) - | Sna(ref bytes) - | Irda(ref bytes) - | Pppox(ref bytes) - | WanPipe(ref bytes) - | Llc(ref bytes) - | Can(ref bytes) - | Tipc(ref bytes) - | Bluetooth(ref bytes) - | Iucv(ref bytes) - | RxRpc(ref bytes) - | Isdn(ref bytes) - | Phonet(ref bytes) - | Ieee802154(ref bytes) - | Caif(ref bytes) - | Alg(ref bytes) - => buffer[..bytes.len()].copy_from_slice(bytes.as_slice()), - Inet6(ref nlas) => nlas.as_slice().emit(buffer), - Inet(ref nlas) => nlas.as_slice().emit(buffer), - Other(ref nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::AfSpecInet::*; - match *self { - Inet(_) => AF_INET, - Unspec(_) => AF_UNSPEC, - Unix(_) => AF_UNIX, - Ax25(_) => AF_AX25, - Ipx(_) => AF_IPX, - AppleTalk(_) => AF_APPLETALK, - Netrom(_) => AF_NETROM, - Bridge(_) => AF_BRIDGE, - AtmPvc(_) => AF_ATMPVC, - X25(_) => AF_X25, - Inet6(_) => AF_INET6, - Rose(_) => AF_ROSE, - DecNet(_) => AF_DECNET, - NetbEui(_) => AF_NETBEUI, - Security(_) => AF_SECURITY, - Key(_) => AF_KEY, - Netlink(_) => AF_NETLINK, - Packet(_) => AF_PACKET, - Ash(_) => AF_ASH, - EcoNet(_) => AF_ECONET, - AtmSvc(_) => AF_ATMSVC, - Rds(_) => AF_RDS, - Sna(_) => AF_SNA, - Irda(_) => AF_IRDA, - Pppox(_) => AF_PPPOX, - WanPipe(_) => AF_WANPIPE, - Llc(_) => AF_LLC, - Can(_) => AF_CAN, - Tipc(_) => AF_TIPC, - Bluetooth(_) => AF_BLUETOOTH, - Iucv(_) => AF_IUCV, - RxRpc(_) => AF_RXRPC, - Isdn(_) => AF_ISDN, - Phonet(_) => AF_PHONET, - Ieee802154(_) => AF_IEEE802154, - Caif(_) => AF_CAIF, - Alg(_) => AF_ALG, - Other(ref nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for AfSpecInet { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::AfSpecInet::*; - - let payload = buf.value(); - Ok(match buf.kind() { - AF_UNSPEC => Unspec(payload.to_vec()), - AF_INET => { - let mut nlas = vec![]; - for nla in NlasIterator::new(payload) { - let nla = nla.context("invalid AF_INET value")?; - nlas.push( - inet::Inet::parse(&nla) - .context("invalid AF_INET value")?, - ); - } - Inet(nlas) - } - AF_INET6 => { - let mut nlas = vec![]; - for nla in NlasIterator::new(payload) { - let nla = nla.context("invalid AF_INET6 value")?; - nlas.push( - inet6::Inet6::parse(&nla) - .context("invalid AF_INET6 value")?, - ); - } - Inet6(nlas) - } - AF_UNIX => Unix(payload.to_vec()), - AF_AX25 => Ax25(payload.to_vec()), - AF_IPX => Ipx(payload.to_vec()), - AF_APPLETALK => AppleTalk(payload.to_vec()), - AF_NETROM => Netrom(payload.to_vec()), - AF_BRIDGE => Bridge(payload.to_vec()), - AF_ATMPVC => AtmPvc(payload.to_vec()), - AF_X25 => X25(payload.to_vec()), - AF_ROSE => Rose(payload.to_vec()), - AF_DECNET => DecNet(payload.to_vec()), - AF_NETBEUI => NetbEui(payload.to_vec()), - AF_SECURITY => Security(payload.to_vec()), - AF_KEY => Key(payload.to_vec()), - AF_NETLINK => Netlink(payload.to_vec()), - AF_PACKET => Packet(payload.to_vec()), - AF_ASH => Ash(payload.to_vec()), - AF_ECONET => EcoNet(payload.to_vec()), - AF_ATMSVC => AtmSvc(payload.to_vec()), - AF_RDS => Rds(payload.to_vec()), - AF_SNA => Sna(payload.to_vec()), - AF_IRDA => Irda(payload.to_vec()), - AF_PPPOX => Pppox(payload.to_vec()), - AF_WANPIPE => WanPipe(payload.to_vec()), - AF_LLC => Llc(payload.to_vec()), - AF_CAN => Can(payload.to_vec()), - AF_TIPC => Tipc(payload.to_vec()), - AF_BLUETOOTH => Bluetooth(payload.to_vec()), - AF_IUCV => Iucv(payload.to_vec()), - AF_RXRPC => RxRpc(payload.to_vec()), - AF_ISDN => Isdn(payload.to_vec()), - AF_PHONET => Phonet(payload.to_vec()), - AF_IEEE802154 => Ieee802154(payload.to_vec()), - AF_CAIF => Caif(payload.to_vec()), - AF_ALG => Alg(payload.to_vec()), - kind => Other( - DefaultNla::parse(buf) - .context(format!("Unknown NLA type {kind}"))?, - ), - }) - } -} diff --git a/src/rtnl/link/nlas/inet/mod.rs b/src/rtnl/link/nlas/inet/mod.rs deleted file mode 100644 index 1f427b02..00000000 --- a/src/rtnl/link/nlas/inet/mod.rs +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; - -use crate::constants::{IFLA_INET_CONF, IFLA_INET_UNSPEC}; - -use netlink_packet_utils::{ - nla::{DefaultNla, Nla, NlaBuffer}, - traits::Parseable, - DecodeError, -}; - -mod dev_conf; -pub use self::dev_conf::*; - -#[derive(Clone, Eq, PartialEq, Debug)] -#[non_exhaustive] -pub enum Inet { - DevConf(Vec), - Unspec(Vec), - Other(DefaultNla), -} - -impl Nla for Inet { - fn value_len(&self) -> usize { - use self::Inet::*; - match *self { - Unspec(ref bytes) => bytes.len(), - DevConf(_) => DEV_CONF_LEN, - Other(ref nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::Inet::*; - match *self { - Unspec(ref bytes) => { - buffer[..bytes.len()].copy_from_slice(bytes.as_slice()) - } - DevConf(ref dev_conf) => { - buffer[..dev_conf.len()].copy_from_slice(dev_conf.as_slice()) - } - Other(ref nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::Inet::*; - match *self { - Unspec(_) => IFLA_INET_UNSPEC, - DevConf(_) => IFLA_INET_CONF, - Other(ref nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Inet { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::Inet::*; - - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_INET_UNSPEC => Unspec(payload.to_vec()), - IFLA_INET_CONF => DevConf(payload.to_vec()), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} diff --git a/src/rtnl/link/nlas/inet6/mod.rs b/src/rtnl/link/nlas/inet6/mod.rs deleted file mode 100644 index 95d504fa..00000000 --- a/src/rtnl/link/nlas/inet6/mod.rs +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; -use byteorder::{ByteOrder, NativeEndian}; - -use crate::constants::*; - -use netlink_packet_utils::{ - nla::{DefaultNla, Nla, NlaBuffer}, - parsers::{parse_ipv6, parse_u32, parse_u8}, - traits::Parseable, - DecodeError, -}; - -mod cache; -pub use self::cache::*; -mod dev_conf; -pub use self::dev_conf::*; -mod icmp6_stats; -pub use self::icmp6_stats::*; -mod stats; -pub use self::stats::*; - -#[derive(Clone, Eq, PartialEq, Debug)] -#[non_exhaustive] -pub enum Inet6 { - Flags(u32), - CacheInfo(Vec), - DevConf(Vec), - Unspec(Vec), - Stats(Vec), - IcmpStats(Vec), - Token([u8; 16]), - AddrGenMode(u8), - Other(DefaultNla), -} - -impl Nla for Inet6 { - fn value_len(&self) -> usize { - use self::Inet6::*; - match *self { - Unspec(ref bytes) => bytes.len(), - CacheInfo(ref cache_info) => cache_info.len(), - DevConf(ref dev_conf) => dev_conf.len(), - Stats(ref stats) => stats.len(), - IcmpStats(ref icmp_stats) => icmp_stats.len(), - Flags(_) => 4, - Token(_) => 16, - AddrGenMode(_) => 1, - Other(ref nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::Inet6::*; - match *self { - Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), - Flags(ref value) => NativeEndian::write_u32(buffer, *value), - CacheInfo(ref cache_info) => { - buffer.copy_from_slice(cache_info.as_slice()) - } - DevConf(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), - Stats(ref inet6_stats) => { - buffer.copy_from_slice(inet6_stats.as_slice()) - } - IcmpStats(ref icmp6_stats) => { - buffer.copy_from_slice(icmp6_stats.as_slice()) - } - Token(ref ipv6) => buffer.copy_from_slice(&ipv6[..]), - AddrGenMode(value) => buffer[0] = value, - Other(ref nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::Inet6::*; - match *self { - Unspec(_) => IFLA_INET6_UNSPEC, - Flags(_) => IFLA_INET6_FLAGS, - CacheInfo(_) => IFLA_INET6_CACHEINFO, - DevConf(_) => IFLA_INET6_CONF, - Stats(_) => IFLA_INET6_STATS, - IcmpStats(_) => IFLA_INET6_ICMP6STATS, - Token(_) => IFLA_INET6_TOKEN, - AddrGenMode(_) => IFLA_INET6_ADDR_GEN_MODE, - Other(ref nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for Inet6 { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::Inet6::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_INET6_UNSPEC => Unspec(payload.to_vec()), - IFLA_INET6_FLAGS => Flags( - parse_u32(payload).context("invalid IFLA_INET6_FLAGS value")?, - ), - IFLA_INET6_CACHEINFO => CacheInfo(payload.to_vec()), - IFLA_INET6_CONF => DevConf(payload.to_vec()), - IFLA_INET6_STATS => Stats(payload.to_vec()), - IFLA_INET6_ICMP6STATS => IcmpStats(payload.to_vec()), - IFLA_INET6_TOKEN => Token( - parse_ipv6(payload) - .context("invalid IFLA_INET6_TOKEN value")?, - ), - IFLA_INET6_ADDR_GEN_MODE => AddrGenMode( - parse_u8(payload) - .context("invalid IFLA_INET6_ADDR_GEN_MODE value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} diff --git a/src/rtnl/link/nlas/link_infos.rs b/src/rtnl/link/nlas/link_infos.rs deleted file mode 100644 index 5ac85742..00000000 --- a/src/rtnl/link/nlas/link_infos.rs +++ /dev/null @@ -1,2889 +0,0 @@ -// SPDX-License-Identifier: MIT - -use anyhow::Context; -use byteorder::{ByteOrder, NativeEndian}; -use netlink_packet_utils::{ - nla::{DefaultNla, Nla, NlaBuffer, NlasIterator}, - parsers::{ - parse_i32, parse_mac, parse_string, parse_u16, parse_u16_be, parse_u32, - parse_u64, parse_u8, - }, - traits::{Emitable, Parseable}, - DecodeError, -}; - -use super::{bond::InfoBond, bond_port::InfoBondPort, bridge::InfoBridge}; -use crate::{ - constants::*, nlas::link::InfoVxlan, LinkMessage, LinkMessageBuffer, -}; - -const DUMMY: &str = "dummy"; -const IFB: &str = "ifb"; -const BRIDGE: &str = "bridge"; -const TUN: &str = "tun"; -const NLMON: &str = "nlmon"; -const VLAN: &str = "vlan"; -const VETH: &str = "veth"; -const VXLAN: &str = "vxlan"; -const BOND: &str = "bond"; -const IPVLAN: &str = "ipvlan"; -const MACVLAN: &str = "macvlan"; -const MACVTAP: &str = "macvtap"; -const GRETAP: &str = "gretap"; -const IP6GRETAP: &str = "ip6gretap"; -const IPIP: &str = "ipip"; -const SIT: &str = "sit"; -const GRE: &str = "gre"; -const IP6GRE: &str = "ip6gre"; -const VTI: &str = "vti"; -const VRF: &str = "vrf"; -const GTP: &str = "gtp"; -const IPOIB: &str = "ipoib"; -const WIREGUARD: &str = "wireguard"; -const XFRM: &str = "xfrm"; -const MACSEC: &str = "macsec"; -const HSR: &str = "hsr"; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum Info { - Unspec(Vec), - Xstats(Vec), - Kind(InfoKind), - Data(InfoData), - PortKind(InfoPortKind), - PortData(InfoPortData), -} - -impl Nla for Info { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::Info::*; - match self { - Unspec(ref bytes) - | Xstats(ref bytes) - => bytes.len(), - Kind(ref nla) => nla.value_len(), - Data(ref nla) => nla.value_len(), - PortKind(ref nla) => nla.value_len(), - PortData(ref nla) => nla.value_len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::Info::*; - match self { - Unspec(ref bytes) - | Xstats(ref bytes) - => buffer.copy_from_slice(bytes), - Kind(ref nla) => nla.emit_value(buffer), - Data(ref nla) => nla.emit_value(buffer), - PortKind(ref nla) => nla.emit_value(buffer), - PortData(ref nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::Info::*; - match self { - Unspec(_) => IFLA_INFO_UNSPEC, - Xstats(_) => IFLA_INFO_XSTATS, - PortKind(_) => IFLA_INFO_PORT_KIND, - PortData(_) => IFLA_INFO_PORT_DATA, - Kind(_) => IFLA_INFO_KIND, - Data(_) => IFLA_INFO_DATA, - } - } -} - -pub(crate) struct VecInfo(pub(crate) Vec); - -// We cannot `impl Parseable<_> for Info` because some attributes -// depend on each other. To parse IFLA_INFO_DATA we first need to -// parse the preceding IFLA_INFO_KIND for example. -// -// Moreover, with cannot `impl Parseable for Vec` due to the -// orphan rule: `Parseable` and `Vec<_>` are both defined outside of -// this crate. Thus, we create this internal VecInfo struct that wraps -// `Vec` and allows us to circumvent the orphan rule. -// -// The downside is that this impl will not be exposed. -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VecInfo { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - let mut res = Vec::new(); - let nlas = NlasIterator::new(buf.into_inner()); - let mut link_info_kind: Option = None; - let mut link_info_port_kind: Option = None; - for nla in nlas { - let nla = nla?; - match nla.kind() { - IFLA_INFO_UNSPEC => { - res.push(Info::Unspec(nla.value().to_vec())) - } - IFLA_INFO_XSTATS => { - res.push(Info::Xstats(nla.value().to_vec())) - } - IFLA_INFO_PORT_KIND => { - let parsed = InfoPortKind::parse(&nla)?; - res.push(Info::PortKind(parsed.clone())); - link_info_port_kind = Some(parsed); - } - IFLA_INFO_PORT_DATA => { - if let Some(link_info_port_kind) = link_info_port_kind { - let payload = nla.value(); - let info_port_data = match link_info_port_kind { - InfoPortKind::Bond => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_PORT_DATA (IFLA_INFO_PORT_KIND is 'bond')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = InfoBondPort::parse(nla) - .context(err)?; - v.push(parsed); - } - InfoPortData::BondPort(v) - } - InfoPortKind::Other(_) => { - InfoPortData::Other(payload.to_vec()) - } - }; - res.push(Info::PortData(info_port_data)); - } else { - return Err("IFLA_INFO_PORT_DATA is not preceded by an IFLA_INFO_PORT_KIND".into()); - } - link_info_port_kind = None; - } - IFLA_INFO_KIND => { - let parsed = InfoKind::parse(&nla)?; - res.push(Info::Kind(parsed.clone())); - link_info_kind = Some(parsed); - } - IFLA_INFO_DATA => { - if let Some(link_info_kind) = link_info_kind { - let payload = nla.value(); - let info_data = match link_info_kind { - InfoKind::Dummy => { - InfoData::Dummy(payload.to_vec()) - } - InfoKind::Ifb => InfoData::Ifb(payload.to_vec()), - InfoKind::Bridge => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bridge')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoBridge::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Bridge(v) - } - InfoKind::Vlan => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vlan')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoVlan::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Vlan(v) - } - InfoKind::Tun => InfoData::Tun(payload.to_vec()), - InfoKind::Nlmon => { - InfoData::Nlmon(payload.to_vec()) - } - InfoKind::Veth => { - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'veth')"; - let nla_buf = NlaBuffer::new_checked(&payload) - .context(err)?; - let parsed = - VethInfo::parse(&nla_buf).context(err)?; - InfoData::Veth(parsed) - } - InfoKind::Vxlan => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vxlan')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoVxlan::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Vxlan(v) - } - InfoKind::Bond => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'bond')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoBond::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Bond(v) - } - InfoKind::IpVlan => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipvlan')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoIpVlan::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::IpVlan(v) - } - InfoKind::MacVlan => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvlan')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoMacVlan::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::MacVlan(v) - } - InfoKind::MacVtap => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macvtap')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoMacVtap::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::MacVtap(v) - } - InfoKind::GreTap => { - InfoData::GreTap(payload.to_vec()) - } - InfoKind::GreTap6 => { - InfoData::GreTap6(payload.to_vec()) - } - InfoKind::IpTun => { - InfoData::IpTun(payload.to_vec()) - } - InfoKind::SitTun => { - InfoData::SitTun(payload.to_vec()) - } - InfoKind::GreTun => { - InfoData::GreTun(payload.to_vec()) - } - InfoKind::GreTun6 => { - InfoData::GreTun6(payload.to_vec()) - } - InfoKind::Vti => InfoData::Vti(payload.to_vec()), - InfoKind::Vrf => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'vrf')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoVrf::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Vrf(v) - } - InfoKind::Gtp => InfoData::Gtp(payload.to_vec()), - InfoKind::Ipoib => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'ipoib')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoIpoib::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Ipoib(v) - } - InfoKind::Wireguard => { - InfoData::Wireguard(payload.to_vec()) - } - InfoKind::Other(_) => { - InfoData::Other(payload.to_vec()) - } - InfoKind::Xfrm => { - let mut v = Vec::new(); - let err = - "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'Xfrm')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoXfrmTun::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Xfrm(v) - } - InfoKind::MacSec => { - let mut v = Vec::new(); - let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'macsec')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoMacSec::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::MacSec(v) - } - InfoKind::Hsr => { - let mut v = Vec::new(); - let err = "failed to parse IFLA_INFO_DATA (IFLA_INFO_KIND is 'hsr')"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = - InfoHsr::parse(nla).context(err)?; - v.push(parsed); - } - InfoData::Hsr(v) - } - }; - res.push(Info::Data(info_data)); - } else { - return Err("IFLA_INFO_DATA is not preceded by an IFLA_INFO_KIND".into()); - } - link_info_kind = None; - } - _ => { - return Err( - format!("unknown NLA type {}", nla.kind()).into() - ) - } - } - } - Ok(VecInfo(res)) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoData { - Bridge(Vec), - Tun(Vec), - Nlmon(Vec), - Vlan(Vec), - Dummy(Vec), - Ifb(Vec), - Veth(VethInfo), - Vxlan(Vec), - Bond(Vec), - IpVlan(Vec), - MacVlan(Vec), - MacVtap(Vec), - GreTap(Vec), - GreTap6(Vec), - IpTun(Vec), - SitTun(Vec), - GreTun(Vec), - GreTun6(Vec), - Vti(Vec), - Vrf(Vec), - Gtp(Vec), - Ipoib(Vec), - Wireguard(Vec), - Xfrm(Vec), - MacSec(Vec), - Hsr(Vec), - Other(Vec), -} - -impl Nla for InfoData { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::InfoData::*; - match self { - Bond(ref nlas) => nlas.as_slice().buffer_len(), - Bridge(ref nlas) => nlas.as_slice().buffer_len(), - Vlan(ref nlas) => nlas.as_slice().buffer_len(), - Veth(ref msg) => msg.buffer_len(), - IpVlan(ref nlas) => nlas.as_slice().buffer_len(), - Ipoib(ref nlas) => nlas.as_slice().buffer_len(), - MacVlan(ref nlas) => nlas.as_slice().buffer_len(), - MacVtap(ref nlas) => nlas.as_slice().buffer_len(), - Vrf(ref nlas) => nlas.as_slice().buffer_len(), - Vxlan(ref nlas) => nlas.as_slice().buffer_len(), - Xfrm(ref nlas) => nlas.as_slice().buffer_len(), - MacSec(ref nlas) => nlas.as_slice().buffer_len(), - Hsr(ref nlas) => nlas.as_slice().buffer_len(), - Dummy(ref bytes) - | Tun(ref bytes) - | Nlmon(ref bytes) - | Ifb(ref bytes) - | GreTap(ref bytes) - | GreTap6(ref bytes) - | IpTun(ref bytes) - | SitTun(ref bytes) - | GreTun(ref bytes) - | GreTun6(ref bytes) - | Vti(ref bytes) - | Gtp(ref bytes) - | Wireguard(ref bytes) - | Other(ref bytes) - => bytes.len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoData::*; - match self { - Bond(ref nlas) => nlas.as_slice().emit(buffer), - Bridge(ref nlas) => nlas.as_slice().emit(buffer), - Vlan(ref nlas) => nlas.as_slice().emit(buffer), - Veth(ref msg) => msg.emit(buffer), - IpVlan(ref nlas) => nlas.as_slice().emit(buffer), - Ipoib(ref nlas) => nlas.as_slice().emit(buffer), - MacVlan(ref nlas) => nlas.as_slice().emit(buffer), - MacVtap(ref nlas) => nlas.as_slice().emit(buffer), - Vrf(ref nlas) => nlas.as_slice().emit(buffer), - Vxlan(ref nlas) => nlas.as_slice().emit(buffer), - Xfrm(ref nlas) => nlas.as_slice().emit(buffer), - MacSec(ref nlas) => nlas.as_slice().emit(buffer), - Hsr(ref nlas) => nlas.as_slice().emit(buffer), - Dummy(ref bytes) - | Tun(ref bytes) - | Nlmon(ref bytes) - | Ifb(ref bytes) - | GreTap(ref bytes) - | GreTap6(ref bytes) - | IpTun(ref bytes) - | SitTun(ref bytes) - | GreTun(ref bytes) - | GreTun6(ref bytes) - | Vti(ref bytes) - | Gtp(ref bytes) - | Wireguard(ref bytes) - | Other(ref bytes) - => buffer.copy_from_slice(bytes), - } - } - - fn kind(&self) -> u16 { - IFLA_INFO_DATA - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoPortData { - BondPort(Vec), - Other(Vec), -} - -impl Nla for InfoPortData { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::InfoPortData::*; - match self { - BondPort(ref nlas) => nlas.as_slice().buffer_len(), - Other(ref bytes) => bytes.len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoPortData::*; - match self { - BondPort(ref nlas) => nlas.as_slice().emit(buffer), - Other(ref bytes) => buffer.copy_from_slice(bytes), - } - } - - fn kind(&self) -> u16 { - IFLA_INFO_PORT_DATA - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoKind { - Dummy, - Ifb, - Bridge, - Tun, - Nlmon, - Vlan, - Veth, - Vxlan, - Bond, - IpVlan, - MacVlan, - MacVtap, - GreTap, - GreTap6, - IpTun, - SitTun, - GreTun, - GreTun6, - Vti, - Vrf, - Gtp, - Ipoib, - Wireguard, - Xfrm, - MacSec, - Hsr, - Other(String), -} - -impl Nla for InfoKind { - fn value_len(&self) -> usize { - use self::InfoKind::*; - let len = match *self { - Dummy => DUMMY.len(), - Ifb => IFB.len(), - Bridge => BRIDGE.len(), - Tun => TUN.len(), - Nlmon => NLMON.len(), - Vlan => VLAN.len(), - Veth => VETH.len(), - Vxlan => VXLAN.len(), - Bond => BOND.len(), - IpVlan => IPVLAN.len(), - MacVlan => MACVLAN.len(), - MacVtap => MACVTAP.len(), - GreTap => GRETAP.len(), - GreTap6 => IP6GRETAP.len(), - IpTun => IPIP.len(), - SitTun => SIT.len(), - GreTun => GRE.len(), - GreTun6 => IP6GRE.len(), - Vti => VTI.len(), - Vrf => VRF.len(), - Gtp => GTP.len(), - Ipoib => IPOIB.len(), - Wireguard => WIREGUARD.len(), - Xfrm => XFRM.len(), - MacSec => MACSEC.len(), - Hsr => HSR.len(), - Other(ref s) => s.len(), - }; - len + 1 - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoKind::*; - let s = match *self { - Dummy => DUMMY, - Ifb => IFB, - Bridge => BRIDGE, - Tun => TUN, - Nlmon => NLMON, - Vlan => VLAN, - Veth => VETH, - Vxlan => VXLAN, - Bond => BOND, - IpVlan => IPVLAN, - MacVlan => MACVLAN, - MacVtap => MACVTAP, - GreTap => GRETAP, - GreTap6 => IP6GRETAP, - IpTun => IPIP, - SitTun => SIT, - GreTun => GRE, - GreTun6 => IP6GRE, - Vti => VTI, - Vrf => VRF, - Gtp => GTP, - Ipoib => IPOIB, - Wireguard => WIREGUARD, - Xfrm => XFRM, - MacSec => MACSEC, - Hsr => HSR, - Other(ref s) => s.as_str(), - }; - buffer[..s.len()].copy_from_slice(s.as_bytes()); - buffer[s.len()] = 0; - } - - fn kind(&self) -> u16 { - IFLA_INFO_KIND - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoKind { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoKind::*; - if buf.kind() != IFLA_INFO_KIND { - return Err(format!( - "failed to parse IFLA_INFO_KIND: NLA type is {}", - buf.kind() - ) - .into()); - } - let s = parse_string(buf.value()) - .context("invalid IFLA_INFO_KIND value")?; - Ok(match s.as_str() { - DUMMY => Dummy, - IFB => Ifb, - BRIDGE => Bridge, - TUN => Tun, - NLMON => Nlmon, - VLAN => Vlan, - VETH => Veth, - VXLAN => Vxlan, - BOND => Bond, - IPVLAN => IpVlan, - MACVLAN => MacVlan, - MACVTAP => MacVtap, - GRETAP => GreTap, - IP6GRETAP => GreTap6, - IPIP => IpTun, - SIT => SitTun, - GRE => GreTun, - IP6GRE => GreTun6, - VTI => Vti, - VRF => Vrf, - GTP => Gtp, - IPOIB => Ipoib, - WIREGUARD => Wireguard, - MACSEC => MacSec, - XFRM => Xfrm, - HSR => Hsr, - _ => Other(s), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoPortKind { - Bond, - Other(String), -} - -impl Nla for InfoPortKind { - fn value_len(&self) -> usize { - use self::InfoPortKind::*; - let len = match *self { - Bond => BOND.len(), - Other(ref s) => s.len(), - }; - len + 1 - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoPortKind::*; - let s = match *self { - Bond => BOND, - Other(ref s) => s.as_str(), - }; - buffer[..s.len()].copy_from_slice(s.as_bytes()); - buffer[s.len()] = 0; - } - - fn kind(&self) -> u16 { - IFLA_INFO_PORT_KIND - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoPortKind { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoPortKind::*; - if buf.kind() != IFLA_INFO_PORT_KIND { - return Err(format!( - "failed to parse IFLA_INFO_PORT_KIND: NLA type is {}", - buf.kind() - ) - .into()); - } - let s = parse_string(buf.value()) - .context("invalid IFLA_INFO_PORT_KIND value")?; - Ok(match s.as_str() { - BOND => Bond, - _ => Other(s), - }) - } -} - -// https://elixir.bootlin.com/linux/latest/source/net/8021q/vlan_netlink.c#L21 -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoVlan { - Unspec(Vec), - Id(u16), - Flags((u32, u32)), - EgressQos(Vec), - IngressQos(Vec), - Protocol(u16), -} - -impl Nla for InfoVlan { - #[rustfmt::skip] - fn value_len(&self) -> usize { - use self::InfoVlan::*; - match self { - Id(_) | Protocol(_) => 2, - Flags(_) => 8, - Unspec(bytes) => bytes.len(), - EgressQos(mappings) - | IngressQos(mappings) - => mappings.as_slice().buffer_len(), - } - } - - #[rustfmt::skip] - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoVlan::*; - match self { - Unspec(ref bytes) - => buffer.copy_from_slice(bytes), - EgressQos(ref mappings) - | IngressQos(ref mappings) => mappings.as_slice().emit(buffer), - Id(ref value) - | Protocol(ref value) - => NativeEndian::write_u16(buffer, *value), - - Flags(ref flags) => { - NativeEndian::write_u32(buffer, flags.0); - NativeEndian::write_u32(buffer, flags.1) - } - } - } - - fn kind(&self) -> u16 { - use self::InfoVlan::*; - match self { - Unspec(_) => IFLA_VLAN_UNSPEC, - Id(_) => IFLA_VLAN_ID, - Flags(_) => IFLA_VLAN_FLAGS, - EgressQos(_) => IFLA_VLAN_EGRESS_QOS, - IngressQos(_) => IFLA_VLAN_INGRESS_QOS, - Protocol(_) => IFLA_VLAN_PROTOCOL, - } - } -} - -fn parse_mappings(payload: &[u8]) -> Result, DecodeError> { - let mut mappings = Vec::new(); - for nla in NlasIterator::new(payload) { - let nla = nla?; - let parsed = VlanQosMapping::parse(&nla)?; - mappings.push(parsed); - } - Ok(mappings) -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVlan { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoVlan::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_VLAN_UNSPEC => Unspec(payload.to_vec()), - IFLA_VLAN_ID => { - Id(parse_u16(payload).context("invalid IFLA_VLAN_ID value")?) - } - IFLA_VLAN_FLAGS => { - let err = "invalid IFLA_VLAN_FLAGS value"; - if payload.len() != 8 { - return Err(err.into()); - } - let flags = parse_u32(&payload[0..4]).context(err)?; - let mask = parse_u32(&payload[4..]).context(err)?; - Flags((flags, mask)) - } - IFLA_VLAN_EGRESS_QOS => EgressQos( - parse_mappings(payload) - .context("failed to parse IFLA_VLAN_EGRESS_QOS")?, - ), - IFLA_VLAN_INGRESS_QOS => IngressQos( - parse_mappings(payload) - .context("failed to parse IFLA_VLAN_INGRESS_QOS")?, - ), - IFLA_VLAN_PROTOCOL => Protocol( - parse_u16_be(payload) - .context("invalid IFLA_VLAN_PROTOCOL value")?, - ), - _ => return Err(format!("unknown NLA type {}", buf.kind()).into()), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoIpoib { - Unspec(Vec), - Pkey(u16), - Mode(u16), - UmCast(u16), - Other(DefaultNla), -} - -impl Nla for InfoIpoib { - fn value_len(&self) -> usize { - use self::InfoIpoib::*; - match self { - Unspec(bytes) => bytes.len(), - Pkey(_) | Mode(_) | UmCast(_) => 2, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoIpoib::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Pkey(value) => NativeEndian::write_u16(buffer, *value), - Mode(value) => NativeEndian::write_u16(buffer, *value), - UmCast(value) => NativeEndian::write_u16(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoIpoib::*; - match self { - Unspec(_) => IFLA_IPOIB_UNSPEC, - Pkey(_) => IFLA_IPOIB_PKEY, - Mode(_) => IFLA_IPOIB_MODE, - UmCast(_) => IFLA_IPOIB_UMCAST, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpoib { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoIpoib::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_IPOIB_UNSPEC => Unspec(payload.to_vec()), - IFLA_IPOIB_PKEY => Pkey( - parse_u16(payload).context("invalid IFLA_IPOIB_PKEY value")?, - ), - IFLA_IPOIB_MODE => Mode( - parse_u16(payload).context("invalid IFLA_IPOIB_MODE value")?, - ), - IFLA_IPOIB_UMCAST => UmCast( - parse_u16(payload) - .context("invalid IFLA_IPOIB_UMCAST value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum VethInfo { - Unspec(Vec), - Peer(LinkMessage), - Other(DefaultNla), -} - -impl Nla for VethInfo { - fn value_len(&self) -> usize { - use self::VethInfo::*; - match *self { - Unspec(ref bytes) => bytes.len(), - Peer(ref message) => message.buffer_len(), - Other(ref attr) => attr.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::VethInfo::*; - match *self { - Unspec(ref bytes) => buffer.copy_from_slice(bytes.as_slice()), - Peer(ref message) => message.emit(buffer), - Other(ref attr) => attr.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::VethInfo::*; - match *self { - Unspec(_) => VETH_INFO_UNSPEC, - Peer(_) => VETH_INFO_PEER, - Other(ref attr) => attr.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for VethInfo { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::VethInfo::*; - let payload = buf.value(); - Ok(match buf.kind() { - VETH_INFO_UNSPEC => Unspec(payload.to_vec()), - VETH_INFO_PEER => { - let err = "failed to parse veth link info"; - let buffer = - LinkMessageBuffer::new_checked(&payload).context(err)?; - Peer(LinkMessage::parse(&buffer).context(err)?) - } - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoIpVlan { - Unspec(Vec), - Mode(u16), - Flags(u16), - Other(DefaultNla), -} - -impl Nla for InfoIpVlan { - fn value_len(&self) -> usize { - use self::InfoIpVlan::*; - match self { - Unspec(bytes) => bytes.len(), - Mode(_) | Flags(_) => 2, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoIpVlan::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Mode(value) => NativeEndian::write_u16(buffer, *value), - Flags(value) => NativeEndian::write_u16(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoIpVlan::*; - match self { - Unspec(_) => IFLA_IPVLAN_UNSPEC, - Mode(_) => IFLA_IPVLAN_MODE, - Flags(_) => IFLA_IPVLAN_FLAGS, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoIpVlan { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoIpVlan::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_IPVLAN_UNSPEC => Unspec(payload.to_vec()), - IFLA_IPVLAN_MODE => Mode( - parse_u16(payload).context("invalid IFLA_IPVLAN_MODE value")?, - ), - IFLA_IPVLAN_FLAGS => Flags( - parse_u16(payload) - .context("invalid IFLA_IPVLAN_FLAGS value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[non_exhaustive] -pub enum MacSecCipherId { - #[deprecated] - DefaultGcmAes128, - GcmAes128, - GcmAes256, - GcmAesXpn128, - GcmAesXpn256, - Other(u64), -} - -impl From for MacSecCipherId { - fn from(d: u64) -> Self { - match d { - #[allow(deprecated)] - MACSEC_DEFAULT_CIPHER_ID => Self::DefaultGcmAes128, - MACSEC_CIPHER_ID_GCM_AES_128 => Self::GcmAes128, - MACSEC_CIPHER_ID_GCM_AES_256 => Self::GcmAes256, - MACSEC_CIPHER_ID_GCM_AES_XPN_128 => Self::GcmAesXpn128, - MACSEC_CIPHER_ID_GCM_AES_XPN_256 => Self::GcmAesXpn256, - _ => Self::Other(d), - } - } -} - -impl From for u64 { - fn from(d: MacSecCipherId) -> Self { - match d { - #[allow(deprecated)] - MacSecCipherId::DefaultGcmAes128 => MACSEC_DEFAULT_CIPHER_ID, - MacSecCipherId::GcmAes128 => MACSEC_CIPHER_ID_GCM_AES_128, - MacSecCipherId::GcmAes256 => MACSEC_CIPHER_ID_GCM_AES_256, - MacSecCipherId::GcmAesXpn128 => MACSEC_CIPHER_ID_GCM_AES_XPN_128, - MacSecCipherId::GcmAesXpn256 => MACSEC_CIPHER_ID_GCM_AES_XPN_256, - MacSecCipherId::Other(value) => value, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[non_exhaustive] -pub enum MacSecValidation { - Disabled, - Check, - Strict, - Other(u8), -} - -impl From for MacSecValidation { - fn from(d: u8) -> Self { - match d { - MACSEC_VALIDATE_DISABLED => Self::Disabled, - MACSEC_VALIDATE_CHECK => Self::Check, - MACSEC_VALIDATE_STRICT => Self::Strict, - _ => Self::Other(d), - } - } -} - -impl From for u8 { - fn from(d: MacSecValidation) -> Self { - match d { - MacSecValidation::Disabled => MACSEC_VALIDATE_DISABLED, - MacSecValidation::Check => MACSEC_VALIDATE_CHECK, - MacSecValidation::Strict => MACSEC_VALIDATE_STRICT, - MacSecValidation::Other(value) => value, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[non_exhaustive] -pub enum MacSecOffload { - Off, - Phy, - Mac, - Other(u8), -} - -impl From for MacSecOffload { - fn from(d: u8) -> Self { - match d { - MACSEC_OFFLOAD_OFF => Self::Off, - MACSEC_OFFLOAD_PHY => Self::Phy, - MACSEC_OFFLOAD_MAC => Self::Mac, - _ => Self::Other(d), - } - } -} - -impl From for u8 { - fn from(d: MacSecOffload) -> Self { - match d { - MacSecOffload::Off => MACSEC_OFFLOAD_OFF, - MacSecOffload::Phy => MACSEC_OFFLOAD_PHY, - MacSecOffload::Mac => MACSEC_OFFLOAD_MAC, - MacSecOffload::Other(value) => value, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoMacSec { - Unspec(Vec), - Sci(u64), - Port(u16), - IcvLen(u8), - CipherSuite(MacSecCipherId), - Window(u32), - EncodingSa(u8), - Encrypt(u8), - Protect(u8), - IncSci(u8), - Es(u8), - Scb(u8), - ReplayProtect(u8), - Validation(MacSecValidation), - Offload(MacSecOffload), - Other(DefaultNla), -} - -impl Nla for InfoMacSec { - fn value_len(&self) -> usize { - use self::InfoMacSec::*; - match self { - Unspec(bytes) => bytes.len(), - Sci(_) | CipherSuite(_) => 8, - Window(_) => 4, - Port(_) => 2, - IcvLen(_) | EncodingSa(_) | Encrypt(_) | Protect(_) | IncSci(_) - | Es(_) | Scb(_) | ReplayProtect(_) | Validation(_) - | Offload(_) => 1, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoMacSec::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Sci(value) => NativeEndian::write_u64(buffer, *value), - CipherSuite(value) => { - NativeEndian::write_u64(buffer, (*value).into()) - } - Window(value) => NativeEndian::write_u32(buffer, *value), - Port(value) => NativeEndian::write_u16(buffer, *value), - IcvLen(value) | EncodingSa(value) | Encrypt(value) - | Protect(value) | IncSci(value) | Es(value) | Scb(value) - | ReplayProtect(value) => buffer[0] = *value, - Offload(value) => buffer[0] = (*value).into(), - Validation(value) => buffer[0] = (*value).into(), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoMacSec::*; - match self { - Unspec(_) => IFLA_MACSEC_UNSPEC, - Sci(_) => IFLA_MACSEC_SCI, - Port(_) => IFLA_MACSEC_PORT, - IcvLen(_) => IFLA_MACSEC_ICV_LEN, - CipherSuite(_) => IFLA_MACSEC_CIPHER_SUITE, - Window(_) => IFLA_MACSEC_WINDOW, - EncodingSa(_) => IFLA_MACSEC_ENCODING_SA, - Encrypt(_) => IFLA_MACSEC_ENCRYPT, - Protect(_) => IFLA_MACSEC_PROTECT, - IncSci(_) => IFLA_MACSEC_INC_SCI, - Es(_) => IFLA_MACSEC_ES, - Scb(_) => IFLA_MACSEC_SCB, - ReplayProtect(_) => IFLA_MACSEC_REPLAY_PROTECT, - Validation(_) => IFLA_MACSEC_VALIDATION, - Offload(_) => IFLA_MACSEC_OFFLOAD, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacSec { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoMacSec::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_MACSEC_UNSPEC => Unspec(payload.to_vec()), - IFLA_MACSEC_SCI => { - Sci(parse_u64(payload) - .context("invalid IFLA_MACSEC_SCI value")?) - } - IFLA_MACSEC_PORT => Port( - parse_u16(payload).context("invalid IFLA_MACSEC_PORT value")?, - ), - IFLA_MACSEC_ICV_LEN => IcvLen( - parse_u8(payload) - .context("invalid IFLA_MACSEC_ICV_LEN value")?, - ), - IFLA_MACSEC_CIPHER_SUITE => CipherSuite( - parse_u64(payload) - .context("invalid IFLA_MACSEC_CIPHER_SUITE value")? - .into(), - ), - IFLA_MACSEC_WINDOW => Window( - parse_u32(payload) - .context("invalid IFLA_MACSEC_WINDOW value")?, - ), - IFLA_MACSEC_ENCODING_SA => EncodingSa( - parse_u8(payload) - .context("invalid IFLA_MACSEC_ENCODING_SA value")?, - ), - IFLA_MACSEC_ENCRYPT => Encrypt( - parse_u8(payload) - .context("invalid IFLA_MACSEC_ENCRYPT value")?, - ), - IFLA_MACSEC_PROTECT => Protect( - parse_u8(payload) - .context("invalid IFLA_MACSEC_PROTECT value")?, - ), - IFLA_MACSEC_INC_SCI => IncSci( - parse_u8(payload) - .context("invalid IFLA_MACSEC_INC_SCI value")?, - ), - IFLA_MACSEC_ES => { - Es(parse_u8(payload).context("invalid IFLA_MACSEC_ES value")?) - } - IFLA_MACSEC_SCB => { - Scb(parse_u8(payload) - .context("invalid IFLA_MACSEC_SCB value")?) - } - IFLA_MACSEC_REPLAY_PROTECT => ReplayProtect( - parse_u8(payload) - .context("invalid IFLA_MACSEC_REPLAY_PROTECT value")?, - ), - IFLA_MACSEC_VALIDATION => Validation( - parse_u8(payload) - .context("invalid IFLA_MACSEC_VALIDATION value")? - .into(), - ), - IFLA_MACSEC_OFFLOAD => Offload( - parse_u8(payload) - .context("invalid IFLA_MACSEC_OFFLOAD value")? - .into(), - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[non_exhaustive] -pub enum HsrProtocol { - Hsr, - Prp, - Other(u8), -} - -impl From for HsrProtocol { - fn from(d: u8) -> Self { - match d { - HSR_PROTOCOL_HSR => Self::Hsr, - HSR_PROTOCOL_PRP => Self::Prp, - _ => Self::Other(d), - } - } -} - -impl From for u8 { - fn from(d: HsrProtocol) -> Self { - match d { - HsrProtocol::Hsr => HSR_PROTOCOL_HSR, - HsrProtocol::Prp => HSR_PROTOCOL_PRP, - HsrProtocol::Other(value) => value, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoHsr { - Unspec(Vec), - Port1(u32), - Port2(u32), - MulticastSpec(u8), - SupervisionAddr([u8; 6]), - Version(u8), - SeqNr(u16), - Protocol(HsrProtocol), - Other(DefaultNla), -} - -impl Nla for InfoHsr { - fn value_len(&self) -> usize { - use self::InfoHsr::*; - match self { - Unspec(bytes) => bytes.len(), - SupervisionAddr(_) => 6, - Port1(_) | Port2(_) => 4, - SeqNr(_) => 2, - MulticastSpec(_) | Version(_) | Protocol(_) => 1, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoHsr::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Port1(value) | Port2(value) => { - NativeEndian::write_u32(buffer, *value) - } - MulticastSpec(value) | Version(value) => buffer[0] = *value, - SeqNr(value) => NativeEndian::write_u16(buffer, *value), - Protocol(value) => buffer[0] = (*value).into(), - SupervisionAddr(ref value) => buffer.copy_from_slice(&value[..]), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoHsr::*; - match self { - Unspec(_) => IFLA_HSR_UNSPEC, - Port1(_) => IFLA_HSR_SLAVE1, - Port2(_) => IFLA_HSR_SLAVE2, - MulticastSpec(_) => IFLA_HSR_MULTICAST_SPEC, - SupervisionAddr(_) => IFLA_HSR_SUPERVISION_ADDR, - SeqNr(_) => IFLA_HSR_SEQ_NR, - Version(_) => IFLA_HSR_VERSION, - Protocol(_) => IFLA_HSR_PROTOCOL, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoHsr { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoHsr::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_HSR_UNSPEC => Unspec(payload.to_vec()), - IFLA_HSR_SLAVE1 => Port1( - parse_u32(payload).context("invalid IFLA_HSR_SLAVE1 value")?, - ), - IFLA_HSR_SLAVE2 => Port2( - parse_u32(payload).context("invalid IFLA_HSR_SLAVE2 value")?, - ), - IFLA_HSR_MULTICAST_SPEC => MulticastSpec( - parse_u8(payload) - .context("invalid IFLA_HSR_MULTICAST_SPEC value")?, - ), - IFLA_HSR_SUPERVISION_ADDR => SupervisionAddr( - parse_mac(payload) - .context("invalid IFLA_HSR_SUPERVISION_ADDR value")?, - ), - IFLA_HSR_SEQ_NR => SeqNr( - parse_u16(payload).context("invalid IFLA_HSR_SEQ_NR value")?, - ), - IFLA_HSR_VERSION => Version( - parse_u8(payload).context("invalid IFLA_HSR_VERSION value")?, - ), - IFLA_HSR_PROTOCOL => Protocol( - parse_u8(payload) - .context("invalid IFLA_HSR_PROTOCOL value")? - .into(), - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoXfrmTun { - Unspec(Vec), - Link(u32), - IfId(u32), - Other(DefaultNla), -} - -impl Nla for InfoXfrmTun { - fn value_len(&self) -> usize { - use self::InfoXfrmTun::*; - match self { - Unspec(bytes) => bytes.len(), - Link(_) => 4, - IfId(_) => 4, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoXfrmTun::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Link(value) => NativeEndian::write_u32(buffer, *value), - IfId(value) => NativeEndian::write_u32(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoXfrmTun::*; - match self { - Unspec(_) => IFLA_XFRM_UNSPEC, - Link(_) => IFLA_XFRM_LINK, - IfId(_) => IFLA_XFRM_IF_ID, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoXfrmTun { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoXfrmTun::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_XFRM_UNSPEC => Unspec(payload.to_vec()), - IFLA_XFRM_LINK => Link( - parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, - ), - IFLA_XFRM_IF_ID => IfId( - parse_u32(payload).context("invalid IFLA_XFRM_IF_ID value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoVrf { - TableId(u32), - Other(DefaultNla), -} - -impl Nla for InfoVrf { - fn value_len(&self) -> usize { - use self::InfoVrf::*; - match self { - TableId(_) => 4, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoVrf::*; - match self { - TableId(value) => NativeEndian::write_u32(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoVrf::*; - match self { - TableId(_) => IFLA_VRF_TABLE, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoVrf { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoVrf::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_VRF_TABLE => TableId( - parse_u32(payload).context("invalid IFLA_VRF_TABLE value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoMacVlan { - Unspec(Vec), - Mode(u32), - Flags(u16), - MacAddrMode(u32), - MacAddr([u8; 6]), - MacAddrData(Vec), - MacAddrCount(u32), - BcQueueLen(u32), - BcQueueLenUsed(u32), - BcCutoff(i32), - Other(DefaultNla), -} - -impl Nla for InfoMacVlan { - fn value_len(&self) -> usize { - use self::InfoMacVlan::*; - match self { - Unspec(bytes) => bytes.len(), - Mode(_) => 4, - Flags(_) => 2, - MacAddrMode(_) => 4, - MacAddr(_) => 6, - MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), - MacAddrCount(_) => 4, - BcQueueLen(_) => 4, - BcQueueLenUsed(_) => 4, - BcCutoff(_) => 4, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoMacVlan::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Mode(value) => NativeEndian::write_u32(buffer, *value), - Flags(value) => NativeEndian::write_u16(buffer, *value), - MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), - MacAddr(bytes) => buffer.copy_from_slice(bytes), - MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), - MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), - BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), - BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), - BcCutoff(value) => NativeEndian::write_i32(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoMacVlan::*; - match self { - Unspec(_) => IFLA_MACVLAN_UNSPEC, - Mode(_) => IFLA_MACVLAN_MODE, - Flags(_) => IFLA_MACVLAN_FLAGS, - MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, - MacAddr(_) => IFLA_MACVLAN_MACADDR, - MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, - MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, - BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, - BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, - BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVlan { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoMacVlan::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_MACVLAN_UNSPEC => Unspec(payload.to_vec()), - IFLA_MACVLAN_MODE => Mode( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MODE value")?, - ), - IFLA_MACVLAN_FLAGS => Flags( - parse_u16(payload) - .context("invalid IFLA_MACVLAN_FLAGS value")?, - ), - IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, - ), - IFLA_MACVLAN_MACADDR => MacAddr( - parse_mac(payload) - .context("invalid IFLA_MACVLAN_MACADDR value")?, - ), - IFLA_MACVLAN_MACADDR_DATA => { - let mut mac_data = Vec::new(); - let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = InfoMacVlan::parse(nla).context(err)?; - mac_data.push(parsed); - } - MacAddrData(mac_data) - } - IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, - ), - IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, - ), - IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, - ), - IFLA_MACVLAN_BC_CUTOFF => BcCutoff( - parse_i32(payload) - .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoMacVtap { - Unspec(Vec), - Mode(u32), - Flags(u16), - MacAddrMode(u32), - MacAddr([u8; 6]), - MacAddrData(Vec), - MacAddrCount(u32), - BcQueueLen(u32), - BcQueueLenUsed(u32), - BcCutoff(i32), - Other(DefaultNla), -} - -impl Nla for InfoMacVtap { - fn value_len(&self) -> usize { - use self::InfoMacVtap::*; - match self { - Unspec(bytes) => bytes.len(), - Mode(_) => 4, - Flags(_) => 2, - MacAddrMode(_) => 4, - MacAddr(_) => 6, - MacAddrData(ref nlas) => nlas.as_slice().buffer_len(), - MacAddrCount(_) => 4, - BcQueueLen(_) => 4, - BcQueueLenUsed(_) => 4, - BcCutoff(_) => 4, - Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use self::InfoMacVtap::*; - match self { - Unspec(bytes) => buffer.copy_from_slice(bytes.as_slice()), - Mode(value) => NativeEndian::write_u32(buffer, *value), - Flags(value) => NativeEndian::write_u16(buffer, *value), - MacAddrMode(value) => NativeEndian::write_u32(buffer, *value), - MacAddr(bytes) => buffer.copy_from_slice(bytes), - MacAddrData(ref nlas) => nlas.as_slice().emit(buffer), - MacAddrCount(value) => NativeEndian::write_u32(buffer, *value), - BcQueueLen(value) => NativeEndian::write_u32(buffer, *value), - BcQueueLenUsed(value) => NativeEndian::write_u32(buffer, *value), - BcCutoff(value) => NativeEndian::write_i32(buffer, *value), - Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - use self::InfoMacVtap::*; - match self { - Unspec(_) => IFLA_MACVLAN_UNSPEC, - Mode(_) => IFLA_MACVLAN_MODE, - Flags(_) => IFLA_MACVLAN_FLAGS, - MacAddrMode(_) => IFLA_MACVLAN_MACADDR_MODE, - MacAddr(_) => IFLA_MACVLAN_MACADDR, - MacAddrData(_) => IFLA_MACVLAN_MACADDR_DATA, - MacAddrCount(_) => IFLA_MACVLAN_MACADDR_COUNT, - BcQueueLen(_) => IFLA_MACVLAN_BC_QUEUE_LEN, - BcQueueLenUsed(_) => IFLA_MACVLAN_BC_QUEUE_LEN_USED, - BcCutoff(_) => IFLA_MACVLAN_BC_CUTOFF, - Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoMacVtap { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use self::InfoMacVtap::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_MACVLAN_UNSPEC => Unspec(payload.to_vec()), - IFLA_MACVLAN_MODE => Mode( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MODE value")?, - ), - IFLA_MACVLAN_FLAGS => Flags( - parse_u16(payload) - .context("invalid IFLA_MACVLAN_FLAGS value")?, - ), - IFLA_MACVLAN_MACADDR_MODE => MacAddrMode( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MACADDR_MODE value")?, - ), - IFLA_MACVLAN_MACADDR => MacAddr( - parse_mac(payload) - .context("invalid IFLA_MACVLAN_MACADDR value")?, - ), - IFLA_MACVLAN_MACADDR_DATA => { - let mut mac_data = Vec::new(); - let err = "failed to parse IFLA_MACVLAN_MACADDR_DATA"; - for nla in NlasIterator::new(payload) { - let nla = &nla.context(err)?; - let parsed = InfoMacVtap::parse(nla).context(err)?; - mac_data.push(parsed); - } - MacAddrData(mac_data) - } - IFLA_MACVLAN_MACADDR_COUNT => MacAddrCount( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_MACADDR_COUNT value")?, - ), - IFLA_MACVLAN_BC_QUEUE_LEN => BcQueueLen( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN value")?, - ), - IFLA_MACVLAN_BC_QUEUE_LEN_USED => BcQueueLenUsed( - parse_u32(payload) - .context("invalid IFLA_MACVLAN_BC_QUEUE_LEN_USED value")?, - ), - IFLA_MACVLAN_BC_CUTOFF => BcCutoff( - parse_i32(payload) - .context("invalid IFLA_MACVLAN_BC_CUTOFF value")?, - ), - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum VlanQosMapping { - Unspec(Vec), - Mapping { from: u32, to: u32 }, - Other(DefaultNla), -} - -impl Nla for VlanQosMapping { - fn value_len(&self) -> usize { - match self { - VlanQosMapping::Unspec(bytes) => bytes.len(), - VlanQosMapping::Mapping { .. } => 8, - VlanQosMapping::Other(nla) => nla.value_len(), - } - } - - fn kind(&self) -> u16 { - match self { - VlanQosMapping::Unspec(_) => IFLA_VLAN_QOS_UNSPEC, - VlanQosMapping::Mapping { .. } => IFLA_VLAN_QOS_MAPPING, - VlanQosMapping::Other(nla) => nla.kind(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - use VlanQosMapping::*; - match self { - Unspec(payload) => buffer.copy_from_slice(payload), - Mapping { from, to } => { - NativeEndian::write_u32(buffer, *from); - NativeEndian::write_u32(&mut buffer[4..], *to); - } - Other(nla) => nla.emit_value(buffer), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> - for VlanQosMapping -{ - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - use VlanQosMapping::*; - let payload = buf.value(); - Ok(match buf.kind() { - IFLA_VLAN_QOS_UNSPEC => Unspec(payload.to_vec()), - IFLA_VLAN_QOS_MAPPING => Mapping { - from: parse_u32(&payload[..4]) - .context("expected u32 from value")?, - to: parse_u32(&payload[4..]) - .context("expected u32 to value")?, - }, - kind => Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind}"))?, - ), - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - nlas::link::{bond::*, Nla}, - LinkHeader, LinkMessage, - }; - use netlink_packet_utils::traits::Emitable; - use std::net::{Ipv4Addr, Ipv6Addr}; - - #[rustfmt::skip] - static BRIDGE: [u8; 424] = [ - 0x0b, 0x00, // L = 11 - 0x01, 0x00, // T = 1 (IFLA_INFO_KIND) - 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, // V = "bridge" - 0x00, // padding - - 0x9c, 0x01, // L = 412 - 0x02, 0x00, // T = 2 (IFLA_INFO_DATA) - - 0x0c, 0x00, // L = 12 - 0x10, 0x00, // T = 16 (IFLA_BR_HELLO_TIMER) - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 35 - - 0x0c, 0x00, // L = 12 - 0x11, 0x00, // T = 17 (IFLA_BR_TCN_TIMER) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 - - 0x0c, 0x00, // L = 12 - 0x12, 0x00, // T = 18 (IFLA_BR_TOPOLOGY_CHANGE_TIMER) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 - - 0x0c, 0x00, // L = 12 - 0x13, 0x00, // T = 19 (IFLA_BR_GC_TIMER) - 0xb5, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 14261 (0x37b5) - - 0x08, 0x00, // L = 8 - 0x01, 0x00, // T = 1 (IFLA_BR_FORWARD_DELAY) - 0xc7, 0x00, 0x00, 0x00, // V = 199 - - 0x08, 0x00, // L = 8 - 0x02, 0x00, // T = 2 (IFLA_BR_HELLO_TIME) - 0xc7, 0x00, 0x00, 0x00, // V = 199 - - 0x08, 0x00, // L = 8 - 0x03, 0x00, // T = 3 (IFLA_BR_MAX_AGE) - 0xcf, 0x07, 0x00, 0x00, // V = 1999 (0x07cf) - - 0x08, 0x00, // L = 8 - 0x04, 0x00, // T = 4 (IFLA_BR_AGEING_TIME) - 0x2f, 0x75, 0x00, 0x00, // V = 29999 (0x752f) - - 0x08, 0x00, // L = 8 - 0x05, 0x00, // T = 5 (IFLA_BR_STP_STATE) - 0x01, 0x00, 0x00, 0x00, // V = 1 - - 0x06, 0x00, // L = 6 - 0x06, 0x00, // T = 6 (IFLA_BR_PRIORITY) - 0x00, 0x80, // V = 32768 (0x8000) - 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x07, 0x00, // T = 7 (IFLA_BR_VLAN_FILTERING) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x06, 0x00, // L = 6 - 0x09, 0x00, // T = 9 (IFLA_BR_GROUP_FWD_MASK) - 0x00, 0x00, // V = 0 - 0x00, 0x00, // Padding - - 0x0c, 0x00, // L = 12 - 0x0b, 0x00, // T = 11 (IFLA_BR_BRIDGE_ID) - 0x80, 0x00, // V (priority) = 128 (0x80) - 0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e, // V (address) = 52:54:00:d7:19:3e - - 0x0c, 0x00, // L = 12 - 0x0a, 0x00, // T = 10 (IFLA_BR_ROOT_ID) - 0x80, 0x00, // V (priority) = 128 (0x80) - 0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e, // V (address) = 52:54:00:d7:19:3e - - 0x06, 0x00, // L = 6 - 0x0c, 0x00, // T = 12 (IFLA_BR_ROOT_PORT) - 0x00, 0x00, // V = 0 - 0x00, 0x00, // Padding - - 0x08, 0x00, // L = 8 - 0x0d, 0x00, // T = 13 (IFLA_BR_ROOT_PATH_COST) - 0x00, 0x00, 0x00, 0x00, // V = 0 - - 0x05, 0x00, // L = 5 - 0x0e, 0x00, // T = 14 (IFLA_BR_TOPOLOGY_CHANGE) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x0f, 0x00, // T = 15 (IFLA_BR_TOPOLOGY_CHANGE_DETECTED) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x0a, 0x00, // L = 10 - 0x14, 0x00, // T = 20 (IFLA_BR_GROUP_ADDR) - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00, // V = 01:80:c2:00:00:00 - 0x00, 0x00, // Padding - - 0x06, 0x00, // L = 6 - 0x08, 0x00, // T = 8 (IFLA_BR_VLAN_PROTOCOL) - 0x81, 0x00, // V = 33024 (big-endian) - 0x00, 0x00, // Padding - - 0x06, 0x00, // L = 6 - 0x27, 0x00, // T = 39 (IFLA_BR_VLAN_DEFAULT_PVID) - 0x01, 0x00, // V = 1 - 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x29, 0x00, // T = 41 (IFLA_BR_VLAN_STATS_ENABLED) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x16, 0x00, // T = 22 (IFLA_BR_MCAST_ROUTER) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x17, 0x00, // T = 23 (IFLA_BR_MCAST_SNOOPING) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x18, 0x00, // T = 24 (IFLA_BR_MCAST_QUERY_USE_IFADDR) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x19, 0x00, // T = 25 (IFLA_BR_MCAST_QUERIER) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x2a, 0x00, // T = 42 (IFLA_BR_MCAST_STATS_ENABLED) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x08, 0x00, // L = 8 - 0x1a, 0x00, // T = 26 (IFLA_BR_MCAST_HASH_ELASTICITY) - 0x04, 0x00, 0x00, 0x00, // V = 4 - - 0x08, 0x00, // L = 8 - 0x1b, 0x00, // T = 27 (IFLA_BR_MCAST_HASH_MAX) - 0x00, 0x02, 0x00, 0x00, // V = 512 (0x0200) - - 0x08, 0x00, // L = 8 - 0x1c, 0x00, // T = 28 (IFLA_BR_MCAST_LAST_MEMBER_CNT) - 0x02, 0x00, 0x00, 0x00, // V = 2 - - 0x08, 0x00, // L = 8 - 0x1d, 0x00, // T = 29 (IFLA_BR_MCAST_STARTUP_QUERY_CNT) - 0x02, 0x00, 0x00, 0x00, // V = 2 - - 0x05, 0x00, // L = 5 - 0x2b, 0x00, // T = 43 (IFLA_BR_MCAST_IGMP_VERSION) - 0x02, // V = 2 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x2c, 0x00, // T = 44 (IFLA_BR_MCAST_MLD_VERSION) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // Padding - - 0x0c, 0x00, // L = 12 - 0x1e, 0x00, // T = 30 (IFLA_BR_MCAST_LAST_MEMBER_INTVL) - 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 99 - - 0x0c, 0x00, // L = 12 - 0x1f, 0x00, // T = 31 (IFLA_BR_MCAST_MEMBERSHIP_INTVL) - 0x8f, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 25999 (0x658f) - - 0x0c, 0x00, // L = 12 - 0x20, 0x00, // T = 32 (IFLA_BR_MCAST_QUERIER_INTVL) - 0x9b, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 25499 (0x639b) - - 0x0c, 0x00, // L = 12 - 0x21, 0x00, // T = 33 (IFLA_BR_MCAST_QUERY_INTVL) - 0xd3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 12499 (0x30d3) - - 0x0c, 0x00, // L = 12 - 0x22, 0x00, // T = 34 (IFLA_BR_MCAST_QUERY_RESPONSE_INTVL) - 0xe7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 999 (0x03e7) - - 0x0c, 0x00, // L = 12 - 0x23, 0x00, // T = 35 (IFLA_BR_MCAST_STARTUP_QUERY_INTVL) - 0x34, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 3124 (0x0c34) - - 0x05, 0x00, // L = 5 - 0x24, 0x00, // T = 36 (IFLA_BR_NF_CALL_IPTABLES) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x25, 0x00, // T = 37 (IFLA_BR_NF_CALL_IP6TABLES) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x26, 0x00, // T = 38 (IFLA_BR_NF_CALL_ARPTABLES) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // Padding - - 0x05, 0x00, // L = 5 - 0x2d, 0x00, // T = 45 (IFLA_BR_VLAN_STATS_PER_PORT) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // Padding - - 0x0c, 0x00, // L = 12 - 0x2e, 0x00, // T = 46 (IFLA_BR_MULTI_BOOLOPT) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // V = 0 - - ]; - - lazy_static! { - static ref BRIDGE_INFO: Vec = vec![ - InfoBridge::HelloTimer(35), - InfoBridge::TcnTimer(0), - InfoBridge::TopologyChangeTimer(0), - InfoBridge::GcTimer(14261), - InfoBridge::ForwardDelay(199), - InfoBridge::HelloTime(199), - InfoBridge::MaxAge(1999), - InfoBridge::AgeingTime(29999), - InfoBridge::StpState(1), - InfoBridge::Priority(0x8000), - InfoBridge::VlanFiltering(0), - InfoBridge::GroupFwdMask(0), - InfoBridge::BridgeId((128, [0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e])), - InfoBridge::RootId((128, [0x52, 0x54, 0x00, 0xd7, 0x19, 0x3e])), - InfoBridge::RootPort(0), - InfoBridge::RootPathCost(0), - InfoBridge::TopologyChange(0), - InfoBridge::TopologyChangeDetected(0), - InfoBridge::GroupAddr([0x01, 0x80, 0xc2, 0x00, 0x00, 0x00]), - InfoBridge::VlanProtocol(33024), - InfoBridge::VlanDefaultPvid(1), - InfoBridge::VlanStatsEnabled(0), - InfoBridge::MulticastRouter(1), - InfoBridge::MulticastSnooping(1), - InfoBridge::MulticastQueryUseIfaddr(0), - InfoBridge::MulticastQuerier(0), - InfoBridge::MulticastStatsEnabled(0), - InfoBridge::MulticastHashElasticity(4), - InfoBridge::MulticastHashMax(512), - InfoBridge::MulticastLastMemberCount(2), - InfoBridge::MulticastStartupQueryCount(2), - InfoBridge::MulticastIgmpVersion(2), - InfoBridge::MulticastMldVersion(1), - InfoBridge::MulticastLastMemberInterval(99), - InfoBridge::MulticastMembershipInterval(25999), - InfoBridge::MulticastQuerierInterval(25499), - InfoBridge::MulticastQueryInterval(12499), - InfoBridge::MulticastQueryResponseInterval(999), - InfoBridge::MulticastStartupQueryInterval(3124), - InfoBridge::NfCallIpTables(0), - InfoBridge::NfCallIp6Tables(0), - InfoBridge::NfCallArpTables(0), - InfoBridge::VlanStatsPerHost(1), - InfoBridge::MultiBoolOpt(0), - ]; - } - - #[test] - fn parse_info_kind() { - let info_kind_nla = NlaBuffer::new_checked(&BRIDGE[..12]).unwrap(); - let parsed = InfoKind::parse(&info_kind_nla).unwrap(); - assert_eq!(parsed, InfoKind::Bridge); - } - - #[test] - fn parse_info_bridge() { - let nlas = NlasIterator::new(&BRIDGE[16..]); - for nla in nlas.map(|nla| nla.unwrap()) { - InfoBridge::parse(&nla).unwrap(); - } - } - - #[rustfmt::skip] - #[test] - fn parse_veth_info() { - let data = [0x08, 0x00, // length = 8 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x76, 0x65, 0x74, 0x68, // VETH - - 0x30, 0x00, // length = 48 - 0x02, 0x00, // type = IFLA_INFO_DATA - - 0x2c, 0x00, // length = 44 - 0x01, 0x00, // type = VETH_INFO_PEER - // The data a NEWLINK message - 0x00, // interface family - 0x00, // padding - 0x00, 0x00, // link layer type - 0x00, 0x00, 0x00, 0x00, // link index - 0x00, 0x00, 0x00, 0x00, // flags - 0x00, 0x00, 0x00, 0x00, // flags change mask - // NLA - 0x10, 0x00, // length = 16 - 0x03, 0x00, // type = IFLA_IFNAME - 0x76, 0x65, 0x74, 0x68, 0x63, 0x30, 0x65, 0x36, 0x30, 0x64, 0x36, 0x00, - // NLA - 0x08, 0x00, // length = 8 - 0x0d, 0x00, // type = IFLA_TXQLEN - 0x00, 0x00, 0x00, 0x00]; - let nla = NlaBuffer::new_checked(&data[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Veth), - Info::Data(InfoData::Veth(VethInfo::Peer(LinkMessage { - header: LinkHeader { - interface_family: 0, - index: 0, - link_layer_type: ARPHRD_NETROM, - flags: 0, - change_mask: 0, - }, - nlas: vec![ - Nla::IfName("vethc0e60d6".to_string()), - Nla::TxQueueLen(0), - ], - }))), - ]; - assert_eq!(expected, parsed); - } - - #[rustfmt::skip] - #[test] - fn parse_info_bondport() { - let data = [0x09, 0x00, // length - 0x04, 0x00, // IFLA_INFO_PORT_KIND - 0x62, 0x6f, 0x6e, 0x64, 0x00, // V = "bond\0" - 0x00, 0x00, 0x00, // padding - - 0x14, 0x00, // length = 20 - 0x05, 0x00, // IFLA_INFO_PORT_DATA - 0x06, 0x00, // length - 0x05, 0x00, // IFLA_BOND_PORT_QUEUE_ID - 0x00, 0x00, // 0 - 0x00, 0x00, // padding - - 0x08, 0x00, // length - 0x09, 0x00, // IFLA_BOND_PORT_PRIO - 0x32, 0x00, 0x00, 0x00]; - let nla = NlaBuffer::new_checked(&data[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::PortKind(InfoPortKind::Bond), - Info::PortData(InfoPortData::BondPort(vec![InfoBondPort::QueueId(0), - InfoBondPort::Prio(50), - ])), - ]; - assert_eq!(expected, parsed); - } - - #[rustfmt::skip] - #[test] - fn parse_info_bond() { - let data = [0x08, 0x00, // length - 0x01, 0x00, // IFLA_INFO_KIND - 0x62, 0x6f, 0x6e, 0x64, // "bond" - - 0x80, 0x00, // length - 0x02, 0x00, // IFLA_INFO_DATA - 0x05, 0x00, // length - 0x01, 0x00, // IFLA_BOND_MODE - 0x04, // 4 (802.3ad) - 0x00, 0x00, 0x00, // padding - - 0x08, 0x00, // length - 0x03, 0x00, // IFLA_BOND_MIIMON - 0x32, 0x00, 0x00, 0x00, // 50 - - 0x08, 0x00, // length - 0x04, 0x00, // IFLA_BOND_UPDELAY - 0x64, 0x00, 0x00, 0x00, // 100 - - 0x08, 0x00, // length - 0x05, 0x00, // IFLA_BOND_DOWNDELAY - 0x64, 0x00, 0x00, 0x00, // 100 - - 0x14, 0x00, // length - 0x08, 0x00, // IFLA_BOND_ARP_IP_TARGET - 0x08, 0x00, // length - 0x00, 0x00, // entry #0 - 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 - 0x08, 0x00, // length - 0x01, 0x00, // entry #1 - 0x09, 0x09, 0x09, 0x09, // 9.9.9.9 - - 0x18, 0x00, // length - 0x1f, 0x00, // IFLA_BOND_NS_IP6_TARGET - 0x14, 0x00, // length - 0x00, 0x00, // entry #0 - 0xfd, 0x01, 0x00, 0x00, // fd01::1 - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, - - 0x08, 0x00, // length - 0x1c, 0x00, // IFLA_BOND_PEER_NOTIF_DELAY - 0xc8, 0x00, 0x00, 0x00, // 200 - - 0x08, 0x00, // length - 0x12, 0x00, // IFLA_BOND_MIN_LINKS - 0x03, 0x00, 0x00, 0x00, // 3 - - 0x20, 0x00, // length - 0x17, 0x00, // IFLA_BOND_AD_INFO - 0x06, 0x00, // length - 0x01, 0x00, // IFLA_BOND_AD_INFO_AGGREGATOR - 0x10, 0x00, // 16 - 0x00, 0x00, // padding - 0x06, 0x00, // length - 0x02, 0x00, // IFLA_BOND_AD_INFO_NUM_PORTS - 0x02, 0x00, // 2 - 0x00, 0x00, // padding - 0x0a, 0x00, // length - 0x05, 0x00, // IFLA_BOND_AD_INFO_PARTNER_MAC - 0x00, 0x11, 0x22, // 00:11:22:33:44:55 - 0x33, 0x44, 0x55, - 0x00, 0x00]; - let nla = NlaBuffer::new_checked(&data[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Bond), - Info::Data(InfoData::Bond(vec![InfoBond::Mode(4), - InfoBond::MiiMon(50), - InfoBond::UpDelay(100), - InfoBond::DownDelay(100), - InfoBond::ArpIpTarget(vec!(Ipv4Addr::new(1, 2, 3, 4), - Ipv4Addr::new(9, 9, 9, 9))), - InfoBond::NsIp6Target(vec!(Ipv6Addr::new(0xfd01, 0, 0, 0, 0, 0, 0, 1))), - InfoBond::PeerNotifDelay(200), - InfoBond::MinLinks(3), - InfoBond::AdInfo(vec!(BondAdInfo::Aggregator(16), - BondAdInfo::NumPorts(2), - BondAdInfo::PartnerMac([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]))), - ])), - ]; - assert_eq!(expected, parsed); - } - - #[rustfmt::skip] - static IPVLAN: [u8; 32] = [ - 0x0b, 0x00, // length = 11 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x69, 0x70, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "ipvlan\0" - 0x00, // padding - - 0x14, 0x00, // length = 20 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x06, 0x00, // length = 6 - 0x01, 0x00, // type = 1 = IFLA_IPVLAN_MODE - 0x01, 0x00, // l3 - 0x00, 0x00, // padding - - 0x06, 0x00, // length = 6 - 0x02, 0x00, // type = 2 = IFLA_IPVLAN_FLAGS - 0x02, 0x00, // vepa flag - 0x00, 0x00, // padding - ]; - - lazy_static! { - static ref IPVLAN_INFO: Vec = vec![ - InfoIpVlan::Mode(1), // L3 - InfoIpVlan::Flags(2), // vepa flag - ]; - } - - #[test] - fn parse_info_ipvlan() { - let nla = NlaBuffer::new_checked(&IPVLAN[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::IpVlan), - Info::Data(InfoData::IpVlan(IPVLAN_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_ipvlan() { - let nlas = vec![ - Info::Kind(InfoKind::IpVlan), - Info::Data(InfoData::IpVlan(IPVLAN_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 32); - - let mut vec = vec![0xff; 32]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &IPVLAN[..]); - } - - #[rustfmt::skip] - static MACVLAN: [u8; 24] = [ - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" - 0x0c, 0x00, // length = 12 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x08, 0x00, // length = 8 - 0x01, 0x00, // type = IFLA_MACVLAN_MODE - 0x04, 0x00, 0x00, 0x00, // V = 4 = bridge - ]; - - lazy_static! { - static ref MACVLAN_INFO: Vec = vec![ - InfoMacVlan::Mode(4), // bridge - ]; - } - - #[test] - fn parse_info_macvlan() { - let nla = NlaBuffer::new_checked(&MACVLAN[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_macvlan() { - let nlas = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 24); - - let mut vec = vec![0xff; 24]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &MACVLAN[..]); - } - - #[rustfmt::skip] - static MACVLAN_BC: [u8; 48] = [ - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" - 0x24, 0x00, // length = 36 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x08, 0x00, // length = 8 - 0x01, 0x00, // type = IFLA_MACVLAN_MODE - 0x02, 0x00, 0x00, 0x00, // V = 2 = vepa - - 0x08, 0x00, // length = 8 - 0x07, 0x00, // type = IFLA_MACVLAN_BC_QUEUE_LEN - 0xe8, 0x03, 0x00, 0x00, // value 1000 - - 0x08, 0x00, // length = 8 - 0x08, 0x00, // type = IFLA_MACVLAN_BC_QUEUE_LEN_USED - 0xe8, 0x03, 0x00, 0x00, // value 1000 - - 0x08, 0x00, // length = 8 - 0x09, 0x00, // type = IFLA_MACVLAN_BC_CUTOFF - 0xff, 0xff, 0xff, 0xff, // value = -1 (signed two's complement) - ]; - - lazy_static! { - static ref MACVLAN_INFO_BC: Vec = vec![ - InfoMacVlan::Mode(2), // vepa - InfoMacVlan::BcQueueLen(1000), - InfoMacVlan::BcQueueLenUsed(1000), - InfoMacVlan::BcCutoff(-1), - ]; - } - - #[test] - fn parse_info_macvlan_bc() { - let nla = NlaBuffer::new_checked(&MACVLAN_BC[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_INFO_BC.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_macvlan_bc() { - let nlas = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_INFO_BC.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 48); - - let mut vec = vec![0xff; 48]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &MACVLAN_BC[..]); - } - - lazy_static! { - static ref XFRMTUN_INFO: Vec = vec![ - InfoXfrmTun::IfId(4), // ifid - InfoXfrmTun::Link(2), - ]; - } - #[rustfmt::skip] - static XFRMTUN: [u8; 32] = [ - 9, 0, 1, 0, 120,102,114,109, - 0, 0, 0, 0, 20, 0, 2, 0, - 8, 0, 2, 0, 4, 0, 0, 0, - 8, 0, 1, 0, 2, 0, 0, 0, - ]; - - #[test] - fn parse_info_xfrmtun() { - let nla = NlaBuffer::new_checked(&XFRMTUN[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Xfrm), - Info::Data(InfoData::Xfrm(XFRMTUN_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_xfrmtun() { - let nlas = vec![ - Info::Kind(InfoKind::Xfrm), - Info::Data(InfoData::Xfrm(XFRMTUN_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 32); - - let mut vec = vec![0xff; 32]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &XFRMTUN[..]); - } - - #[rustfmt::skip] - static MACVLAN_SOURCE_SET: [u8; 84] = [ - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x6d, 0x61, 0x63, 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "macvlan\0" - 0x48, 0x00, // length = 72 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x08, 0x00, // length = 8 - 0x03, 0x00, // type = 3 = IFLA_MACVLAN_MACADDR_MODE - 0x03, 0x00, 0x00, 0x00, // V = 3 = set - - 0x34, 0x00, // length = 52 - 0x05, 0x00, // type = 5 = IFLA_MACVLAN_MACADDR_DATA - 0x0a, 0x00, // length = 10 - 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR - 0x22, 0xf5, 0x54, 0x09, 0x88, 0xd7, // V = mac address - 0x00, 0x00, // padding - - 0x0a, 0x00, // length = 10 - 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR - 0x22, 0xf5, 0x54, 0x09, 0x99, 0x32, // V = mac address - 0x00, 0x00, // padding - - 0x0a, 0x00, // length = 10 - 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR - 0x22, 0xf5, 0x54, 0x09, 0x87, 0x45, // V = mac address - 0x00, 0x00, // padding - - 0x0a, 0x00, // length = 10 - 0x04, 0x00, // type = 4 = IFLA_MACVLAN_MACADDR - 0x22, 0xf5, 0x54, 0x09, 0x11, 0x45, // V = mac address - 0x00, 0x00, // padding - 0x08, 0x00, // length = 8 - 0x01, 0x00, // Type = 1 = IFLA_MACVLAN_MODE - 0x10, 0x00, 0x00, 0x00, // V = 16 = source - ]; - - lazy_static! { - static ref MACVLAN_SOURCE_SET_INFO: Vec = vec![ - InfoMacVlan::MacAddrMode(3), // set - InfoMacVlan::MacAddrData(vec![ - InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x88, 0xd7,]), - InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x99, 0x32,]), - InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x87, 0x45,]), - InfoMacVlan::MacAddr([0x22, 0xf5, 0x54, 0x09, 0x11, 0x45,]), - ]), - InfoMacVlan::Mode(16), // source - ]; - } - - #[test] - fn parse_info_macvlan_source_set() { - let nla = NlaBuffer::new_checked(&MACVLAN_SOURCE_SET[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_SOURCE_SET_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_macvlan_source_set() { - let nlas = vec![ - Info::Kind(InfoKind::MacVlan), - Info::Data(InfoData::MacVlan(MACVLAN_SOURCE_SET_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 84); - - let mut vec = vec![0xff; 84]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &MACVLAN_SOURCE_SET[..]); - } - - #[test] - fn parse() { - let nla = NlaBuffer::new_checked(&BRIDGE[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - assert_eq!(parsed.len(), 2); - assert_eq!(parsed[0], Info::Kind(InfoKind::Bridge)); - if let Info::Data(InfoData::Bridge(nlas)) = parsed[1].clone() { - assert_eq!(nlas.len(), BRIDGE_INFO.len()); - for (expected, parsed) in BRIDGE_INFO.iter().zip(nlas) { - assert_eq!(*expected, parsed); - } - } else { - panic!( - "expected Info::Data(InfoData::Bridge(_) got {:?}", - parsed[1] - ) - } - } - - #[test] - fn emit() { - let nlas = vec![ - Info::Kind(InfoKind::Bridge), - Info::Data(InfoData::Bridge(BRIDGE_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), 424); - - let mut vec = vec![0xff; 424]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &BRIDGE[..]); - } - - #[rustfmt::skip] - static VLAN: [u8; 68] = [ - 0x09, 0x00, // length = 9 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x76, 0x6c, 0x61, 0x6e, 0x00, // V = "vlan\0" - 0x00, 0x00, 0x00, // padding - 0x38, 0x00, // length = 56 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x06, 0x00, // length - 6 - 0x01, 0x00, // type = 1 = IFLA_VLAN_ID - 0x4b, 0x00, // id = 0x4b = 75 - 0x00, 0x00, // padding - 0x10, 0x00, // length = 16 - 0x03, 0x00, // type = 3 = IFLA_VLAN_EGRESS_QOS_MAPPING - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING - 0x03, 0x00, 0x00, 0x00, // from = 3 - 0x04, 0x00, 0x00, 0x00, // to = 4 - 0x1c, 0x00, // length = 44 - 0x04, 0x00, // type = 4 = IFLA_VLAN_INGRESS_QOS_MAPPING - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING - 0x00, 0x00, 0x00, 0x00, // from = 0 - 0x01, 0x00, 0x00, 0x00, // to = 1 - 0x0c, 0x00, // length = 12 - 0x01, 0x00, // type = 1 = IFLA_VLAN_QOS_MAPPING - 0x01, 0x00, 0x00, 0x00, // from = 1 - 0x02, 0x00, 0x00, 0x00, // to = 2 - ]; - - lazy_static! { - static ref VLAN_INFO: Vec = vec![ - InfoVlan::Id(75), - InfoVlan::EgressQos(vec![VlanQosMapping::Mapping { - from: 3, - to: 4 - }]), - InfoVlan::IngressQos(vec![ - VlanQosMapping::Mapping { from: 0, to: 1 }, - VlanQosMapping::Mapping { from: 1, to: 2 } - ]), - ]; - } - - #[test] - fn parse_info_vlan() { - let nla = NlaBuffer::new_checked(&VLAN[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Vlan), - Info::Data(InfoData::Vlan(VLAN_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_vlan() { - let nlas = vec![ - Info::Kind(InfoKind::Vlan), - Info::Data(InfoData::Vlan(VLAN_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), VLAN.len()); - - let mut vec = vec![0xff; VLAN.len()]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &VLAN[..]); - } - - #[rustfmt::skip] - static VXLAN: [u8; 32] = [ - 0x0a, 0x00, // length = 10 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x76, 0x78, 0x6C, 0x61, 0x6E, // V = "vxlan\0" - 0x00, 0x00, 0x00, // padding - 0x14, 0x00, // length = 20 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x08, 0x00, // length - 8 - 0x01, 0x00, // type = 1 = IFLA_VXLAN_ID - 0x0A, 0x00, // id = 0x0A = 10 - 0x00, 0x00, // padding - 0x06, 0x00, // length = 6 - 0x0F, 0x00, // type = 15 = IFLA_VXLAN_PORT - 0x12, 0xB5, // port = 4789 - 0x00, 0x00 // padding - ]; - - lazy_static! { - static ref VXLAN_INFO: Vec = - vec![InfoVxlan::Id(10), InfoVxlan::Port(4789),]; - } - - #[test] - fn parse_info_vxlan() { - let nla = NlaBuffer::new_checked(&VXLAN[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Vxlan), - Info::Data(InfoData::Vxlan(VXLAN_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_vxlan() { - let nlas = vec![ - Info::Kind(InfoKind::Vxlan), - Info::Data(InfoData::Vxlan(VXLAN_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), VXLAN.len()); - - let mut vec = vec![0xff; VXLAN.len()]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &VXLAN[..]); - } - - lazy_static! { - static ref VXLAN_INFO_WITH_PORT_RANGE: Vec = - vec![InfoVxlan::Id(10), InfoVxlan::PortRange((9000, 9050)),]; - } - - #[rustfmt::skip] - static VXLAN_WITH_PORT_RANGE: [u8; 32] = [ - 0x0a, 0x00, // length = 10 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - 0x76, 0x78, 0x6C, 0x61, 0x6E, // V = "vxlan\0" - 0x00, 0x00, 0x00, // padding - 0x14, 0x00, // length = 20 - 0x02, 0x00, // type = 2 = IFLA_INFO_DATA - 0x08, 0x00, // length - 8 - 0x01, 0x00, // type = 1 = IFLA_VXLAN_ID - 0x0A, 0x00, // id = 0x0A = 10 - 0x00, 0x00, // padding - 0x08, 0x00, // length = 6 - 0x0A, 0x00, // type = 10 = IFLA_VXLAN_PORT_RANGE - 0x23, 0x28, // min port: 9000 - 0x23, 0x5a // max port: 9050 - ]; - - #[test] - fn emit_info_vxlan_with_port_range() { - let nlas = vec![ - Info::Kind(InfoKind::Vxlan), - Info::Data(InfoData::Vxlan(VXLAN_INFO_WITH_PORT_RANGE.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), VXLAN_WITH_PORT_RANGE.len()); - - let mut vec = vec![0xff; VXLAN_WITH_PORT_RANGE.len()]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &VXLAN_WITH_PORT_RANGE[..]); - } - - #[test] - fn parse_info_vxlan_with_port_range() { - let nla = NlaBuffer::new_checked(&VXLAN_WITH_PORT_RANGE[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Vxlan), - Info::Data(InfoData::Vxlan(VXLAN_INFO_WITH_PORT_RANGE.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[rustfmt::skip] - static MACSEC: [u8; 120] = [ - 0x0b, 0x00, // L = 11 - 0x01, 0x00, // T = 1 (IFLA_INFO_KIND) - 0x6d, 0x61, 0x63, 0x73, 0x65, 0x63, 0x00, // V = "macsec" - 0x00, // padding - - 0x6c, 0x00, // L = 108 - 0x02, 0x00, // T = 2 (IFLA_INFO_DATA) - - 0x0c, 0x00, // L = 16 - 0x01, 0x00, // T = 1 (IFLA_MACSEC_SCI) - 0x76, 0x83, 0x22, 0x9e, 0xd6, 0xfd, 0x00, 0x0b, // V = 792912632635097974 - - 0x05, 0x00, // L = 5 - 0x03, 0x00, // T = 3 (IFLA_MACSEC_ICV_LEN) - 0x10, // V = 16 - 0x00, 0x00, 0x00, // padding - - 0x0c, 0x00, // L = 16 - 0x04, 0x00, // T = 4 (IFLA_MACSEC_CIPHER_SUITE) - 0x01, 0x00, 0x00, 0x01, 0x00, 0x02, 0x80, 0x00, // V = MACSEC_DEFAULT_CIPHER_ID - - 0x05, 0x00, // L = 5 - 0x06, 0x00, // T = 6 (IFLA_MACSEC_ENCODING_SA) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x07, 0x00, // T = 7 (IFLA_MACSEC_ENCRYPT) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x08, 0x00, // T = 8 (IFLA_MACSEC_PROTECT) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x09, 0x00, // T = 9 (IFLA_MACSEC_INC_SCI) - 0x01, // V = 1 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x0a, 0x00, // T = 10 (IFLA_MACSEC_ES) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x0b, 0x00, // T = 11 (IFLA_MACSEC_SCB) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x0c, 0x00, // T = 12 (IFLA_MACSEC_REPLAY_PROTECT) - 0x00, // V = 0 - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x0d, 0x00, // T = 13 (IFLA_MACSEC_VALIDATION) - 0x02, // V = 2 (MACSEC_VALIDATE_STRICT) - 0x00, 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x0f, 0x00, // T = 15 (IFLA_MACSEC_OFFLOAD) - 0x00, // V = 0 (MACSEC_OFFLOAD_OFF) - 0x00, 0x00, 0x00 // padding - ]; - - lazy_static! { - static ref MACSEC_INFO: Vec = vec![ - InfoMacSec::Sci(792912632635097974), - InfoMacSec::IcvLen(16), - #[allow(deprecated)] - InfoMacSec::CipherSuite(MacSecCipherId::DefaultGcmAes128), - InfoMacSec::EncodingSa(0), - InfoMacSec::Encrypt(1), - InfoMacSec::Protect(1), - InfoMacSec::IncSci(1), - InfoMacSec::Es(0), - InfoMacSec::Scb(0), - InfoMacSec::ReplayProtect(0), - InfoMacSec::Validation(MacSecValidation::Strict), - InfoMacSec::Offload(MacSecOffload::Off), - ]; - } - - #[test] - fn parse_info_macsec() { - let nla = NlaBuffer::new_checked(&MACSEC[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::MacSec), - Info::Data(InfoData::MacSec(MACSEC_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_macsec() { - let nlas = vec![ - Info::Kind(InfoKind::MacSec), - Info::Data(InfoData::MacSec(MACSEC_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), MACSEC.len()); - - let mut vec = vec![0xff; MACSEC.len()]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &MACSEC[..]); - } - - #[rustfmt::skip] - static HSR: [u8; 56] = [ - 0x08, 0x00, // L = 8 - 0x01, 0x00, // T = 1 (IFLA_INFO_KIND) - 0x68, 0x73, 0x72, 0x00, // V = "hsr" - - 0x30, 0x00, // L = 48 - 0x02, 0x00, // T = 2 (IFLA_INFO_DATA) - - 0x08, 0x00, // L = 8 - 0x01, 0x00, // T = 1 (IFLA_HSR_SLAVE1) - 0x18, 0x00, 0x00, 0x00, // V = 24 - - 0x08, 0x00, // L = 8 - 0x02, 0x00, // T = 2 (IFLA_HSR_SLAVE2) - 0x1a, 0x00, 0x00, 0x00, // V = 26 - - 0x0a, 0x00, // L = 10 - 0x04, 0x00, // T = 4 (IFLA_HSR_SUPERVISION_ADDR) - 0x01, 0x15, 0x4e, 0x00, 0x01, 0x2d, // V = "01:15:4e:00:01:2d" - 0x00, 0x00, // padding - - 0x06, 0x00, // L = 6 - 0x05, 0x00, // T = 5 (IFLA_HSR_SEQ_NR) - 0x4b, 0xfc, // V = 64587 - 0x00, 0x00, // padding - - 0x05, 0x00, // L = 5 - 0x07, 0x00, // T = 7 (IFLA_HSR_PROTOCOL) - 0x01, // V = 1 (HSR_PROTOCOL_PRP) - 0x00, 0x00, 0x00 // padding - ]; - - lazy_static! { - static ref HSR_INFO: Vec = vec![ - InfoHsr::Port1(24), - InfoHsr::Port2(26), - InfoHsr::SupervisionAddr([0x01, 0x15, 0x4e, 0x00, 0x01, 0x2d]), - InfoHsr::SeqNr(64587), - InfoHsr::Protocol(HsrProtocol::Prp), - ]; - } - - #[test] - fn parse_info_hsr() { - let nla = NlaBuffer::new_checked(&HSR[..]).unwrap(); - let parsed = VecInfo::parse(&nla).unwrap().0; - let expected = vec![ - Info::Kind(InfoKind::Hsr), - Info::Data(InfoData::Hsr(HSR_INFO.clone())), - ]; - assert_eq!(expected, parsed); - } - - #[test] - fn emit_info_hsr() { - let nlas = vec![ - Info::Kind(InfoKind::Hsr), - Info::Data(InfoData::Hsr(HSR_INFO.clone())), - ]; - - assert_eq!(nlas.as_slice().buffer_len(), HSR.len()); - - let mut vec = vec![0xff; HSR.len()]; - nlas.as_slice().emit(&mut vec); - assert_eq!(&vec[..], &HSR[..]); - } -} diff --git a/src/rtnl/link/nlas/tests.rs b/src/rtnl/link/nlas/tests.rs deleted file mode 100644 index 59c95482..00000000 --- a/src/rtnl/link/nlas/tests.rs +++ /dev/null @@ -1,417 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_utils::{nla::Nla, DecodeError}; - -use super::*; -// https://lists.infradead.org/pipermail/libnl/2015-November/002034.html -// https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/if_link.h#L89 -#[rustfmt::skip] -static BYTES: [u8; 748] = [ - // AF_SPEC (L=748, T=26) - 0xec, 0x02, 0x1a, 0x00, - // AF_INET (L=132, T=2) - 0x84, 0x00, 0x02, 0x00, - // IFLA_INET_CONF (L=128, T=1) - 0x80, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x00, 0x00, // 1 forwarding - 0x00, 0x00, 0x00, 0x00, // 2 mc_forwarding - 0x00, 0x00, 0x00, 0x00, // 3 proxy_arp - 0x01, 0x00, 0x00, 0x00, // 4 accept_redirects - 0x01, 0x00, 0x00, 0x00, // 5 secure_redirects - 0x01, 0x00, 0x00, 0x00, // 6 send_redirects - 0x01, 0x00, 0x00, 0x00, // 7 shared_media - 0x00, 0x00, 0x00, 0x00, // 8 rp_filter - 0x01, 0x00, 0x00, 0x00, // 9 accept_source_route - 0x00, 0x00, 0x00, 0x00, // 10 bootp_relay (40 bytes) - 0x00, 0x00, 0x00, 0x00, // 11 log_martians - 0x00, 0x00, 0x00, 0x00, // 12 tag - 0x00, 0x00, 0x00, 0x00, // 13 arpfilter - 0x00, 0x00, 0x00, 0x00, // 14 medium_id - 0x01, 0x00, 0x00, 0x00, // 15 noxfrm - 0x01, 0x00, 0x00, 0x00, // 16 nopolicy - 0x00, 0x00, 0x00, 0x00, // 17 force_igmp_version - 0x00, 0x00, 0x00, 0x00, // 18 arp_announce - 0x00, 0x00, 0x00, 0x00, // 19 arp_ignore - 0x00, 0x00, 0x00, 0x00, // 20 promote_secondaries (80 bytes) - 0x00, 0x00, 0x00, 0x00, // 21 arp_accept - 0x00, 0x00, 0x00, 0x00, // 22 arp_notify - 0x00, 0x00, 0x00, 0x00, // 23 accept_local - 0x00, 0x00, 0x00, 0x00, // 24 src_vmark - 0x00, 0x00, 0x00, 0x00, // 25 proxy_arp_pvlan - 0x00, 0x00, 0x00, 0x00, // 26 route_localnet - 0x10, 0x27, 0x00, 0x00, // 27 igmpv2_unsolicited_report_interval - 0xe8, 0x03, 0x00, 0x00, // 28 igmpv3_unsolicited_report_interval - 0x00, 0x00, 0x00, 0x00, // 29 ignore_routes_with_linkdown - 0x00, 0x00, 0x00, 0x00, // 30 drop_unicast_in_l2_multicast (120 bytes) - 0x00, 0x00, 0x00, 0x00, // 31 drop_gratuitous_arp - - // AF_INET6 (L=612, T=10) - 0x64, 0x02, 0x0a, 0x00, - // IFLA_INET6_FLAGS (L=8,T=1) - 0x08, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x80, - - // IFLA_INET6_CACHEINFO (L=20, T=5) - 0x14, 0x00, 0x05, 0x00, - 0xff, 0xff, 0x00, 0x00, // max_reasm_len - 0xaf, 0x00, 0x00, 0x00, // tstamp - 0x82, 0x64, 0x00, 0x00, // reachable_time - 0xe8, 0x03, 0x00, 0x00, // retrans_time - - // IFLA_INET6_CONF (L=208, T=2) - 0xd0, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, // forwarding - 0x40, 0x00, 0x00, 0x00, // hoplimit - 0x00, 0x00, 0x01, 0x00, // mtu6 - 0x01, 0x00, 0x00, 0x00, // accept_ra - 0x01, 0x00, 0x00, 0x00, // accept_redirects - 0x01, 0x00, 0x00, 0x00, // autoconf - 0x01, 0x00, 0x00, 0x00, // dad_transmits - 0xff, 0xff, 0xff, 0xff, // rtr_solicits - 0xa0, 0x0f, 0x00, 0x00, // rtr_solicit_interval - 0xe8, 0x03, 0x00, 0x00, // rtr_solicit_delay - 0xff, 0xff, 0xff, 0xff, // use_tempaddr - 0x80, 0x3a, 0x09, 0x00, // temp_valid_lft - 0x80, 0x51, 0x01, 0x00, // temp_prefered_lft - 0x03, 0x00, 0x00, 0x00, // regen_max_retry - 0x58, 0x02, 0x00, 0x00, // max_desync_factor - 0x10, 0x00, 0x00, 0x00, // max_addresses - 0x00, 0x00, 0x00, 0x00, // force_mld_version - 0x01, 0x00, 0x00, 0x00, // accept_ra_defrtr - 0x01, 0x00, 0x00, 0x00, // accept_ra_pinfo - 0x01, 0x00, 0x00, 0x00, // accept_ra_rtr_pref - 0x60, 0xea, 0x00, 0x00, // rtr_probe_interval - 0x00, 0x00, 0x00, 0x00, // accept_ra_rt_info_max_plen - 0x00, 0x00, 0x00, 0x00, // proxy_ndp - 0x00, 0x00, 0x00, 0x00, // optimistic_dad - 0x00, 0x00, 0x00, 0x00, // accept_source_route - 0x00, 0x00, 0x00, 0x00, // mc_forwarding - 0x00, 0x00, 0x00, 0x00, // disable_ipv6 - 0xff, 0xff, 0xff, 0xff, // accept_dad - 0x00, 0x00, 0x00, 0x00, // force_tllao - 0x00, 0x00, 0x00, 0x00, // ndisc_notify - 0x10, 0x27, 0x00, 0x00, // mldv1_unsolicited_report_interval - 0xe8, 0x03, 0x00, 0x00, // mldv2_unsolicited_report_interval - 0x01, 0x00, 0x00, 0x00, // suppress_frag_ndisc - 0x00, 0x00, 0x00, 0x00, // accept_ra_from_local - 0x00, 0x00, 0x00, 0x00, // use_optimistic - 0x01, 0x00, 0x00, 0x00, // accept_ra_mtu - 0x00, 0x00, 0x00, 0x00, // stable_secret - 0x00, 0x00, 0x00, 0x00, // use_oif_addrs_only - 0x01, 0x00, 0x00, 0x00, // accept_ra_min_hop_limit - 0x00, 0x00, 0x00, 0x00, // ignore_routes_with_linkdown - 0x00, 0x00, 0x00, 0x00, // drop_unicast_in_l2_multicast - 0x00, 0x00, 0x00, 0x00, // drop_unsolicited_na - 0x00, 0x00, 0x00, 0x00, // keep_addr_on_down - 0x80, 0xee, 0x36, 0x00, // rtr_solicit_max_interval - 0x00, 0x00, 0x00, 0x00, // seg6_enabled - 0x00, 0x00, 0x00, 0x00, // seg6_require_hmac - 0x01, 0x00, 0x00, 0x00, // enhanced_dad - 0x00, 0x00, 0x00, 0x00, // addr_gen_mode - 0x00, 0x00, 0x00, 0x00, // disable_policy - 0x00, 0x00, 0x00, 0x00, // accept_ra_rt_info_min_plen - 0x00, 0x00, 0x00, 0x00, // ndisc_tclass - - // IFLA_INET6_STATS (L=292, T=3) - 0x24, 0x01, 0x03, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1 num - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2 in_pkts - 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3 in_octets - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 in_delivers - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 5 out_forw_datagrams - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6 out_pkts - 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 out_octets - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 in_hdr_errors - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9 in_too_big_errors - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 in_no_routes (40 bytes) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 11 in_addr_errors - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 in_unknown_protos - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 13 in_truncated_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 14 in_discards - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 15 out_discards - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 16 out_no_routes - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 17 reasm_timeout - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 18 reasm_reqds - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 19 reasm_oks - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 20 reasm_fails (80 bytes) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 21 frag_oks - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 22 frag_fails - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 23 frag_creates - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 24 in_mcast_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 25 out_mcast_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 26 in_bcast_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 27 out_bcast_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 28 in_mcast_octets - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 29 out_mcast_octets - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 30 in_bcast_octets (120 bytes) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 31 out_bcast_octets - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 in_csum_errors - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 33 in_no_ect_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 34 in_ect1_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 35 in_ect0_pkts - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 36 in_ce_pkts - - // IFLA_INET6_ICMP6STATS (L=52, T=6) - 0x34, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // num - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // in_msgs - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // in_errors - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // out_msgs - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // out_errors - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // csum_errors - - // IFLA_INET6_TOKEN (L=20, T=7) - 0x14, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - // IFLA_INET6_ADDR_GEN_MODE (L=5, T=8) - 0x05, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x00, 0x00]; - -lazy_static! { - static ref BUFFER: NlaBuffer<&'static [u8]> = - NlaBuffer::new_checked(&BYTES[..]).unwrap(); -} - -fn get_nlas( -) -> impl Iterator, DecodeError>> { - NlasIterator::new(BUFFER.value()) -} - -fn get_byte_buffer(nla: &dyn Emitable) -> Vec { - let mut buf = vec![0u8; nla.buffer_len()]; - nla.emit(&mut buf); - buf -} - -lazy_static! { - static ref PARSED_AF_INET6: AfSpecInet = AfSpecInet::Inet6(vec![ - Inet6::Flags(2147483648), - Inet6::CacheInfo(get_byte_buffer(&Inet6CacheInfo { - max_reasm_len: 65535, - tstamp: 175, - reachable_time: 25730, - retrans_time: 1000, - })), - Inet6::DevConf(get_byte_buffer(&Inet6DevConf { - forwarding: 0, - hoplimit: 64, - mtu6: 65536, - accept_ra: 1, - accept_redirects: 1, - autoconf: 1, - dad_transmits: 1, - rtr_solicits: -1, - rtr_solicit_interval: 4000, - rtr_solicit_delay: 1000, - use_tempaddr: -1, - temp_valid_lft: 604800, - temp_prefered_lft: 86400, - regen_max_retry: 3, - max_desync_factor: 600, - max_addresses: 16, - force_mld_version: 0, - accept_ra_defrtr: 1, - accept_ra_pinfo: 1, - accept_ra_rtr_pref: 1, - rtr_probe_interval: 60000, - accept_ra_rt_info_max_plen: 0, - proxy_ndp: 0, - optimistic_dad: 0, - accept_source_route: 0, - mc_forwarding: 0, - disable_ipv6: 0, - accept_dad: -1, - force_tllao: 0, - ndisc_notify: 0, - mldv1_unsolicited_report_interval: 10000, - mldv2_unsolicited_report_interval: 1000, - suppress_frag_ndisc: 1, - accept_ra_from_local: 0, - use_optimistic: 0, - accept_ra_mtu: 1, - stable_secret: 0, - use_oif_addrs_only: 0, - accept_ra_min_hop_limit: 1, - ignore_routes_with_linkdown: 0, - drop_unicast_in_l2_multicast: 0, - drop_unsolicited_na: 0, - keep_addr_on_down: 0, - rtr_solicit_max_interval: 3600000, - seg6_enabled: 0, - seg6_require_hmac: 0, - enhanced_dad: 1, - addr_gen_mode: 0, - disable_policy: 0, - accept_ra_rt_info_min_plen: 0, - ndisc_tclass: 0, - })), - Inet6::Stats(get_byte_buffer(&Inet6Stats { - num: 36, - in_pkts: 6, - in_octets: 420, - in_delivers: 6, - out_forw_datagrams: 0, - out_pkts: 6, - out_octets: 420, - in_hdr_errors: 0, - in_too_big_errors: 0, - in_no_routes: 2, - in_addr_errors: 0, - in_unknown_protos: 0, - in_truncated_pkts: 0, - in_discards: 0, - out_discards: 0, - out_no_routes: 0, - reasm_timeout: 0, - reasm_reqds: 0, - reasm_oks: 0, - reasm_fails: 0, - frag_oks: 0, - frag_fails: 0, - frag_creates: 0, - in_mcast_pkts: 0, - out_mcast_pkts: 0, - in_bcast_pkts: 0, - out_bcast_pkts: 0, - in_mcast_octets: 0, - out_mcast_octets: 0, - in_bcast_octets: 0, - out_bcast_octets: 0, - in_csum_errors: 0, - in_no_ect_pkts: 6, - in_ect1_pkts: 0, - in_ect0_pkts: 0, - in_ce_pkts: 0, - })), - Inet6::IcmpStats(get_byte_buffer(&Icmp6Stats { - num: 6, - in_msgs: 0, - in_errors: 0, - out_msgs: 0, - out_errors: 0, - csum_errors: 0, - })), - Inet6::Token([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - Inet6::AddrGenMode(0), - ]); -} - -lazy_static! { - static ref PARSED_AF_INET: AfSpecInet = - AfSpecInet::Inet(vec![Inet::DevConf(get_byte_buffer(&InetDevConf { - forwarding: 1, - mc_forwarding: 0, - proxy_arp: 0, - accept_redirects: 1, - secure_redirects: 1, - send_redirects: 1, - shared_media: 1, - rp_filter: 0, - accept_source_route: 1, - bootp_relay: 0, - log_martians: 0, - tag: 0, - arpfilter: 0, - medium_id: 0, - noxfrm: 1, - nopolicy: 1, - force_igmp_version: 0, - arp_announce: 0, - arp_ignore: 0, - promote_secondaries: 0, - arp_accept: 0, - arp_notify: 0, - accept_local: 0, - src_vmark: 0, - proxy_arp_pvlan: 0, - route_localnet: 0, - igmpv2_unsolicited_report_interval: 10000, - igmpv3_unsolicited_report_interval: 1000, - ignore_routes_with_linkdown: 0, - drop_unicast_in_l2_multicast: 0, - drop_gratuitous_arp: 0, - }))]); -} - -#[test] -fn af_spec_header() { - assert_eq!(BUFFER.length(), 748); - assert_eq!(BUFFER.kind(), IFLA_AF_SPEC); -} - -#[test] -fn parse_af_inet() { - let mut nlas = get_nlas(); - // take the first nla - let inet_buf = nlas.next().unwrap().unwrap(); - - // buffer checks - assert_eq!(inet_buf.length(), 132); - assert_eq!(inet_buf.kind(), AF_INET); - assert_eq!(inet_buf.value().len(), 128); - - // parsing check - let parsed = AfSpecInet::parse(&inet_buf).unwrap(); - assert_eq!(parsed, *PARSED_AF_INET); -} - -#[test] -fn emit_af_inet() { - let mut bytes = [0xff; 132]; - - // Note: the value is a Vec of nlas, so the padding is automatically added - // for each nla. - assert_eq!(PARSED_AF_INET.value_len(), 128); - assert_eq!(PARSED_AF_INET.buffer_len(), 128 + 4); - - PARSED_AF_INET.emit(&mut bytes[..]); - - let buf = NlaBuffer::new_checked(&bytes[..]).unwrap(); - - let mut nlas = get_nlas(); - let expected_buf = nlas.next().unwrap().unwrap(); - - assert_eq!(expected_buf.kind(), buf.kind()); - assert_eq!(expected_buf.length(), buf.length()); - assert_eq!(expected_buf.value(), buf.value()); -} - -#[test] -fn emit_af_inet6() { - let mut bytes = vec![0xff; 612]; - - // Note: the value is a Vec of nlas, so the padding is automatically added - // for each nla. - assert_eq!(PARSED_AF_INET6.value_len(), 608); - assert_eq!(PARSED_AF_INET6.buffer_len(), 608 + 4); - PARSED_AF_INET6.emit(&mut bytes[..]); - - let buf = NlaBuffer::new_checked(&bytes[..]).unwrap(); - - let mut nlas = get_nlas(); - let _ = nlas.next(); - let expected_buf = nlas.next().unwrap().unwrap(); - - assert_eq!(expected_buf.kind(), buf.kind()); - assert_eq!(expected_buf.length(), buf.length()); - assert_eq!(expected_buf.value(), buf.value()); -} - -#[test] -fn parse_af_inet6() { - let mut nlas = get_nlas(); - // take the first nla - let _ = nlas.next().unwrap(); - let inet6_buf = nlas.next().unwrap().unwrap(); - - assert_eq!(inet6_buf.length(), 612); - assert_eq!(inet6_buf.kind(), AF_INET6); - assert_eq!(inet6_buf.value().len(), 608); - let parsed = AfSpecInet::parse(&inet6_buf).unwrap(); - - assert_eq!(parsed, *PARSED_AF_INET6); - - // Normally this is the end of the nla iterator - assert!(nlas.next().is_none()); -} diff --git a/src/rtnl/link/tests/mod.rs b/src/rtnl/link/tests/mod.rs deleted file mode 100644 index 0e85e340..00000000 --- a/src/rtnl/link/tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: MIT - -#[cfg(test)] -mod vxlan; diff --git a/src/rtnl/link/tests/vxlan.rs b/src/rtnl/link/tests/vxlan.rs deleted file mode 100644 index 322019e7..00000000 --- a/src/rtnl/link/tests/vxlan.rs +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: MIT - -use crate::RtnlMessage; -use netlink_packet_core::NetlinkMessage; - -#[test] -fn test_parsing_link_vxlan() { - let raw = vec![ - 0xe8, 0x04, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x96, 0xc9, 0x34, 0x65, - 0x87, 0x86, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x43, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x03, 0x00, - 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x30, 0x00, 0x00, 0x08, 0x00, 0x0d, 0x00, - 0xe8, 0x03, 0x00, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, - 0xaa, 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, 0x3d, 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, 0x3a, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x3b, 0x00, - 0xf8, 0xff, 0x07, 0x00, 0x08, 0x00, 0x3c, 0x00, 0xff, 0xff, 0x00, 0x00, - 0x08, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x21, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x6e, 0x6f, 0x71, 0x75, - 0x65, 0x75, 0x65, 0x00, 0x08, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x30, 0x00, - 0x00, 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, 0x00, 0x23, 0x45, 0x67, 0x89, 0x1c, 0x00, 0x00, - 0x0a, 0x00, 0x02, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 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, 0xba, 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, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 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, 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, - 0xba, 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, - 0xba, 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, 0x0c, 0x00, 0x2b, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x12, 0x00, 0x0a, 0x00, 0x01, 0x00, - 0x76, 0x78, 0x6c, 0x61, 0x6e, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x02, 0x00, - 0x08, 0x00, 0x01, 0x00, 0x65, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, - 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x04, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x08, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x08, 0x00, 0x09, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x12, 0xb5, 0x00, 0x00, - 0x05, 0x00, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x13, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x1a, 0x00, - 0x8c, 0x00, 0x02, 0x00, 0x88, 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, 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x0a, 0x00, - 0x08, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x80, 0x14, 0x00, 0x05, 0x00, - 0xff, 0xff, 0x00, 0x00, 0xb0, 0x1c, 0x00, 0x00, 0x0e, 0x8c, 0x00, 0x00, - 0xe8, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0xaa, 0x05, 0x00, 0x00, 0x01, 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, - 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, 0x04, 0x00, 0x3e, 0x80, - ]; - - // TODO: Expand this test to check both deserialize and serialize - NetlinkMessage::::deserialize(raw.as_slice()).unwrap(); -} diff --git a/src/rtnl/message.rs b/src/rtnl/message.rs index f23591b7..b99ef9d4 100644 --- a/src/rtnl/message.rs +++ b/src/rtnl/message.rs @@ -10,7 +10,7 @@ use netlink_packet_core::{ }; use crate::{ - constants::*, AddressMessage, LinkMessage, NeighbourMessage, + constants::*, link::LinkMessage, AddressMessage, NeighbourMessage, NeighbourTableMessage, NsidMessage, RouteMessage, RtnlMessageBuffer, RuleMessage, TcMessage, }; diff --git a/src/rtnl/mod.rs b/src/rtnl/mod.rs index 1a64948e..d98c7ef1 100644 --- a/src/rtnl/mod.rs +++ b/src/rtnl/mod.rs @@ -5,9 +5,6 @@ pub use address::{ AddressHeader, AddressMessage, AddressMessageBuffer, ADDRESS_HEADER_LEN, }; -pub mod link; -pub use link::{LinkHeader, LinkMessage, LinkMessageBuffer, LINK_HEADER_LEN}; - pub mod neighbour; pub use neighbour::{ NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, @@ -45,12 +42,8 @@ pub use self::message::*; pub mod nlas { pub use super::{ - address::nlas as address, link::nlas as link, - neighbour::nlas as neighbour, neighbour_table::nlas as neighbour_table, - nsid::nlas as nsid, route::nlas as route, rule::nlas as rule, - tc::nlas as tc, + address::nlas as address, neighbour::nlas as neighbour, + neighbour_table::nlas as neighbour_table, nsid::nlas as nsid, + route::nlas as route, rule::nlas as rule, tc::nlas as tc, }; } - -#[cfg(test)] -mod test; diff --git a/src/rtnl/neighbour/message.rs b/src/rtnl/neighbour/message.rs index 30fafbba..2b2143b0 100644 --- a/src/rtnl/neighbour/message.rs +++ b/src/rtnl/neighbour/message.rs @@ -56,7 +56,8 @@ impl<'a, T: AsRef<[u8]> + 'a> Parseable> #[cfg(test)] mod test { use crate::{ - constants::*, NeighbourHeader, NeighbourMessage, NeighbourMessageBuffer, + constants::*, AddressFamily, NeighbourHeader, NeighbourMessage, + NeighbourMessageBuffer, }; use netlink_packet_utils::traits::Emitable; @@ -82,7 +83,7 @@ mod test { #[test] fn packet_header_read() { let packet = NeighbourMessageBuffer::new(&HEADER[0..12]); - assert_eq!(packet.family(), AF_INET6 as u8); + assert_eq!(packet.family(), u8::from(AddressFamily::Inet6)); assert_eq!(packet.ifindex(), 1); assert_eq!(packet.state(), NUD_REACHABLE); assert_eq!(packet.flags(), NTF_ROUTER); @@ -94,7 +95,7 @@ mod test { let mut buf = vec![0xff; 12]; { let mut packet = NeighbourMessageBuffer::new(&mut buf); - packet.set_family(AF_INET6 as u8); + packet.set_family(u8::from(AddressFamily::Inet6)); packet.set_ifindex(1); packet.set_state(NUD_REACHABLE); packet.set_flags(NTF_ROUTER); @@ -106,7 +107,7 @@ mod test { #[test] fn emit() { let header = NeighbourHeader { - family: AF_INET6 as u8, + family: u8::from(AddressFamily::Inet6), ifindex: 1, state: NUD_REACHABLE, flags: NTF_ROUTER, diff --git a/src/rtnl/tc/test.rs b/src/rtnl/tc/test.rs index 8316fcca..724c121a 100644 --- a/src/rtnl/tc/test.rs +++ b/src/rtnl/tc/test.rs @@ -94,7 +94,7 @@ fn tc_packet_nlas_read() { nla.check_buffer_length().unwrap(); assert_eq!(nla.length(), 4); assert_eq!(nla.kind(), TCA_OPTIONS); - assert_eq!(nla.value(), []); + assert!(nla.value().is_empty()); let nla = nlas.next().unwrap().unwrap(); nla.check_buffer_length().unwrap(); diff --git a/src/rtnl/test.rs b/src/rtnl/test.rs deleted file mode 100644 index 2dd6d151..00000000 --- a/src/rtnl/test.rs +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: MIT - -#![cfg(test)] - -use netlink_packet_core::NetlinkBuffer; -use netlink_packet_utils::traits::ParseableParametrized; - -use crate::{ - nlas::link::{Info, InfoKind, Nla}, - LinkHeader, LinkMessage, RtnlMessage, RtnlMessageBuffer, RTM_NEWLINK, -}; - -// This test was added because one of the NLA's payload is a string that is not -// null terminated. I'm not sure if we missed something in the IFLA_LINK_INFO -// spec, or if linux/iproute2 is being a bit inconsistent here. -// -// This message was created using `ip link add qemu-br1 type bridge`. -#[rustfmt::skip] -#[test] -fn test_non_null_terminated_string() { - let data = vec![ - 0x40, 0x00, 0x00, 0x00, // length = 64 - 0x10, 0x00, // message type = 16 = (create network interface) - 0x05, 0x06, // flags - 0x81, 0x74, 0x57, 0x5c, // seq id - 0x00, 0x00, 0x00, 0x00, // pid - 0x00, // interface family - 0x00, // padding - 0x00, 0x00, // device type (NET/ROM pseudo) - 0x00, 0x00, 0x00, 0x00, // interface index - 0x00, 0x00, 0x00, 0x00, // device flags - 0x00, 0x00, 0x00, 0x00, // device change flags - // NLA: device name - 0x0d, 0x00, // length = 13 - 0x03, 0x00, // type = 3 - // value=qemu-br1 NOTE THAT THIS IS NULL-TERMINATED - 0x71, 0x65, 0x6d, 0x75, 0x2d, 0x62, 0x72, 0x31, 0x00, - 0x00, 0x00, 0x00, // padding - // NLA: Link info - 0x10, 0x00, // length = 16 - 0x12, 0x00, // type = link info - // nested NLA: - 0x0a, 0x00, // length = 10 - 0x01, 0x00, // type = 1 = IFLA_INFO_KIND - // "bridge" NOTE THAT THIS IS NOT NULL-TERMINATED! - 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, - 0x00, 0x00, // padding - ]; - let expected = RtnlMessage::NewLink(LinkMessage { - header: LinkHeader::default(), - nlas: vec![ - Nla::IfName(String::from("qemu-br1")), - Nla::Info(vec![Info::Kind(InfoKind::Bridge)]), - ], - }); - let nl_buffer = NetlinkBuffer::new(&data).payload(); - let rtnl_buffer = RtnlMessageBuffer::new(&nl_buffer); - let actual = RtnlMessage::parse_with_param(&rtnl_buffer, RTM_NEWLINK).unwrap(); - assert_eq!(expected, actual); -} - -#[rustfmt::skip] -#[test] -fn test_attach_to_bridge() { - use crate::*; - let data = vec![ - 0x28, 0x00, 0x00, 0x00, // length - 0x10, 0x00, // type - 0x05, 0x00, // flags - 0x9c, 0x9d, 0x57, 0x5c, // seq id - 0x00, 0x00, 0x00, 0x00, // pid - 0x00, // interface family - 0x00, // padding - 0x00, 0x00, // device type - 0x06, 0x00, 0x00, 0x00, // interface index - 0x00, 0x00, 0x00, 0x00, // device flags - 0x00, 0x00, 0x00, 0x00, // device change flags - // NLA (set master) - 0x08, 0x00, // length - 0x0a, 0x00, // type - 0x05, 0x00, 0x00, 0x00 // index of the master interface - ]; - let nl_buffer = NetlinkBuffer::new(&data).payload(); - let rtnl_buffer = RtnlMessageBuffer::new(&nl_buffer); - let actual = RtnlMessage::parse_with_param(&rtnl_buffer, RTM_NEWLINK).unwrap(); - let expected = RtnlMessage::NewLink(LinkMessage { - header: LinkHeader { - interface_family: 0, - index: 6, - link_layer_type: 0, - flags: 0, - change_mask: 0, - }, - nlas: vec![Nla::Master(5)], - }); - assert_eq!(expected, actual); -} diff --git a/tools/test_cross_build.sh b/tools/test_cross_build.sh new file mode 100755 index 00000000..779af286 --- /dev/null +++ b/tools/test_cross_build.sh @@ -0,0 +1,19 @@ +#!/bin/bash -ex +# SPDX-License-Identifier: MIT + +EXEC_PATH=$(dirname "$(realpath "$0")") +PROJECT_PATH="$(dirname $EXEC_PATH)" + +CI_CONFIG_FOR_BUILD="$PROJECT_PATH/.github/workflows/build.yml" +BUILD_TARGETS=$(sed -ne 's/^.*rust_target: "\(.*\)"/\1/p' $CI_CONFIG_FOR_BUILD) + +cd $PROJECT_PATH + +for BUILD_TARGET in $BUILD_TARGETS;do + if [ "CHK$(rustup target list --installed \ + | grep $BUILD_TARGET)" == "CHK" ];then + rustup target add $BUILD_TARGET + fi + + cargo build --target $BUILD_TARGET +done