This repository has been archived by the owner on Oct 26, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
27 changed files
with
2,208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
} | ||
} |
Oops, something went wrong.