1#![deny(clippy::all)]
16
17mod errors;
18mod types;
19
20#[cfg(test)]
21mod test;
22
23use std::borrow::Borrow;
24use std::borrow::BorrowMut;
25use std::collections::BTreeMap;
26
27use errors::TcError;
28use netlink_packet_core::NetlinkHeader;
29use netlink_packet_core::NetlinkMessage;
30use netlink_packet_core::NetlinkPayload;
31use netlink_packet_core::NLM_F_DUMP;
32use netlink_packet_core::NLM_F_REQUEST;
33use netlink_packet_route::tc::TcMessage;
34use netlink_packet_route::RouteNetlinkMessage;
35use netlink_sys::constants::NETLINK_ROUTE;
36use netlink_sys::Socket;
37use netlink_sys::SocketAddr;
38use nix::net::if_;
39pub use types::FqCodelQDisc;
40pub use types::FqCodelQdStats;
41pub use types::FqCodelXStats;
42pub use types::QDisc;
43pub use types::TcStat;
44pub use types::XStats;
45
46pub type TcStats = Vec<TcStat>;
47type Result<T> = std::result::Result<T, TcError>;
48
49pub fn tc_stats() -> Result<TcStats> {
51 let ifaces = get_interfaces()?;
52 read_tc_stats(ifaces, &get_netlink_qdiscs)
53}
54
55fn read_tc_stats(
56 interfaces: BTreeMap<u32, String>,
57 netlink_retriever: &dyn Fn() -> Result<Vec<TcMessage>>,
58) -> Result<TcStats> {
59 let messages = netlink_retriever()?;
60 let tc_stats = messages
61 .into_iter()
62 .filter_map(|msg| {
63 interfaces
64 .get(&(msg.header.index as u32))
65 .cloned()
66 .map(|if_name| TcStat::new(if_name, &msg))
67 })
68 .collect();
69
70 Ok(tc_stats)
71}
72
73fn get_netlink_qdiscs() -> Result<Vec<TcMessage>> {
77 let socket = Socket::new(NETLINK_ROUTE).map_err(|e| TcError::Netlink(e.to_string()))?;
79 socket
80 .connect(&SocketAddr::new(0, 0))
81 .map_err(|e| TcError::Netlink(e.to_string()))?;
82
83 let mut nl_hdr = NetlinkHeader::default();
85 nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP;
86 let msg = RouteNetlinkMessage::GetQueueDiscipline(TcMessage::default());
87 let mut packet = NetlinkMessage::new(nl_hdr, NetlinkPayload::from(msg));
88 packet.finalize();
89 let mut buf = vec![0; packet.header.length as usize];
90 packet.serialize(buf[..].borrow_mut());
91
92 socket
94 .send(buf[..].borrow(), 0)
95 .map_err(|e| TcError::Netlink(e.to_string()))?;
96
97 let mut recv_buf = vec![0; 4096];
99 let mut offset = 0;
100 let mut response = Vec::new();
101 'out: while let Ok(size) = socket.recv(&mut recv_buf[..].borrow_mut(), 0) {
102 loop {
103 let bytes = recv_buf[offset..].borrow();
104 let rx_packet = <NetlinkMessage<RouteNetlinkMessage>>::deserialize(bytes)
105 .map_err(|e| TcError::Netlink(e.to_string()))?;
106 response.push(rx_packet.clone());
107 let payload = rx_packet.payload;
108 if let NetlinkPayload::Error(err) = payload {
109 return Err(TcError::Netlink(err.to_string()));
110 }
111 if let NetlinkPayload::Done(_) = payload {
112 break 'out;
113 }
114
115 offset += rx_packet.header.length as usize;
116 if offset == size || rx_packet.header.length == 0 {
117 offset = 0;
118 break;
119 }
120 }
121 }
122
123 let mut tc_msgs = Vec::new();
124 for msg in response {
125 if let NetlinkPayload::InnerMessage(RouteNetlinkMessage::NewQueueDiscipline(tc)) =
126 msg.payload
127 {
128 tc_msgs.push(tc);
129 }
130 }
131
132 Ok(tc_msgs)
133}
134
135fn get_interfaces() -> Result<BTreeMap<u32, String>> {
137 let ifaces = if_::if_nameindex().map_err(|e| TcError::ReadInterfaces(e.to_string()))?;
138 let if_map = ifaces
139 .iter()
140 .map(|iface| {
141 let index = iface.index();
142 let name = if let Ok(name) = iface.name().to_str() {
143 name.to_string()
144 } else {
145 String::new()
146 };
147 (index, name)
148 })
149 .collect::<BTreeMap<u32, String>>();
150
151 Ok(if_map)
152}