Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
Add support for generic netlink
Browse files Browse the repository at this point in the history
Squashed 41 commits from branch 'generic-netlink'

Implemented 'netlink-packet-generic' and 'genetlink' to provide generic
netlink packet definition and asynchronous connection.
  • Loading branch information
Leo1003 authored and cathay4t committed Sep 16, 2021
1 parent 8bca238 commit 89ee697
Show file tree
Hide file tree
Showing 27 changed files with 2,208 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ jobs:
cd netlink-packet-core
cargo test
- name: test (netlink-packet-generic)
run: |
cd netlink-packet-generic
cargo test
- name: test (netlink-packet-route)
run: |
cd netlink-packet-route
Expand Down Expand Up @@ -71,3 +76,8 @@ jobs:
run: |
cd audit
cargo test
- name: test (genetlink)
run: |
cd genetlink
cargo test --features tokio_socket
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ members = [
"netlink-sys",
"netlink-packet-core",
"netlink-packet-utils",
"netlink-packet-generic",
"netlink-packet-route",
"netlink-packet-route/fuzz",
"netlink-packet-audit",
"netlink-packet-audit/fuzz",
"netlink-packet-sock-diag",
"netlink-proto",
"genetlink",
"rtnetlink",
"audit",
]
Expand All @@ -19,10 +21,12 @@ default-members = [
"netlink-sys",
"netlink-packet-core",
"netlink-packet-utils",
"netlink-packet-generic",
"netlink-packet-route",
"netlink-packet-audit",
"netlink-packet-sock-diag",
"netlink-proto",
"genetlink",
"rtnetlink",
"audit",
]
Expand All @@ -31,9 +35,11 @@ default-members = [
netlink-sys = { path = "netlink-sys" }
netlink-packet-core = { path = "netlink-packet-core" }
netlink-packet-utils = { path = "netlink-packet-utils" }
netlink-packet-generic = { path = "netlink-packet-generic" }
netlink-packet-route = { path = "netlink-packet-route" }
netlink-packet-audit = { path = "netlink-packet-audit" }
netlink-packet-sock-diag = { path = "netlink-packet-sock-diag" }
netlink-proto = { path = "netlink-proto" }
genetlink = { path = "genetlink" }
rtnetlink = { path = "rtnetlink" }
audit = { path = "audit" }
38 changes: 38 additions & 0 deletions genetlink/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[package]
name = "genetlink"
version = "0.1.0"
authors = ["Leo <leo881003@gmail.com>"]
edition = "2018"
homepage = "https://github.com/little-dude/netlink"
repository = "https://github.com/little-dude/netlink"
keywords = ["netlink", "linux"]
license = "MIT"
readme = "../README.md"
description = "communicate with generic netlink"

[features]
default = ["tokio_socket"]
tokio_socket = ["netlink-proto/tokio_socket","netlink-proto/workaround-audit-bug", "tokio"]
smol_socket = ["netlink-proto/smol_socket","netlink-proto/workaround-audit-bug","async-std"]

[dependencies]
futures = "0.3.16"
netlink-packet-generic = "0.1.0"
netlink-proto = { default-features = false, version = "0.7.0" }
tokio = { version = "1.9.0", features = ["rt"], optional = true }
async-std = { version = "1.9.0", optional = true }
netlink-packet-utils = "0.4.1"
netlink-packet-core = "0.2.4"
thiserror = "1.0.26"

[dev-dependencies]
anyhow = "1.0.42"
tokio = { version = "1.9.0", features = ["rt", "rt-multi-thread", "macros"] }

[[example]]
name = "list_generic_family"
required-features = ["tokio_socket"]

[[example]]
name = "dump_family_policy"
required-features = ["tokio_socket"]
60 changes: 60 additions & 0 deletions genetlink/examples/dump_family_policy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use std::env::args;

use anyhow::{bail, Error};
use futures::StreamExt;
use genetlink::new_connection;
use netlink_packet_core::{
NetlinkHeader,
NetlinkMessage,
NetlinkPayload,
NLM_F_DUMP,
NLM_F_REQUEST,
};
use netlink_packet_generic::{
ctrl::{nlas::GenlCtrlAttrs, GenlCtrl, GenlCtrlCmd},
GenlMessage,
};

#[tokio::main]
async fn main() -> Result<(), Error> {
let argv: Vec<_> = args().collect();

if argv.len() < 2 {
eprintln!("Usage: dump_family_policy <family name>");
bail!("Required arguments not given");
}

let nlmsg = NetlinkMessage {
header: NetlinkHeader {
flags: NLM_F_REQUEST | NLM_F_DUMP,
..Default::default()
},
payload: GenlMessage::from_payload(GenlCtrl {
cmd: GenlCtrlCmd::GetPolicy,
nlas: vec![GenlCtrlAttrs::FamilyName(argv[1].to_owned())],
})
.into(),
};
let (conn, mut handle, _) = new_connection()?;
tokio::spawn(conn);

let mut responses = handle.request(nlmsg).await?;

while let Some(result) = responses.next().await {
let resp = result?;
match resp.payload {
NetlinkPayload::InnerMessage(genlmsg) => {
if genlmsg.payload.cmd == GenlCtrlCmd::GetPolicy {
println!("<<< {:?}", genlmsg);
}
}
NetlinkPayload::Error(err) => {
eprintln!("Received a netlink error message: {:?}", err);
bail!(err);
}
_ => {}
}
}

Ok(())
}
108 changes: 108 additions & 0 deletions genetlink/examples/list_generic_family.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Example of listing generic families based on `netlink_proto`
//!
//! This example's functionality is same as the identical name example in `netlink_packet_generic`.
//! But this example shows you the usage of this crate to run generic netlink protocol asynchronously.

use anyhow::{bail, Error};
use futures::StreamExt;
use genetlink::new_connection;
use netlink_packet_core::{
NetlinkHeader,
NetlinkMessage,
NetlinkPayload,
NLM_F_DUMP,
NLM_F_REQUEST,
};
use netlink_packet_generic::{
ctrl::{nlas::GenlCtrlAttrs, GenlCtrl, GenlCtrlCmd},
GenlMessage,
};

#[tokio::main]
async fn main() -> Result<(), Error> {
let nlmsg = NetlinkMessage {
header: NetlinkHeader {
flags: NLM_F_REQUEST | NLM_F_DUMP,
..Default::default()
},
payload: GenlMessage::from_payload(GenlCtrl {
cmd: GenlCtrlCmd::GetFamily,
nlas: vec![],
})
.into(),
};
let (conn, mut handle, _) = new_connection()?;
tokio::spawn(conn);

let mut responses = handle.request(nlmsg).await?;

while let Some(result) = responses.next().await {
let resp = result?;
match resp.payload {
NetlinkPayload::InnerMessage(genlmsg) => {
if genlmsg.payload.cmd == GenlCtrlCmd::NewFamily {
print_entry(genlmsg.payload.nlas);
}
}
NetlinkPayload::Error(err) => {
eprintln!("Received a netlink error message: {:?}", err);
bail!(err);
}
_ => {}
}
}

Ok(())
}

fn print_entry(entry: Vec<GenlCtrlAttrs>) {
let family_id = entry
.iter()
.find_map(|nla| {
if let GenlCtrlAttrs::FamilyId(id) = nla {
Some(*id)
} else {
None
}
})
.expect("Cannot find FamilyId attribute");
let family_name = entry
.iter()
.find_map(|nla| {
if let GenlCtrlAttrs::FamilyName(name) = nla {
Some(name.as_str())
} else {
None
}
})
.expect("Cannot find FamilyName attribute");
let version = entry
.iter()
.find_map(|nla| {
if let GenlCtrlAttrs::Version(ver) = nla {
Some(*ver)
} else {
None
}
})
.expect("Cannot find Version attribute");
let hdrsize = entry
.iter()
.find_map(|nla| {
if let GenlCtrlAttrs::HdrSize(hdr) = nla {
Some(*hdr)
} else {
None
}
})
.expect("Cannot find HdrSize attribute");

if hdrsize == 0 {
println!("0x{:04x} {} [Version {}]", family_id, family_name, version);
} else {
println!(
"0x{:04x} {} [Version {}] [Header {} bytes]",
family_id, family_name, version, hdrsize
);
}
}
32 changes: 32 additions & 0 deletions genetlink/src/connection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::{message::RawGenlMessage, GenetlinkHandle};
use futures::channel::mpsc::UnboundedReceiver;
use netlink_packet_core::NetlinkMessage;
use netlink_proto::{
self,
sys::{protocols::NETLINK_GENERIC, SocketAddr},
Connection,
};
use std::io;

/// Construct a generic netlink connection
///
/// The function would return a tuple containing three objects.
/// - an async netlink connection
/// - a connection handle to interact with the connection
/// - a receiver of the unsolicited messages
///
/// The connection object is also a event loop which implements [`std::future::Future`].
/// In most cases, users spawn it on an async runtime and use the handle to send
/// messages. For detailed documentation, please refer to [`netlink_proto::new_connection`].
///
/// The [`GenetlinkHandle`] can send and receive any type of generic netlink message.
/// And it can automatic resolve the generic family id before sending.
#[allow(clippy::type_complexity)]
pub fn new_connection() -> io::Result<(
Connection<RawGenlMessage>,
GenetlinkHandle,
UnboundedReceiver<(NetlinkMessage<RawGenlMessage>, SocketAddr)>,
)> {
let (conn, handle, messages) = netlink_proto::new_connection(NETLINK_GENERIC)?;
Ok((conn, GenetlinkHandle::new(handle), messages))
}
24 changes: 24 additions & 0 deletions genetlink/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::message::RawGenlMessage;

/// Error type of genetlink
#[derive(Debug, Error)]
pub enum GenetlinkError {
#[error("Failed to send netlink request")]
ProtocolError(#[from] netlink_proto::Error<RawGenlMessage>),
#[error("Failed to decode generic packet")]
DecodeError(#[from] netlink_packet_utils::DecodeError),
#[error("Netlink error message: {0}")]
NetlinkError(std::io::Error),
#[error("Cannot find specified netlink attribute: {0}")]
AttributeNotFound(String),
#[error("Desire netlink message type not received")]
NoMessageReceived,
}

// Since `netlink_packet_core::error::ErrorMessage` doesn't impl `Error` trait,
// it need to convert to `std::io::Error` here
impl From<netlink_packet_core::error::ErrorMessage> for GenetlinkError {
fn from(err_msg: netlink_packet_core::error::ErrorMessage) -> Self {
Self::NetlinkError(err_msg.to_io())
}
}
Loading

0 comments on commit 89ee697

Please sign in to comment.