use crate::{
netlink::{
Connection, MessageBuilder, Result, Route,
connection::{ack_request, create_request, replace_request},
message::NlMsgType,
types::tc::{TcMsg, TcaAttr, tc_handle},
},
tc::options::{cake, codel, fq, fq_codel, htb, netem, prio, sfq, tbf},
};
fn build_tcmsg(dev: &str, parent: &str, handle: Option<&str>) -> Result<TcMsg> {
let ifindex = crate::util::get_ifindex(dev).map_err(crate::netlink::Error::InvalidMessage)?;
let parent_handle = tc_handle::parse(parent).ok_or_else(|| {
crate::netlink::Error::InvalidMessage(format!("invalid parent handle: {}", parent))
})?;
let qdisc_handle = if let Some(h) = handle {
tc_handle::parse(h).ok_or_else(|| {
crate::netlink::Error::InvalidMessage(format!("invalid handle: {}", h))
})?
} else {
0
};
Ok(TcMsg::new()
.with_ifindex(ifindex as i32)
.with_parent(parent_handle)
.with_handle(qdisc_handle))
}
pub fn add_options(builder: &mut MessageBuilder, kind: &str, params: &[String]) -> Result<()> {
if params.is_empty() {
return Ok(());
}
let options_token = builder.nest_start(TcaAttr::Options as u16);
match kind {
"cake" => cake::build(builder, params)?,
"codel" => codel::build(builder, params)?,
"fq" => fq::build(builder, params)?,
"fq_codel" => fq_codel::build(builder, params)?,
"tbf" => tbf::build(builder, params)?,
"htb" => htb::build(builder, params)?,
"prio" => prio::build(builder, params)?,
"sfq" => sfq::build(builder, params)?,
"netem" => netem::build(builder, params)?,
"noqueue" | "pfifo_fast" | "mq" | "ingress" | "clsact" => {
}
_ => {
}
}
builder.nest_end(options_token);
Ok(())
}
pub async fn add(
conn: &Connection<Route>,
dev: &str,
parent: &str,
handle: Option<&str>,
kind: &str,
params: &[String],
) -> Result<()> {
let tcmsg = build_tcmsg(dev, parent, handle)?;
let mut builder = create_request(NlMsgType::RTM_NEWQDISC);
builder.append(&tcmsg);
builder.append_attr_str(TcaAttr::Kind as u16, kind);
add_options(&mut builder, kind, params)?;
conn.send_ack(builder).await?;
Ok(())
}
pub async fn del(
conn: &Connection<Route>,
dev: &str,
parent: &str,
handle: Option<&str>,
) -> Result<()> {
let tcmsg = build_tcmsg(dev, parent, handle)?;
let mut builder = ack_request(NlMsgType::RTM_DELQDISC);
builder.append(&tcmsg);
conn.send_ack(builder).await?;
Ok(())
}
pub async fn replace(
conn: &Connection<Route>,
dev: &str,
parent: &str,
handle: Option<&str>,
kind: &str,
params: &[String],
) -> Result<()> {
let tcmsg = build_tcmsg(dev, parent, handle)?;
let mut builder = replace_request(NlMsgType::RTM_NEWQDISC);
builder.append(&tcmsg);
builder.append_attr_str(TcaAttr::Kind as u16, kind);
add_options(&mut builder, kind, params)?;
conn.send_ack(builder).await?;
Ok(())
}
pub async fn change(
conn: &Connection<Route>,
dev: &str,
parent: &str,
handle: Option<&str>,
kind: &str,
params: &[String],
) -> Result<()> {
let tcmsg = build_tcmsg(dev, parent, handle)?;
let mut builder = ack_request(NlMsgType::RTM_NEWQDISC);
builder.append(&tcmsg);
builder.append_attr_str(TcaAttr::Kind as u16, kind);
add_options(&mut builder, kind, params)?;
conn.send_ack(builder).await?;
Ok(())
}