diff --git a/src/ip.rs b/src/ip.rs index ef7130ae..ad5c443e 100644 --- a/src/ip.rs +++ b/src/ip.rs @@ -209,3 +209,16 @@ impl From for i32 { } } } + +impl From for IpProtocol { + fn from(d: u8) -> Self { + IpProtocol::from(d as i32) + } +} + +impl From for u8 { + fn from(p: IpProtocol) -> u8 { + let v: i32 = p.into(); + v as u8 + } +} diff --git a/src/link/link_info/info_data.rs b/src/link/link_info/info_data.rs index 8c664d59..e26930b2 100644 --- a/src/link/link_info/info_data.rs +++ b/src/link/link_info/info_data.rs @@ -4,14 +4,14 @@ use anyhow::Context; use netlink_packet_utils::{ nla::{Nla, NlaBuffer, NlasIterator}, - DecodeError, Emitable, Parseable, + DecodeError, Emitable, Parseable, ParseableParametrized, }; 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, + InfoGreTun6, InfoGtp, InfoHsr, InfoIpTunnel, InfoIpVlan, InfoIpVtap, + InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoSitTun, + InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm, }; const IFLA_INFO_DATA: u16 = 2; @@ -42,6 +42,7 @@ pub enum InfoData { MacSec(Vec), Hsr(Vec), Geneve(Vec), + IpTunnel(Vec), Other(Vec), } @@ -71,6 +72,7 @@ impl Nla for InfoData { Self::Vti(nlas) => nlas.as_slice().buffer_len(), Self::Gtp(nlas) => nlas.as_slice().buffer_len(), Self::Geneve(nlas) => nlas.as_slice().buffer_len(), + Self::IpTunnel(nlas) => nlas.as_slice().buffer_len(), Self::Other(v) => v.len(), } } @@ -100,6 +102,7 @@ impl Nla for InfoData { Self::Vti(nlas) => nlas.as_slice().emit(buffer), Self::Gtp(nlas) => nlas.as_slice().emit(buffer), Self::Geneve(nlas) => nlas.as_slice().emit(buffer), + Self::IpTunnel(nlas) => nlas.as_slice().emit(buffer), Self::Other(v) => buffer.copy_from_slice(v.as_slice()), } } @@ -353,6 +356,18 @@ impl InfoData { } InfoData::Hsr(v) } + InfoKind::IpIp | InfoKind::Ip6Tnl => { + let mut v = Vec::new(); + for nla in NlasIterator::new(payload) { + let nla = &nla.context(format!( + "invalid IFLA_INFO_DATA for {kind} {payload:?}" + ))?; + let parsed = + InfoIpTunnel::parse_with_param(nla, kind.clone())?; + v.push(parsed); + } + InfoData::IpTunnel(v) + } InfoKind::Geneve => { let mut v = Vec::new(); for nla in NlasIterator::new(payload) { diff --git a/src/link/link_info/infos.rs b/src/link/link_info/infos.rs index 2ffa60de..8a57418b 100644 --- a/src/link/link_info/infos.rs +++ b/src/link/link_info/infos.rs @@ -31,6 +31,7 @@ const MACVTAP: &str = "macvtap"; const GRETAP: &str = "gretap"; const IP6GRETAP: &str = "ip6gretap"; const IPIP: &str = "ipip"; +const IP6TNL: &str = "ip6tnl"; const SIT: &str = "sit"; const GRE: &str = "gre"; const IP6GRE: &str = "ip6gre"; @@ -188,7 +189,8 @@ pub enum InfoKind { MacVtap, GreTap, GreTap6, - IpTun, + IpIp, + Ip6Tnl, SitTun, GreTun, GreTun6, @@ -225,7 +227,8 @@ impl std::fmt::Display for InfoKind { Self::MacVtap => MACVTAP, Self::GreTap => GRETAP, Self::GreTap6 => IP6GRETAP, - Self::IpTun => IPIP, + Self::IpIp => IPIP, + Self::Ip6Tnl => IP6TNL, Self::SitTun => SIT, Self::GreTun => GRE, Self::GreTun6 => IP6GRE, @@ -262,7 +265,8 @@ impl Nla for InfoKind { Self::MacVtap => MACVTAP.len(), Self::GreTap => GRETAP.len(), Self::GreTap6 => IP6GRETAP.len(), - Self::IpTun => IPIP.len(), + Self::IpIp => IPIP.len(), + Self::Ip6Tnl => IP6TNL.len(), Self::SitTun => SIT.len(), Self::GreTun => GRE.len(), Self::GreTun6 => IP6GRE.len(), @@ -319,7 +323,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> for InfoKind { MACVTAP => Self::MacVtap, GRETAP => Self::GreTap, IP6GRETAP => Self::GreTap6, - IPIP => Self::IpTun, + IPIP => Self::IpIp, + IP6TNL => Self::Ip6Tnl, SIT => Self::SitTun, GRE => Self::GreTun, IP6GRE => Self::GreTun6, diff --git a/src/link/link_info/iptunnel.rs b/src/link/link_info/iptunnel.rs new file mode 100644 index 00000000..2aaf8239 --- /dev/null +++ b/src/link/link_info/iptunnel.rs @@ -0,0 +1,378 @@ +// B +// SPDX-License-Identifier: MIT + +use std::net::{IpAddr, Ipv6Addr}; + +use anyhow::Context; +use byteorder::{ByteOrder, NativeEndian}; +use netlink_packet_utils::{ + nla::{DefaultNla, Nla, NlaBuffer}, + parsers::{parse_u16, parse_u32, parse_u8}, + traits::{Parseable, ParseableParametrized}, + DecodeError, +}; + +use crate::ip::{parse_ip_addr, IpProtocol}; + +const IFLA_IPTUN_LINK: u16 = 1; +const IFLA_IPTUN_LOCAL: u16 = 2; +const IFLA_IPTUN_REMOTE: u16 = 3; +const IFLA_IPTUN_TTL: u16 = 4; +const IFLA_IPTUN_TOS: u16 = 5; +const IFLA_IPTUN_ENCAP_LIMIT: u16 = 6; +const IFLA_IPTUN_FLOWINFO: u16 = 7; +const IFLA_IPTUN_FLAGS: u16 = 8; +const IFLA_IPTUN_PROTO: u16 = 9; +const IFLA_IPTUN_PMTUDISC: u16 = 10; +const IFLA_IPTUN_6RD_PREFIX: u16 = 11; +const IFLA_IPTUN_6RD_RELAY_PREFIX: u16 = 12; +const IFLA_IPTUN_6RD_PREFIXLEN: u16 = 13; +const IFLA_IPTUN_6RD_RELAY_PREFIXLEN: u16 = 14; +const IFLA_IPTUN_ENCAP_TYPE: u16 = 15; +const IFLA_IPTUN_ENCAP_FLAGS: u16 = 16; +const IFLA_IPTUN_ENCAP_SPORT: u16 = 17; +const IFLA_IPTUN_ENCAP_DPORT: u16 = 18; +const IFLA_IPTUN_COLLECT_METADATA: u16 = 19; +const IFLA_IPTUN_FWMARK: u16 = 20; + +#[derive(Debug, PartialEq, Eq, Clone)] +#[non_exhaustive] +pub enum InfoIpTunnel { + Link(u32), + Local(IpAddr), + Remote(IpAddr), + Ttl(u8), + Tos(u8), + EncapLimit(u8), + FlowInfo(u32), + Ipv6SitFlags(u16), + Ipv4Flags(u16), + Ipv6Flags(u32), + Protocol(IpProtocol), + PMtuDisc(bool), + Ipv6RdPrefix(Ipv6Addr), + Ipv6RdRelayPrefix(Ipv6Addr), + Ipv6RdPrefixLen(u16), + Ipv6RdRelayPrefixLen(u16), + EncapType(TunnelEncapType), + EncapFlags(TunnelEncapFlags), + EncapSPort(u16), + EncapDPort(u16), + CollectMetada(bool), + FwMark(u32), + Other(DefaultNla), +} + +impl Nla for InfoIpTunnel { + fn value_len(&self) -> usize { + use self::InfoIpTunnel::*; + match self { + Ipv6RdPrefix(_) | Ipv6RdRelayPrefix(_) => 16, + Local(value) | Remote(value) => match value { + IpAddr::V4(_) => 4, + IpAddr::V6(_) => 16, + }, + Link(_) | FwMark(_) | FlowInfo(_) | Ipv6Flags(_) => 4, + Ipv6SitFlags(_) + | Ipv4Flags(_) + | EncapType(_) + | EncapFlags(_) + | EncapSPort(_) + | EncapDPort(_) + | Ipv6RdPrefixLen(_) + | Ipv6RdRelayPrefixLen(_) => 2, + Ttl(_) | Tos(_) | Protocol(_) | PMtuDisc(_) | CollectMetada(_) + | EncapLimit(_) => 1, + Other(nla) => nla.value_len(), + } + } + + fn emit_value(&self, buffer: &mut [u8]) { + use self::InfoIpTunnel::*; + match self { + Ipv6RdPrefix(value) | Ipv6RdRelayPrefix(value) => { + buffer.copy_from_slice(&value.octets()) + } + Link(value) | FwMark(value) | FlowInfo(value) => { + NativeEndian::write_u32(buffer, *value) + } + Ipv6Flags(val) => { + NativeEndian::write_u32(buffer, *val); + } + Ipv6SitFlags(val) | Ipv4Flags(val) => { + NativeEndian::write_u16(buffer, *val); + } + Local(value) | Remote(value) => match value { + IpAddr::V4(ipv4) => buffer.copy_from_slice(&ipv4.octets()), + IpAddr::V6(ipv6) => buffer.copy_from_slice(&ipv6.octets()), + }, + EncapType(value) => { + NativeEndian::write_u16(buffer, (*value).into()) + } + EncapFlags(f) => NativeEndian::write_u16(buffer, f.bits()), + EncapSPort(value) + | EncapDPort(value) + | Ipv6RdPrefixLen(value) + | Ipv6RdRelayPrefixLen(value) => { + NativeEndian::write_u16(buffer, *value) + } + Protocol(value) => buffer[0] = u8::from(*value), + Ttl(value) | Tos(value) | EncapLimit(value) => buffer[0] = *value, + PMtuDisc(value) | CollectMetada(value) => { + buffer[0] = if *value { 1 } else { 0 } + } + Other(nla) => nla.emit_value(buffer), + } + } + + fn kind(&self) -> u16 { + use self::InfoIpTunnel::*; + match self { + Link(_) => IFLA_IPTUN_LINK, + Local(_) => IFLA_IPTUN_LOCAL, + Remote(_) => IFLA_IPTUN_REMOTE, + Ttl(_) => IFLA_IPTUN_TTL, + Tos(_) => IFLA_IPTUN_TOS, + EncapLimit(_) => IFLA_IPTUN_ENCAP_LIMIT, + FlowInfo(_) => IFLA_IPTUN_FLOWINFO, + Ipv6SitFlags(_) | Ipv4Flags(_) | Ipv6Flags(_) => IFLA_IPTUN_FLAGS, + Protocol(_) => IFLA_IPTUN_PROTO, + PMtuDisc(_) => IFLA_IPTUN_PMTUDISC, + Ipv6RdPrefix(_) => IFLA_IPTUN_6RD_PREFIX, + Ipv6RdRelayPrefix(_) => IFLA_IPTUN_6RD_RELAY_PREFIX, + Ipv6RdPrefixLen(_) => IFLA_IPTUN_6RD_PREFIXLEN, + Ipv6RdRelayPrefixLen(_) => IFLA_IPTUN_6RD_RELAY_PREFIXLEN, + EncapType(_) => IFLA_IPTUN_ENCAP_TYPE, + EncapFlags(_) => IFLA_IPTUN_ENCAP_FLAGS, + EncapSPort(_) => IFLA_IPTUN_ENCAP_SPORT, + EncapDPort(_) => IFLA_IPTUN_ENCAP_DPORT, + CollectMetada(_) => IFLA_IPTUN_COLLECT_METADATA, + FwMark(_) => IFLA_IPTUN_FWMARK, + Other(nla) => nla.kind(), + } + } +} + +impl<'a, T: AsRef<[u8]> + ?Sized> + ParseableParametrized, super::InfoKind> for InfoIpTunnel +{ + fn parse_with_param( + buf: &NlaBuffer<&'a T>, + kind: super::InfoKind, + ) -> Result { + use self::InfoIpTunnel::*; + let payload = buf.value(); + Ok(match buf.kind() { + IFLA_IPTUN_LINK => Link( + parse_u32(payload).context("invalid IFLA_IPTUN_LINK value")?, + ), + IFLA_IPTUN_LOCAL => { + let ip = parse_ip_addr(payload) + .context("invalid IFLA_IPTUN_LOCAL")?; + Self::Local(ip) + } + IFLA_IPTUN_REMOTE => { + let ip = parse_ip_addr(payload) + .context("invalid IFLA_IPTUN_REMOTE")?; + Self::Remote(ip) + } + IFLA_IPTUN_TTL => { + Ttl(parse_u8(payload) + .context("invalid IFLA_IPTUN_TTL value")?) + } + IFLA_IPTUN_TOS => { + Tos(parse_u8(payload) + .context("invalid IFLA_IPTUN_TOS value")?) + } + IFLA_IPTUN_ENCAP_LIMIT => EncapLimit( + parse_u8(payload) + .context("invalid IFLA_IPTUN_ENCAP_LIMIT value")?, + ), + IFLA_IPTUN_FLOWINFO => FlowInfo( + parse_u32(payload) + .context("invalid IFLA_IPTUN_FLOWINFO value")?, + ), + IFLA_IPTUN_FLAGS => match kind { + super::InfoKind::IpIp => InfoIpTunnel::Ipv4Flags( + parse_u16(payload) + .context("invalid IFLA_IPTUN_FLAGS for IPIP")?, + ), + super::InfoKind::SitTun => InfoIpTunnel::Ipv6SitFlags( + parse_u16(payload) + .context("invalid IFLA_IPTUN_FLAGS for SIT")?, + ), + super::InfoKind::Ip6Tnl => InfoIpTunnel::Ipv6Flags( + parse_u32(payload) + .context("invalid IFLA_IPTUN_FLAGS for IP6")?, + ), + _ => { + return Err(DecodeError::from(format!( + "unsupported InfoKind for IFLA_IPTUN_FLAGS: {kind:?}" + ))); + } + }, + IFLA_IPTUN_PROTO => Protocol(IpProtocol::from( + parse_u8(payload).context("invalid IFLA_IPTUN_PROTO value")?, + )), + IFLA_IPTUN_PMTUDISC => PMtuDisc( + parse_u8(payload) + .context("invalid IFLA_IPTUN_PMTUDISC value")? + > 0, + ), + IFLA_IPTUN_6RD_PREFIX => { + if payload.len() == 16 { + let mut data = [0u8; 16]; + data.copy_from_slice(&payload[0..16]); + Self::Ipv6RdPrefix(Ipv6Addr::from(data)) + } else { + return Err(DecodeError::from(format!( + "Invalid IFLA_IPTUN_6RD_PREFIX, got unexpected length \ + of IPv6 address payload {payload:?}" + ))); + } + } + IFLA_IPTUN_6RD_RELAY_PREFIX => { + if payload.len() == 16 { + let mut data = [0u8; 16]; + data.copy_from_slice(&payload[0..16]); + Self::Ipv6RdRelayPrefix(Ipv6Addr::from(data)) + } else { + return Err(DecodeError::from(format!( + "Invalid IFLA_IPTUN_6RD_RELAY_PREFIX, got unexpected \ + length of IPv6 address payload {payload:?}" + ))); + } + } + IFLA_IPTUN_6RD_PREFIXLEN => Ipv6RdPrefixLen( + parse_u16(payload) + .context("invalid IFLA_IPTUN_6RD_PREFIXLEN value")?, + ), + IFLA_IPTUN_6RD_RELAY_PREFIXLEN => Ipv6RdRelayPrefixLen( + parse_u16(payload) + .context("invalid IFLA_IPTUN_6RD_RELAY_PREFIXLEN value")?, + ), + IFLA_IPTUN_ENCAP_TYPE => EncapType( + parse_u16(payload) + .context("invalid IFLA_IPTUN_ENCAP_TYPE value")? + .into(), + ), + IFLA_IPTUN_ENCAP_FLAGS => { + EncapFlags(TunnelEncapFlags::from_bits_retain( + parse_u16(payload) + .context("failed to parse IFLA_IPTUN_ENCAP_FLAGS")?, + )) + } + IFLA_IPTUN_ENCAP_SPORT => EncapSPort( + parse_u16(payload) + .context("invalid IFLA_IPTUN_ENCAP_SPORT value")?, + ), + IFLA_IPTUN_ENCAP_DPORT => EncapDPort( + parse_u16(payload) + .context("invalid IFLA_IPTUN_ENCAP_DPORT value")?, + ), + IFLA_IPTUN_COLLECT_METADATA => CollectMetada( + parse_u8(payload) + .context("invalid IFLA_IPTUN_COLLECT_METADATA value")? + > 0, + ), + IFLA_IPTUN_FWMARK => FwMark( + parse_u32(payload) + .context("invalid IFLA_IPTUN_FWMARK value")?, + ), + kind => Other( + DefaultNla::parse(buf) + .context(format!("unknown NLA type {kind}"))?, + ), + }) + } +} + +const TUNNEL_ENCAP_NONE: u16 = 0; +const TUNNEL_ENCAP_FOU: u16 = 1; +const TUNNEL_ENCAP_GUE: u16 = 2; +const TUNNEL_ENCAP_MPLS: u16 = 3; + +//TODO PROTO, FLAGS.. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +#[repr(u16)] +pub enum TunnelEncapType { + None = TUNNEL_ENCAP_NONE, + Fou = TUNNEL_ENCAP_FOU, + Gue = TUNNEL_ENCAP_GUE, + Mpls = TUNNEL_ENCAP_MPLS, + Other(u16), +} + +impl From for TunnelEncapType { + fn from(d: u16) -> Self { + match d { + TUNNEL_ENCAP_NONE => Self::None, + TUNNEL_ENCAP_FOU => Self::Fou, + TUNNEL_ENCAP_GUE => Self::Gue, + TUNNEL_ENCAP_MPLS => Self::Mpls, + _ => Self::Other(d), + } + } +} + +impl From for u16 { + fn from(d: TunnelEncapType) -> Self { + match d { + TunnelEncapType::None => TUNNEL_ENCAP_NONE, + TunnelEncapType::Fou => TUNNEL_ENCAP_FOU, + TunnelEncapType::Gue => TUNNEL_ENCAP_GUE, + TunnelEncapType::Mpls => TUNNEL_ENCAP_MPLS, + TunnelEncapType::Other(value) => value, + } + } +} + +impl std::fmt::Display for TunnelEncapType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::None => write!(f, "none"), + Self::Fou => write!(f, "fou"), + Self::Gue => write!(f, "gue"), + Self::Mpls => write!(f, "mpls"), + Self::Other(d) => write!(f, "{d}"), + } + } +} + +const TUNNEL_ENCAP_FLAG_CSUM: u16 = 1 << 0; +const TUNNEL_ENCAP_FLAG_CSUM6: u16 = 1 << 1; +const TUNNEL_ENCAP_FLAG_REMCSUM: u16 = 1 << 2; + +bitflags! { + #[non_exhaustive] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct TunnelEncapFlags: u16 { + const CSum = TUNNEL_ENCAP_FLAG_CSUM; + const CSum6 = TUNNEL_ENCAP_FLAG_CSUM6; + const RemCSum = TUNNEL_ENCAP_FLAG_REMCSUM; + const _ = !0; + } +} + +const IP6_TNL_F_IGN_ENCAP_LIMIT: u32 = 0x1; +const IP6_TNL_F_USE_ORIG_TCLASS: u32 = 0x2; +const IP6_TNL_F_USE_ORIG_FLOWLABEL: u32 = 0x4; +const IP6_TNL_F_MIP6_DEV: u32 = 0x8; +const IP6_TNL_F_RCV_DSCP_COPY: u32 = 0x10; +const IP6_TNL_F_USE_ORIG_FWMARK: u32 = 0x20; +const IP6_TNL_F_ALLOW_LOCAL_REMOTE: u32 = 0x40; + +bitflags! { + #[non_exhaustive] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct TunnelFlags: u32 { + const IgnEncapLimit = IP6_TNL_F_IGN_ENCAP_LIMIT; + const UseOrigTclass = IP6_TNL_F_USE_ORIG_TCLASS; + const UseOrigFlowlabel = IP6_TNL_F_USE_ORIG_FLOWLABEL; + const Mip6Dev = IP6_TNL_F_MIP6_DEV; + const RcvDscpCopy = IP6_TNL_F_RCV_DSCP_COPY; + const UseOrigFwMark = IP6_TNL_F_USE_ORIG_FWMARK; + const AllowLocalRemote = IP6_TNL_F_ALLOW_LOCAL_REMOTE; + } +} diff --git a/src/link/link_info/mod.rs b/src/link/link_info/mod.rs index 54922aa0..0dbb0eb3 100644 --- a/src/link/link_info/mod.rs +++ b/src/link/link_info/mod.rs @@ -15,6 +15,7 @@ mod info_data; mod info_port; mod infos; mod ipoib; +mod iptunnel; mod ipvlan; mod mac_vlan; mod macsec; @@ -50,6 +51,9 @@ pub use self::info_data::InfoData; pub use self::info_port::{InfoPortData, InfoPortKind, InfoVrfPort}; pub use self::infos::{InfoKind, LinkInfo}; pub use self::ipoib::InfoIpoib; +pub use self::iptunnel::{ + InfoIpTunnel, TunnelEncapFlags, TunnelEncapType, TunnelFlags, +}; pub use self::ipvlan::{ InfoIpVlan, InfoIpVtap, IpVlanFlags, IpVlanMode, IpVtapFlags, IpVtapMode, }; diff --git a/src/link/mod.rs b/src/link/mod.rs index 82504cbc..92e6c8b2 100644 --- a/src/link/mod.rs +++ b/src/link/mod.rs @@ -44,13 +44,13 @@ pub use self::link_info::{ BridgeIdBuffer, BridgePortMulticastRouter, BridgePortState, BridgeQuerierState, GeneveDf, HsrProtocol, InfoBond, InfoBondPort, InfoBridge, InfoBridgePort, InfoData, InfoGeneve, InfoGreTap, InfoGreTap6, - InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpVlan, InfoIpVtap, - InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoPortData, - InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, InfoVrf, - InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanFlags, IpVlanMode, - IpVtapFlags, IpVtapMode, LinkInfo, LinkXstats, MacSecCipherId, + InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpTunnel, InfoIpVlan, + InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, + InfoPortData, InfoPortKind, InfoSitTun, InfoTun, InfoVeth, InfoVlan, + InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanFlags, + IpVlanMode, IpVtapFlags, IpVtapMode, LinkInfo, LinkXstats, MacSecCipherId, MacSecOffload, MacSecValidate, MacVlanMode, MacVtapMode, MiiStatus, - VlanQosMapping, + TunnelEncapFlags, TunnelEncapType, TunnelFlags, VlanQosMapping, }; pub use self::link_layer_type::LinkLayerType; pub use self::link_state::State; diff --git a/src/link/tests/iptunnel.rs b/src/link/tests/iptunnel.rs new file mode 100644 index 00000000..d2280367 --- /dev/null +++ b/src/link/tests/iptunnel.rs @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: MIT + +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; + +use netlink_packet_utils::{nla::DefaultNla, Emitable, Parseable}; + +use crate::link::{ + InfoData, InfoIpTunnel, InfoKind, InfoSitTun, LinkAttribute, LinkFlags, + LinkHeader, LinkInfo, LinkLayerType, LinkMessage, LinkMessageBuffer, + TunnelEncapFlags, TunnelEncapType, +}; + +use crate::{AddressFamily, IpProtocol}; + +#[test] +fn test_iptunnel_ipip_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, // AF_UNSPEC and reserved + 0x00, 0x03, // Link Layer Type IPTUNNEL (768) + 0x06, 0x00, 0x00, 0x00, // iface ifindex 6 + 0x90, 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // changed flags + 0x74, 0x00, // length 74 + 0x12, 0x00, // IFLA_LINK_INFO (18) + 0x09, 0x00, 0x01, 0x00, 0x69, 0x70, 0x69, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x02, 0x00, 0xc0, 0xa8, 0x7a, 0xb7, 0x08, 0x00, 0x03, 0x00, + 0x0a, 0xff, 0xfe, 0x02, 0x05, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, // data + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 6, + link_layer_type: LinkLayerType::Tunnel, + flags: LinkFlags::Noarp | LinkFlags::Pointopoint, + change_mask: LinkFlags::empty(), + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::IpIp), + LinkInfo::Data(InfoData::IpTunnel(vec![ + InfoIpTunnel::Link(0), + InfoIpTunnel::Local(std::net::IpAddr::V4( + Ipv4Addr::from_str("192.168.122.183").unwrap(), + )), + InfoIpTunnel::Remote(std::net::IpAddr::V4( + Ipv4Addr::from_str("10.255.254.2").unwrap(), + )), + InfoIpTunnel::Ttl(0), + InfoIpTunnel::Tos(0), + InfoIpTunnel::Protocol(IpProtocol::Ipip), + InfoIpTunnel::PMtuDisc(true), + InfoIpTunnel::FwMark(0), + InfoIpTunnel::EncapType(TunnelEncapType::None), + InfoIpTunnel::EncapSPort(0), + InfoIpTunnel::EncapDPort(0), + InfoIpTunnel::EncapFlags(TunnelEncapFlags::from_bits_retain(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); +} + +#[test] +fn test_iptunnel_ipip6_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, // AF_UNSPEC and reserved + 0x01, 0x03, // Link Layer Type IP6TUNNEL (769) + 0x06, 0x00, 0x00, 0x00, // iface ifindex 6 + 0x90, 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // changed flags + 0x94, 0x00, // length 148 + 0x12, 0x00, // IFLA_LINK_INFO (18) + 0x0b, 0x00, 0x01, 0x00, 0x69, 0x70, 0x36, 0x74, 0x6e, 0x6c, 0x00, 0x00, + 0x84, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x03, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, //data + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 6, + link_layer_type: LinkLayerType::Tunnel6, + flags: LinkFlags::Noarp | LinkFlags::Pointopoint, + change_mask: LinkFlags::empty(), + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Ip6Tnl), + LinkInfo::Data(InfoData::IpTunnel(vec![ + InfoIpTunnel::Link(0), + InfoIpTunnel::Local(std::net::IpAddr::V6( + Ipv6Addr::from_str("2001:db8:1::1").unwrap(), + )), + InfoIpTunnel::Remote(std::net::IpAddr::V6( + Ipv6Addr::from_str("2001:db8:1::2").unwrap(), + )), + InfoIpTunnel::Ttl(64), + InfoIpTunnel::EncapLimit(4), + InfoIpTunnel::FlowInfo(0), + InfoIpTunnel::Ipv6Flags(0x30000), + InfoIpTunnel::Protocol(IpProtocol::Ipip), + InfoIpTunnel::FwMark(0), + InfoIpTunnel::EncapType(TunnelEncapType::None), + InfoIpTunnel::EncapSPort(0), + InfoIpTunnel::EncapDPort(0), + InfoIpTunnel::EncapFlags(TunnelEncapFlags::from_bits_retain(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); +} + +#[test] +fn test_iptunnel_ip6ip6_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, // AF_UNSPEC and reserved + 0x01, 0x03, // Link Layer Type IP6TUNNEL (769) + 0x06, 0x00, 0x00, 0x00, // iface ifindex 6 + 0x90, 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // changed flags + 0x94, 0x00, // length 148 + 0x12, 0x00, // IFLA_LINK_INFO (18) + 0x0b, 0x00, 0x01, 0x00, 0x69, 0x70, 0x36, 0x74, 0x6e, 0x6c, 0x00, 0x00, + 0x84, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x14, 0x00, 0x03, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x05, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, // data + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 6, + link_layer_type: LinkLayerType::Tunnel6, + flags: LinkFlags::Noarp | LinkFlags::Pointopoint, + change_mask: LinkFlags::empty(), + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::Ip6Tnl), + LinkInfo::Data(InfoData::IpTunnel(vec![ + InfoIpTunnel::Link(0), + InfoIpTunnel::Local(std::net::IpAddr::V6( + Ipv6Addr::from_str("2001:db8:1::1").unwrap(), + )), + InfoIpTunnel::Remote(std::net::IpAddr::V6( + Ipv6Addr::from_str("2001:db8:1::2").unwrap(), + )), + InfoIpTunnel::Ttl(64), + InfoIpTunnel::EncapLimit(4), + InfoIpTunnel::FlowInfo(0), + InfoIpTunnel::Ipv6Flags(0x30000), + InfoIpTunnel::Protocol(IpProtocol::Ipv6), + InfoIpTunnel::FwMark(0), + InfoIpTunnel::EncapType(TunnelEncapType::None), + InfoIpTunnel::EncapSPort(0), + InfoIpTunnel::EncapDPort(0), + InfoIpTunnel::EncapFlags(TunnelEncapFlags::from_bits_retain(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); +} + +#[test] +fn test_iptunnel_sit_link_info() { + let raw: Vec = vec![ + 0x00, 0x00, // AF_UNSPEC and reserved + 0x00, 0x03, // Link Layer Type IPTUNNEL (768) + 0x07, 0x00, 0x00, 0x00, // iface ifindex 7 + 0x90, 0x00, 0x00, 0x00, // flags + 0x00, 0x00, 0x00, 0x00, // changed flags + 0xa4, 0x00, // length = 164 + 0x12, 0x00, // IFLA_LINK_INFO (18) + 0x08, 0x00, 0x01, 0x00, b's', b'i', b't', 0x00, // "sit\0" + 0x98, 0x00, 0x02, 0x00, // IFLA_INFO_DATA nested + 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, + 0xc0, 0xa8, 0x7a, 0xb7, 0x08, 0x00, 0x03, 0x00, 0x0a, 0xff, 0xfe, 0x02, + 0x05, 0x00, 0x04, 0x00, 0x40, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x29, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, + ]; + + let expected = LinkMessage { + header: LinkHeader { + interface_family: AddressFamily::Unspec, + index: 7, + link_layer_type: LinkLayerType::Tunnel, + flags: LinkFlags::Noarp | LinkFlags::Pointopoint, + change_mask: LinkFlags::empty(), + }, + attributes: vec![LinkAttribute::LinkInfo(vec![ + LinkInfo::Kind(InfoKind::SitTun), + LinkInfo::Data(InfoData::SitTun(vec![ + InfoSitTun::Other(DefaultNla::new(1, vec![0, 0, 0, 0])), + InfoSitTun::Other(DefaultNla::new(2, vec![192, 168, 122, 183])), + InfoSitTun::Other(DefaultNla::new(3, vec![10, 255, 254, 2])), + InfoSitTun::Other(DefaultNla::new(4, vec![64])), + InfoSitTun::Other(DefaultNla::new(5, vec![0])), + InfoSitTun::Other(DefaultNla::new(10, vec![1])), + InfoSitTun::Other(DefaultNla::new(9, vec![41])), + InfoSitTun::Other(DefaultNla::new(8, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(20, vec![0, 0, 0, 0])), + InfoSitTun::Other(DefaultNla::new(11, vec![0; 16])), + InfoSitTun::Other(DefaultNla::new(12, vec![0, 0, 0, 0])), + InfoSitTun::Other(DefaultNla::new(13, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(14, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(15, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(17, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(18, vec![0, 0])), + InfoSitTun::Other(DefaultNla::new(16, vec![0, 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/mod.rs b/src/link/tests/mod.rs index c3ffceb0..1bee0793 100644 --- a/src/link/tests/mod.rs +++ b/src/link/tests/mod.rs @@ -11,6 +11,8 @@ mod geneve; #[cfg(test)] mod hsr; #[cfg(test)] +mod iptunnel; +#[cfg(test)] mod ipvlan; #[cfg(test)] mod ipvtap; diff --git a/src/rule/attribute.rs b/src/rule/attribute.rs index 2c38972c..78e5a60f 100644 --- a/src/rule/attribute.rs +++ b/src/rule/attribute.rs @@ -142,7 +142,7 @@ impl Nla for RuleAttribute { | Self::SuppressPrefixLen(value) | Self::Table(value) => NativeEndian::write_u32(buffer, *value), Self::L3MDev(value) => buffer[0] = (*value).into(), - Self::IpProtocol(value) => buffer[0] = i32::from(*value) as u8, + Self::IpProtocol(value) => buffer[0] = u8::from(*value), Self::Protocol(value) => buffer[0] = u8::from(*value), Self::Other(attr) => attr.emit_value(buffer), } @@ -212,7 +212,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable> .into(), ), FRA_IP_PROTO => Self::IpProtocol(IpProtocol::from( - parse_u8(payload).context("invalid FRA_IP_PROTO value")? as i32, + parse_u8(payload).context("invalid FRA_IP_PROTO value")?, )), FRA_SPORT_RANGE => Self::SourcePortRange( RulePortRange::parse(payload)