Skip to content

Commit 98fa3ca

Browse files
committed
feat: add netlink support
1 parent 7a51870 commit 98fa3ca

File tree

7 files changed

+269
-7
lines changed

7 files changed

+269
-7
lines changed

src/link/link_info/info_data.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use netlink_packet_core::{
88
use super::super::{
99
InfoBond, InfoBridge, InfoGeneve, InfoGreTap, InfoGreTap6, InfoGreTun,
1010
InfoGreTun6, InfoGtp, InfoHsr, InfoIpTunnel, InfoIpVlan, InfoIpVtap,
11-
InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoTun,
12-
InfoVeth, InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm,
11+
InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan, InfoMacVtap, InfoNetkit,
12+
InfoTun, InfoVeth, InfoVlan, InfoVrf, InfoVti, InfoVxlan, InfoXfrm,
1313
};
1414

1515
const IFLA_INFO_DATA: u16 = 2;
@@ -41,6 +41,7 @@ pub enum InfoData {
4141
Hsr(Vec<InfoHsr>),
4242
Geneve(Vec<InfoGeneve>),
4343
IpTunnel(Vec<InfoIpTunnel>),
44+
Netkit(Vec<InfoNetkit>),
4445
Other(Vec<u8>),
4546
}
4647

@@ -71,6 +72,7 @@ impl Nla for InfoData {
7172
Self::Gtp(nlas) => nlas.as_slice().buffer_len(),
7273
Self::Geneve(nlas) => nlas.as_slice().buffer_len(),
7374
Self::IpTunnel(nlas) => nlas.as_slice().buffer_len(),
75+
Self::Netkit(nlas) => nlas.as_slice().buffer_len(),
7476
Self::Other(v) => v.len(),
7577
}
7678
}
@@ -101,6 +103,7 @@ impl Nla for InfoData {
101103
Self::Gtp(nlas) => nlas.as_slice().emit(buffer),
102104
Self::Geneve(nlas) => nlas.as_slice().emit(buffer),
103105
Self::IpTunnel(nlas) => nlas.as_slice().emit(buffer),
106+
Self::Netkit(nlas) => nlas.as_slice().emit(buffer),
104107
Self::Other(v) => buffer.copy_from_slice(v.as_slice()),
105108
}
106109
}
@@ -378,6 +381,17 @@ impl InfoData {
378381
}
379382
InfoData::Geneve(v)
380383
}
384+
InfoKind::Netkit => {
385+
let mut v = Vec::new();
386+
for nla in NlasIterator::new(payload) {
387+
let nla = &nla.context(format!(
388+
"invalid IFLA_INFO_DATA for {kind} {payload:?}"
389+
))?;
390+
let parsed = InfoNetkit::parse(nla)?;
391+
v.push(parsed);
392+
}
393+
InfoData::Netkit(v)
394+
}
381395
_ => InfoData::Other(payload.to_vec()),
382396
})
383397
}

src/link/link_info/infos.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const XFRM: &str = "xfrm";
4242
const MACSEC: &str = "macsec";
4343
const HSR: &str = "hsr";
4444
const GENEVE: &str = "geneve";
45+
const NETKIT: &str = "netkit";
4546

4647
#[derive(Debug, PartialEq, Eq, Clone)]
4748
#[non_exhaustive]
@@ -201,6 +202,7 @@ pub enum InfoKind {
201202
MacSec,
202203
Hsr,
203204
Geneve,
205+
Netkit,
204206
Other(String),
205207
}
206208

@@ -239,6 +241,7 @@ impl std::fmt::Display for InfoKind {
239241
Self::MacSec => MACSEC,
240242
Self::Hsr => HSR,
241243
Self::Geneve => GENEVE,
244+
Self::Netkit => NETKIT,
242245
Self::Other(s) => s.as_str(),
243246
}
244247
)
@@ -277,6 +280,7 @@ impl Nla for InfoKind {
277280
Self::MacSec => MACSEC.len(),
278281
Self::Hsr => HSR.len(),
279282
Self::Geneve => GENEVE.len(),
283+
Self::Netkit => NETKIT.len(),
280284
Self::Other(s) => s.len(),
281285
};
282286
len + 1
@@ -335,6 +339,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoKind {
335339
XFRM => Self::Xfrm,
336340
HSR => Self::Hsr,
337341
GENEVE => Self::Geneve,
342+
NETKIT => Self::Netkit,
338343
_ => Self::Other(s),
339344
})
340345
}

src/link/link_info/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod iptunnel;
1919
mod ipvlan;
2020
mod mac_vlan;
2121
mod macsec;
22+
mod netkit;
2223
mod tun;
2324
mod veth;
2425
mod vlan;
@@ -55,6 +56,7 @@ pub use self::{
5556
},
5657
mac_vlan::{InfoMacVlan, InfoMacVtap, MacVlanMode, MacVtapMode},
5758
macsec::{InfoMacSec, MacSecCipherId, MacSecOffload, MacSecValidate},
59+
netkit::{InfoNetkit, NetkitMode, NetkitPolicy},
5860
tun::InfoTun,
5961
veth::InfoVeth,
6062
vlan::{InfoVlan, VlanQosMapping},

src/link/link_info/netkit.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_core::{
4+
emit_u32, parse_u32, parse_u8, DecodeError, DefaultNla, Emitable,
5+
ErrorContext, Nla, NlaBuffer, Parseable,
6+
};
7+
8+
use super::super::{LinkMessage, LinkMessageBuffer};
9+
10+
const NETKIT_MODE_L2: u32 = 0;
11+
const NETKIT_MODE_L3: u32 = 1;
12+
13+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14+
#[non_exhaustive]
15+
pub enum NetkitMode {
16+
L2,
17+
L3,
18+
Other(u32),
19+
}
20+
21+
impl From<NetkitMode> for u32 {
22+
fn from(mode: NetkitMode) -> Self {
23+
match mode {
24+
NetkitMode::L2 => NETKIT_MODE_L2,
25+
NetkitMode::L3 => NETKIT_MODE_L3,
26+
NetkitMode::Other(value) => value,
27+
}
28+
}
29+
}
30+
31+
impl From<u32> for NetkitMode {
32+
fn from(value: u32) -> Self {
33+
match value {
34+
NETKIT_MODE_L2 => NetkitMode::L2,
35+
NETKIT_MODE_L3 => NetkitMode::L3,
36+
_ => NetkitMode::Other(value),
37+
}
38+
}
39+
}
40+
41+
const NETKIT_POLICY_PASS: u32 = 0;
42+
const NETKIT_POLICY_DROP: u32 = 2;
43+
44+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
45+
#[non_exhaustive]
46+
pub enum NetkitPolicy {
47+
Pass,
48+
Drop,
49+
Other(u32),
50+
}
51+
52+
impl From<NetkitPolicy> for u32 {
53+
fn from(policy: NetkitPolicy) -> Self {
54+
match policy {
55+
NetkitPolicy::Pass => NETKIT_POLICY_PASS,
56+
NetkitPolicy::Drop => NETKIT_POLICY_DROP,
57+
NetkitPolicy::Other(value) => value,
58+
}
59+
}
60+
}
61+
62+
impl From<u32> for NetkitPolicy {
63+
fn from(value: u32) -> Self {
64+
match value {
65+
NETKIT_POLICY_PASS => NetkitPolicy::Pass,
66+
NETKIT_POLICY_DROP => NetkitPolicy::Drop,
67+
_ => NetkitPolicy::Other(value),
68+
}
69+
}
70+
}
71+
72+
const IFLA_NETKIT_PEER_INFO: u16 = 1;
73+
const IFLA_NETKIT_PRIMARY: u16 = 2;
74+
const IFLA_NETKIT_POLICY: u16 = 3;
75+
const IFLA_NETKIT_PEER_POLICY: u16 = 4;
76+
const IFLA_NETKIT_MODE: u16 = 5;
77+
78+
#[derive(Debug, PartialEq, Eq, Clone)]
79+
#[non_exhaustive]
80+
pub enum InfoNetkit {
81+
Peer(LinkMessage),
82+
Primary(bool),
83+
Policy(NetkitPolicy),
84+
PeerPolicy(NetkitPolicy),
85+
Mode(NetkitMode),
86+
Other(DefaultNla),
87+
}
88+
89+
impl Nla for InfoNetkit {
90+
fn value_len(&self) -> usize {
91+
match *self {
92+
Self::Peer(ref message) => message.buffer_len(),
93+
Self::Primary(_) => 1,
94+
Self::Policy(_) | Self::PeerPolicy(_) | Self::Mode(_) => 4,
95+
Self::Other(ref attr) => attr.value_len(),
96+
}
97+
}
98+
99+
fn emit_value(&self, buffer: &mut [u8]) {
100+
match *self {
101+
Self::Peer(ref message) => message.emit(buffer),
102+
Self::Primary(value) => {
103+
buffer[0] = value as u8;
104+
}
105+
Self::Policy(value) | Self::PeerPolicy(value) => {
106+
emit_u32(buffer, value.into()).unwrap();
107+
}
108+
Self::Mode(value) => {
109+
emit_u32(buffer, value.into()).unwrap();
110+
}
111+
Self::Other(ref attr) => attr.emit_value(buffer),
112+
}
113+
}
114+
115+
fn kind(&self) -> u16 {
116+
match *self {
117+
Self::Peer(_) => IFLA_NETKIT_PEER_INFO,
118+
Self::Primary(_) => IFLA_NETKIT_PRIMARY,
119+
Self::Policy(_) => IFLA_NETKIT_POLICY,
120+
Self::PeerPolicy(_) => IFLA_NETKIT_PEER_POLICY,
121+
Self::Mode(_) => IFLA_NETKIT_MODE,
122+
Self::Other(ref attr) => attr.kind(),
123+
}
124+
}
125+
}
126+
127+
impl<'a, T: AsRef<[u8]> + ?Sized> Parseable<NlaBuffer<&'a T>> for InfoNetkit {
128+
fn parse(buf: &NlaBuffer<&'a T>) -> Result<Self, DecodeError> {
129+
let payload = buf.value();
130+
Ok(match buf.kind() {
131+
IFLA_NETKIT_PEER_INFO => {
132+
let err = "failed to parse netkit peer info";
133+
let buffer =
134+
LinkMessageBuffer::new_checked(&payload).context(err)?;
135+
Self::Peer(LinkMessage::parse(&buffer).context(err)?)
136+
}
137+
IFLA_NETKIT_PRIMARY => {
138+
let value = parse_u8(payload)? != 0;
139+
Self::Primary(value)
140+
}
141+
IFLA_NETKIT_POLICY => Self::Policy(parse_u32(payload)?.into()),
142+
IFLA_NETKIT_PEER_POLICY => {
143+
Self::PeerPolicy(parse_u32(payload)?.into())
144+
}
145+
IFLA_NETKIT_MODE => Self::Mode(parse_u32(payload)?.into()),
146+
kind => Self::Other(
147+
DefaultNla::parse(buf)
148+
.context(format!("unknown NLA type {kind} for netkit"))?,
149+
),
150+
})
151+
}
152+
}

src/link/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ pub use self::{
4848
InfoBridge, InfoBridgePort, InfoData, InfoGeneve, InfoGreTap,
4949
InfoGreTap6, InfoGreTun, InfoGreTun6, InfoGtp, InfoHsr, InfoIpTunnel,
5050
InfoIpVlan, InfoIpVtap, InfoIpoib, InfoKind, InfoMacSec, InfoMacVlan,
51-
InfoMacVtap, InfoPortData, InfoPortKind, InfoTun, InfoVeth, InfoVlan,
52-
InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm, IpVlanFlags,
53-
IpVlanMode, IpVtapFlags, IpVtapMode, LinkInfo, LinkXstats,
51+
InfoMacVtap, InfoNetkit, InfoPortData, InfoPortKind, InfoTun, InfoVeth,
52+
InfoVlan, InfoVrf, InfoVrfPort, InfoVti, InfoVxlan, InfoXfrm,
53+
IpVlanFlags, IpVlanMode, IpVtapFlags, IpVtapMode, LinkInfo, LinkXstats,
5454
MacSecCipherId, MacSecOffload, MacSecValidate, MacVlanMode,
55-
MacVtapMode, MiiStatus, TunnelEncapFlags, TunnelEncapType,
56-
VlanQosMapping,
55+
MacVtapMode, MiiStatus, NetkitMode, NetkitPolicy, TunnelEncapFlags,
56+
TunnelEncapType, VlanQosMapping,
5757
},
5858
link_layer_type::LinkLayerType,
5959
link_mode::LinkMode,

src/link/tests/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ mod macvtap;
2727
#[cfg(test)]
2828
mod message;
2929
#[cfg(test)]
30+
mod netkit;
31+
#[cfg(test)]
3032
mod prop_list;
3133
#[cfg(test)]
3234
mod sriov;

src/link/tests/netkit.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
use netlink_packet_core::{Emitable, Parseable};
4+
5+
use crate::{
6+
link::{
7+
InfoData, InfoKind, InfoNetkit, LinkAttribute, LinkHeader, LinkInfo,
8+
LinkLayerType, LinkMessage, LinkMessageBuffer, NetkitMode,
9+
NetkitPolicy,
10+
},
11+
AddressFamily,
12+
};
13+
14+
#[test]
15+
fn test_create_netkit() {
16+
// Captured from kernel 6.14.0 with iproute2-6.14.0
17+
// ```sh
18+
// sudo ip link add nlmon0 type nlmon
19+
// sudo ip link set nlmon0 up
20+
// sudo tcpdump -i nlmon0 -w netkit.pcap &
21+
// sudo ip link add nktest0 type netkit peer name nktest1
22+
// sudo pkill tcpdump
23+
// ```
24+
//
25+
// Tests basic netkit attributes (PRIMARY, POLICY, MODE) without
26+
// kernel-version-specific attributes that may vary
27+
let raw: Vec<u8> = vec![
28+
0x00, // interface family AF_UNSPEC
29+
0x00, // reserved
30+
0x00, 0x00, // link layer type 0 (Netrom)
31+
0x00, 0x00, 0x00, 0x00, // iface index 0
32+
0x00, 0x00, 0x00, 0x00, // device flags 0
33+
0x00, 0x00, 0x00, 0x00, // change flags 0
34+
0x0c, 0x00, // length 12
35+
0x03, 0x00, // IFLA_IFNAME
36+
0x6e, 0x6b, 0x74, 0x65, 0x73, 0x74, 0x30, 0x00, // "nktest0\0"
37+
0x2c, 0x00, // length 44
38+
0x12, 0x00, // IFLA_LINKINFO 18
39+
0x0b, 0x00, // length 11
40+
0x01, 0x00, // IFLA_INFO_KIND 1
41+
0x6e, 0x65, 0x74, 0x6b, 0x69, 0x74, 0x00, // 'netkit\0'
42+
0x00, // padding
43+
0x1c, 0x00, // length 28
44+
0x02, 0x00, // IFLA_INFO_DATA 2
45+
0x05, 0x00, // length 5
46+
0x02, 0x00, // NETKIT_INFO_PRIMARY
47+
0x01, // value: true
48+
0x00, 0x00, 0x00, // padding
49+
0x08, 0x00, // length 8
50+
0x03, 0x00, // NETKIT_INFO_POLICY
51+
0x00, 0x00, 0x00, 0x00, // value: 0 (PASS)
52+
0x08, 0x00, // length 8
53+
0x05, 0x00, // NETKIT_INFO_MODE
54+
0x01, 0x00, 0x00, 0x00, // value: 1 (L3)
55+
];
56+
57+
let expected = LinkMessage {
58+
header: LinkHeader {
59+
interface_family: AddressFamily::Unspec,
60+
index: 0,
61+
link_layer_type: LinkLayerType::Netrom,
62+
..Default::default()
63+
},
64+
attributes: vec![
65+
LinkAttribute::IfName("nktest0".to_string()),
66+
LinkAttribute::LinkInfo(vec![
67+
LinkInfo::Kind(InfoKind::Netkit),
68+
LinkInfo::Data(InfoData::Netkit(vec![
69+
InfoNetkit::Primary(true),
70+
InfoNetkit::Policy(NetkitPolicy::Pass),
71+
InfoNetkit::Mode(NetkitMode::L3),
72+
])),
73+
]),
74+
],
75+
};
76+
77+
assert_eq!(
78+
expected,
79+
LinkMessage::parse(&LinkMessageBuffer::new(&raw)).unwrap()
80+
);
81+
82+
let mut buf = vec![0; expected.buffer_len()];
83+
84+
expected.emit(&mut buf);
85+
86+
assert_eq!(buf, raw);
87+
}

0 commit comments

Comments
 (0)