Skip to content

Commit

Permalink
Delegate fetching links to the caller (#61)
Browse files Browse the repository at this point in the history
* Remove `link` types and methods
Since the caller passes the netlink messages, the library does not need
to get links for retrieving tc classes.

* Use `nix` functions in tests to fetch links
  • Loading branch information
mmynk committed Nov 22, 2023
1 parent a965cc6 commit bf8c894
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 164 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ netlink-sys = "0.8"
netlink-packet-utils = "0.5"
serde = { version = "1", features = ["derive"] }
thiserror = "1.0"

[dev-dependencies]
nix = { version = "0.27", features = ["net"] }
37 changes: 24 additions & 13 deletions examples/get.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
use netlink_packet_core::{
NetlinkHeader, NetlinkMessage, NetlinkPayload, NLM_F_DUMP, NLM_F_REQUEST,
};
use netlink_packet_route::{LinkMessage, RtnlMessage, TcHeader, TcMessage};
use netlink_packet_route::{RtnlMessage, TcHeader, TcMessage};
use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
use netlink_tc::ParseOptions;
use nix::ifaddrs::getifaddrs;
use nix::net::if_::if_nametoindex;
use std::collections::BTreeSet;
use std::ffi::OsStr;

fn socket() -> Socket {
let socket = Socket::new(NETLINK_ROUTE).unwrap();
Expand Down Expand Up @@ -57,10 +61,6 @@ fn get_classes(index: i32) -> Vec<NetlinkMessage<RtnlMessage>> {
receive_netlink_messages(RtnlMessage::GetTrafficClass(message))
}

fn get_links() -> Vec<NetlinkMessage<RtnlMessage>> {
receive_netlink_messages(RtnlMessage::GetLink(LinkMessage::default()))
}

fn send_request(socket: &Socket, message: RtnlMessage) {
let mut nl_hdr = NetlinkHeader::default();
nl_hdr.flags = NLM_F_REQUEST | NLM_F_DUMP;
Expand All @@ -74,6 +74,23 @@ fn send_request(socket: &Socket, message: RtnlMessage) {
socket.send(&buf[..], 0).unwrap();
}

fn get_links() -> BTreeSet<i32> {
if let Ok(addrs) = getifaddrs() {
addrs
.filter(|ifaddr| ifaddr.flags.contains(nix::net::if_::InterfaceFlags::IFF_UP))
.filter_map(|ifaddr| {
if let Ok(index) = if_nametoindex::<OsStr>(ifaddr.interface_name.as_ref()) {
Some(index as i32)
} else {
None
}
})
.collect()
} else {
BTreeSet::new()
}
}

fn main() {
let messages = get_qdiscs();
let qdiscs = ParseOptions::new()
Expand All @@ -84,18 +101,12 @@ fn main() {
.unwrap();
println!("length: {}, qdiscs: {:#?}", qdiscs.len(), qdiscs);

let messages = get_links();
let links = ParseOptions::new()
.fail_on_unknown_netlink_message(false)
.fail_on_unknown_attribute(false)
.fail_on_unknown_option(false)
.links(messages)
.unwrap();
let links = get_links();
println!("length: {}, links: {:#?}", links.len(), links);

let mut messages = Vec::new();
for link in links {
let classes = get_classes(link.index as i32);
let classes = get_classes(link);
messages.extend(classes);
}
let classes = ParseOptions::new()
Expand Down
61 changes: 4 additions & 57 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,19 @@
//! .fail_on_unknown_attribute(false)
//! .fail_on_unknown_option(false)
//! .tc(messages.clone()).unwrap();
//!
//! // Get list of links
//! let links = ParseOptions::new()
//! .links(messages.clone()).unwrap();
//! ```
use netlink_packet_core::{NetlinkMessage, NetlinkPayload};
use netlink_packet_route::{
link as netlink_link, tc as netlink_tc, LinkMessage as NlLinkMessage, RtnlMessage,
TcMessage as NlTcMessage,
};
use netlink_packet_route::{tc as netlink_tc, RtnlMessage, TcMessage as NlTcMessage};
use netlink_packet_utils::{nla::Nla, Emitable};

use errors::Error;
use types::{Link, LinkAttr, LinkHeader, LinkMsg, Tc, TcAttr, TcHeader, TcMsg, TcOption, TcStats2};
use types::{Tc, TcAttr, TcHeader, TcMsg, TcOption, TcStats2};

pub mod errors;
pub mod types;

mod class;
mod constants;
mod link;
mod qdiscs;
mod tc;

Expand All @@ -52,9 +44,8 @@ mod tests;
/// Possible message types for `tc` messages.
/// A subset of `rtnl::RtnlMessage` enum.
pub enum RtNetlinkMessage {
GetQdisc(TcMsg), /* RTM_GETQDISC */
GetClass(TcMsg), /* RTM_GETCLASS */
GetLink(LinkMsg), /* RTM_GETLINK */
GetQdisc(TcMsg), /* RTM_GETQDISC */
GetClass(TcMsg), /* RTM_GETCLASS */
}

/// `OpenOptions` provides options for controlling how `netlink-tc` parses netlink messages.
Expand Down Expand Up @@ -124,12 +115,6 @@ impl ParseOptions {
pub fn tc(&self, messages: Vec<NetlinkMessage<RtnlMessage>>) -> Result<Vec<Tc>, Error> {
tc_stats(messages, self)
}

/// Parses `link` messages for the corresponding Netlink messages
/// with the options specified by `self`.
pub fn links(&self, messages: Vec<NetlinkMessage<RtnlMessage>>) -> Result<Vec<Link>, Error> {
links(messages, self)
}
}

fn to_tc(tc_message: NlTcMessage, opts: &ParseOptions) -> Result<TcMsg, Error> {
Expand Down Expand Up @@ -220,32 +205,6 @@ fn to_tc(tc_message: NlTcMessage, opts: &ParseOptions) -> Result<TcMsg, Error> {
Ok(TcMsg { header, attrs })
}

fn to_link(link_message: NlLinkMessage) -> Result<LinkMsg, Error> {
let NlLinkMessage {
header: link_header,
nlas,
..
} = link_message;
let header = LinkHeader {
index: link_header.index,
};

let mut name = None;
for nla in nlas {
if let netlink_link::nlas::Nla::IfName(if_name) = nla {
name = Some(if_name);
break;
}
}

if let Some(if_name) = name {
let attr = LinkAttr { name: if_name };
Ok(LinkMsg { header, attr })
} else {
Err(Error::Parse("Attribute IFLA_IFNAME not found".to_string()))
}
}

fn parse(
messages: Vec<NetlinkMessage<RtnlMessage>>,
opts: &ParseOptions,
Expand All @@ -259,9 +218,6 @@ fn parse(
NetlinkPayload::InnerMessage(RtnlMessage::NewTrafficClass(message)) => {
tc_messages.push(RtNetlinkMessage::GetClass(to_tc(message.clone(), opts)?))
}
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(message)) => {
tc_messages.push(RtNetlinkMessage::GetLink(to_link(message.clone())?))
}
payload => {
if opts.fail_on_unknown_netlink_message {
return Err(Error::Parse(format!(
Expand All @@ -283,12 +239,3 @@ fn tc_stats(
let messages = parse(messages, opts)?;
tc::tc_stats(messages, opts)
}

/// Parse `link` messages for the corresponding Netlink messages
fn links(
messages: Vec<NetlinkMessage<RtnlMessage>>,
opts: &ParseOptions,
) -> Result<Vec<Link>, Error> {
let messages = parse(messages, opts)?;
link::links(messages)
}
22 changes: 0 additions & 22 deletions src/link.rs

This file was deleted.

1 change: 0 additions & 1 deletion src/tc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ pub fn tc_stats(messages: Vec<RtNetlinkMessage>, opts: &ParseOptions) -> Result<
match message {
RtNetlinkMessage::GetQdisc(message) => tcs.push(qdiscs(message, opts)?),
RtNetlinkMessage::GetClass(message) => tcs.push(classes(message, opts)?),
_ => {}
}
}

Expand Down
21 changes: 1 addition & 20 deletions src/test_data.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use netlink_packet_core::{NetlinkHeader, NetlinkMessage, NetlinkPayload};
use netlink_packet_route::tc::Nla;
use netlink_packet_route::{nlas, tc, LinkMessage, RtnlMessage, TcHeader as NlTcHeader, TcMessage};
use netlink_packet_route::{tc, RtnlMessage, TcHeader as NlTcHeader, TcMessage};
use netlink_packet_utils::{nla, Parseable};

pub fn nl_qdiscs() -> Vec<TcMessage> {
Expand Down Expand Up @@ -220,13 +220,6 @@ pub fn nl_classes() -> Vec<TcMessage> {
messages
}

pub fn nl_links() -> Vec<LinkMessage> {
let mut msg = LinkMessage::default();
msg.header.index = 1;
msg.nlas = vec![nlas::link::Nla::IfName("eth0".to_string())];
vec![msg]
}

pub fn get_qdiscs() -> Vec<NetlinkMessage<RtnlMessage>> {
nl_qdiscs()
.into_iter()
Expand All @@ -251,18 +244,6 @@ pub fn get_classes() -> Vec<NetlinkMessage<RtnlMessage>> {
.collect()
}

pub fn get_links() -> Vec<NetlinkMessage<RtnlMessage>> {
nl_links()
.into_iter()
.map(|link| {
NetlinkMessage::new(
NetlinkHeader::default(),
NetlinkPayload::InnerMessage(RtnlMessage::NewLink(link)),
)
})
.collect()
}

//noinspection DuplicatedCode
pub fn qdisc(kind: &str) -> TcMessage {
// TcMessage { header: TcHeader { family: 0, index: 2, handle: 0, parent: 2, info: 1 }, nlas: [Kind("fq_codel"), Options([Other(DefaultNla { kind: 1, value: [135, 19, 0, 0] }), Other(DefaultNla { kind: 2, value: [0, 40, 0, 0] }), Other(DefaultNla { kind: 3, value: [159, 134, 1, 0] }), Other(DefaultNla { kind: 4, value: [1, 0, 0, 0] }), Other(DefaultNla { kind: 6, value: [234, 5, 0, 0] }), Other(DefaultNla { kind: 8, value: [64, 0, 0, 0] }), Other(DefaultNla { kind: 9, value: [0, 0, 0, 2] }), Other(DefaultNla { kind: 5, value: [0, 4, 0, 0] })]), HwOffload(0), Stats2([StatsApp([0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), StatsBasic([76, 222, 96, 2, 0, 0, 0, 0, 55, 135, 2, 0, 0, 0, 0, 0]), StatsQueue([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0])]), Stats(Stats { bytes: 39902796, packets: 165687, drops: 0, overlimits: 0, bps: 0, pps: 0, qlen: 0, backlog: 0 }), XStats([0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])] }
Expand Down
10 changes: 1 addition & 9 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use netlink_packet_route::TcMessage;

use crate::class::{Htb, HtbGlob, HtbOpt, HtbXstats};
use crate::qdiscs::{FqCodel, FqCodelXStats};
use crate::test_data::{get_classes, get_links, get_qdiscs, nlas, qdisc};
use crate::test_data::{get_classes, get_qdiscs, nlas, qdisc};
use crate::types::{Class, QDisc, RateSpec, XStats};

use super::*;
Expand Down Expand Up @@ -216,14 +216,6 @@ fn test_htb() {
);
}

#[test]
fn test_links() {
let links = ParseOptions::new().links(get_links()).unwrap();

assert_eq!(links[0].index, 1);
assert_eq!(links[0].name, "eth0");
}

#[test]
fn test_unknown_netlink_msg_fail() {
let messages = vec![NetlinkMessage::new(
Expand Down
25 changes: 0 additions & 25 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,6 @@ pub enum TcStats2 {
StatsApp(Vec<u8>),
}

/// This struct is an intermediate representation for netlink `link` messages.
/// Any downstream structs should be constructed into this struct.
#[derive(Debug)]
pub struct LinkMsg {
pub header: LinkHeader,
pub attr: LinkAttr,
}

#[derive(Debug)]
pub struct LinkHeader {
pub index: u32,
}

#[derive(Debug)]
pub struct LinkAttr {
pub name: String,
}

#[derive(Debug, Default)]
pub struct Tc {
pub msg: TcMessage,
Expand Down Expand Up @@ -155,10 +137,3 @@ pub struct RateSpec {
pub fn unmarshal_rate_spec(buf: &[u8]) -> Result<RateSpec, Error> {
bincode::deserialize(buf).map_err(|e| Error::Parse(e.to_string()))
}

/// A subset of structs defined in `include/uapi/linux/if_link.h`.
#[derive(Debug)]
pub struct Link {
pub index: u32,
pub name: String,
}
Loading

0 comments on commit bf8c894

Please sign in to comment.