1use std::vec;
2
3use netlink_packet_core::{
4 NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST,
5};
6use netlink_packet_route::{link, tc as netlink_tc, LinkMessage, RtnlMessage, TcMessage};
7use netlink_packet_utils::{nla::Nla, Emitable};
8use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
9
10use crate::{errors::NetlinkError, types::*};
11
12pub trait NetlinkConnection {
16 fn new() -> Result<Self, NetlinkError>
19 where
20 Self: Sized;
21
22 fn qdiscs(&self) -> Result<Vec<TcMsg>, NetlinkError>;
24
25 fn classes(&self, index: i32) -> Result<Vec<TcMsg>, NetlinkError>;
27
28 fn links(&self) -> Result<Vec<LinkMsg>, NetlinkError>;
30}
31
32pub struct Netlink {
34 socket: Socket,
35}
36
37impl NetlinkConnection for Netlink {
38 fn new() -> Result<Self, NetlinkError>
39 where
40 Self: Sized,
41 {
42 let socket =
43 Socket::new(NETLINK_ROUTE).map_err(|err| NetlinkError::Socket(Box::new(err)))?;
44 socket
45 .connect(&SocketAddr::new(0, 0))
46 .map_err(|err| NetlinkError::Socket(Box::new(err)))?;
47 Ok(Self { socket })
48 }
49
50 fn qdiscs(&self) -> Result<Vec<TcMsg>, NetlinkError> {
51 send_get_qdisc_request(&self.socket)?;
52
53 let mut receive_buffer = vec![0; 4096];
54 let mut offset = 0;
55
56 let mut tc_messages = Vec::new();
57 while let Ok(size) = self.socket.recv(&mut &mut receive_buffer[..], 0) {
58 loop {
59 let bytes = &receive_buffer[offset..];
60 let rx_packet = <NetlinkMessage<RtnlMessage>>::deserialize(bytes).unwrap();
61
62 let payload = rx_packet.payload;
63 match payload {
64 NetlinkPayload::InnerMessage(RtnlMessage::NewQueueDiscipline(message)) => {
65 tc_messages.push(message.clone())
66 }
67 NetlinkPayload::Error(error) => {
68 return Err(NetlinkError::Netlink(error.to_string()))
69 }
70 NetlinkPayload::Done(_) => return Ok(to_tc(tc_messages)),
71 _ => {}
72 }
73
74 offset += rx_packet.header.length as usize;
75 if offset == size || rx_packet.header.length == 0 {
76 offset = 0;
77 break;
78 }
79 }
80 }
81
82 Ok(to_tc(tc_messages))
83 }
84
85 fn classes(&self, index: i32) -> Result<Vec<TcMsg>, NetlinkError> {
86 send_get_class_request(&self.socket, index)?;
87
88 let mut receive_buffer = vec![0; 4096];
89 let mut offset = 0;
90
91 let mut tc_messages = Vec::new();
92 while let Ok(size) = self.socket.recv(&mut &mut receive_buffer[..], 0) {
93 loop {
94 let bytes = &receive_buffer[offset..];
95 let rx_packet = <NetlinkMessage<RtnlMessage>>::deserialize(bytes).unwrap();
96
97 let payload = rx_packet.payload;
98 match payload {
99 NetlinkPayload::InnerMessage(RtnlMessage::NewTrafficClass(message)) => {
100 tc_messages.push(message.clone())
101 }
102 NetlinkPayload::Error(error) => {
103 return Err(NetlinkError::Netlink(error.to_string()))
104 }
105 NetlinkPayload::Done(_) => return Ok(to_tc(tc_messages)),
106 _ => {}
107 }
108
109 offset += rx_packet.header.length as usize;
110 if offset == size || rx_packet.header.length == 0 {
111 offset = 0;
112 break;
113 }
114 }
115 }
116
117 Ok(to_tc(tc_messages))
118 }
119
120 fn links(&self) -> Result<Vec<LinkMsg>, NetlinkError> {
121 send_get_link_request(&self.socket)?;
122
123 let mut receive_buffer = vec![0; 4096];
124 let mut offset = 0;
125
126 let mut link_messages = Vec::new();
127 while let Ok(size) = self.socket.recv(&mut &mut receive_buffer[..], 0) {
128 loop {
129 let bytes = &receive_buffer[offset..];
130 let rx_packet = <NetlinkMessage<RtnlMessage>>::deserialize(bytes).unwrap();
131
132 let payload = rx_packet.payload;
133 match payload {
134 NetlinkPayload::InnerMessage(RtnlMessage::NewLink(message)) => {
135 link_messages.push(message.clone())
136 }
137 NetlinkPayload::Error(error) => {
138 return Err(NetlinkError::Netlink(error.to_string()))
139 }
140 NetlinkPayload::Done(_) => return Ok(to_link(link_messages)),
141 _ => {}
142 }
143
144 offset += rx_packet.header.length as usize;
145 if offset == size || rx_packet.header.length == 0 {
146 offset = 0;
147 break;
148 }
149 }
150 }
151
152 Ok(to_link(link_messages))
153 }
154}
155
156fn send_get_qdisc_request(socket: &Socket) -> Result<(), NetlinkError> {
157 let mut nl_hdr = NetlinkHeader::default();
158 nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP;
159
160 let mut packet = NetlinkMessage::new(
161 nl_hdr,
162 NetlinkPayload::from(RtnlMessage::GetQueueDiscipline(TcMessage::default())),
163 );
164 packet.finalize();
165
166 let mut buf = vec![0; packet.header.length as usize];
167 packet.serialize(&mut buf[..]);
168
169 match socket.send(&buf[..], 0) {
170 Ok(_) => Ok(()),
171 Err(e) => Err(NetlinkError::Send(e.to_string())),
172 }
173}
174
175fn send_get_class_request(socket: &Socket, index: i32) -> Result<(), NetlinkError> {
176 let mut nl_hdr = NetlinkHeader::default();
177 nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP;
178
179 let tc_hdr = netlink_tc::TcHeader {
180 index,
181 ..Default::default()
182 };
183 let mut tc_msg = TcMessage::default();
184 tc_msg.header = tc_hdr;
185 let mut packet = NetlinkMessage::new(
186 nl_hdr,
187 NetlinkPayload::from(RtnlMessage::GetTrafficClass(tc_msg)),
188 );
189 packet.finalize();
190
191 let mut buf = vec![0; packet.header.length as usize];
192 packet.serialize(&mut buf[..]);
193
194 match socket.send(&buf[..], 0) {
195 Ok(_) => Ok(()),
196 Err(e) => Err(NetlinkError::Send(e.to_string())),
197 }
198}
199
200fn send_get_link_request(socket: &Socket) -> Result<(), NetlinkError> {
201 let mut nl_hdr = NetlinkHeader::default();
202 nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP;
203
204 let mut packet = NetlinkMessage::new(
205 nl_hdr,
206 NetlinkPayload::from(RtnlMessage::GetLink(LinkMessage::default())),
207 );
208 packet.finalize();
209
210 let mut buf = vec![0; packet.header.length as usize];
211 packet.serialize(&mut buf[..]);
212
213 match socket.send(&buf[..], 0) {
214 Ok(_) => Ok(()),
215 Err(e) => Err(NetlinkError::Send(e.to_string())),
216 }
217}
218
219fn to_tc(tc_messages: Vec<TcMessage>) -> Vec<TcMsg> {
220 tc_messages
221 .into_iter()
222 .map(|tc_message| {
223 let TcMessage {
224 header: tc_header,
225 nlas,
226 ..
227 } = tc_message;
228 let header = TcHeader {
229 index: tc_header.index,
230 handle: tc_header.handle,
231 parent: tc_header.parent,
232 };
233 let mut attrs = Vec::new();
234
235 for nla in nlas {
236 match nla {
237 netlink_tc::Nla::Kind(kind) => attrs.push(TcAttr::Kind(kind)),
238 netlink_tc::Nla::Options(tc_opts) => {
239 let mut opts = Vec::new();
240 for opt in tc_opts {
241 match opt {
242 netlink_tc::TcOpt::Ingress => {
243 let option = TcOption {
244 kind: 0u16, bytes: vec![],
246 };
247 opts.push(option);
248 }
249 netlink_tc::TcOpt::U32(nla) => {
250 let mut buf = vec![0u8; nla.value_len()];
251 nla.emit_value(buf.as_mut_slice());
252 let option = TcOption {
253 kind: nla.kind(),
254 bytes: buf,
255 };
256 opts.push(option);
257 }
258 netlink_tc::TcOpt::Matchall(nla) => {
259 let mut buf = vec![0u8; nla.value_len()];
260 nla.emit_value(buf.as_mut_slice());
261 let option = TcOption {
262 kind: nla.kind(),
263 bytes: buf,
264 };
265 opts.push(option);
266 }
267 netlink_tc::TcOpt::Other(nla) => {
268 let mut buf = vec![0u8; nla.value_len()];
269 nla.emit_value(buf.as_mut_slice());
270 let option = TcOption {
271 kind: nla.kind(),
272 bytes: buf,
273 };
274 opts.push(option);
275 }
276 _ => (),
277 };
278 }
279 attrs.push(TcAttr::Options(opts));
280 }
281 netlink_tc::Nla::Stats(tc_stats) => {
282 let mut buf = vec![0u8; tc_stats.buffer_len()];
283 tc_stats.emit(buf.as_mut_slice());
284 attrs.push(TcAttr::Stats(buf));
285 }
286 netlink_tc::Nla::Stats2(tc_stats) => {
287 let mut stats2 = Vec::new();
288 for stat in tc_stats {
289 match stat {
290 netlink_tc::Stats2::StatsBasic(bytes) => {
291 stats2.push(TcStats2::StatsBasic(bytes))
292 }
293 netlink_tc::Stats2::StatsQueue(bytes) => {
294 stats2.push(TcStats2::StatsQueue(bytes))
295 }
296 netlink_tc::Stats2::StatsApp(bytes) => {
297 stats2.push(TcStats2::StatsApp(bytes))
298 }
299 _ => (),
300 }
301 }
302 attrs.push(TcAttr::Stats2(stats2));
303 }
304 netlink_tc::Nla::XStats(bytes) => attrs.push(TcAttr::Xstats(bytes)),
305 netlink_tc::Nla::Rate(bytes) => attrs.push(TcAttr::Rate(bytes)),
306 netlink_tc::Nla::Fcnt(bytes) => attrs.push(TcAttr::Fcnt(bytes)),
307 netlink_tc::Nla::Stab(bytes) => attrs.push(TcAttr::Stab(bytes)),
308 netlink_tc::Nla::Chain(bytes) => attrs.push(TcAttr::Chain(bytes)),
309 netlink_tc::Nla::HwOffload(byte) => attrs.push(TcAttr::HwOffload(byte)),
310 _ => (),
311 }
312 }
313
314 TcMsg { header, attrs }
315 })
316 .collect()
317}
318
319fn to_link(link_messages: Vec<LinkMessage>) -> Vec<LinkMsg> {
320 link_messages
321 .into_iter()
322 .map(|link_message| {
323 let LinkMessage {
324 header: link_header,
325 nlas,
326 ..
327 } = link_message;
328 let header = LinkHeader {
329 index: link_header.index,
330 };
331
332 let mut name = String::new();
333 for nla in nlas {
334 if let link::nlas::Nla::IfName(if_name) = nla {
335 name = if_name;
336 }
337 }
338
339 LinkMsg {
340 header,
341 attr: LinkAttr { name },
342 }
343 })
344 .collect()
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 use crate::test_data;
352
353 #[test]
354 fn test_qdiscs_to_tc() {
355 let qdiscs = test_data::nl_qdiscs();
356 let tcs = to_tc(qdiscs);
357
358 fn test_no_queue(tc: &TcMsg) {
361 assert_eq!(tc.header.index, 1);
363 assert_eq!(tc.header.handle, 0);
364 assert_eq!(tc.header.parent, 4294967295);
365
366 let attrs = &tc.attrs;
368 for attr in attrs {
369 match attr {
370 TcAttr::Kind(kind) => assert_eq!(kind, "noqueue"),
371 TcAttr::Stats(bytes) => assert_eq!(bytes, &vec![0u8; 36]),
372 TcAttr::Stats2(stats) => {
373 for stat in stats {
374 match stat {
375 TcStats2::StatsBasic(bytes) => assert_eq!(bytes, &vec![0u8; 16]),
376 TcStats2::StatsQueue(bytes) => assert_eq!(bytes, &vec![0u8; 20]),
377 _ => (),
378 }
379 }
380 }
381 TcAttr::HwOffload(byte) => assert_eq!(byte, &0),
382 _ => (),
383 }
384 }
385 }
386
387 fn test_mq(tc: &TcMsg) {
390 assert_eq!(tc.header.index, 2);
392 assert_eq!(tc.header.handle, 0);
393 assert_eq!(tc.header.parent, 4294967295);
394
395 let attrs = &tc.attrs;
397 for attr in attrs {
398 match attr {
399 TcAttr::Kind(kind) => assert_eq!(kind, "mq"),
400 TcAttr::Stats(bytes) => {
401 assert_eq!(
402 bytes,
403 &vec![
404 28, 146, 82, 7, 0, 0, 0, 0, 119, 55, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0,
405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
406 ]
407 );
408 }
409 TcAttr::Stats2(stats) => {
410 for stat in stats {
411 match stat {
412 TcStats2::StatsBasic(bytes) => assert_eq!(
413 bytes,
414 &vec![28, 146, 82, 7, 0, 0, 0, 0, 119, 55, 6, 0, 0, 0, 0, 0,]
415 ),
416 TcStats2::StatsQueue(bytes) => assert_eq!(
417 bytes,
418 &vec![
419 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0,
420 0,
421 ]
422 ),
423 _ => (),
424 }
425 }
426 }
427 TcAttr::HwOffload(byte) => assert_eq!(byte, &0),
428 _ => (),
429 }
430 }
431 }
432
433 fn test_fq_codel(tc: &TcMsg) {
436 assert_eq!(tc.header.index, 2);
438 assert_eq!(tc.header.handle, 0);
439 assert_eq!(tc.header.parent, 2);
440
441 let attrs = &tc.attrs;
443 for attr in attrs {
444 match attr {
445 TcAttr::Kind(kind) => assert_eq!(kind, "fq_codel"),
446 TcAttr::Options(opts) => {
447 for opt in opts {
448 match opt {
449 TcOption { kind, bytes } => match kind {
450 1 => assert_eq!(bytes, &vec![135, 19, 0, 0]),
451 2 => assert_eq!(bytes, &vec![0, 40, 0, 0]),
452 3 => assert_eq!(bytes, &vec![159, 134, 1, 0]),
453 4 => assert_eq!(bytes, &vec![1, 0, 0, 0]),
454 5 => assert_eq!(bytes, &vec![0, 4, 0, 0]),
455 6 => assert_eq!(bytes, &vec![234, 5, 0, 0]),
456 8 => assert_eq!(bytes, &vec![64, 0, 0, 0]),
457 9 => assert_eq!(bytes, &vec![0, 0, 0, 2]),
458 _ => (),
459 },
460 }
461 }
462 }
463 TcAttr::Stats(bytes) => assert_eq!(
464 bytes,
465 &vec![
466 76, 222, 96, 2, 0, 0, 0, 0, 55, 135, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
467 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
468 ]
469 ),
470 TcAttr::Stats2(stats) => {
471 for stat in stats {
472 match stat {
473 TcStats2::StatsBasic(bytes) => assert_eq!(
474 bytes,
475 &vec![76, 222, 96, 2, 0, 0, 0, 0, 55, 135, 2, 0, 0, 0, 0, 0,]
476 ),
477 TcStats2::StatsQueue(bytes) => assert_eq!(
478 bytes,
479 &vec![
480 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
481 ]
482 ),
483 TcStats2::StatsApp(bytes) => assert_eq!(
484 bytes,
485 &vec![
486 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0,
487 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488 0,
489 ]
490 ),
491 }
492 }
493 }
494 TcAttr::Xstats(bytes) => assert_eq!(
495 bytes,
496 &vec![
497 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0,
498 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
499 ]
500 ),
501 TcAttr::HwOffload(byte) => assert_eq!(byte, &0),
502 _ => (),
503 }
504 }
505 }
506
507 fn test_htb(tc: &TcMsg) {
510 assert_eq!(tc.header.index, 3);
512 assert_eq!(tc.header.handle, 65536);
513 assert_eq!(tc.header.parent, 4294967295);
514
515 let attrs = &tc.attrs;
517 for attr in attrs {
518 match attr {
519 TcAttr::Kind(kind) => assert_eq!(kind, "htb"),
520 TcAttr::Options(opts) => {
521 for opt in opts {
522 match opt {
523 TcOption { kind, bytes } => match kind {
524 2 => assert_eq!(
525 bytes,
526 &vec![
527 17, 0, 3, 0, 10, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0,
528 0, 0, 0,
529 ]
530 ),
531 5 => assert_eq!(bytes, &vec![232, 3, 0, 0]),
532 _ => (),
533 },
534 }
535 }
536 }
537 TcAttr::Stats(bytes) => assert_eq!(bytes, &vec![0u8; 36]),
538 TcAttr::Stats2(stats2) => {
539 for stat in stats2 {
540 match stat {
541 TcStats2::StatsBasic(bytes) => assert_eq!(bytes, &vec![0u8; 16]),
542 TcStats2::StatsQueue(bytes) => assert_eq!(bytes, &vec![0u8; 20]),
543 _ => (),
544 }
545 }
546 }
547 TcAttr::HwOffload(byte) => assert_eq!(byte, &0),
548 _ => (),
549 }
550 }
551 }
552
553 test_no_queue(tcs.get(0).unwrap());
554 test_mq(tcs.get(1).unwrap());
555 test_fq_codel(tcs.get(2).unwrap());
556 test_htb(tcs.get(3).unwrap());
557 }
558
559 #[test]
560 fn test_classes_to_tc() {
561 let classes = test_data::nl_classes();
562 let tcs = to_tc(classes);
563
564 fn test_htb(tc: &TcMsg) {
567 assert_eq!(tc.header.index, 3);
569 assert_eq!(tc.header.handle, 65537);
570 assert_eq!(tc.header.parent, 4294967295);
571
572 let attrs = &tc.attrs;
574 for attr in attrs {
575 match attr {
576 TcAttr::Kind(kind) => assert_eq!(kind, "htb"),
577 TcAttr::Options(opts) => {
578 for opt in opts {
579 match opt {
580 TcOption { kind, bytes } => match kind {
581 1 => assert_eq!(
582 bytes,
583 &vec![
584 0, 1, 0, 0, 0, 0, 0, 0, 72, 232, 1, 0, 0, 1, 0, 0, 0,
585 0, 0, 0, 72, 232, 1, 0, 64, 13, 3, 0, 64, 13, 3, 0,
586 212, 48, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,
587 ]
588 ),
589 _ => (),
590 },
591 }
592 }
593 }
594 TcAttr::Stats(bytes) => assert_eq!(bytes, &vec![0u8; 36]),
595 TcAttr::Stats2(stats2) => {
596 for stat in stats2 {
597 match stat {
598 TcStats2::StatsBasic(bytes) => assert_eq!(bytes, &vec![0u8; 16]),
599 TcStats2::StatsQueue(bytes) => assert_eq!(bytes, &vec![0u8; 20]),
600 TcStats2::StatsApp(bytes) => assert_eq!(
601 bytes,
602 &vec![
603 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 13, 3, 0, 64, 13,
604 3, 0,
605 ]
606 ),
607 }
608 }
609 }
610 TcAttr::Xstats(bytes) => assert_eq!(
611 bytes,
612 &vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 13, 3, 0, 64, 13, 3, 0,]
613 ),
614 _ => (),
615 }
616 }
617 }
618
619 test_htb(tcs.get(0).unwrap());
620 }
621
622 #[test]
623 fn test_to_link() {
624 let links = test_data::nl_links();
625 let link_msgs = to_link(links);
626
627 let link = link_msgs.get(0).unwrap();
628 assert_eq!(link.header.index, 1);
629 assert_eq!(link.attr.name, "eth0");
630 }
631}