Skip to content

Commit 7021cdc

Browse files
yonatan-linikcathay4t
authored andcommitted
ip-link: Reorganize link details structs between files
1 parent 939412b commit 7021cdc

4 files changed

Lines changed: 346 additions & 335 deletions

File tree

src/ip/link/link_details.rs

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
use std::ffi::CStr;
2+
3+
use rtnetlink::packet_core::DefaultNla;
4+
use rtnetlink::{
5+
packet_core::Nla as _,
6+
packet_route::link::{AfSpecInet6, AfSpecUnspec, LinkAttribute},
7+
};
8+
use serde::Serialize;
9+
10+
use crate::link::link_info::CliLinkInfoKindNData;
11+
12+
// Use constants until support is added to netlink-packet-route
13+
const IFLA_PARENT_DEV_NAME: u16 = 56;
14+
const IFLA_PARENT_DEV_BUS_NAME: u16 = 57;
15+
const IFLA_GRO_MAX_SIZE: u16 = 58;
16+
const IFLA_TSO_MAX_SIZE: u16 = 59;
17+
const IFLA_TSO_MAX_SEGS: u16 = 60;
18+
const IFLA_ALLMULTI: u16 = 61;
19+
20+
fn get_addr_gen_mode(af_spec_unspec: &[AfSpecUnspec]) -> String {
21+
af_spec_unspec
22+
.iter()
23+
.filter_map(|s| {
24+
let AfSpecUnspec::Inet6(v) = s else {
25+
return None;
26+
};
27+
v.iter()
28+
.filter_map(|i| {
29+
if let AfSpecInet6::AddrGenMode(mode) = i {
30+
Some(mode)
31+
} else {
32+
None
33+
}
34+
})
35+
.next()
36+
})
37+
.next()
38+
.map(|i| i.to_string())
39+
.unwrap_or_default()
40+
}
41+
fn default_nla_to_string(default_nla: &DefaultNla) -> String {
42+
let val_len = default_nla.value_len();
43+
let mut val = vec![0u8; val_len];
44+
default_nla.emit_value(&mut val);
45+
CStr::from_bytes_with_nul(&val)
46+
.expect("String nla to be nul-terminated and not contain interior nuls")
47+
.to_str()
48+
.expect("To be valid UTF-8")
49+
.to_string()
50+
}
51+
52+
#[derive(Serialize)]
53+
pub(crate) struct CliLinkInfoDetails {
54+
promiscuity: u32,
55+
allmulti: u32,
56+
min_mtu: u32,
57+
max_mtu: u32,
58+
#[serde(skip_serializing_if = "Option::is_none")]
59+
linkinfo: Option<CliLinkInfoKindNData>,
60+
#[serde(skip_serializing_if = "String::is_empty")]
61+
inet6_addr_gen_mode: String,
62+
num_tx_queues: u32,
63+
num_rx_queues: u32,
64+
gso_max_size: u32,
65+
gso_max_segs: u32,
66+
tso_max_size: u32,
67+
tso_max_segs: u32,
68+
gro_max_size: u32,
69+
#[serde(skip_serializing_if = "String::is_empty")]
70+
parentbus: String,
71+
#[serde(skip_serializing_if = "String::is_empty")]
72+
parentdev: String,
73+
}
74+
75+
impl CliLinkInfoDetails {
76+
pub fn new(nl_attrs: &[LinkAttribute]) -> Self {
77+
let mut linkinfo = None;
78+
let mut promiscuity = 0;
79+
let mut allmulti = 0;
80+
let mut min_mtu = 0;
81+
let mut max_mtu = 0;
82+
let mut num_tx_queues = 0;
83+
let mut num_rx_queues = 0;
84+
let mut gso_max_size = 0;
85+
let mut gso_max_segs = 0;
86+
let mut tso_max_size = 0;
87+
let mut tso_max_segs = 0;
88+
let mut gro_max_size = 0;
89+
let mut inet6_addr_gen_mode = String::new();
90+
let mut parentbus = String::new();
91+
let mut parentdev = String::new();
92+
93+
for nl_attr in nl_attrs {
94+
match nl_attr {
95+
LinkAttribute::Promiscuity(p) => promiscuity = *p,
96+
LinkAttribute::MinMtu(m) => min_mtu = *m,
97+
LinkAttribute::MaxMtu(m) => max_mtu = *m,
98+
LinkAttribute::AfSpecUnspec(a) => {
99+
inet6_addr_gen_mode = get_addr_gen_mode(a)
100+
}
101+
LinkAttribute::NumTxQueues(n) => num_tx_queues = *n,
102+
LinkAttribute::NumRxQueues(n) => num_rx_queues = *n,
103+
LinkAttribute::GsoMaxSize(g) => gso_max_size = *g,
104+
LinkAttribute::GsoMaxSegs(g) => gso_max_segs = *g,
105+
LinkAttribute::Other(default_nla) => match default_nla.kind() {
106+
IFLA_PARENT_DEV_BUS_NAME => {
107+
parentbus = default_nla_to_string(default_nla);
108+
}
109+
IFLA_PARENT_DEV_NAME => {
110+
parentdev = default_nla_to_string(default_nla);
111+
}
112+
IFLA_GRO_MAX_SIZE => {
113+
let mut val = [0u8; 4];
114+
default_nla.emit_value(&mut val);
115+
gro_max_size = u32::from_ne_bytes(val);
116+
}
117+
IFLA_TSO_MAX_SIZE => {
118+
let mut val = [0u8; 4];
119+
default_nla.emit_value(&mut val);
120+
tso_max_size = u32::from_ne_bytes(val);
121+
}
122+
IFLA_TSO_MAX_SEGS => {
123+
let mut val = [0u8; 4];
124+
default_nla.emit_value(&mut val);
125+
tso_max_segs = u32::from_ne_bytes(val);
126+
}
127+
IFLA_ALLMULTI => {
128+
let mut val = [0u8; 4];
129+
default_nla.emit_value(&mut val);
130+
allmulti = u32::from_ne_bytes(val);
131+
}
132+
_ => { /* println!("Remains {:?}", default_nla); */ }
133+
},
134+
LinkAttribute::LinkInfo(info) => {
135+
linkinfo = CliLinkInfoKindNData::new(info);
136+
}
137+
_ => {
138+
// println!("Remains {:?}", nl_attr);
139+
}
140+
}
141+
}
142+
143+
Self {
144+
promiscuity,
145+
allmulti,
146+
min_mtu,
147+
max_mtu,
148+
linkinfo,
149+
inet6_addr_gen_mode,
150+
num_tx_queues,
151+
num_rx_queues,
152+
gso_max_size,
153+
gso_max_segs,
154+
tso_max_size,
155+
tso_max_segs,
156+
gro_max_size,
157+
parentbus,
158+
parentdev,
159+
}
160+
}
161+
}
162+
163+
impl std::fmt::Display for CliLinkInfoDetails {
164+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165+
write!(
166+
f,
167+
" promiscuity {} allmulti {} minmtu {} maxmtu {} ",
168+
self.promiscuity, self.allmulti, self.min_mtu, self.max_mtu,
169+
)?;
170+
171+
if let Some(linkinfo) = &self.linkinfo {
172+
write!(f, "{linkinfo}")?;
173+
}
174+
175+
write!(
176+
f,
177+
"addrgenmode {} numtxqueues {} numrxqueues {} gso_max_size {} gso_max_segs {} tso_max_size {} tso_max_segs {} gro_max_size {} ",
178+
self.inet6_addr_gen_mode,
179+
self.num_tx_queues,
180+
self.num_rx_queues,
181+
self.gso_max_size,
182+
self.gso_max_segs,
183+
self.tso_max_size,
184+
self.tso_max_segs,
185+
self.gro_max_size,
186+
)?;
187+
188+
if !self.parentbus.is_empty() {
189+
write!(f, "parentbus {} ", self.parentbus)?;
190+
}
191+
if !self.parentdev.is_empty() {
192+
write!(f, "parentdev {} ", self.parentdev)?;
193+
}
194+
195+
Ok(())
196+
}
197+
}

src/ip/link/link_info.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use rtnetlink::packet_route::link::{InfoData, LinkInfo};
2+
use serde::Serialize;
3+
4+
const VLAN_FLAG_REORDER_HDR: u32 = 0x1;
5+
const VLAN_FLAG_GVRP: u32 = 0x2;
6+
const VLAN_FLAG_LOOSE_BINDING: u32 = 0x4;
7+
const VLAN_FLAG_MVRP: u32 = 0x8;
8+
9+
#[derive(Serialize)]
10+
#[serde(untagged)]
11+
enum CliLinkInfoData {
12+
Vlan {
13+
protocol: String,
14+
id: u16,
15+
flags: Vec<String>,
16+
},
17+
}
18+
19+
impl CliLinkInfoData {
20+
fn new(info_data: &InfoData) -> Self {
21+
match info_data {
22+
InfoData::Bridge(_info_bridge) => todo!(),
23+
InfoData::Tun(_info_tun) => todo!(),
24+
InfoData::Vlan(info_vlan) => {
25+
use rtnetlink::packet_route::link::InfoVlan;
26+
let mut id = 0;
27+
let mut flags = Vec::new();
28+
let mut protocol = String::new();
29+
30+
for nla in info_vlan {
31+
match nla {
32+
InfoVlan::Id(v) => id = *v,
33+
InfoVlan::Flags((flags_val, _)) => {
34+
if flags_val & VLAN_FLAG_REORDER_HDR != 0 {
35+
flags.push("REORDER_HDR".to_string());
36+
}
37+
if flags_val & VLAN_FLAG_GVRP != 0 {
38+
flags.push("GVRP".to_string());
39+
}
40+
if flags_val & VLAN_FLAG_LOOSE_BINDING != 0 {
41+
flags.push("LOOSE_BINDING".to_string());
42+
}
43+
if flags_val & VLAN_FLAG_MVRP != 0 {
44+
flags.push("MVRP".to_string());
45+
}
46+
}
47+
InfoVlan::Protocol(v) => {
48+
protocol = v.to_string().to_uppercase();
49+
}
50+
_ => (),
51+
}
52+
}
53+
54+
Self::Vlan {
55+
id,
56+
flags,
57+
protocol,
58+
}
59+
}
60+
InfoData::Veth(_info_veth) => todo!(),
61+
InfoData::Vxlan(_info_vxlan) => todo!(),
62+
InfoData::Bond(_info_bond) => todo!(),
63+
InfoData::IpVlan(_info_ip_vlan) => todo!(),
64+
InfoData::IpVtap(_info_ip_vtap) => todo!(),
65+
InfoData::MacVlan(_info_mac_vlan) => todo!(),
66+
InfoData::MacVtap(_info_mac_vtap) => todo!(),
67+
InfoData::GreTap(_info_gre_tap) => todo!(),
68+
InfoData::GreTap6(_info_gre_tap6) => todo!(),
69+
InfoData::SitTun(_info_sit_tun) => todo!(),
70+
InfoData::GreTun(_info_gre_tun) => todo!(),
71+
InfoData::GreTun6(_info_gre_tun6) => todo!(),
72+
InfoData::Vti(_info_vti) => todo!(),
73+
InfoData::Vrf(_info_vrf) => todo!(),
74+
InfoData::Gtp(_info_gtp) => todo!(),
75+
InfoData::Ipoib(_info_ipoib) => todo!(),
76+
InfoData::Xfrm(_info_xfrm) => todo!(),
77+
InfoData::MacSec(_info_mac_sec) => todo!(),
78+
InfoData::Hsr(_info_hsr) => todo!(),
79+
InfoData::Geneve(_info_geneve) => todo!(),
80+
InfoData::Other(_items) => todo!(),
81+
_ => todo!(),
82+
}
83+
}
84+
}
85+
86+
impl std::fmt::Display for CliLinkInfoData {
87+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88+
match self {
89+
CliLinkInfoData::Vlan {
90+
id,
91+
flags,
92+
protocol,
93+
} => {
94+
write!(f, "protocol {} ", protocol)?;
95+
write!(f, "id {} ", id)?;
96+
if !flags.is_empty() {
97+
write!(f, "<{}>", flags.as_slice().join(","))?;
98+
}
99+
}
100+
}
101+
102+
Ok(())
103+
}
104+
}
105+
106+
#[derive(Serialize)]
107+
pub(crate) struct CliLinkInfoKindNData {
108+
info_kind: String,
109+
#[serde(skip_serializing_if = "Option::is_none")]
110+
info_data: Option<CliLinkInfoData>,
111+
}
112+
113+
impl std::fmt::Display for CliLinkInfoKindNData {
114+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115+
write!(f, "\n ")?;
116+
write!(f, "{} ", self.info_kind)?;
117+
if let Some(data) = &self.info_data {
118+
write!(f, "{data} ")?;
119+
}
120+
Ok(())
121+
}
122+
}
123+
124+
impl CliLinkInfoKindNData {
125+
pub fn new(link_info: &[LinkInfo]) -> Option<Self> {
126+
let mut info_kind = String::new();
127+
let mut info_data = Option::None;
128+
for nla in link_info {
129+
match nla {
130+
LinkInfo::Kind(t) => {
131+
info_kind = t.to_string();
132+
}
133+
LinkInfo::Data(data) => {
134+
info_data = Some(CliLinkInfoData::new(data));
135+
}
136+
_ => (),
137+
}
138+
}
139+
140+
Some(CliLinkInfoKindNData {
141+
info_kind,
142+
info_data,
143+
})
144+
}
145+
}

src/ip/link/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
mod cli;
44
mod flags;
5+
mod link_details;
6+
mod link_info;
57
mod show;
68

79
#[cfg(test)]

0 commit comments

Comments
 (0)