From cc7cd573079bc7c8e6385687b0636fdcad775aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julius=20R=C3=BCberg?= Date: Sat, 9 Aug 2025 18:22:59 +0200 Subject: [PATCH] Add enum entries to InfoGreTun and InfoGreTun6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Julius Rüberg --- src/link/link_info/gre.rs | 43 -- src/link/link_info/gre/gre_common.rs | 110 +++++ src/link/link_info/gre/info_gre.rs | 184 +++++++ src/link/link_info/gre/info_gre6.rs | 174 +++++++ src/link/link_info/gre/mod.rs | 11 + src/link/link_info/gre6.rs | 43 -- src/link/link_info/gre_tap.rs | 43 -- src/link/link_info/gre_tap6.rs | 43 -- src/link/link_info/info_data.rs | 24 +- src/link/link_info/mod.rs | 8 +- src/link/mod.rs | 6 +- src/link/tests/gre.rs | 692 +++++++++++++++++++++++++++ src/link/tests/mod.rs | 2 + 13 files changed, 1189 insertions(+), 194 deletions(-) delete mode 100644 src/link/link_info/gre.rs create mode 100644 src/link/link_info/gre/gre_common.rs create mode 100644 src/link/link_info/gre/info_gre.rs create mode 100644 src/link/link_info/gre/info_gre6.rs create mode 100644 src/link/link_info/gre/mod.rs delete mode 100644 src/link/link_info/gre6.rs delete mode 100644 src/link/link_info/gre_tap.rs delete mode 100644 src/link/link_info/gre_tap6.rs create mode 100644 src/link/tests/gre.rs diff --git a/src/link/link_info/gre.rs b/src/link/link_info/gre.rs deleted file mode 100644 index 7dbdc051..00000000 --- a/src/link/link_info/gre.rs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_core::{ - DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoGreTun { - Other(DefaultNla), -} - -impl Nla for InfoGreTun { - fn value_len(&self) -> usize { - match self { - Self::Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - match self { - Self::Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - match self { - Self::Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - #[allow(clippy::match_single_binding)] - Ok(match buf.kind() { - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind} for gre"))?, - ), - }) - } -} diff --git a/src/link/link_info/gre/gre_common.rs b/src/link/link_info/gre/gre_common.rs new file mode 100644 index 00000000..219fcee6 --- /dev/null +++ b/src/link/link_info/gre/gre_common.rs @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT + +use std::fmt::Display; + +const GRE_CSUM: u16 = 0x8000; +const GRE_ROUTING: u16 = 0x4000; +const GRE_KEY: u16 = 0x2000; +const GRE_SEQ: u16 = 0x1000; +const GRE_STRICT: u16 = 0x0800; +const GRE_REC: u16 = 0x0700; +const GRE_ACK: u16 = 0x0080; +const GRE_FLAGS: u16 = 0x0078; +const GRE_VERSION: u16 = 0x0007; + +const TUNNEL_ENCAP_FLAG_CSUM: u16 = 0x0100; +const TUNNEL_ENCAP_FLAG_CSUM6: u16 = 0x0200; +const TUNNEL_ENCAP_FLAG_REMCSUM: u16 = 0x0400; + +bitflags! { + #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] + #[non_exhaustive] + pub struct GreIOFlags: u16 { + const Checksum = GRE_CSUM; + const Routing = GRE_ROUTING; + const Key = GRE_KEY; + const Seq = GRE_SEQ; + const Strict0 = GRE_STRICT; + const Rec = GRE_REC; + const Ack = GRE_ACK; + const Flags = GRE_FLAGS; + const Version = GRE_VERSION; + + const _ = !0; + } + + #[derive(Clone, Eq, PartialEq, Debug, Copy, Default)] + #[non_exhaustive] + pub struct GreEncapFlags: u16 { + const Checksum = TUNNEL_ENCAP_FLAG_CSUM; + const Checksum6 = TUNNEL_ENCAP_FLAG_CSUM6; + const RemoteChecksum = TUNNEL_ENCAP_FLAG_REMCSUM; + + const _ = !0; + } +} + +const TUNNEL_ENCAP_NONE: u16 = 0; +const TUNNEL_ENCAP_FOU: u16 = 1; +const TUNNEL_ENCAP_GUE: u16 = 2; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] +#[non_exhaustive] +pub enum GreEncapType { + #[default] + None, + Fou, + Gue, + Other(u16), +} +impl From for GreEncapType { + fn from(d: u16) -> Self { + match d { + TUNNEL_ENCAP_NONE => GreEncapType::None, + TUNNEL_ENCAP_FOU => GreEncapType::Fou, + TUNNEL_ENCAP_GUE => GreEncapType::Gue, + _ => Self::Other(d), + } + } +} +impl From<&GreEncapType> for u16 { + fn from(t: &GreEncapType) -> Self { + match t { + GreEncapType::None => TUNNEL_ENCAP_NONE, + GreEncapType::Fou => TUNNEL_ENCAP_FOU, + GreEncapType::Gue => TUNNEL_ENCAP_GUE, + GreEncapType::Other(d) => *d, + } + } +} + +impl Display for GreEncapType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GreEncapType::None => write!(f, "none"), + GreEncapType::Fou => write!(f, "fou"), + GreEncapType::Gue => write!(f, "gue"), + GreEncapType::Other(d) => write!(f, "other({d})"), + } + } +} + +pub(super) const IFLA_GRE_LINK: u16 = 1; +pub(super) const IFLA_GRE_IFLAGS: u16 = 2; +pub(super) const IFLA_GRE_OFLAGS: u16 = 3; +pub(super) const IFLA_GRE_IKEY: u16 = 4; +pub(super) const IFLA_GRE_OKEY: u16 = 5; +pub(super) const IFLA_GRE_LOCAL: u16 = 6; +pub(super) const IFLA_GRE_REMOTE: u16 = 7; +pub(super) const IFLA_GRE_TTL: u16 = 8; +pub(super) const IFLA_GRE_TOS: u16 = 9; +pub(super) const IFLA_GRE_PMTUDISC: u16 = 10; +pub(super) const IFLA_GRE_ENCAP_LIMIT: u16 = 11; +pub(super) const IFLA_GRE_FLOWINFO: u16 = 12; +// pub(super) const IFLA_GRE_FLAGS: u16 = 13; +pub(super) const IFLA_GRE_ENCAP_TYPE: u16 = 14; +pub(super) const IFLA_GRE_ENCAP_FLAGS: u16 = 15; +pub(super) const IFLA_GRE_ENCAP_SPORT: u16 = 16; +pub(super) const IFLA_GRE_ENCAP_DPORT: u16 = 17; +pub(super) const IFLA_GRE_COLLECT_METADATA: u16 = 18; +pub(super) const IFLA_GRE_FWMARK: u16 = 20; diff --git a/src/link/link_info/gre/info_gre.rs b/src/link/link_info/gre/info_gre.rs new file mode 100644 index 00000000..25ce0e5a --- /dev/null +++ b/src/link/link_info/gre/info_gre.rs @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: MIT + +use std::{mem::size_of, net::Ipv4Addr}; + +use netlink_packet_core::{ + emit_u16_be, emit_u32_be, parse_u16_be, parse_u32_be, parse_u8, + DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, +}; + +use super::{ + gre_common::{ + IFLA_GRE_COLLECT_METADATA, IFLA_GRE_ENCAP_DPORT, IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_SPORT, IFLA_GRE_ENCAP_TYPE, IFLA_GRE_FWMARK, + IFLA_GRE_IFLAGS, IFLA_GRE_IKEY, IFLA_GRE_LOCAL, IFLA_GRE_OFLAGS, + IFLA_GRE_OKEY, IFLA_GRE_PMTUDISC, IFLA_GRE_REMOTE, IFLA_GRE_TOS, + IFLA_GRE_TTL, + }, + GreEncapFlags, GreEncapType, GreIOFlags, +}; +use crate::{ + ip::parse_ipv4_addr, link::link_info::gre::gre_common::IFLA_GRE_LINK, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoGre { + Link(u32), + IFlags(GreIOFlags), + OFlags(GreIOFlags), + IKey(u32), + OKey(u32), + Local(Ipv4Addr), + Remote(Ipv4Addr), + Ttl(u8), + Tos(u8), + PathMTUDiscovery(bool), + EncapType(GreEncapType), + EncapFlags(GreEncapFlags), + SourcePort(u16), + DestinationPort(u16), + CollectMetadata, + FwMask(u32), + Other(DefaultNla), +} + +impl Nla for InfoGre { + fn value_len(&self) -> usize { + match self { + Self::Link(_) => size_of::(), + Self::IFlags(_) | Self::OFlags(_) => size_of::(), + Self::IKey(_) | Self::OKey(_) => size_of::(), + Self::Local(_) | Self::Remote(_) => size_of::(), + Self::Ttl(_) | Self::Tos(_) | Self::PathMTUDiscovery(_) => { + size_of::() + } + Self::EncapType(_) => size_of::(), + Self::EncapFlags(_) => size_of::(), + Self::SourcePort(_) | Self::DestinationPort(_) => size_of::(), + Self::CollectMetadata => 0, + Self::FwMask(_) => size_of::(), + Self::Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::Link(id) => emit_u32_be(buffer, *id).unwrap(), + Self::IFlags(flags) | Self::OFlags(flags) => { + emit_u16_be(buffer, flags.bits()).unwrap() + } + Self::IKey(key) | Self::OKey(key) => { + emit_u32_be(buffer, *key).unwrap() + } + Self::Local(ip) | Self::Remote(ip) => { + buffer.copy_from_slice(&ip.octets()); + } + Self::Ttl(value) | Self::Tos(value) => buffer[0] = *value, + Self::PathMTUDiscovery(discover) => { + buffer[0] = if *discover { 1 } else { 0 } + } + Self::EncapType(t) => emit_u16_be(buffer, t.into()).unwrap(), + Self::EncapFlags(flags) => { + emit_u16_be(buffer, flags.bits()).unwrap() + } + Self::SourcePort(port) | Self::DestinationPort(port) => { + emit_u16_be(buffer, *port).unwrap() + } + Self::CollectMetadata => {} + Self::FwMask(fw_mask) => emit_u32_be(buffer, *fw_mask).unwrap(), + Self::Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + match self { + Self::Link(_) => IFLA_GRE_LINK, + Self::IFlags(_) => IFLA_GRE_IFLAGS, + Self::OFlags(_) => IFLA_GRE_OFLAGS, + Self::IKey(_) => IFLA_GRE_IKEY, + Self::Local(_) => IFLA_GRE_LOCAL, + Self::Remote(_) => IFLA_GRE_REMOTE, + Self::OKey(_) => IFLA_GRE_OKEY, + Self::Ttl(_) => IFLA_GRE_TTL, + Self::Tos(_) => IFLA_GRE_TOS, + Self::PathMTUDiscovery(_) => IFLA_GRE_PMTUDISC, + Self::EncapType(_) => IFLA_GRE_ENCAP_TYPE, + Self::EncapFlags(_) => IFLA_GRE_ENCAP_FLAGS, + Self::SourcePort(_) => IFLA_GRE_ENCAP_SPORT, + Self::DestinationPort(_) => IFLA_GRE_ENCAP_DPORT, + Self::CollectMetadata => IFLA_GRE_COLLECT_METADATA, + Self::FwMask(_) => IFLA_GRE_FWMARK, + Self::Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGre { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_GRE_LINK => Self::Link( + parse_u32_be(payload).context("invalid IFLA_GRE_LINK value")?, + ), + IFLA_GRE_IFLAGS => Self::IFlags(GreIOFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_IFLAGS value")?, + )), + IFLA_GRE_OFLAGS => Self::OFlags(GreIOFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_OFLAGS value")?, + )), + IFLA_GRE_IKEY => Self::IKey( + parse_u32_be(payload).context("invalid IFLA_GRE_IKEY value")?, + ), + IFLA_GRE_OKEY => Self::OKey( + parse_u32_be(payload).context("invalid IFLA_GRE_OKEY value")?, + ), + IFLA_GRE_LOCAL => Self::Local( + parse_ipv4_addr(payload) + .context("invalid IFLA_GRE_LOCAL value")?, + ), + IFLA_GRE_REMOTE => Self::Remote( + parse_ipv4_addr(payload) + .context("invalid IFLA_GRE_LOCAL value")?, + ), + IFLA_GRE_TTL => Self::Ttl( + parse_u8(payload).context("invalid IFLA_GRE_TTL value")?, + ), + IFLA_GRE_TOS => Self::Tos( + parse_u8(payload).context("invalid IFLA_GRE_TOS value")?, + ), + IFLA_GRE_PMTUDISC => Self::PathMTUDiscovery( + parse_u8(payload).context("invalid IFLA_GRE_TOS value")? == 1, + ), + IFLA_GRE_ENCAP_TYPE => Self::EncapType(GreEncapType::from( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_TYPE value")?, + )), + IFLA_GRE_ENCAP_FLAGS => { + Self::EncapFlags(GreEncapFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_FLAGS value")?, + )) + } + IFLA_GRE_ENCAP_SPORT => Self::SourcePort( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_SPORT value")?, + ), + IFLA_GRE_ENCAP_DPORT => Self::DestinationPort( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_DPORT value")?, + ), + IFLA_GRE_COLLECT_METADATA => Self::CollectMetadata, + IFLA_GRE_FWMARK => Self::FwMask( + parse_u32_be(payload) + .context("invalid IFLA_GRE_FWMARK value")?, + ), + kind => Self::Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind} for ip6gre"))?, + ), + }) + } +} diff --git a/src/link/link_info/gre/info_gre6.rs b/src/link/link_info/gre/info_gre6.rs new file mode 100644 index 00000000..8b32d5b5 --- /dev/null +++ b/src/link/link_info/gre/info_gre6.rs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: MIT + +use std::{mem::size_of, net::Ipv6Addr}; + +use netlink_packet_core::{ + emit_u16_be, emit_u32_be, parse_u16_be, parse_u32_be, parse_u8, + DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, +}; + +use super::{ + gre_common::{ + IFLA_GRE_COLLECT_METADATA, IFLA_GRE_ENCAP_DPORT, IFLA_GRE_ENCAP_FLAGS, + IFLA_GRE_ENCAP_LIMIT, IFLA_GRE_ENCAP_SPORT, IFLA_GRE_ENCAP_TYPE, + IFLA_GRE_FLOWINFO, IFLA_GRE_FWMARK, IFLA_GRE_IFLAGS, IFLA_GRE_IKEY, + IFLA_GRE_LOCAL, IFLA_GRE_OFLAGS, IFLA_GRE_OKEY, IFLA_GRE_REMOTE, + IFLA_GRE_TTL, + }, + GreEncapFlags, GreEncapType, GreIOFlags, +}; +use crate::ip::parse_ipv6_addr; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoGre6 { + IFlags(GreIOFlags), + OFlags(GreIOFlags), + IKey(u32), + OKey(u32), + Local(Ipv6Addr), + Remote(Ipv6Addr), + Ttl(u8), + EncapLimit(u8), + FlowLabel(u32), + EncapType(GreEncapType), + EncapFlags(GreEncapFlags), + SourcePort(u16), + DestinationPort(u16), + CollectMetadata, + FwMask(u32), + Other(DefaultNla), +} + +impl Nla for InfoGre6 { + fn value_len(&self) -> usize { + match self { + Self::IFlags(_) | Self::OFlags(_) => size_of::(), + Self::IKey(_) | Self::OKey(_) => size_of::(), + Self::Local(_) | Self::Remote(_) => size_of::(), + Self::Ttl(_) | Self::EncapLimit(_) => size_of::(), + Self::FlowLabel(_) => size_of::(), + Self::EncapType(_) => size_of::(), + Self::EncapFlags(_) => size_of::(), + Self::SourcePort(_) | Self::DestinationPort(_) => size_of::(), + Self::CollectMetadata => 0, + Self::FwMask(_) => size_of::(), + Self::Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + match self { + Self::IFlags(flags) | Self::OFlags(flags) => { + emit_u16_be(buffer, flags.bits()).unwrap() + } + Self::IKey(key) | Self::OKey(key) => { + emit_u32_be(buffer, *key).unwrap() + } + Self::Local(ip) | Self::Remote(ip) => { + buffer.copy_from_slice(&ip.octets()); + } + Self::Ttl(value) | Self::EncapLimit(value) => buffer[0] = *value, + Self::EncapFlags(flags) => { + emit_u16_be(buffer, flags.bits()).unwrap() + } + Self::FlowLabel(value) => emit_u32_be(buffer, *value).unwrap(), + Self::EncapType(t) => emit_u16_be(buffer, t.into()).unwrap(), + Self::SourcePort(port) | Self::DestinationPort(port) => { + emit_u16_be(buffer, *port).unwrap() + } + Self::CollectMetadata => {} + Self::FwMask(fw_mask) => emit_u32_be(buffer, *fw_mask).unwrap(), + Self::Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + match self { + Self::IFlags(_) => IFLA_GRE_IFLAGS, + Self::OFlags(_) => IFLA_GRE_OFLAGS, + Self::IKey(_) => IFLA_GRE_IKEY, + Self::OKey(_) => IFLA_GRE_OKEY, + Self::Local(_) => IFLA_GRE_LOCAL, + Self::Remote(_) => IFLA_GRE_REMOTE, + Self::Ttl(_) => IFLA_GRE_TTL, + Self::EncapLimit(_) => IFLA_GRE_ENCAP_LIMIT, + Self::FlowLabel(_) => IFLA_GRE_FLOWINFO, + Self::EncapType(_) => IFLA_GRE_ENCAP_TYPE, + Self::EncapFlags(_) => IFLA_GRE_ENCAP_FLAGS, + Self::SourcePort(_) => IFLA_GRE_ENCAP_SPORT, + Self::DestinationPort(_) => IFLA_GRE_ENCAP_DPORT, + Self::CollectMetadata => IFLA_GRE_COLLECT_METADATA, + Self::FwMask(_) => IFLA_GRE_FWMARK, + Self::Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGre6 { + fn parse(buf: &NlaBuffer<&'a T>) -> Result { + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_GRE_IFLAGS => Self::IFlags(GreIOFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_IFLAGS value")?, + )), + IFLA_GRE_OFLAGS => Self::OFlags(GreIOFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_OFLAGS value")?, + )), + IFLA_GRE_IKEY => Self::IKey( + parse_u32_be(payload).context("invalid IFLA_GRE_IKEY value")?, + ), + IFLA_GRE_OKEY => Self::OKey( + parse_u32_be(payload).context("invalid IFLA_GRE_OKEY value")?, + ), + IFLA_GRE_LOCAL => Self::Local( + parse_ipv6_addr(payload) + .context("invalid IFLA_GRE_LOCAL value")?, + ), + IFLA_GRE_REMOTE => Self::Remote( + parse_ipv6_addr(payload) + .context("invalid IFLA_GRE_LOCAL value")?, + ), + IFLA_GRE_TTL => Self::Ttl( + parse_u8(payload).context("invalid IFLA_GRE_TTL value")?, + ), + IFLA_GRE_ENCAP_LIMIT => Self::EncapLimit( + parse_u8(payload) + .context("invalid IFLA_GRE_ENCAP_LIMIT value")?, + ), + IFLA_GRE_FLOWINFO => Self::FlowLabel( + parse_u32_be(payload) + .context("invalid IFLA_GRE_FLOWINFO value")?, + ), + IFLA_GRE_ENCAP_TYPE => Self::EncapType(GreEncapType::from( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_TYPE value")?, + )), + IFLA_GRE_ENCAP_FLAGS => { + Self::EncapFlags(GreEncapFlags::from_bits_retain( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_FLAGS value")?, + )) + } + IFLA_GRE_ENCAP_SPORT => Self::SourcePort( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_SPORT value")?, + ), + IFLA_GRE_ENCAP_DPORT => Self::DestinationPort( + parse_u16_be(payload) + .context("invalid IFLA_GRE_ENCAP_DPORT value")?, + ), + IFLA_GRE_COLLECT_METADATA => Self::CollectMetadata, + IFLA_GRE_FWMARK => Self::FwMask( + parse_u32_be(payload) + .context("invalid IFLA_GRE_FWMARK value")?, + ), + kind => Self::Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind} for ip6gre"))?, + ), + }) + } +} diff --git a/src/link/link_info/gre/mod.rs b/src/link/link_info/gre/mod.rs new file mode 100644 index 00000000..99e07bfc --- /dev/null +++ b/src/link/link_info/gre/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +mod gre_common; +pub mod info_gre; +pub mod info_gre6; + +pub use self::{ + gre_common::{GreEncapFlags, GreEncapType, GreIOFlags}, + info_gre::InfoGre, + info_gre6::InfoGre6, +}; diff --git a/src/link/link_info/gre6.rs b/src/link/link_info/gre6.rs deleted file mode 100644 index 2dbc146b..00000000 --- a/src/link/link_info/gre6.rs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_core::{ - DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoGreTun6 { - Other(DefaultNla), -} - -impl Nla for InfoGreTun6 { - fn value_len(&self) -> usize { - match self { - Self::Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - match self { - Self::Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - match self { - Self::Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTun6 { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - #[allow(clippy::match_single_binding)] - Ok(match buf.kind() { - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind} for ip6gre"))?, - ), - }) - } -} diff --git a/src/link/link_info/gre_tap.rs b/src/link/link_info/gre_tap.rs deleted file mode 100644 index 440cb608..00000000 --- a/src/link/link_info/gre_tap.rs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_core::{ - DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoGreTap { - Other(DefaultNla), -} - -impl Nla for InfoGreTap { - fn value_len(&self) -> usize { - match self { - Self::Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - match self { - Self::Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - match self { - Self::Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - #[allow(clippy::match_single_binding)] - Ok(match buf.kind() { - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind} for gretap"))?, - ), - }) - } -} diff --git a/src/link/link_info/gre_tap6.rs b/src/link/link_info/gre_tap6.rs deleted file mode 100644 index d1d35214..00000000 --- a/src/link/link_info/gre_tap6.rs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: MIT - -use netlink_packet_core::{ - DecodeError, DefaultNla, ErrorContext, Nla, NlaBuffer, Parseable, -}; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum InfoGreTap6 { - Other(DefaultNla), -} - -impl Nla for InfoGreTap6 { - fn value_len(&self) -> usize { - match self { - Self::Other(nla) => nla.value_len(), - } - } - - fn emit_value(&self, buffer: &mut [u8]) { - match self { - Self::Other(nla) => nla.emit_value(buffer), - } - } - - fn kind(&self) -> u16 { - match self { - Self::Other(nla) => nla.kind(), - } - } -} - -impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoGreTap6 { - fn parse(buf: &NlaBuffer<&'a T>) -> Result { - #[allow(clippy::match_single_binding)] - Ok(match buf.kind() { - kind => Self::Other( - DefaultNla::parse(buf) - .context(format!("unknown NLA type {kind} for gretap6"))?, - ), - }) - } -} diff --git a/src/link/link_info/info_data.rs b/src/link/link_info/info_data.rs index 4dcb777a..98d9105b 100644 --- a/src/link/link_info/info_data.rs +++ b/src/link/link_info/info_data.rs @@ -6,10 +6,10 @@ use netlink_packet_core::{ }; use super::super::{ - InfoBond, InfoBridge, InfoGeneve, InfoGreTap, InfoGreTap6, InfoGreTun, - InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, - InfoMacSec, InfoMacVlan, InfoMacVtap, InfoSitTun, InfoTun, InfoVeth, - InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm, + InfoBond, InfoBridge, InfoGeneve, InfoGre, InfoGre6, InfoGtp, InfoHsr, + InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, + InfoMacVtap, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVti, + InfoVxlan, InfoXfrm, }; const IFLA_INFO_DATA: u16 = 2; @@ -27,11 +27,11 @@ pub enum InfoData { IpVtap(Vec), MacVlan(Vec), MacVtap(Vec), - GreTap(Vec), - GreTap6(Vec), + GreTap(Vec), + GreTap6(Vec), SitTun(Vec), - GreTun(Vec), - GreTun6(Vec), + GreTun(Vec), + GreTun6(Vec), Vti(Vec), Vrf(Vec), Gtp(Vec), @@ -225,7 +225,7 @@ impl InfoData { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; - let parsed = InfoGreTap::parse(nla)?; + let parsed = InfoGre::parse(nla)?; v.push(parsed); } InfoData::GreTap(v) @@ -236,7 +236,7 @@ impl InfoData { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; - let parsed = InfoGreTap6::parse(nla)?; + let parsed = InfoGre6::parse(nla)?; v.push(parsed); } InfoData::GreTap6(v) @@ -258,7 +258,7 @@ impl InfoData { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; - let parsed = InfoGreTun::parse(nla)?; + let parsed = InfoGre::parse(nla)?; v.push(parsed); } InfoData::GreTun(v) @@ -269,7 +269,7 @@ impl InfoData { let nla = &nla.context(format!( "invalid IFLA_INFO_DATA for {kind} {payload:?}" ))?; - let parsed = InfoGreTun6::parse(nla)?; + let parsed = InfoGre6::parse(nla)?; v.push(parsed); } InfoData::GreTun6(v) diff --git a/src/link/link_info/mod.rs b/src/link/link_info/mod.rs index 3233852b..25ad0553 100644 --- a/src/link/link_info/mod.rs +++ b/src/link/link_info/mod.rs @@ -6,9 +6,6 @@ mod bridge; mod bridge_port; mod geneve; mod gre; -mod gre6; -mod gre_tap; -mod gre_tap6; mod gtp; mod hsr; mod info_data; @@ -38,10 +35,7 @@ pub use self::{ bridge::{BridgeId, BridgeIdBuffer, BridgeQuerierState, InfoBridge}, bridge_port::{BridgePortMulticastRouter, BridgePortState, InfoBridgePort}, geneve::{GeneveDf, InfoGeneve}, - gre::InfoGreTun, - gre6::InfoGreTun6, - gre_tap::InfoGreTap, - gre_tap6::InfoGreTap6, + gre::{GreEncapFlags, GreEncapType, GreIOFlags, InfoGre, InfoGre6}, gtp::InfoGtp, hsr::{HsrProtocol, InfoHsr}, info_data::InfoData, diff --git a/src/link/mod.rs b/src/link/mod.rs index b060041b..072faee3 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -44,9 +44,9 @@ pub use self::{ BondAdInfo, BondArpAllTargets, BondArpValidate, BondFailOverMac, BondMode, BondPortState, BondPrimaryReselect, BondXmitHashPolicy, BridgeId, BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, - BridgeQuerierState, GeneveDf, HsrProtocol, InfoBond, InfoBondPort, - InfoBridge, InfoBridgePort, InfoData, InfoGeneve, InfoGreTap, - InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, + BridgeQuerierState, GeneveDf, GreEncapFlags, GreEncapType, GreIOFlags, + HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, + InfoData, InfoGeneve, InfoGre, InfoGre6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanFlags, diff --git a/src/link/tests/gre.rs b/src/link/tests/gre.rs new file mode 100644 index 00000000..2878129c --- /dev/null +++ b/src/link/tests/gre.rs @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: MIT + +use std::net::{Ipv4Addr, Ipv6Addr}; + +use netlink_packet_core::{DefaultNla, Emitable, Parseable}; + +use crate::link::{ + GreEncapFlags, GreEncapType, GreIOFlags, InfoData, InfoGre, InfoGre6, + InfoKind, LinkAttribute, LinkInfo, LinkMessage, LinkMessageBuffer, +}; + +#[test] +fn test_create_gre6_external() { + // This is nlmon capture for `ip link add name foo type ip6gre external` + // on Linux kernel 6.15.9 with iproute2 version 6.16.0 + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x18, 0x00, // length 24 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0b, 0x00, // length 12 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x69, 0x70, 0x36, 0x67, 0x72, 0x65, 0x00, // ip6gre\0 + // NOTE: padding is included in the length (0x0b) by the Kernel + // response but iproute does not include the padding (0x0a). + 0x00, //padding + 0x08, 0x00, // length 8 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x04, 0x00, // length 4 + 0x12, 0x00, // IFLA_GRE_COLLECT_METADATA 18 + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTun6), + LinkInfo::Data(InfoData::GreTun6(vec![ + InfoGre6::CollectMetadata, + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gre6tap_external() { + // This is nlmon capture for `ip link add name foo type ip6gretap external` + // on Linux kernel 6.15.9 with iproute2 version 6.16.0 + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x1c, 0x00, // length 28 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0e, 0x00, // length 14 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x69, 0x70, 0x36, // ip6 + 0x67, 0x72, 0x65, 0x74, 0x61, 0x70, 0x00, // gretap\0 + 0x00, 0x00, // padding + 0x08, 0x00, // length 8 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x04, 0x00, // length 4 + 0x12, 0x00, // IFLA_GRE_COLLECT_METADATA 18 + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTap6), + LinkInfo::Data(InfoData::GreTap6(vec![ + InfoGre6::CollectMetadata, + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gre6() { + // This is nlmon capture was created on Linux kernel 6.15.9 with iproute + // version 6.16.0: + // ```sh + // ip link add name foo type ip6gre \ + // ikey 42 okey 42 \ + // local fc00::1 remote fc00::2 \ + // ttl 64 encaplimit 2 flowlabel 42 \ + // encap-sport 4242 encap-dport 4242 + // ``` + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0xac, 0x00, // length 172 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0b, 0x00, // length 12 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x69, 0x70, 0x36, 0x67, 0x72, 0x65, 0x00, // ip6gre\0 + // NOTE: padding is included in the length (0x0b) by the Kernel + // response but iproute does not include the padding (0x0a). + 0x00, // padding + 0x9c, 0x00, // length 156 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x08, 0x00, // length 8 + 0x04, 0x00, // IFLA_GRE_IKEY 4 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x08, 0x00, // length 8 + 0x05, 0x00, // IFLA_GRE_OKEY 5 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x06, 0x00, // length 6 + 0x02, 0x00, // IFLA_GRE_IFLAGS 2 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x03, 0x00, // IFLA_GRE_OFLAGS 3 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x14, 0x00, // length 20 + 0x06, 0x00, // IFLA_LOCAL 6 + 0xfc, 0x00, 0x00, 0x00, // fc00::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x14, 0x00, // length 20 + 0x07, 0x00, // IFLA_REMOTE 7 + 0xfc, 0x00, 0x00, 0x00, // fc00::2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x00, // length 5 + 0x08, 0x00, // IFLA_TTL 8 + 0x40, // 64 + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x0b, 0x00, // IFLA_GRE_ENCAP_LIMIT 11 + 0x02, // 2 + 0x00, 0x00, 0x00, // padding + 0x08, 0x00, // length 8 + 0x0c, 0x00, // IFLA_GRE_FLOWINFO 12 + 0x00, 0x00, 0x00, 0x42, // 0x00042 + 0x08, 0x00, // length 8 + 0x0d, 0x00, // IFLA_GRE_FLAGS 13 + 0x00, 0x00, 0x00, 0x00, // 0 + 0x08, 0x00, // length 8 + 0x14, 0x00, // IFLA_GRE_FWMARK 20 + 0x00, 0x00, 0x00, 0x00, // + 0x05, 0x00, // length 5 + 0x16, 0x00, // IFLA_GRE_ERSPAN_VER 22 + 0x01, // 1 + 0x00, 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0e, 0x00, // IFLA_GRE_ENCAP_TYPE 14 + 0x00, 0x00, // UNSPEC + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0f, 0x00, // IFLA_GRE_ENCAP_FLAGS 15 + 0x02, 0x00, // + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x10, 0x00, // IFLA_GRE_ENCAP_SPORT 16 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x11, 0x00, // IFLA_GRE_ENCAP_DPORT 17 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTun6), + LinkInfo::Data(InfoData::GreTun6(vec![ + InfoGre6::IKey(42), + InfoGre6::OKey(42), + InfoGre6::IFlags(GreIOFlags::Key), + InfoGre6::OFlags(GreIOFlags::Key), + InfoGre6::Local(Ipv6Addr::new( + 0xfc00, 0, 0, 0, 0, 0, 0, 0x01, + )), + InfoGre6::Remote(Ipv6Addr::new( + 0xfc00, 0, 0, 0, 0, 0, 0, 0x02, + )), + InfoGre6::Ttl(64), + InfoGre6::EncapLimit(2), + InfoGre6::FlowLabel(0x42), + InfoGre6::Other(DefaultNla::new(13, vec![0; 4])), + InfoGre6::FwMask(0), + InfoGre6::Other(DefaultNla::new(22, vec![0x01])), + InfoGre6::EncapType(GreEncapType::default()), + // enabled by default for IPv6 + InfoGre6::EncapFlags(GreEncapFlags::Checksum6), + InfoGre6::SourcePort(4242), + InfoGre6::DestinationPort(4242), + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gre6tap() { + // This is nlmon capture was created on Linux kernel 6.15.9 with iproute + // version 6.16.0: + // ```sh + // ip link add name foo type ip6gretap \ + // ikey 42 okey 42 \ + // local fc00::1 remote fc00::2 \ + // ttl 64 encaplimit 2 flowlabel 42 \ + // encap-sport 4242 encap-dport 4242 + // ``` + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0xb0, 0x00, // length 176 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0e, 0x00, // length 14 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x69, 0x70, 0x36, // ip6 + 0x67, 0x72, 0x65, 0x74, 0x61, 0x70, 0x00, // gretap\0 + 0x00, 0x00, // padding + 0x9c, 0x00, // length 156 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x08, 0x00, // length 8 + 0x04, 0x00, // IFLA_GRE_IKEY 4 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x08, 0x00, // length 8 + 0x05, 0x00, // IFLA_GRE_OKEY 5 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x06, 0x00, // length 6 + 0x02, 0x00, // IFLA_GRE_IFLAGS 2 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x03, 0x00, // IFLA_GRE_OFLAGS 3 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x14, 0x00, // length 20 + 0x06, 0x00, // IFLA_LOCAL 6 + 0xfc, 0x00, 0x00, 0x00, // fc00::1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x14, 0x00, // length 20 + 0x07, 0x00, // IFLA_REMOTE 7 + 0xfc, 0x00, 0x00, 0x00, // fc00::2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x05, 0x00, // length 5 + 0x08, 0x00, // IFLA_TTL 8 + 0x40, // 64 + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x0b, 0x00, // IFLA_GRE_ENCAP_LIMIT 11 + 0x02, // 2 + 0x00, 0x00, 0x00, // padding + 0x08, 0x00, // length 8 + 0x0c, 0x00, // IFLA_GRE_FLOWINFO 12 + 0x00, 0x00, 0x00, 0x42, // 0x00042 + 0x08, 0x00, // length 8 + 0x0d, 0x00, // IFLA_GRE_FLAGS 13 + 0x00, 0x00, 0x00, 0x00, // 0 + 0x08, 0x00, // length 8 + 0x14, 0x00, // IFLA_GRE_FWMARK 20 + 0x00, 0x00, 0x00, 0x00, // + 0x05, 0x00, // length 5 + 0x16, 0x00, // IFLA_GRE_ERSPAN_VER 22 + 0x01, // 1 + 0x00, 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0e, 0x00, // IFLA_GRE_ENCAP_TYPE 14 + 0x00, 0x00, // UNSPEC + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0f, 0x00, // IFLA_GRE_ENCAP_FLAGS 15 + 0x02, 0x00, // + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x10, 0x00, // IFLA_GRE_ENCAP_SPORT 16 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x11, 0x00, // IFLA_GRE_ENCAP_DPORT 17 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTap6), + LinkInfo::Data(InfoData::GreTap6(vec![ + InfoGre6::IKey(42), + InfoGre6::OKey(42), + InfoGre6::IFlags(GreIOFlags::Key), + InfoGre6::OFlags(GreIOFlags::Key), + InfoGre6::Local(Ipv6Addr::new( + 0xfc00, 0, 0, 0, 0, 0, 0, 0x01, + )), + InfoGre6::Remote(Ipv6Addr::new( + 0xfc00, 0, 0, 0, 0, 0, 0, 0x02, + )), + InfoGre6::Ttl(64), + InfoGre6::EncapLimit(2), + InfoGre6::FlowLabel(0x42), + InfoGre6::Other(DefaultNla::new(13, vec![0; 4])), + InfoGre6::FwMask(0), + InfoGre6::Other(DefaultNla::new(22, vec![0x01])), + InfoGre6::EncapType(GreEncapType::default()), + // enabled by default for IPv6 + InfoGre6::EncapFlags(GreEncapFlags::Checksum6), + InfoGre6::SourcePort(4242), + InfoGre6::DestinationPort(4242), + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gre_external() { + // This is nlmon capture for `ip link add name foo type gre external` + // on Linux kernel 6.15.9 with iproute2 version 6.16.0 + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x14, 0x00, // length 20 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x08, 0x00, // length 8 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x67, 0x72, 0x65, 0x00, // gre\0 + 0x08, 0x00, // length 8 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x04, 0x00, // length 4 + 0x12, 0x00, // IFLA_GRE_COLLECT_METADATA 18 + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTun), + LinkInfo::Data(InfoData::GreTun(vec![ + InfoGre::CollectMetadata, + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gretap_external() { + // This is nlmon capture for `ip link add name foo type gretap external` + // on Linux kernel 6.15.9 with iproute2 version 6.16.0 + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x18, 0x00, // length 24 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0b, 0x00, // length 10 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x67, 0x72, 0x65, 0x74, 0x61, 0x70, 0x00, // gretap\0 + 0x00, // padding + 0x08, 0x00, // length 8 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x04, 0x00, // length 4 + 0x12, 0x00, // IFLA_GRE_COLLECT_METADATA 18 + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTap), + LinkInfo::Data(InfoData::GreTap(vec![ + InfoGre::CollectMetadata, + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gre() { + // This is nlmon capture was created on Linux kernel 6.15.9 with iproute + // version 6.16.0: ```sh + // ip link add name foo type gre \ + // ikey 42 okey 42 \ + // local 192.0.2.1 remote 192.0.2.2 \ + // ttl 64 encap-sport 4242 encap-dport 4242 + // ``` + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x80, 0x00, // length 128 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x08, 0x00, // length 8 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x67, 0x72, 0x65, 0x00, // gre\0 + 0x74, 0x00, // length 116 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x08, 0x00, // length 8 + 0x04, 0x00, // IFLA_GRE_IKEY 4 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x08, 0x00, // length 8 + 0x05, 0x00, // IFLA_GRE_OKEY 5 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x06, 0x00, // length 6 + 0x02, 0x00, // IFLA_GRE_IFLAGS 2 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x03, 0x00, // IFLA_GRE_OFLAGS 3 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x08, 0x00, // length 8 + 0x06, 0x00, // IFLA_LOCAL 6 + 0xc0, 0x00, 0x02, 0x01, // 192.0.2.1 + 0x08, 0x00, // length 8 + 0x07, 0x00, // IFLA_REMOTE 7 + 0xc0, 0x00, 0x02, 0x01, // 192.0.2.2 + 0x05, 0x00, // length 5 + 0x0a, 0x00, // IFLA_GRE_PMTUDISC 10 + 0x01, // + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x09, 0x00, // IFLA_GRE_TOS 9 + 0x00, // 0 + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x08, 0x00, // IFLA_TTL 8 + 0x40, // 64 + 0x00, 0x00, 0x00, // padding + 0x08, 0x00, // length 8, + 0x14, 0x00, // IFLA_GRE_FWMARK 20 + 0x00, 0x00, 0x00, 0x00, // + 0x06, 0x00, // length 6 + 0x0e, 0x00, // IFLA_GRE_ENCAP_TYPE 14 + 0x00, 0x00, // UNSPEC + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0f, 0x00, // IFLA_GRE_ENCAP_FLAGS 15 + 0x00, 0x00, // + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x10, 0x00, // IFLA_GRE_ENCAP_SPORT 16 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x11, 0x00, // IFLA_GRE_ENCAP_DPORT 17 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTun), + LinkInfo::Data(InfoData::GreTun(vec![ + InfoGre::IKey(42), + InfoGre::OKey(42), + InfoGre::IFlags(GreIOFlags::Key), + InfoGre::OFlags(GreIOFlags::Key), + InfoGre::Local(Ipv4Addr::new(192, 0, 2, 1)), + InfoGre::Remote(Ipv4Addr::new(192, 0, 2, 1)), + InfoGre::PathMTUDiscovery(true), // enabled by default + InfoGre::Tos(0), + InfoGre::Ttl(64), + InfoGre::FwMask(0), + InfoGre::EncapType(GreEncapType::default()), + InfoGre::EncapFlags(GreEncapFlags::default()), + InfoGre::SourcePort(4242), + InfoGre::DestinationPort(4242), + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} + +#[test] +fn test_create_gretap() { + // This is nlmon capture was created on Linux kernel 6.15.9 with iproute + // version 6.16.0: ```sh + // ip link add name foo type gretap \ + // ikey 42 okey 42 \ + // local 192.0.2.1 remote 192.0.2.2 \ + // ttl 64 encap-sport 4242 encap-dport 4242 + // ``` + 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 + 0x08, 0x00, // length 8 + 0x03, 0x00, // device name + 0x66, 0x6f, 0x6f, 0x00, // foo\0 + 0x84, 0x00, // length 132 + 0x12, 0x00, // IFLA_LINKINFO 18 + 0x0b, 0x00, // length 11 + 0x01, 0x00, // IFLA_INFO_KIND 1 + 0x67, 0x72, 0x65, 0x74, 0x61, 0x70, 0x00, // gretap\0 + 0x00, // padding + 0x74, 0x00, // length 116 + 0x02, 0x00, // IFLA_INFO_DATA 2 + 0x08, 0x00, // length 8 + 0x04, 0x00, // IFLA_GRE_IKEY 4 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x08, 0x00, // length 8 + 0x05, 0x00, // IFLA_GRE_OKEY 5 + 0x00, 0x00, 0x00, 0x2a, // 42 + 0x06, 0x00, // length 6 + 0x02, 0x00, // IFLA_GRE_IFLAGS 2 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x03, 0x00, // IFLA_GRE_OFLAGS 3 + 0x20, 0x00, // GRE_KEY + 0x00, 0x00, // padding + 0x08, 0x00, // length 8 + 0x06, 0x00, // IFLA_LOCAL 6 + 0xc0, 0x00, 0x02, 0x01, // 192.0.2.1 + 0x08, 0x00, // length 8 + 0x07, 0x00, // IFLA_REMOTE 7 + 0xc0, 0x00, 0x02, 0x01, // 192.0.2.2 + 0x05, 0x00, // length 5 + 0x0a, 0x00, // IFLA_GRE_PMTUDISC 10 + 0x01, // + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x09, 0x00, // IFLA_GRE_TOS 9 + 0x00, // 0 + 0x00, 0x00, 0x00, // padding + 0x05, 0x00, // length 5 + 0x08, 0x00, // IFLA_TTL 8 + 0x40, // 64 + 0x00, 0x00, 0x00, // padding + 0x08, 0x00, // length 8, + 0x14, 0x00, // IFLA_GRE_FWMARK 20 + 0x00, 0x00, 0x00, 0x00, // + 0x06, 0x00, // length 6 + 0x0e, 0x00, // IFLA_GRE_ENCAP_TYPE 14 + 0x00, 0x00, // UNSPEC + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x0f, 0x00, // IFLA_GRE_ENCAP_FLAGS 15 + 0x00, 0x00, // + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x10, 0x00, // IFLA_GRE_ENCAP_SPORT 16 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + 0x06, 0x00, // length 6 + 0x11, 0x00, // IFLA_GRE_ENCAP_DPORT 17 + 0x10, 0x92, // 4242 + 0x00, 0x00, // padding + ]; + + let expected = LinkMessage { + attributes: vec![ + LinkAttribute::IfName("foo".to_string()), + LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::GreTap), + LinkInfo::Data(InfoData::GreTap(vec![ + InfoGre::IKey(42), + InfoGre::OKey(42), + InfoGre::IFlags(GreIOFlags::Key), + InfoGre::OFlags(GreIOFlags::Key), + InfoGre::Local(Ipv4Addr::new(192, 0, 2, 1)), + InfoGre::Remote(Ipv4Addr::new(192, 0, 2, 1)), + InfoGre::PathMTUDiscovery(true), // enabled by default + InfoGre::Tos(0), + InfoGre::Ttl(64), + InfoGre::FwMask(0), + InfoGre::EncapType(GreEncapType::default()), + InfoGre::EncapFlags(GreEncapFlags::default()), + InfoGre::SourcePort(4242), + InfoGre::DestinationPort(4242), + ])), + ]), + ], + ..Default::default() + }; + assert_eq!( + LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap(), + expected + ); + let mut buf = vec![0; expected.buffer_len()]; + expected.emit(&mut buf); + assert_eq!(buf, raw); +} diff --git a/src/link/tests/mod.rs b/src/link/tests/mod.rs index c3ffceb0..b8a02d6a 100644 --- a/src/link/tests/mod.rs +++ b/src/link/tests/mod.rs @@ -9,6 +9,8 @@ mod bridge; #[cfg(test)] mod geneve; #[cfg(test)] +mod gre; +#[cfg(test)] mod hsr; #[cfg(test)] mod ipvlan;