1use crate::{
2 class::htb::Htb,
3 constants::*,
4 errors::TcError,
5 link, netlink,
6 qdiscs::{
7 clsact::Clsact,
8 fq_codel::{FqCodel, FqCodelXStats},
9 },
10 types::*,
11 HtbXstats,
12};
13
14pub fn qdiscs<T: netlink::NetlinkConnection>() -> Result<Vec<Tc>, TcError> {
17 let mut tcs = Vec::new();
18
19 let messages = T::new()?.qdiscs()?;
20 for message in &messages {
21 let tc = TcMessage {
22 index: message.header.index as u32,
23 handle: message.header.handle,
24 parent: message.header.parent,
25 };
26 let mut attribute = Attribute::default();
27
28 let mut options = Vec::new();
29 let mut xstats = Vec::new();
30 for attr in &message.attrs {
31 match attr {
32 TcAttr::Kind(kind) => attribute.kind = kind.to_string(),
33 TcAttr::Options(opts) => options = opts.to_vec(),
34 TcAttr::Stats(bytes) => attribute.stats = parse_stats(bytes).ok(),
35 TcAttr::Xstats(bytes) => xstats.extend(bytes.as_slice()),
36 TcAttr::Stats2(stats) => attribute.stats2 = parse_stats2(stats).ok(),
37 _ => (),
38 }
39 }
40
41 attribute.qdisc = parse_qdiscs(attribute.kind.as_str(), options);
42 attribute.xstats = parse_xstats(attribute.kind.as_str(), xstats.as_slice()).ok();
43
44 tcs.push(Tc {
45 msg: tc,
46 attr: attribute,
47 });
48 }
49
50 Ok(tcs)
51}
52
53pub fn class_for_index<T: netlink::NetlinkConnection>(index: u32) -> Result<Vec<Tc>, TcError> {
56 let mut tcs = Vec::new();
57
58 let messages = T::new()?.classes(index as i32)?;
59 for message in &messages {
60 let tc = TcMessage {
61 index,
62 handle: message.header.handle,
63 parent: message.header.parent,
64 };
65 let mut attribute = Attribute::default();
66
67 let mut opts = vec![];
68 let mut xstats = Vec::new();
69 for attr in &message.attrs {
70 match attr {
71 TcAttr::Kind(kind) => attribute.kind = kind.to_string(),
72 TcAttr::Options(tc_opts) => opts = tc_opts.to_vec(),
73 TcAttr::Stats(bytes) => attribute.stats = parse_stats(bytes).ok(),
74 TcAttr::Xstats(bytes) => xstats.extend(bytes.as_slice()),
75 TcAttr::Stats2(stats) => attribute.stats2 = parse_stats2(stats).ok(),
76 _ => (),
77 }
78 }
79
80 attribute.class = parse_classes(attribute.kind.as_str(), opts);
81 attribute.xstats = parse_xstats(attribute.kind.as_str(), xstats.as_slice()).ok();
82
83 tcs.push(Tc {
84 msg: tc,
85 attr: attribute,
86 });
87 }
88
89 Ok(tcs)
90}
91
92pub fn class<T: netlink::NetlinkConnection>(name: &str) -> Result<Vec<Tc>, TcError> {
96 let links = link::links::<T>()?;
97
98 if let Some(link) = links.iter().find(|link| link.name == name) {
99 class_for_index::<T>(link.index)
100 } else {
101 Ok(Vec::new())
102 }
103}
104
105pub fn classes<T: netlink::NetlinkConnection>() -> Result<Vec<Tc>, TcError> {
108 let mut tcs = Vec::new();
109
110 let links = link::links::<T>()?;
111 for link in links {
112 tcs.append(&mut class_for_index::<T>(link.index)?);
113 }
114
115 Ok(tcs)
116}
117
118pub fn tc_stats<T: netlink::NetlinkConnection>() -> Result<Vec<Tc>, TcError> {
119 let mut tcs = qdiscs::<T>()?;
120 tcs.append(&mut classes::<T>()?);
121
122 Ok(tcs)
123}
124
125fn parse_stats(bytes: &[u8]) -> Result<Stats, TcError> {
126 bincode::deserialize(bytes).map_err(TcError::UnmarshalStruct)
127}
128
129fn parse_stats2(stats2: &Vec<TcStats2>) -> Result<Stats2, TcError> {
130 let mut stats = Stats2::default();
131 let mut errors = Vec::new();
132 for stat in stats2 {
133 match stat {
134 TcStats2::StatsBasic(bytes) => match bincode::deserialize(bytes.as_slice()) {
135 Ok(stats_basic) => stats.basic = Some(stats_basic),
136 Err(e) => errors.push(format!("Failed to parse StatsBasic: {e}")),
137 },
138 TcStats2::StatsQueue(bytes) => match bincode::deserialize(bytes.as_slice()) {
139 Ok(stats_queue) => stats.queue = Some(stats_queue),
140 Err(e) => errors.push(format!("Failed to parse StatsQueue: {e}")),
141 },
142 _ => (),
144 }
145 }
146
147 if !errors.is_empty() {
148 let message = errors.join(", ");
149 Err(TcError::UnmarshalStructs(message))
150 } else {
151 Ok(stats)
152 }
153}
154
155fn parse_qdiscs(kind: &str, opts: Vec<TcOption>) -> Option<QDisc> {
156 match kind {
157 FQ_CODEL => Some(QDisc::FqCodel(FqCodel::new(opts))),
158 CLSACT => Some(QDisc::Clsact(Clsact {})),
159 HTB => Htb::new(opts).init.map(QDisc::Htb),
160 _ => None,
161 }
162}
163
164fn parse_classes(kind: &str, opts: Vec<TcOption>) -> Option<Class> {
165 match kind {
166 HTB => Some(Class::Htb(Htb::new(opts))),
167 _ => None,
168 }
169}
170
171fn parse_xstats(kind: &str, bytes: &[u8]) -> Result<XStats, TcError> {
172 match kind {
173 FQ_CODEL => FqCodelXStats::new(bytes).map(XStats::FqCodel),
174 HTB => HtbXstats::new(bytes).map(XStats::Htb),
175 _ => Err(TcError::UnimplementedAttribute(format!(
176 "XStats for {kind}"
177 ))),
178 }
179}