partly working implementation

This commit is contained in:
Nils Andreas Svee committed 2022-09-17 14:39:27 +02:00
1 parent 27aa972fe6
commit b147006a69
13 files changed
+1542 -678

No files matched your search

+148 -160
View File
@@ -1,4 +1,5 @@
use crate::cake::TcaCake;
use crate::error::NoQdiscFoundError;
use neli::consts::nl::{NlmF, NlmFFlags};
use neli::consts::rtnl::{Arphrd, IffFlags, Ifla, RtAddrFamily, Rtm, Tca};
@@ -10,27 +11,8 @@ use neli::socket::NlSocketHandle;
use neli::types::{Buffer, RtBuffer};
use serde::Deserialize;
use std::error::Error;
use std::fmt::Display;
use bincode::deserialize;
use std::fmt;
#[derive(Default, Debug)]
struct NoQdiscFoundError;
impl Error for NoQdiscFoundError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
// The compiler transparently casts `&sqlx::Error` into a `&dyn Error`
None
}
}
// Implement std::fmt::Display for AppError
impl Display for NoQdiscFoundError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Couldn't find a CAKE qdisc for interface") // user-facing output
}
}
#[derive(Deserialize, Copy, Clone, Default, Debug)]
pub struct RtnlLinkStats64 {
@@ -61,6 +43,7 @@ pub struct RtnlLinkStats64 {
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub struct Qdisc {
ifindex: i32,
handle: u32,
@@ -69,184 +52,189 @@ pub struct Qdisc {
parent: u32,
}
fn nl_interface_get(socket: &mut NlSocketHandle, ifname: &str) -> Result<(), Box<dyn Error>> {
let mut attrs = RtBuffer::new();
pub struct Netlink {}
const RTEXT_FILTER_VF: i32 = 1 << 0;
impl Netlink {
fn nl_interface_get(socket: &mut NlSocketHandle, ifname: &str) -> Result<(), Box<dyn Error>> {
let mut attrs = RtBuffer::new();
let attr_ifname = Rtattr::new(None, Ifla::Ifname, ifname).unwrap();
let attr_ext_mask = Rtattr::new(None, Ifla::ExtMask, RTEXT_FILTER_VF).unwrap();
attrs.push(attr_ifname);
attrs.push(attr_ext_mask);
const RTEXT_FILTER_VF: i32 = 1 << 0;
let if_msg = Ifinfomsg::new(
RtAddrFamily::Unspecified,
Arphrd::None,
-1,
IffFlags::empty(),
IffFlags::empty(),
attrs,
);
let attr_ifname = Rtattr::new(None, Ifla::Ifname, ifname).unwrap();
let attr_ext_mask = Rtattr::new(None, Ifla::ExtMask, RTEXT_FILTER_VF).unwrap();
attrs.push(attr_ifname);
attrs.push(attr_ext_mask);
let nlhdr = Nlmsghdr::new(
None,
Rtm::Getlink,
NlmFFlags::new(&[NlmF::Request, NlmF::Ack]),
None,
None,
NlPayload::Payload(if_msg),
);
let if_msg = Ifinfomsg::new(
RtAddrFamily::Unspecified,
Arphrd::None,
-1,
IffFlags::empty(),
IffFlags::empty(),
attrs,
);
socket.send(nlhdr)?;
let nlhdr = Nlmsghdr::new(
None,
Rtm::Getlink,
NlmFFlags::new(&[NlmF::Request, NlmF::Ack]),
None,
None,
NlPayload::Payload(if_msg),
);
Ok(())
}
socket.send(nlhdr)?;
pub fn find_interface(ifname: &str) -> Result<i32, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
nl_interface_get(&mut socket, ifname).expect("Wrong when sending intf");
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
if header.nl_type != Rtm::Newlink {
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
}
if let NlPayload::Payload(p) = header.nl_payload {
return Ok(p.ifi_index);
}
Ok(())
}
// we shouldn't reach here
Ok(-1)
}
pub fn find_interface(ifname: &str) -> Result<i32, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
pub fn get_interface_stats(ifname: &str) -> Result<RtnlLinkStats64, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
Self::nl_interface_get(&mut socket, ifname).expect("Wrong when sending intf");
nl_interface_get(&mut socket, ifname).expect("Wrong when sending intf");
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
if header.nl_type != Rtm::Newlink {
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
}
if header.nl_type != Rtm::Newlink {
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
}
if let NlPayload::Payload(p) = header.nl_payload {
for attr in p.rtattrs.iter() {
if attr.rta_type == Ifla::Stats64 {
let buf = attr.rta_payload.as_ref();
let stats: RtnlLinkStats64 = deserialize(buf).unwrap();
return Ok(stats);
}
if let NlPayload::Payload(p) = header.nl_payload {
return Ok(p.ifi_index);
}
}
// we shouldn't reach here
Ok(-1)
}
Err(Box::new(NlError::msg("Error getting stats")))
}
pub fn get_interface_stats(ifname: &str) -> Result<RtnlLinkStats64, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
pub fn find_qdisc(ifindex: i32) -> Result<Qdisc, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let tc_msg = Tcmsg::new(
u8::from(RtAddrFamily::Unspecified),
0,
0,
0,
0,
RtBuffer::new(),
);
Self::nl_interface_get(&mut socket, ifname)?;
let nlhdr = Nlmsghdr::new(
None,
Rtm::Getqdisc,
NlmFFlags::new(&[NlmF::Request, NlmF::Dump]),
None,
None,
NlPayload::Payload(tc_msg),
);
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Ifinfomsg> = response?;
socket.send(nlhdr)?;
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Tcmsg> = response?;
if let NlPayload::Payload(p) = header.nl_payload {
if header.nl_type != Rtm::Newqdisc {
return Err(Box::new(NlError::msg("Netlink error retrieving qdisc")));
if header.nl_type != Rtm::Newlink {
return Err(Box::new(NlError::msg("Netlink error retrieving link")));
}
if p.tcm_ifindex == ifindex {
let mut _type = "";
if let NlPayload::Payload(p) = header.nl_payload {
for attr in p.rtattrs.iter() {
if attr.rta_type == Tca::Kind {
let buff = attr.rta_payload.as_ref();
_type = std::str::from_utf8(buff)
.expect("Found invalid UTF-8 string")
.trim_end_matches('\0'); // Null terminator is valid UTF-8, but breaks comparison, so we remove it
if attr.rta_type == Ifla::Stats64 {
let buf = attr.rta_payload.as_ref();
let stats: RtnlLinkStats64 = deserialize(buf).unwrap();
return Ok(stats);
}
}
}
}
let type_to_look_for = "cake";
Err(Box::new(NlError::msg("Error getting stats")))
}
if _type.eq(type_to_look_for) {
let qdisc = Qdisc {
ifindex: p.tcm_ifindex as i32,
handle: p.tcm_handle,
family: p.tcm_family,
refcnt: p.tcm_info,
parent: p.tcm_parent,
};
pub fn find_qdisc(ifindex: i32) -> Result<Qdisc, Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let tc_msg = Tcmsg::new(
u8::from(RtAddrFamily::Unspecified),
0,
0,
0,
0,
RtBuffer::new(),
);
return Ok(qdisc);
let nlhdr = Nlmsghdr::new(
None,
Rtm::Getqdisc,
NlmFFlags::new(&[NlmF::Request, NlmF::Dump]),
None,
None,
NlPayload::Payload(tc_msg),
);
socket.send(nlhdr)?;
for response in socket.iter(false) {
let header: Nlmsghdr<Rtm, Tcmsg> = response?;
if let NlPayload::Payload(p) = header.nl_payload {
if header.nl_type != Rtm::Newqdisc {
return Err(Box::new(NlError::msg("Netlink error retrieving qdisc")));
}
if p.tcm_ifindex == ifindex {
let mut _type = "";
for attr in p.rtattrs.iter() {
if attr.rta_type == Tca::Kind {
let buff = attr.rta_payload.as_ref();
_type = std::str::from_utf8(buff)
.expect("Found invalid UTF-8 string")
.trim_end_matches('\0'); // Null terminator is valid UTF-8, but breaks comparison, so we remove it
}
}
let type_to_look_for = "cake";
if _type.eq(type_to_look_for) {
let qdisc = Qdisc {
ifindex: p.tcm_ifindex as i32,
handle: p.tcm_handle,
family: p.tcm_family,
refcnt: p.tcm_info,
parent: p.tcm_parent,
};
return Ok(qdisc);
}
}
}
}
return Err(Box::new(NoQdiscFoundError));
}
return Err(Box::new(NoQdiscFoundError));
}
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth: u64) -> Result<(), Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let bandwidth = bandwidth * 1000 / 8;
pub fn set_qdisc_rate(qdisc: Qdisc, bandwidth: u64) -> Result<(), Box<dyn Error>> {
let mut socket = NlSocketHandle::connect(NlFamily::Route, None, &[]).unwrap();
let mut attrs = RtBuffer::new();
let mut attrs = RtBuffer::new();
let attr_type = Rtattr::new(None, Tca::Kind, "cake").unwrap();
let mut attr_options = Rtattr::new(None, Tca::Options, Buffer::from(Vec::new())).unwrap();
attr_options.add_nested_attribute(&Rtattr::new(
None,
TcaCake::BaseRate64 as u16,
bandwidth,
)?)?;
let attr_type = Rtattr::new(None, Tca::Kind, "cake").unwrap();
let mut attr_options = Rtattr::new(None, Tca::Options, Buffer::from(Vec::new())).unwrap();
attr_options.add_nested_attribute(&Rtattr::new(
None,
TcaCake::BaseRate64 as u16,
bandwidth,
)?)?;
attrs.push(attr_type);
attrs.push(attr_options);
attrs.push(attr_type);
attrs.push(attr_options);
let tc_msg = Tcmsg::new(
u8::from(RtAddrFamily::Unspecified),
qdisc.ifindex,
0,
qdisc.parent,
0,
attrs,
);
let tc_msg = Tcmsg::new(
u8::from(RtAddrFamily::Unspecified),
qdisc.ifindex,
0,
qdisc.parent,
0,
attrs,
);
let nlhdr = Nlmsghdr::new(
None,
Rtm::Newqdisc,
NlmFFlags::new(&[NlmF::Request, NlmF::Ack]),
None,
None,
NlPayload::Payload(tc_msg),
);
let nlhdr = Nlmsghdr::new(
None,
Rtm::Newqdisc,
NlmFFlags::new(&[NlmF::Request, NlmF::Ack]),
None,
None,
NlPayload::Payload(tc_msg),
);
socket.send(nlhdr)?;
Ok(())
socket.send(nlhdr)?;
Ok(())
}
}