Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/libknet/bindings/rust/src/knet_bindings.rs b/libknet/bindings/rust/src/knet_bindings.rs
index 01262186..8858a6a9 100644
--- a/libknet/bindings/rust/src/knet_bindings.rs
+++ b/libknet/bindings/rust/src/knet_bindings.rs
@@ -1,2556 +1,2560 @@
// libknet interface for Rust
// Copyright (C) 2021-2023 Red Hat, Inc.
//
// All rights reserved.
//
// Author: Christine Caulfield (ccaulfi@redhat.com)
//
#![allow(clippy::too_many_arguments)]
#![allow(clippy::collapsible_else_if)]
// For the code generated by bindgen
use crate::sys::libknet as ffi;
use std::ffi::{CString, CStr};
use std::sync::mpsc::*;
use std::ptr::{copy_nonoverlapping, null, null_mut};
use std::sync::Mutex;
use std::collections::HashMap;
use std::io::{Result, Error, ErrorKind};
use std::os::raw::{c_void, c_char, c_uchar, c_uint};
use std::mem::size_of;
use std::net::SocketAddr;
use std::fmt;
use std::thread::spawn;
use std::time::{Duration, SystemTime};
use os_socketaddr::OsSocketAddr;
#[derive(Copy, Clone, PartialEq, Eq)]
/// The ID of a host known to knet.
pub struct HostId {
host_id: u16,
}
impl HostId {
pub fn new(id: u16) -> HostId
{
HostId{host_id: id}
}
pub fn to_u16(self: HostId) -> u16
{
self.host_id
}
}
impl fmt::Display for HostId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,"{}", self.host_id)?;
Ok(())
}
}
pub enum TxRx {
Tx = 0,
Rx = 1
}
impl TxRx {
pub fn new (tx_rx: u8) -> TxRx
{
match tx_rx {
1 => TxRx::Rx,
_ => TxRx::Tx
}
}
}
impl fmt::Display for TxRx {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TxRx::Tx => write!(f, "Tx"),
TxRx::Rx => write!(f, "Rx"),
}
}
}
bitflags! {
/// Flags passed into [handle_new]
pub struct HandleFlags: u64
{
const PRIVILEGED = 1;
const NONE = 0;
}
}
bitflags! {
/// Flags passed into [link_set_config]
pub struct LinkFlags: u64
{
const TRAFFICHIPRIO = 1;
const NONE = 0;
}
}
/// for passing to [handle_crypto_set_config]
pub struct CryptoConfig<'a> {
pub crypto_model: String,
pub crypto_cipher_type: String,
pub crypto_hash_type: String,
pub private_key: &'a [u8],
}
/// for passing to [handle_compress]
pub struct CompressConfig {
pub compress_model: String,
pub compress_threshold: u32,
pub compress_level: i32,
}
/// Return value from packet filter
pub enum FilterDecision {
Discard,
Unicast,
Multicast
}
impl FilterDecision {
pub fn to_i32(self: &FilterDecision) -> i32
{
match self {
FilterDecision::Discard => -1,
FilterDecision::Unicast => 0,
FilterDecision::Multicast => 1,
}
}
}
impl fmt::Display for FilterDecision {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FilterDecision::Discard => write!(f, "Discard"),
FilterDecision::Unicast => write!(f, "Unicast"),
FilterDecision::Multicast => write!(f, "Multicast"),
}
}
}
// Used to convert a knet_handle_t into one of ours
lazy_static! {
static ref HANDLE_HASH: Mutex<HashMap<u64, PrivHandle>> = Mutex::new(HashMap::new());
}
fn get_errno() -> i32
{
match Error::last_os_error().raw_os_error() {
Some(e) => e,
None => libc::EINVAL,
}
}
/// Callback from [handle_enable_sock_notify]
pub type SockNotifyFn = fn(private_data: u64,
datafd: i32,
channel: i8,
txrx: TxRx,
Result<()>);
/// Callback called when packets arrive/are sent [handle_enable_filter]
pub type FilterFn = fn(private_data: u64,
outdata: &[u8],
txrx: TxRx,
this_host_id: HostId,
src_host_id: HostId,
channel: &mut i8,
dst_host_ids: &mut Vec<HostId>) -> FilterDecision;
/// Callback called when PMTU changes, see [handle_enable_pmtud_notify]
pub type PmtudNotifyFn = fn(private_data: u64,
data_mtu: u32);
/// Called when the onwire version number for a node changes, see [handle_enable_onwire_ver_notify]
pub type OnwireNotifyFn = fn(private_data: u64,
onwire_min_ver: u8,
onwire_max_ver: u8,
onwire_ver: u8);
/// Called when a host status changes, see [host_enable_status_change_notify]
pub type HostStatusChangeNotifyFn = fn(private_data: u64,
host_id: HostId,
reachable: bool,
remote: bool,
external: bool);
/// Called when a link status changes, see [link_enable_status_change_notify]
pub type LinkStatusChangeNotifyFn = fn(private_data: u64,
host_id: HostId,
link_id: u8,
connected: bool,
remote: bool,
external: bool);
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_sock_notify_fn(
private_data: *mut c_void,
datafd: i32,
channel: i8,
tx_rx: u8,
error: i32,
errorno: i32)
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
let res = if error == 0 {
Ok(())
} else {
Err(Error::from_raw_os_error(errorno))
};
// Call user fn
if let Some(f) = h.sock_notify_fn {
f(h.sock_notify_private_data,
datafd,
channel,
TxRx::new(tx_rx),
res);
}
}
}
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_filter_fn(
private_data: *mut c_void,
outdata: *const c_uchar,
outdata_len: isize,
tx_rx: u8,
this_host_id: u16,
src_host_id: u16,
channel: *mut i8,
dst_host_ids: *mut u16,
dst_host_ids_entries: *mut usize) -> i32
{
let mut ret : i32 = -1;
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
// Is there is filter fn?
if let Some(f) = h.filter_fn {
let data : &[u8] = unsafe {
std::slice::from_raw_parts(outdata as *const u8, outdata_len as usize)
};
let mut r_channel = unsafe {*channel};
let mut hosts_vec = Vec::<HostId>::new();
// Call Rust callback
ret = f(h.filter_private_data,
data,
TxRx::new(tx_rx),
HostId{host_id: this_host_id},
HostId{host_id: src_host_id},
&mut r_channel,
&mut hosts_vec).to_i32();
// Pass back mutable params dst_hosts
unsafe {
*channel = r_channel;
*dst_host_ids_entries = hosts_vec.len();
let mut retvec = dst_host_ids;
for i in &hosts_vec {
*retvec = i.host_id;
retvec = retvec.offset(1); // next entry
}
}
}
}
ret
}
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_pmtud_notify_fn(
private_data: *mut c_void,
data_mtu: u32)
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
// Call user fn
if let Some(f) = h.pmtud_notify_fn {
f(h.pmtud_notify_private_data, data_mtu);
}
}
}
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_onwire_notify_fn(
private_data: *mut c_void,
onwire_min_ver: u8,
onwire_max_ver: u8,
onwire_ver: u8)
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
// Call user fn
if let Some(f) = h.onwire_notify_fn {
f(h.onwire_notify_private_data,
onwire_min_ver,
onwire_max_ver,
onwire_ver);
}
}
}
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_host_status_change_notify_fn(
private_data: *mut c_void,
host_id: u16,
reachable: u8,
remote: u8,
external: u8)
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
// Call user fn
if let Some(f) = h.host_status_change_notify_fn {
f(h.host_status_change_notify_private_data,
HostId{host_id},
crate::u8_to_bool(reachable),
crate::u8_to_bool(remote),
crate::u8_to_bool(external));
}
}
}
// Called from knet, we work out where to route it to and convert params
extern "C" fn rust_link_status_change_notify_fn(
private_data: *mut c_void,
host_id: u16,
link_id: u8,
connected: u8,
remote: u8,
external: u8)
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get(&(private_data as u64)) {
// Call user fn
if let Some(f) = h.link_status_change_notify_fn {
f(h.link_status_change_notify_private_data,
HostId{host_id},
link_id,
crate::u8_to_bool(connected),
crate::u8_to_bool(remote),
crate::u8_to_bool(external));
}
}
}
// Logging thread
fn logging_thread(knet_pipe: i32, sender: Sender<LogMsg>)
{
let mut logbuf = ffi::knet_log_msg {msg: [0; 254],
subsystem: 0,
msglevel: 0,
knet_h: 0 as ffi::knet_handle_t};
// Make it blocking
unsafe { libc::fcntl(knet_pipe, libc::F_SETFL,
libc::fcntl(knet_pipe, libc::F_GETFL, 0) & !libc::O_NONBLOCK)};
loop {
let msglen = unsafe {libc::read(knet_pipe, &mut logbuf as *mut _ as *mut c_void,
size_of::<ffi::knet_log_msg>())};
if msglen < 1 {
unsafe { libc::close(knet_pipe); }
// EOF on pipe, handle is closed.
return;
}
if msglen == size_of::<ffi::knet_log_msg>() as isize {
let rmsg = LogMsg {
msg: crate::string_from_bytes_safe(logbuf.msg.as_ptr(), 254),
subsystem: SubSystem::new(logbuf.subsystem),
level: LogLevel::new(logbuf.msglevel),
handle: Handle{knet_handle: logbuf.knet_h as u64}};
if let Err(e) = sender.send(rmsg) {
println!("Error sending log message: {e}");
}
}
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(transparent)]
/// a handle into the knet library, returned from [handle_new]
pub struct Handle {
knet_handle: u64,
}
// Private version of knet handle, contains all the callback data so
// we only need to access it in the calback functions, making the rest
// a bit quicker & neater
struct PrivHandle {
log_fd: i32,
sock_notify_fn: Option<SockNotifyFn>,
sock_notify_private_data: u64,
filter_fn: Option<FilterFn>,
filter_private_data: u64,
pmtud_notify_fn: Option<PmtudNotifyFn>,
pmtud_notify_private_data: u64,
onwire_notify_fn: Option<OnwireNotifyFn>,
onwire_notify_private_data: u64,
host_status_change_notify_fn: Option<HostStatusChangeNotifyFn>,
host_status_change_notify_private_data: u64,
link_status_change_notify_fn: Option<LinkStatusChangeNotifyFn>,
link_status_change_notify_private_data: u64,
}
/// A knet logging message returned down the log_sender channel set in [handle_new]
pub struct LogMsg {
pub msg: String,
pub subsystem: SubSystem,
pub level: LogLevel,
pub handle: Handle,
}
/// Initialise the knet library, returns a handle for use with the other API calls
pub fn handle_new(host_id: &HostId,
log_sender: Option<Sender<LogMsg>>,
default_log_level: LogLevel,
flags: HandleFlags) -> Result<Handle>
{
// If a log sender was passed, make an FD & thread for knet
let log_fd = match log_sender {
Some(s) => {
let mut pipes = [0i32; 2];
if unsafe {libc::pipe(pipes.as_mut_ptr())} != 0 {
return Err(Error::last_os_error());
}
spawn(move || logging_thread(pipes[0], s));
pipes[1]
},
None => 0
};
let res = unsafe {
ffi::knet_handle_new(host_id.host_id,
log_fd,
default_log_level.to_u8(),
flags.bits)
};
if res.is_null() {
Err(Error::last_os_error())
} else {
let rhandle = PrivHandle{log_fd,
sock_notify_fn: None,
sock_notify_private_data: 0u64,
filter_fn: None,
filter_private_data: 0u64,
pmtud_notify_fn: None,
pmtud_notify_private_data: 0u64,
onwire_notify_fn: None,
onwire_notify_private_data: 0u64,
host_status_change_notify_fn: None,
host_status_change_notify_private_data: 0u64,
link_status_change_notify_fn: None,
link_status_change_notify_private_data: 0u64,
};
HANDLE_HASH.lock().unwrap().insert(res as u64, rhandle);
Ok(Handle{knet_handle: res as u64})
}
}
/// Finish with knet, frees the handle returned by [handle_new]
pub fn handle_free(handle: Handle) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_free(handle.knet_handle as ffi::knet_handle_t)
};
if res == 0 {
// Close the log fd as knet doesn't "do ownership" and this will shut down
// our logging thread.
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
unsafe {
libc::close(h.log_fd);
};
}
HANDLE_HASH.lock().unwrap().remove(&handle.knet_handle);
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Enable notifications of socket state changes, set callback to 'None' to disable
pub fn handle_enable_sock_notify(handle: Handle,
private_data: u64,
sock_notify_fn: Option<SockNotifyFn>) -> Result<()>
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.sock_notify_private_data = private_data;
h.sock_notify_fn = sock_notify_fn;
let res = match sock_notify_fn {
Some(_f) =>
unsafe {
ffi::knet_handle_enable_sock_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_sock_notify_fn))
},
None =>
unsafe {
ffi::knet_handle_enable_sock_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
return Ok(());
} else {
return Err(Error::last_os_error());
}
}
Err(Error::new(ErrorKind::Other, "Rust handle not found"))
}
/// Add a data FD to knet. if datafd is 0 then knet will allocate one for you.
pub fn handle_add_datafd(handle: Handle, datafd: i32, channel: i8) -> Result<(i32, i8)>
{
let mut c_datafd = datafd;
let mut c_channel = channel;
let res = unsafe {
ffi::knet_handle_add_datafd(handle.knet_handle as ffi::knet_handle_t,
&mut c_datafd,
&mut c_channel)
};
if res == 0 {
Ok((c_datafd, c_channel))
} else {
Err(Error::last_os_error())
}
}
/// Remove a datafd from knet
pub fn handle_remove_datafd(handle: Handle, datafd: i32) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_remove_datafd(handle.knet_handle as ffi::knet_handle_t,
datafd)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Returns the channel associated with data fd
pub fn handle_get_channel(handle: Handle, datafd: i32) -> Result<i8>
{
let mut c_channel = 0i8;
let res = unsafe {
ffi::knet_handle_get_channel(handle.knet_handle as ffi::knet_handle_t,
datafd, &mut c_channel)
};
if res == 0 {
Ok(c_channel)
} else {
Err(Error::last_os_error())
}
}
/// Returns the data FD associated with a channel
pub fn handle_get_datafd(handle: Handle, channel: i8) -> Result<i32>
{
let mut c_datafd = 0i32;
let res = unsafe {
ffi::knet_handle_get_datafd(handle.knet_handle as ffi::knet_handle_t,
channel, &mut c_datafd)
};
if res == 0 {
Ok(c_datafd)
} else {
Err(Error::last_os_error())
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum DefragReclaimPolicy {
Average = 0,
Absolute = 1
}
impl DefragReclaimPolicy {
pub fn new (policy: u32) -> DefragReclaimPolicy {
{
match policy {
1 => DefragReclaimPolicy::Absolute,
_ => DefragReclaimPolicy::Average,
}
}
}
pub fn to_u32 (&self) -> u32 {
{
match self {
DefragReclaimPolicy::Absolute => 1,
DefragReclaimPolicy::Average => 0,
}
}
}
}
impl fmt::Display for DefragReclaimPolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DefragReclaimPolicy::Absolute => write!(f, "Absolute"),
DefragReclaimPolicy::Average => write!(f, "Average"),
}
}
}
/// Configure the defrag buffer parameters - applies to all hosts
pub fn handle_set_host_defrag_bufs(handle: Handle, min_defrag_bufs: u16, max_defrag_bufs: u16,
shrink_threshold: u8,
reclaim_policy: DefragReclaimPolicy) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_set_host_defrag_bufs(handle.knet_handle as ffi::knet_handle_t,
min_defrag_bufs, max_defrag_bufs,
shrink_threshold,
reclaim_policy.to_u32())
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the defrag buffer parameters.
/// Returns (min_defrag_bufs, max_defrag_bufs, shrink_threshold, usage_samples, usage_samples_timespan, reclaim_policy)
pub fn handle_get_host_defrag_bufs(handle: Handle) -> Result<(u16, u16, u8, DefragReclaimPolicy)>
{
let mut min_defrag_bufs: u16 = 0;
let mut max_defrag_bufs: u16 = 0;
let mut shrink_threshold: u8 = 0;
let mut reclaim_policy: u32 = 0;
let res = unsafe {
ffi::knet_handle_get_host_defrag_bufs(handle.knet_handle as ffi::knet_handle_t,
&mut min_defrag_bufs, &mut max_defrag_bufs,
&mut shrink_threshold,
&mut reclaim_policy)
};
if res == 0 {
Ok((min_defrag_bufs, max_defrag_bufs,
shrink_threshold,
DefragReclaimPolicy::new(reclaim_policy)))
} else {
Err(Error::last_os_error())
}
}
/// Receive messages from knet
pub fn recv(handle: Handle, buf: &[u8], channel: i8) -> Result<isize>
{
let res = unsafe {
ffi::knet_recv(handle.knet_handle as ffi::knet_handle_t,
buf.as_ptr() as *mut c_char,
buf.len(),
channel)
};
if res >= 0 {
Ok(res)
} else {
if get_errno() == libc::EAGAIN {
Err(Error::new(ErrorKind::WouldBlock, "Try again"))
} else {
Err(Error::last_os_error())
}
}
}
/// Send messages knet
pub fn send(handle: Handle, buf: &[u8], channel: i8) -> Result<isize>
{
let res = unsafe {
ffi::knet_send(handle.knet_handle as ffi::knet_handle_t,
buf.as_ptr() as *const c_char,
buf.len(),
channel)
};
if res >= 0 {
Ok(res)
} else {
if get_errno() == libc::EAGAIN {
Err(Error::new(ErrorKind::WouldBlock, "Try again"))
} else {
Err(Error::last_os_error())
}
}
}
/// Send messages to knet and wait till they have gone
pub fn send_sync(handle: Handle, buf: &[u8], channel: i8) -> Result<()>
{
let res = unsafe {
ffi::knet_send_sync(handle.knet_handle as ffi::knet_handle_t,
buf.as_ptr() as *const c_char,
buf.len(),
channel)
};
if res == 0 {
Ok(())
} else {
if get_errno() == libc::EAGAIN {
Err(Error::new(ErrorKind::WouldBlock, "Try again"))
} else {
Err(Error::last_os_error())
}
}
}
/// Enable the packet filter. pass 'None' as the callback to disable.
pub fn handle_enable_filter(handle: Handle,
private_data: u64,
filter_fn: Option<FilterFn>) -> Result<()>
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.filter_private_data = private_data;
h.filter_fn = filter_fn;
let res = match filter_fn {
Some(_f) =>
unsafe {
ffi::knet_handle_enable_filter(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_filter_fn))
},
None =>
unsafe {
ffi::knet_handle_enable_filter(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
return Ok(());
} else {
return Err(Error::last_os_error());
}
};
Err(Error::new(ErrorKind::Other, "Rust handle not found"))
}
/// Set timer resolution
pub fn handle_set_threads_timer_res(handle: Handle, timeres: u32) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_set_threads_timer_res(handle.knet_handle as ffi::knet_handle_t, timeres)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get timer resolution
pub fn handle_get_threads_timer_res(handle: Handle) -> Result<u32>
{
let mut c_timeres: u32 = 0;
let res = unsafe {
ffi::knet_handle_get_threads_timer_res(handle.knet_handle as ffi::knet_handle_t, &mut c_timeres)
};
if res == 0 {
Ok(c_timeres)
} else {
Err(Error::last_os_error())
}
}
/// Starts traffic moving. You must call this before knet will do anything.
pub fn handle_setfwd(handle: Handle, enabled: bool) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_setfwd(handle.knet_handle as ffi::knet_handle_t,
enabled as c_uint)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Enable access control lists
pub fn handle_enable_access_lists(handle: Handle, enabled: bool) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_enable_access_lists(handle.knet_handle as ffi::knet_handle_t,
enabled as c_uint)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Set frequency that PMTUd will check for MTU changes. value in milliseconds
pub fn handle_pmtud_setfreq(handle: Handle, interval: u32) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_pmtud_setfreq(handle.knet_handle as ffi::knet_handle_t,
interval)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get frequency that PMTUd will check for MTU changes. value in milliseconds
pub fn handle_pmtud_getfreq(handle: Handle) -> Result<u32>
{
let mut c_interval = 0u32;
let res = unsafe {
ffi::knet_handle_pmtud_getfreq(handle.knet_handle as ffi::knet_handle_t,
&mut c_interval)
};
if res == 0 {
Ok(c_interval)
} else {
Err(Error::last_os_error())
}
}
/// Get the current MTU
pub fn handle_pmtud_get(handle: Handle) -> Result<u32>
{
let mut c_mtu = 0u32;
let res = unsafe {
ffi::knet_handle_pmtud_get(handle.knet_handle as ffi::knet_handle_t,
&mut c_mtu)
};
if res == 0 {
Ok(c_mtu)
} else {
Err(Error::last_os_error())
}
}
/// Set the interface MTU (this should not be necessary)
pub fn handle_pmtud_set(handle: Handle, iface_mtu: u32) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_pmtud_set(handle.knet_handle as ffi::knet_handle_t,
iface_mtu)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Enable notification of MTU changes
pub fn handle_enable_pmtud_notify(handle: Handle,
private_data: u64,
pmtud_notify_fn: Option<PmtudNotifyFn>) -> Result<()>
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.pmtud_notify_private_data = private_data;
h.pmtud_notify_fn = pmtud_notify_fn;
let res = match pmtud_notify_fn {
Some(_f) =>
unsafe {
ffi::knet_handle_enable_pmtud_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_pmtud_notify_fn))
},
None =>
unsafe {
ffi::knet_handle_enable_pmtud_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
return Ok(());
} else {
return Err(Error::last_os_error());
}
}
Err(Error::new(ErrorKind::Other, "Rust handle not found"))
}
/// Configure cryptographic seetings for packets being transmitted
pub fn handle_crypto_set_config(handle: Handle, config: &CryptoConfig, config_num: u8) -> Result<()>
{
let mut crypto_cfg = ffi::knet_handle_crypto_cfg {
crypto_model: [0; 16],
crypto_cipher_type: [0; 16],
crypto_hash_type: [0; 16],
private_key: [0; 4096],
private_key_len: 0,
};
if config.private_key.len() > 4096 {
return Err(Error::new(ErrorKind::Other, "key too long"));
}
crate::string_to_bytes(&config.crypto_model, &mut crypto_cfg.crypto_model)?;
crate::string_to_bytes(&config.crypto_cipher_type, &mut crypto_cfg.crypto_cipher_type)?;
crate::string_to_bytes(&config.crypto_hash_type, &mut crypto_cfg.crypto_hash_type)?;
unsafe {
// NOTE param order is 'wrong-way round' from C
copy_nonoverlapping(config.private_key.as_ptr(), crypto_cfg.private_key.as_mut_ptr(), config.private_key.len());
}
crypto_cfg.private_key_len = config.private_key.len() as u32;
let res = unsafe {
ffi::knet_handle_crypto_set_config(handle.knet_handle as ffi::knet_handle_t,
&mut crypto_cfg,
config_num)
};
if res == 0 {
Ok(())
} else {
if res == -2 {
Err(Error::new(ErrorKind::Other, "Other cryto error"))
} else {
Err(Error::last_os_error())
}
}
}
/// Whether to allow or disallow clear-text traffic when crypto is enabled with [handle_crypto_rx_clear_traffic]
pub enum RxClearTraffic {
Allow = 0,
Disallow = 1,
}
/// Enable or disable clear-text traffic when crypto is enabled
pub fn handle_crypto_rx_clear_traffic(handle: Handle, value: RxClearTraffic) -> Result<()>
{
let c_value : u8 =
match value {
RxClearTraffic::Allow => 0,
RxClearTraffic::Disallow => 1
};
let res = unsafe {
ffi::knet_handle_crypto_rx_clear_traffic(handle.knet_handle as ffi::knet_handle_t,
c_value)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Tell knet which crypto settings to use
pub fn handle_crypto_use_config(handle: Handle, config_num: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_crypto_use_config(handle.knet_handle as ffi::knet_handle_t,
config_num)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Set up packet compression
pub fn handle_compress(handle: Handle, config: &CompressConfig) -> Result<()>
{
let mut compress_cfg = ffi::knet_handle_compress_cfg {
compress_model: [0; 16],
compress_threshold : config.compress_threshold,
compress_level : config.compress_level
};
if config.compress_model.len() > 16 {
return Err(Error::new(ErrorKind::Other, "key too long"));
}
crate::string_to_bytes(&config.compress_model, &mut compress_cfg.compress_model)?;
let res = unsafe {
ffi::knet_handle_compress(handle.knet_handle as ffi::knet_handle_t,
&mut compress_cfg)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Stats for the knet handle
pub type HandleStats = ffi::knet_handle_stats;
impl HandleStats {
pub fn new() -> HandleStats
{
HandleStats {
size: 0,
tx_uncompressed_packets: 0,
tx_compressed_packets: 0,
tx_compressed_original_bytes: 0,
tx_compressed_size_bytes: 0,
tx_compress_time_ave: 0,
tx_compress_time_min: 0,
tx_compress_time_max: 0,
tx_failed_to_compress: 0,
tx_unable_to_compress: 0,
rx_compressed_packets: 0,
rx_compressed_original_bytes: 0,
rx_compressed_size_bytes: 0,
rx_compress_time_ave: 0,
rx_compress_time_min: 0,
rx_compress_time_max: 0,
rx_failed_to_decompress: 0,
tx_crypt_packets: 0,
tx_crypt_byte_overhead: 0,
tx_crypt_time_ave: 0,
tx_crypt_time_min: 0,
tx_crypt_time_max: 0,
rx_crypt_packets: 0,
rx_crypt_time_ave: 0,
rx_crypt_time_min: 0,
rx_crypt_time_max: 0,
}
}
}
impl Default for ffi::knet_handle_stats {
fn default() -> Self {
ffi::knet_handle_stats::new()
}
}
impl fmt::Display for HandleStats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}, ", self.tx_uncompressed_packets)?;
write!(f, "{}, ", self.tx_compressed_packets)?;
write!(f, "{}, ", self.tx_compressed_original_bytes)?;
write!(f, "{}, ", self.tx_compressed_size_bytes)?;
write!(f, "{}, ", self.tx_compress_time_ave)?;
write!(f, "{}, ", self.tx_compress_time_min)?;
write!(f, "{}, ", self.tx_compress_time_max)?;
write!(f, "{}, ", self.tx_failed_to_compress)?;
write!(f, "{}, ", self.tx_unable_to_compress)?;
write!(f, "{}, ", self.rx_compressed_packets)?;
write!(f, "{}, ", self.rx_compressed_original_bytes)?;
write!(f, "{}, ", self.rx_compressed_size_bytes)?;
write!(f, "{}, ", self.rx_compress_time_ave)?;
write!(f, "{}, ", self.rx_compress_time_min)?;
write!(f, "{}, ", self.rx_compress_time_max)?;
write!(f, "{}, ", self.rx_failed_to_decompress)?;
write!(f, "{}, ", self.tx_crypt_packets)?;
write!(f, "{}, ", self.tx_crypt_byte_overhead)?;
write!(f, "{}, ", self.tx_crypt_time_ave)?;
write!(f, "{}, ", self.tx_crypt_time_min)?;
write!(f, "{}, ", self.tx_crypt_time_max)?;
write!(f, "{}, ", self.rx_crypt_packets)?;
write!(f, "{}, ", self.rx_crypt_time_ave)?;
write!(f, "{}, ", self.rx_crypt_time_min)?;
write!(f, "{}, ", self.rx_crypt_time_max)?;
Ok(())
}
}
/// Return statistics for this knet handle
pub fn handle_get_stats(handle: Handle) -> Result<HandleStats>
{
let (res, stats) = unsafe {
let mut c_stats = HandleStats::new();
let res = ffi::knet_handle_get_stats(handle.knet_handle as ffi::knet_handle_t,
&mut c_stats, size_of::<HandleStats>());
(res, c_stats)
};
if res == 0 {
Ok(stats)
} else {
Err(Error::last_os_error())
}
}
/// Tell [handle_clear_stats] whether to cleat all stats or just handle stats
pub enum ClearStats {
Handle = 1,
HandleAndLink = 2,
}
/// Clear statistics
pub fn handle_clear_stats(handle: Handle, clear_options: ClearStats) -> Result<()>
{
let c_value : i32 =
match clear_options {
ClearStats::Handle => 1,
ClearStats::HandleAndLink => 2
};
let res = unsafe {
ffi::knet_handle_clear_stats(handle.knet_handle as ffi::knet_handle_t,
c_value)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Crypto info returned from [get_crypto_list]
pub struct CryptoInfo
{
pub name: String,
pub properties: u8, // Unused
}
impl CryptoInfo
{
pub fn new(c_info: ffi::knet_crypto_info) -> CryptoInfo
{
let cstr = unsafe {CStr::from_ptr(c_info.name) };
let name = match cstr.to_str() {
Ok(s) => s.to_string(),
Err(e) => e.to_string(),
};
CryptoInfo {properties: 0,
name}
}
}
/// Get a list of valid crypto options
pub fn get_crypto_list() -> Result<Vec<CryptoInfo>>
{
let mut list_entries: usize = 256;
let mut c_list : [ffi::knet_crypto_info; 256] =
[ ffi::knet_crypto_info{name: null(), properties: 0u8, pad:[0; 256]}; 256];
let res = unsafe {
ffi::knet_get_crypto_list(&mut c_list[0],
&mut list_entries)
};
if res == 0 {
let mut retvec = Vec::<CryptoInfo>::new();
for i in c_list.iter().take(list_entries) {
retvec.push(CryptoInfo::new(*i));
}
Ok(retvec)
} else {
Err(Error::last_os_error())
}
}
/// Compressions types returned from [get_compress_list]
pub struct CompressInfo
{
pub name: String,
pub properties: u8, // Unused
}
impl CompressInfo
{
pub fn new(c_info: ffi::knet_compress_info) -> CompressInfo
{
let cstr = unsafe {CStr::from_ptr(c_info.name) };
let name = match cstr.to_str() {
Ok(s) => s.to_string(),
Err(e) => e.to_string(),
};
CompressInfo {properties: 0,
name}
}
}
/// Return a list of compression options
pub fn get_compress_list() -> Result<Vec<CompressInfo>>
{
let mut list_entries: usize = 256;
let mut c_list : [ffi::knet_compress_info; 256] =
[ ffi::knet_compress_info{name: null(), properties: 0u8, pad:[0; 256]}; 256];
let res = unsafe {
ffi::knet_get_compress_list(&mut c_list[0],
&mut list_entries)
};
if res == 0 {
let mut retvec = Vec::<CompressInfo>::new();
for i in c_list.iter().take(list_entries) {
retvec.push(CompressInfo::new(*i));
}
Ok(retvec)
} else {
Err(Error::last_os_error())
}
}
/// Enable callback when the onwire version for a node changes
pub fn handle_enable_onwire_ver_notify(handle: Handle,
private_data: u64,
onwire_notify_fn: Option<OnwireNotifyFn>) -> Result<()>
{
// This looks a bit different to the other _enable*_notify calls because knet
// calls the callback function in the API. Which results in a deadlock with our
// own mutex
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.onwire_notify_private_data = private_data;
h.onwire_notify_fn = onwire_notify_fn;
} else {
return Err(Error::new(ErrorKind::Other, "Rust handle not found"));
};
let res = match onwire_notify_fn {
Some(_f) =>
unsafe {
ffi::knet_handle_enable_onwire_ver_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_onwire_notify_fn))
},
None =>
unsafe {
ffi::knet_handle_enable_onwire_ver_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the onsure version for a node
pub fn handle_get_onwire_ver(handle: Handle, host_id: &HostId) -> Result<(u8,u8,u8)>
{
let mut onwire_min_ver = 0u8;
let mut onwire_max_ver = 0u8;
let mut onwire_ver = 0u8;
let res = unsafe {
ffi::knet_handle_get_onwire_ver(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id,
&mut onwire_min_ver,
&mut onwire_max_ver,
&mut onwire_ver)
};
if res == 0 {
Ok((onwire_min_ver, onwire_max_ver, onwire_ver))
} else {
Err(Error::last_os_error())
}
}
/// Set the onsire version for this node
pub fn handle_set_onwire_ver(handle: Handle, onwire_ver: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_set_onwire_ver(handle.knet_handle as ffi::knet_handle_t,
onwire_ver)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Set the reconnect interval.
pub fn handle_set_transport_reconnect_interval(handle: Handle, msecs: u32) -> Result<()>
{
let res = unsafe {
ffi::knet_handle_set_transport_reconnect_interval(handle.knet_handle as ffi::knet_handle_t,
msecs)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the reconnect interval.
pub fn handle_get_transport_reconnect_interval(handle: Handle) -> Result<u32>
{
let mut msecs = 0u32;
let res = unsafe {
ffi::knet_handle_get_transport_reconnect_interval(handle.knet_handle as ffi::knet_handle_t,
&mut msecs)
};
if res == 0 {
Ok(msecs)
} else {
Err(Error::last_os_error())
}
}
/// Add a new host ID
pub fn host_add(handle: Handle, host_id: &HostId) -> Result<()>
{
let res = unsafe {
ffi::knet_host_add(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Remove a Host ID
pub fn host_remove(handle: Handle, host_id: &HostId) -> Result<()>
{
let res = unsafe {
ffi::knet_host_remove(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Set the name of a host
pub fn host_set_name(handle: Handle, host_id: &HostId, name: &str) -> Result<()>
{
let c_name = CString::new(name)?;
let res = unsafe {
ffi::knet_host_set_name(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, c_name.as_ptr())
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
const KNET_MAX_HOST_LEN:usize = 256;
const KNET_MAX_PORT_LEN:usize = 6;
/// Retrieve the name of a host given its ID
pub fn host_get_name_by_host_id(handle: Handle, host_id: &HostId) -> Result<String>
{
let mut c_name: [c_char; KNET_MAX_HOST_LEN] = [0; KNET_MAX_HOST_LEN];
let res = unsafe {
ffi::knet_host_get_name_by_host_id(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, c_name.as_mut_ptr())
};
if res == 0 {
crate::string_from_bytes(c_name.as_ptr(), KNET_MAX_HOST_LEN)
} else {
Err(Error::last_os_error())
}
}
/// Return the ID of a host given its name
pub fn host_get_id_by_host_name(handle: Handle, name: &str) -> Result<HostId>
{
let c_name = CString::new(name)?;
let mut c_host_id = 0u16;
let res = unsafe {
ffi::knet_host_get_id_by_host_name(handle.knet_handle as ffi::knet_handle_t,
c_name.as_ptr(), &mut c_host_id)
};
if res == 0 {
Ok(HostId{host_id: c_host_id})
} else {
Err(Error::last_os_error())
}
}
const KNET_MAX_HOST: usize = 65536;
/// Return a list of host IDs known to this handle
pub fn host_get_host_list(handle: Handle) -> Result<Vec<HostId>>
{
let mut c_host_ids: [u16; KNET_MAX_HOST] = [0; KNET_MAX_HOST];
let mut c_host_ids_entries: usize = 0;
let res = unsafe {
ffi::knet_host_get_host_list(handle.knet_handle as ffi::knet_handle_t,
&mut c_host_ids[0], &mut c_host_ids_entries)
};
if res == 0 {
let mut host_vec = Vec::<HostId>::new();
for i in c_host_ids.iter().take(c_host_ids_entries) {
host_vec.push(HostId {host_id: *i});
}
Ok(host_vec)
} else {
Err(Error::last_os_error())
}
}
/// Link Policies for [host_set_policy]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum LinkPolicy {
Passive,
Active,
Rr,
}
impl LinkPolicy{
pub fn new(value: u8) -> LinkPolicy
{
match value {
2 => LinkPolicy::Rr,
1 => LinkPolicy::Active,
_ => LinkPolicy::Passive,
}
}
pub fn to_u8(self: LinkPolicy) -> u8
{
match self {
LinkPolicy::Passive => 0,
LinkPolicy::Active => 1,
LinkPolicy::Rr => 2,
}
}
}
impl fmt::Display for LinkPolicy {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LinkPolicy::Passive => write!(f, "Passive"),
LinkPolicy::Active => write!(f, "Active"),
LinkPolicy::Rr => write!(f, "RR"),
}
}
}
/// Set the policy for this host, this only makes sense if multiple links between hosts are configured
pub fn host_set_policy(handle: Handle, host_id: &HostId, policy: LinkPolicy) -> Result<()>
{
let c_value: u8 = policy.to_u8();
let res = unsafe {
ffi::knet_host_set_policy(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, c_value)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Return the current link policy for a node
pub fn host_get_policy(handle: Handle, host_id: &HostId) -> Result<LinkPolicy>
{
let mut c_value: u8 = 0;
let res = unsafe {
ffi::knet_host_get_policy(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, &mut c_value)
};
if res == 0 {
Ok(LinkPolicy::new(c_value))
} else {
Err(Error::last_os_error())
}
}
/// Current status of a host. remote & reachable are current not used
pub struct HostStatus
{
pub reachable: bool,
pub remote: bool,
pub external: bool,
}
impl fmt::Display for HostStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "reachable: {}, ", self.reachable)?;
write!(f, "remote: {}, ", self.remote)?;
write!(f, "external: {}", self.external)?;
Ok(())
}
}
/// Return the current status of a host
pub fn host_get_status(handle: Handle, host_id: &HostId) -> Result<HostStatus>
{
let mut c_value = ffi::knet_host_status { reachable:0, remote:0, external:0};
let res = unsafe {
ffi::knet_host_get_status(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, &mut c_value)
};
if res == 0 {
Ok(HostStatus {
reachable: crate::u8_to_bool(c_value.reachable),
remote: crate::u8_to_bool(c_value.remote),
external: crate::u8_to_bool(c_value.external)
})
} else {
Err(Error::last_os_error())
}
}
/// Enable callbacks when the status of a host changes
pub fn host_enable_status_change_notify(handle: Handle,
private_data: u64,
host_status_change_notify_fn: Option<HostStatusChangeNotifyFn>) -> Result<()>
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.host_status_change_notify_private_data = private_data;
h.host_status_change_notify_fn = host_status_change_notify_fn;
let res = match host_status_change_notify_fn {
Some(_f) =>
unsafe {
ffi::knet_host_enable_status_change_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_host_status_change_notify_fn))
},
None =>
unsafe {
ffi::knet_host_enable_status_change_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
return Ok(());
} else {
return Err(Error::last_os_error());
}
}
Err(Error::new(ErrorKind::Other, "Rust handle not found"))
}
/// Transport types supported in knet
pub enum TransportId {
Loopback,
Udp,
Sctp,
}
impl TransportId {
pub fn new(id: u8) -> TransportId
{
match id {
2 => TransportId::Sctp,
1 => TransportId::Udp,
_ => TransportId::Loopback,
}
}
pub fn to_u8(self: &TransportId) -> u8
{
match self {
TransportId::Loopback => 0,
TransportId::Udp => 1,
TransportId::Sctp => 2,
}
}
pub fn to_string(self: &TransportId) -> String
{
match self {
TransportId::Udp => "UDP".to_string(),
TransportId::Sctp => "SCTP".to_string(),
TransportId::Loopback => "Loopback".to_string()
}
}
pub fn from_string(name: String) -> TransportId
{
match name.as_str() {
"UDP" => TransportId::Udp,
"SCTP" => TransportId::Sctp,
"Loopback" => TransportId::Loopback,
_ => TransportId::Loopback,
}
}
}
/// Transport info returned from [get_transport_list]
pub struct TransportInfo
{
pub name: String,
pub id: TransportId,
pub properties: u8, // currently unused
}
// Controversially implementing name_by_id and id_by_name here
impl TransportInfo
{
pub fn new(c_info: ffi::knet_transport_info) -> TransportInfo
{
let cstr = unsafe {CStr::from_ptr(c_info.name) };
let name = match cstr.to_str() {
Ok(s) => s.to_string(),
Err(e) => e.to_string(),
};
TransportInfo {properties: 0,
id: TransportId::new(c_info.id),
name}
}
}
pub fn get_transport_list() -> Result<Vec<TransportInfo>>
{
let mut list_entries: usize = 256;
let mut c_list : [ffi::knet_transport_info; 256] =
[ ffi::knet_transport_info{name: null(), id: 0u8, properties: 0u8, pad:[0; 256]}; 256];
let res = unsafe {
ffi::knet_get_transport_list(&mut c_list[0],
&mut list_entries)
};
if res == 0 {
let mut retvec = Vec::<TransportInfo>::new();
for i in c_list.iter().take(list_entries) {
retvec.push(TransportInfo::new(*i));
}
Ok(retvec)
} else {
Err(Error::last_os_error())
}
}
/// Configure a link to a host ID. dst_addr may be None for a dynamic link.
pub fn link_set_config(handle: Handle, host_id: &HostId, link_id: u8,
transport: TransportId,
src_addr: &SocketAddr, dst_addr: Option<&SocketAddr>, flags: LinkFlags) -> Result<()>
{
// Not really mut, but C is dumb
let mut c_srcaddr = make_new_sockaddr_storage(src_addr);
// dst_addr can be NULL/None if this is a dynamic link
let res = if let Some(dst) = dst_addr {
let mut c_dstaddr = make_new_sockaddr_storage(dst);
unsafe {
ffi::knet_link_set_config(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
transport.to_u8(),
&mut c_srcaddr,
&mut c_dstaddr,
flags.bits)
}
} else {
unsafe {
ffi::knet_link_set_config(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
transport.to_u8(),
&mut c_srcaddr,
null_mut(),
flags.bits)
}
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Return a link's configuration
pub fn link_get_config(handle: Handle, host_id: &HostId, link_id: u8) ->
Result<(TransportId, Option<SocketAddr>, Option<SocketAddr>, LinkFlags)>
{
let mut c_srcaddr = OsSocketAddr::new();
let mut c_dstaddr = OsSocketAddr::new();
let mut c_transport = 0u8;
let mut c_flags = 0u64;
let mut c_dynamic = 0u8;
let res = unsafe {
ffi::knet_link_get_config(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_transport,
c_srcaddr.as_mut_ptr() as *mut ffi::sockaddr_storage,
c_dstaddr.as_mut_ptr() as *mut ffi::sockaddr_storage,
&mut c_dynamic,
&mut c_flags)
};
if res == 0 {
let r_transport = TransportId::new(c_transport);
Ok((r_transport, c_srcaddr.into(), c_dstaddr.into(), LinkFlags{bits:c_flags}))
} else {
Err(Error::last_os_error())
}
}
/// Clear a link configuration.
pub fn link_clear_config(handle: Handle, host_id: &HostId, link_id: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_link_clear_config(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Type of ACL
pub enum AclAcceptReject
{
Accept,
Reject,
}
impl AclAcceptReject {
pub fn new(ar: u32) -> AclAcceptReject
{
match ar {
ffi::CHECK_ACCEPT => AclAcceptReject::Accept,
ffi::CHECK_REJECT => AclAcceptReject::Reject,
_ => AclAcceptReject::Reject,
}
}
pub fn to_u32(self: &AclAcceptReject) -> u32
{
match self {
AclAcceptReject::Accept => ffi::CHECK_ACCEPT,
AclAcceptReject::Reject => ffi::CHECK_REJECT,
}
}
}
/// What the ACL should check
pub enum AclCheckType
{
Address,
Mask,
Range,
}
impl AclCheckType {
pub fn new(ct: u32) -> AclCheckType
{
match ct {
ffi::CHECK_TYPE_ADDRESS => AclCheckType::Address,
ffi::CHECK_TYPE_MASK => AclCheckType::Mask,
ffi::CHECK_TYPE_RANGE => AclCheckType::Range,
_ => AclCheckType::Address,
}
}
pub fn to_u32(self: &AclCheckType) -> u32
{
match self {
AclCheckType::Address => ffi::CHECK_TYPE_ADDRESS,
AclCheckType::Mask => ffi::CHECK_TYPE_MASK,
AclCheckType::Range => ffi::CHECK_TYPE_RANGE,
}
}
}
// We need to have a zeroed-out stackaddr storage to pass to the ACL APIs
// as knet compares the whole sockaddr_storage when using knet_rm_acl()
fn make_new_sockaddr_storage(ss: &SocketAddr) -> ffi::sockaddr_storage
{
// A blank one
#[cfg(any(target_os="freebsd"))]
let mut new_ss = ffi::sockaddr_storage {
ss_family: 0,
ss_len: 0,
__ss_pad1: [0; 6],
__ss_align: 0,
__ss_pad2: [0; 112],
};
#[cfg(any(target_os="linux"))]
let mut new_ss = ffi::sockaddr_storage {
ss_family: 0,
__ss_padding: [0; 118],
__ss_align: 0,
};
let p_new_ss : *mut ffi::sockaddr_storage = &mut new_ss;
// Rust only fills in what it thinks is necessary
let c_ss : OsSocketAddr = (*ss).into();
// Copy it
unsafe {
// Only copy as much as is in the OsSocketAddr
copy_nonoverlapping(c_ss.as_ptr(),
p_new_ss as *mut libc::sockaddr,
1);
}
new_ss
}
/// Add an ACL to a link, adds the ACL to the end of the list.
pub fn link_add_acl(handle: Handle, host_id: &HostId, link_id: u8,
ss1: &SocketAddr, ss2: &SocketAddr,
check_type: AclCheckType, acceptreject: AclAcceptReject) -> Result<()>
{
// Not really mut, but C is dumb
let mut c_ss1 = make_new_sockaddr_storage(ss1);
let mut c_ss2 = make_new_sockaddr_storage(ss2);
let res = unsafe {
ffi::knet_link_add_acl(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_ss1,
&mut c_ss2,
check_type.to_u32(), acceptreject.to_u32())
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Insert an ACL anywhere in the ACL list for this host/link
pub fn link_insert_acl(handle: Handle, host_id: &HostId, link_id: u8,
index: i32,
ss1: &SocketAddr, ss2: &SocketAddr,
check_type: AclCheckType, acceptreject: AclAcceptReject) -> Result<()>
{
// Not really mut, but C is dumb
let mut c_ss1 = make_new_sockaddr_storage(ss1);
let mut c_ss2 = make_new_sockaddr_storage(ss2);
let res = unsafe {
ffi::knet_link_insert_acl(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
index,
&mut c_ss1,
&mut c_ss2,
check_type.to_u32(), acceptreject.to_u32())
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Remove an ACL for this host/link
pub fn link_rm_acl(handle: Handle, host_id: &HostId, link_id: u8,
ss1: &SocketAddr, ss2: &SocketAddr,
check_type: AclCheckType, acceptreject: AclAcceptReject) -> Result<()>
{
// Not really mut, but C is dumb
let mut c_ss1 = make_new_sockaddr_storage(ss1);
let mut c_ss2 = make_new_sockaddr_storage(ss2);
let res = unsafe {
ffi::knet_link_rm_acl(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_ss1,
&mut c_ss2,
check_type.to_u32(), acceptreject.to_u32())
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Clear out all ACLs from this host/link
pub fn link_clear_acl(handle: Handle, host_id: &HostId, link_id: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_link_clear_acl(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Enable/disable a link (you still need to call [handle_setfwd] for traffic to flow
pub fn link_set_enable(handle: Handle, host_id: &HostId, link_id: u8, enable: bool) -> Result<()>
{
let res = unsafe {
ffi::knet_link_set_enable(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id, enable as u32)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the 'enabled' status for a link
pub fn link_get_enable(handle: Handle, host_id: &HostId, link_id: u8) -> Result<bool>
{
let mut c_enable = 0u32;
let res = unsafe {
ffi::knet_link_get_enable(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id, &mut c_enable)
};
if res == 0 {
Ok(crate::u32_to_bool(c_enable))
} else {
Err(Error::last_os_error())
}
}
/// Set the ping timers for a link
pub fn link_set_ping_timers(handle: Handle, host_id: &HostId, link_id: u8,
interval: i64, timeout: i64, precision: u32) -> Result<()>
{
let res = unsafe {
ffi::knet_link_set_ping_timers(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
interval, timeout, precision)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the ping timers for a link
pub fn link_get_ping_timers(handle: Handle, host_id: &HostId, link_id: u8) -> Result<(i64, i64, u32)>
{
let mut c_interval : ffi::time_t = 0;
let mut c_timeout : ffi::time_t = 0;
let mut c_precision = 0u32;
let res = unsafe {
ffi::knet_link_get_ping_timers(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_interval, &mut c_timeout, &mut c_precision)
};
if res == 0 {
Ok((c_interval, c_timeout, c_precision))
} else {
Err(Error::last_os_error())
}
}
/// Set the pong count for a link
pub fn link_set_pong_count(handle: Handle, host_id: &HostId, link_id: u8,
pong_count: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_link_set_pong_count(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
pong_count)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the pong count for a link
pub fn link_get_pong_count(handle: Handle, host_id: &HostId, link_id: u8) -> Result<u8>
{
let mut c_pong_count = 0u8;
let res = unsafe {
ffi::knet_link_get_pong_count(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_pong_count)
};
if res == 0 {
Ok(c_pong_count)
} else {
Err(Error::last_os_error())
}
}
/// Set the link priority (only useful with multiple links to a node)
pub fn link_set_priority(handle: Handle, host_id: &HostId, link_id: u8,
priority: u8) -> Result<()>
{
let res = unsafe {
ffi::knet_link_set_priority(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
priority)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the link priority
pub fn link_get_priority(handle: Handle, host_id: &HostId, link_id: u8) -> Result<u8>
{
let mut c_priority = 0u8;
let res = unsafe {
ffi::knet_link_get_priority(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_priority)
};
if res == 0 {
Ok(c_priority)
} else {
Err(Error::last_os_error())
}
}
const KNET_MAX_LINK: usize = 8;
/// Get a list of links for this host
pub fn link_get_link_list(handle: Handle, host_id: &HostId) -> Result<Vec<u8>>
{
let mut c_link_ids: [u8; KNET_MAX_LINK] = [0; KNET_MAX_LINK];
let mut c_link_ids_entries: usize = 0;
let res = unsafe {
ffi::knet_link_get_link_list(handle.knet_handle as ffi::knet_handle_t, host_id.host_id,
&mut c_link_ids[0], &mut c_link_ids_entries)
};
if res == 0 {
let mut link_vec = Vec::<u8>::new();
for i in c_link_ids.iter().take(c_link_ids_entries) {
link_vec.push(*i);
}
Ok(link_vec)
} else {
Err(Error::last_os_error())
}
}
/// Enable callbacks when a link status changes
pub fn link_enable_status_change_notify(handle: Handle,
private_data: u64,
link_status_change_notify_fn: Option<LinkStatusChangeNotifyFn>) -> Result<()>
{
if let Some(h) = HANDLE_HASH.lock().unwrap().get_mut(&(handle.knet_handle)) {
h.link_status_change_notify_private_data = private_data;
h.link_status_change_notify_fn = link_status_change_notify_fn;
let res = match link_status_change_notify_fn {
Some(_f) =>
unsafe {
ffi::knet_link_enable_status_change_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
Some(rust_link_status_change_notify_fn))
},
None =>
unsafe {
ffi::knet_link_enable_status_change_notify(handle.knet_handle as ffi::knet_handle_t,
handle.knet_handle as *mut c_void,
None)
},
};
if res == 0 {
return Ok(());
} else {
return Err(Error::last_os_error());
}
}
Err(Error::new(ErrorKind::Other, "Rust handle not found"))
}
/// Link stats
pub struct LinkStats {
pub tx_data_packets: u64,
pub rx_data_packets: u64,
pub tx_data_bytes: u64,
pub rx_data_bytes: u64,
pub rx_ping_packets: u64,
pub tx_ping_packets: u64,
pub rx_ping_bytes: u64,
pub tx_ping_bytes: u64,
pub rx_pong_packets: u64,
pub tx_pong_packets: u64,
pub rx_pong_bytes: u64,
pub tx_pong_bytes: u64,
pub rx_pmtu_packets: u64,
pub tx_pmtu_packets: u64,
pub rx_pmtu_bytes: u64,
pub tx_pmtu_bytes: u64,
pub tx_total_packets: u64,
pub rx_total_packets: u64,
pub tx_total_bytes: u64,
pub rx_total_bytes: u64,
pub tx_total_errors: u64,
pub tx_total_retries: u64,
pub tx_pmtu_errors: u32,
pub tx_pmtu_retries: u32,
pub tx_ping_errors: u32,
pub tx_ping_retries: u32,
pub tx_pong_errors: u32,
pub tx_pong_retries: u32,
pub tx_data_errors: u32,
pub tx_data_retries: u32,
pub latency_min: u32,
pub latency_max: u32,
pub latency_ave: u32,
pub latency_samples: u32,
pub down_count: u32,
pub up_count: u32,
pub last_up_times: Vec<SystemTime>,
pub last_down_times: Vec<SystemTime>,
}
// Quick & Dirty printing
impl fmt::Display for LinkStats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}, ", self.tx_data_packets)?;
write!(f, "{}, ", self.rx_data_packets)?;
write!(f, "{}, ", self.tx_data_bytes)?;
write!(f, "{}, ", self.rx_data_bytes)?;
write!(f, "{}, ", self.rx_ping_packets)?;
write!(f, "{}, ", self.tx_ping_packets)?;
write!(f, "{}, ", self.rx_ping_bytes)?;
write!(f, "{}, ", self.tx_ping_bytes)?;
write!(f, "{}, ", self.rx_pong_packets)?;
write!(f, "{}, ", self.tx_pong_packets)?;
write!(f, "{}, ", self.rx_pong_bytes)?;
write!(f, "{}, ", self.tx_pong_bytes)?;
write!(f, "{}, ", self.rx_pmtu_packets)?;
write!(f, "{}, ", self.tx_pmtu_packets)?;
write!(f, "{}, ", self.rx_pmtu_bytes)?;
write!(f, "{}, ", self.tx_pmtu_bytes)?;
write!(f, "{}, ", self.tx_total_packets)?;
write!(f, "{}, ", self.rx_total_packets)?;
write!(f, "{}, ", self.tx_total_bytes)?;
write!(f, "{}, ", self.rx_total_bytes)?;
write!(f, "{}, ", self.tx_total_errors)?;
write!(f, "{}, ", self.tx_total_retries)?;
write!(f, "{}, ", self.tx_pmtu_errors)?;
write!(f, "{}, ", self.tx_pmtu_retries)?;
write!(f, "{}, ", self.tx_ping_errors)?;
write!(f, "{}, ", self.tx_ping_retries)?;
write!(f, "{}, ", self.tx_pong_errors)?;
write!(f, "{}, ", self.tx_pong_retries)?;
write!(f, "{}, ", self.tx_data_errors)?;
write!(f, "{}, ", self.tx_data_retries)?;
write!(f, "{}, ", self.latency_min)?;
write!(f, "{}, ", self.latency_max)?;
write!(f, "{}, ", self.latency_ave)?;
write!(f, "{}, ", self.latency_samples)?;
write!(f, "{}, ", self.down_count)?;
write!(f, "{}, ", self.up_count)?;
write!(f, "Last up times: ")?;
// There's no sensible print for SystemTime in the std library
// and I don't want to add dependancies here for printing as it
// mostly going to be the client's responsibility, so use the Debug option
for i in &self.last_up_times {
write!(f, "{i:?}")?;
}
write!(f, " Last down times: ")?;
for i in &self.last_down_times {
write!(f, "{i:?}")?;
}
Ok(())
}
}
// I wish this all wasn't necessary!
impl ffi::knet_link_stats {
pub fn new() -> ffi::knet_link_stats {
ffi::knet_link_stats {
tx_data_packets: 0,
rx_data_packets: 0,
tx_data_bytes: 0,
rx_data_bytes: 0,
rx_ping_packets: 0,
tx_ping_packets: 0,
rx_ping_bytes: 0,
tx_ping_bytes: 0,
rx_pong_packets: 0,
tx_pong_packets: 0,
rx_pong_bytes: 0,
tx_pong_bytes: 0,
rx_pmtu_packets: 0,
tx_pmtu_packets: 0,
rx_pmtu_bytes: 0,
tx_pmtu_bytes: 0,
tx_total_packets: 0,
rx_total_packets: 0,
tx_total_bytes: 0,
rx_total_bytes: 0,
tx_total_errors: 0,
tx_total_retries: 0,
tx_pmtu_errors: 0,
tx_pmtu_retries: 0,
tx_ping_errors: 0,
tx_ping_retries: 0,
tx_pong_errors: 0,
tx_pong_retries: 0,
tx_data_errors: 0,
tx_data_retries: 0,
latency_min: 0,
latency_max: 0,
latency_ave: 0,
latency_samples: 0,
down_count: 0,
up_count: 0,
last_up_times: [0; 16],
last_down_times: [0; 16],
last_up_time_index: 0,
last_down_time_index: 0,
}
}
}
impl Default for ffi::knet_link_stats {
fn default() -> Self {
ffi::knet_link_stats::new()
}
}
impl ffi::knet_link_status {
pub fn new()-> ffi::knet_link_status
{
ffi::knet_link_status {
size: 0,
src_ipaddr : [0; KNET_MAX_HOST_LEN],
dst_ipaddr : [0; KNET_MAX_HOST_LEN],
src_port : [0; KNET_MAX_PORT_LEN],
dst_port : [0; KNET_MAX_PORT_LEN],
enabled: 0,
connected: 0,
dynconnected: 0,
pong_last: ffi::timespec{ tv_sec: 0, tv_nsec: 0},
mtu: 0,
proto_overhead: 0,
stats: ffi::knet_link_stats::new(),
}
}
}
impl Default for ffi::knet_link_status {
fn default() -> Self {
ffi::knet_link_status::new()
}
}
/// Link status (includes a [LinkStats])
pub struct LinkStatus
{
pub src_ipaddr: String,
pub dst_ipaddr: String,
pub src_port: String,
pub dst_port: String,
pub enabled: bool,
pub connected: bool,
pub dynconnected: bool,
pub pong_last: SystemTime,
pub mtu: u32,
pub proto_overhead: u32,
pub stats: LinkStats,
}
impl LinkStatus {
pub fn new(c_stats: ffi::knet_link_status) -> LinkStatus
{
LinkStatus {
src_ipaddr : crate::string_from_bytes_safe(c_stats.src_ipaddr.as_ptr(), KNET_MAX_HOST_LEN),
src_port : crate::string_from_bytes_safe(c_stats.src_port.as_ptr(), KNET_MAX_HOST_LEN),
dst_ipaddr : crate::string_from_bytes_safe(c_stats.dst_ipaddr.as_ptr(), KNET_MAX_HOST_LEN),
dst_port : crate::string_from_bytes_safe(c_stats.dst_port.as_ptr(), KNET_MAX_HOST_LEN),
enabled : crate::u8_to_bool(c_stats.enabled),
connected : crate::u8_to_bool(c_stats.connected),
dynconnected : crate::u8_to_bool(c_stats.dynconnected),
pong_last : systemtime_from_timespec(c_stats.pong_last),
mtu : c_stats.mtu,
proto_overhead : c_stats.proto_overhead,
stats : LinkStats::new(c_stats.stats),
}
}
}
impl fmt::Display for LinkStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "src_ip_addr: {}:{}, ", self.src_ipaddr, self.src_port)?;
write!(f, "dst_ip_addr: {}:{}, ", self.dst_ipaddr, self.src_port)?;
write!(f, "enabled: {}, connected: {}, mtu: {}, overhead: {}, ",
self.enabled, self.connected, self.mtu, self.proto_overhead)?;
write!(f, "stats: {}", self.stats)?;
Ok(())
}
}
fn systemtime_from_time_t(t: u64) -> SystemTime
{
SystemTime::UNIX_EPOCH+Duration::from_secs(t)
}
fn systemtime_from_timespec(t: ffi::timespec) -> SystemTime
{
SystemTime::UNIX_EPOCH+Duration::from_secs(t.tv_sec as u64)
+Duration::from_nanos(t.tv_nsec as u64) // TODO may panic??
}
fn copy_circular_buffer_of_link_events(num: usize, times: &[ffi::time_t]) -> Vec<SystemTime>
{
let mut times_vec = Vec::<SystemTime>::new();
for index in (0 .. num).rev() {
if times[index] == 0 {
break
}
times_vec.push(systemtime_from_time_t(times[index] as u64)); // TODO may panic??
}
for index in (num+1 .. MAX_LINK_EVENTS).rev() {
if times[index] == 0 {
break;
}
times_vec.push(systemtime_from_time_t(times[index] as u64)); // TODO may panic??
}
times_vec
}
const MAX_LINK_EVENTS: usize = 16;
impl LinkStats {
pub fn new(cstats: ffi::knet_link_stats) -> LinkStats
{
let up_times = copy_circular_buffer_of_link_events(cstats.last_up_time_index as usize,
&cstats.last_up_times);
let down_times = copy_circular_buffer_of_link_events(cstats.last_down_time_index as usize,
&cstats.last_down_times);
LinkStats {
tx_data_packets: cstats.tx_data_packets,
rx_data_packets: cstats.rx_data_packets,
tx_data_bytes: cstats.tx_data_bytes,
rx_data_bytes: cstats.rx_data_bytes,
rx_ping_packets: cstats.rx_ping_packets,
tx_ping_packets: cstats.tx_ping_packets,
rx_ping_bytes: cstats.rx_ping_bytes,
tx_ping_bytes: cstats.tx_ping_bytes,
rx_pong_packets: cstats.rx_pong_packets,
tx_pong_packets: cstats.tx_pong_packets,
rx_pong_bytes: cstats.rx_pong_bytes,
tx_pong_bytes: cstats.tx_pong_bytes,
rx_pmtu_packets: cstats.rx_pmtu_packets,
tx_pmtu_packets: cstats.tx_pmtu_packets,
rx_pmtu_bytes: cstats.rx_pmtu_bytes,
tx_pmtu_bytes: cstats.tx_pmtu_bytes,
tx_total_packets: cstats.tx_total_packets,
rx_total_packets: cstats.rx_total_packets,
tx_total_bytes: cstats.tx_total_bytes,
rx_total_bytes: cstats.rx_total_bytes,
tx_total_errors: cstats.tx_total_errors,
tx_total_retries: cstats.tx_total_retries,
tx_pmtu_errors: cstats.tx_pmtu_errors,
tx_pmtu_retries: cstats.tx_pmtu_retries,
tx_ping_errors: cstats.tx_ping_errors,
tx_ping_retries: cstats.tx_ping_retries,
tx_pong_errors: cstats.tx_pong_errors,
tx_pong_retries: cstats.tx_pong_retries,
tx_data_errors: cstats.tx_data_errors,
tx_data_retries: cstats.tx_data_retries,
latency_min: cstats.latency_min,
latency_max: cstats.latency_max,
latency_ave: cstats.latency_ave,
latency_samples: cstats.latency_samples,
down_count: cstats.down_count,
up_count: cstats.up_count,
last_up_times: up_times,
last_down_times: down_times,
}
}
}
/// Get the status (and stats) of a link
pub fn link_get_status(handle: Handle, host_id: &HostId, link_id: u8) -> Result<LinkStatus>
{
let (res, stats) = unsafe {
let mut c_stats : ffi::knet_link_status = ffi::knet_link_status::new();
let res = ffi::knet_link_get_status(handle.knet_handle as ffi::knet_handle_t,
host_id.host_id, link_id,
&mut c_stats, size_of::<ffi::knet_link_status>());
(res, c_stats)
};
if res == 0 {
let r_status = LinkStatus::new(stats);
Ok(r_status)
} else {
Err(Error::last_os_error())
}
}
/// Get the logging subsystem ID given its name
pub fn log_get_subsystem_id(name: &str) -> Result<u8>
{
let c_name = CString::new(name)?;
let res = unsafe {
ffi::knet_log_get_subsystem_id(c_name.as_ptr())
};
Ok(res)
}
/// Get the logging subsystem name given its ID
pub fn log_get_subsystem_name(id: u8) -> Result<String>
{
let res = unsafe {
ffi::knet_log_get_subsystem_name(id)
};
crate::string_from_bytes(res, 256)
}
/// Get the name of a logging level
pub fn log_get_loglevel_id(name: &str) -> Result<u8>
{
let c_name = CString::new(name)?;
let res = unsafe {
ffi::knet_log_get_loglevel_id(c_name.as_ptr())
};
Ok(res)
}
/// Get the ID of a logging level, given its name
pub fn log_get_loglevel_name(id: u8) -> Result<String>
{
let res = unsafe {
ffi::knet_log_get_loglevel_name(id)
};
crate::string_from_bytes(res, 256)
}
/// Logging levels
pub enum LogLevel {
Err,
Warn,
Info,
Debug,
+ Trace,
}
impl LogLevel {
pub fn new(level: u8) -> LogLevel {
match level {
0 => LogLevel::Err,
1 => LogLevel::Warn,
2 => LogLevel::Info,
- _ => LogLevel::Debug, // 3=Debug, but default anything to it too
+ 3 => LogLevel::Debug,
+ _ => LogLevel::Trace // 4=Debug, but default anything to it too
}
}
pub fn to_u8(self: &LogLevel) -> u8
{
match self {
LogLevel::Err => 0,
LogLevel::Warn => 1,
LogLevel::Info => 2,
LogLevel::Debug => 3,
+ LogLevel::Trace => 4,
}
}
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
LogLevel::Err => write!(f, "Err"),
LogLevel::Warn => write!(f, "Warn"),
LogLevel::Info => write!(f, "Info"),
LogLevel::Debug => write!(f, "Debug"),
+ LogLevel::Trace => write!(f, "Trace"),
}
}
}
/// Subsystems known to the knet logger
pub enum SubSystem
{
Common,
Handle,
Host,
Listener,
Link,
Transport,
Crypto,
Compress,
Filter,
Dstcache,
Heartbeat,
Pmtud,
Tx,
Rx,
TranspBase,
TranspLoopback,
TranspUdp,
TranspSctp,
NssCrypto,
OpensslCrypto,
Zlibcomp,
Lz4comp,
Lz4hccomp,
Lzo2comp,
Lzmacomp,
Bzip2comp,
Zstdcomp,
Unknown,
}
impl SubSystem {
pub fn to_u8(self: &SubSystem) -> u8
{
match self {
SubSystem::Common => ffi::KNET_SUB_COMMON,
SubSystem::Handle => ffi::KNET_SUB_HANDLE,
SubSystem::Host => ffi::KNET_SUB_HOST,
SubSystem::Listener => ffi::KNET_SUB_LISTENER,
SubSystem::Link => ffi::KNET_SUB_LINK,
SubSystem::Transport => ffi::KNET_SUB_TRANSPORT,
SubSystem::Crypto => ffi::KNET_SUB_CRYPTO,
SubSystem::Compress => ffi::KNET_SUB_COMPRESS,
SubSystem::Filter => ffi::KNET_SUB_FILTER,
SubSystem::Dstcache => ffi::KNET_SUB_DSTCACHE,
SubSystem::Heartbeat => ffi::KNET_SUB_HEARTBEAT,
SubSystem::Pmtud => ffi::KNET_SUB_PMTUD,
SubSystem::Tx => ffi::KNET_SUB_TX,
SubSystem::Rx => ffi::KNET_SUB_RX,
SubSystem::TranspBase => ffi::KNET_SUB_TRANSP_BASE,
SubSystem::TranspLoopback => ffi::KNET_SUB_TRANSP_LOOPBACK,
SubSystem::TranspUdp => ffi::KNET_SUB_TRANSP_UDP,
SubSystem::TranspSctp => ffi::KNET_SUB_TRANSP_SCTP,
SubSystem::NssCrypto => ffi::KNET_SUB_NSSCRYPTO,
SubSystem::OpensslCrypto => ffi::KNET_SUB_OPENSSLCRYPTO,
SubSystem::Zlibcomp => ffi::KNET_SUB_ZLIBCOMP,
SubSystem::Lz4comp => ffi::KNET_SUB_LZ4COMP,
SubSystem::Lz4hccomp => ffi::KNET_SUB_LZ4HCCOMP,
SubSystem::Lzo2comp => ffi::KNET_SUB_LZO2COMP,
SubSystem::Lzmacomp => ffi::KNET_SUB_LZMACOMP,
SubSystem::Bzip2comp => ffi::KNET_SUB_BZIP2COMP,
SubSystem::Zstdcomp => ffi::KNET_SUB_ZSTDCOMP,
SubSystem::Unknown => ffi::KNET_SUB_UNKNOWN,
}
}
pub fn new(subsys: u8) -> SubSystem
{
match subsys {
1 => SubSystem::Unknown,
2 => SubSystem::Unknown,
_ => SubSystem::Unknown,
}
}
}
/// Set the current logging level
pub fn log_set_loglevel(handle: Handle, subsystem: SubSystem, level: LogLevel) -> Result<()>
{
let c_level = level.to_u8();
let c_subsys = subsystem.to_u8();
let res = unsafe {
ffi::knet_log_set_loglevel(handle.knet_handle as ffi::knet_handle_t,
c_subsys, c_level)
};
if res == 0 {
Ok(())
} else {
Err(Error::last_os_error())
}
}
/// Get the current logging level
pub fn log_get_loglevel(handle: Handle, subsystem: SubSystem) -> Result<LogLevel>
{
let mut c_level:u8 = 0;
let c_subsys = subsystem.to_u8();
let res = unsafe {
ffi::knet_log_get_loglevel(handle.knet_handle as ffi::knet_handle_t,
c_subsys, &mut c_level)
};
if res == 0 {
Ok(LogLevel::new(c_level))
} else {
Err(Error::last_os_error())
}
}
diff --git a/libknet/bindings/rust/tests/src/bin/knet-test.rs b/libknet/bindings/rust/tests/src/bin/knet-test.rs
index 0158e7ce..8a9d7ded 100644
--- a/libknet/bindings/rust/tests/src/bin/knet-test.rs
+++ b/libknet/bindings/rust/tests/src/bin/knet-test.rs
@@ -1,971 +1,979 @@
// Testing the Knet Rust APIs
//
// Copyright (C) 2021-2023 Red Hat, Inc.
//
// All rights reserved.
//
// Author: Christine Caulfield (ccaulfi@redhat.com)
//
use knet_bindings::knet_bindings as knet;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::thread::spawn;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::channel;
use std::io::{Result, ErrorKind, Error};
use std::{thread, time};
use std::env;
const CHANNEL: i8 = 1;
// Dirty C function to set the plugin path for testing (only)
extern {
fn set_plugin_path(knet_h: knet::Handle);
}
fn is_memcheck() -> bool
{
match env::var("KNETMEMCHECK") {
Ok(s) => {
s == "yes"
}
Err(_) => false
}
}
// Probably this will never happen, but just-in-case
fn is_helgrind() -> bool
{
match env::var("KNETHELGRIND") {
Ok(s) => {
s == "yes"
}
Err(_) => false
}
}
fn get_scaled_tmo(millis: u64) -> time::Duration
{
if is_memcheck() || is_helgrind() {
println!("Running under valgrind, increasing timer from {} to {}", millis, millis*16);
time::Duration::from_millis(millis * 16)
} else {
time::Duration::from_millis(millis)
}
}
// Callbacks
fn sock_notify_fn(private_data: u64,
datafd: i32,
channel: i8,
txrx: knet::TxRx,
_res: Result<()>)
{
println!("sock notify called for host {private_data}, datafd: {datafd}, channel: {channel}, {txrx}");
}
fn link_notify_fn(private_data: u64,
host_id: knet::HostId,
link_id: u8,
connected: bool,
_remote: bool,
_external: bool)
{
println!("link status notify called ({}) for host {}, linkid: {}, connected: {}",
private_data, host_id.to_u16(), link_id, connected);
}
fn host_notify_fn(private_data: u64,
host_id: knet::HostId,
connected: bool,
_remote: bool,
_external: bool)
{
println!("host status notify called ({}) for host {}, connected: {}",
private_data, host_id.to_u16(), connected);
}
fn pmtud_fn(private_data: u64, data_mtu: u32) {
println!("PMTUD notify: host {private_data}, MTU:{data_mtu} ");
}
fn onwire_fn(private_data: u64,
onwire_min_ver: u8,
onwire_max_ver: u8,
onwire_ver: u8) {
println!("Onwire ver notify for {private_data} : {onwire_min_ver}/{onwire_max_ver}/{onwire_ver}");
}
fn filter_fn(private_data: u64,
_outdata: &[u8],
txrx: knet::TxRx,
this_host_id: knet::HostId,
src_host_id: knet::HostId,
channel: &mut i8,
dst_host_ids: &mut Vec<knet::HostId>) -> knet::FilterDecision
{
println!("Filter ({private_data}) called {txrx} to {this_host_id} from {src_host_id}, channel: {channel}");
let dst: u16 = (private_data & 0xFFFF) as u16;
match txrx {
knet::TxRx::Tx => {
dst_host_ids.push(knet::HostId::new(3-dst));
knet::FilterDecision::Unicast
}
knet::TxRx::Rx => {
dst_host_ids.push(this_host_id);
knet::FilterDecision::Unicast
}
}
}
fn logging_thread(recvr: Receiver<knet::LogMsg>)
{
for i in &recvr {
eprintln!("KNET: {}", i.msg);
}
eprintln!("Logging thread finished");
}
fn setup_node(our_hostid: &knet::HostId, other_hostid: &knet::HostId, name: &str) -> Result<knet::Handle>
{
let (log_sender, log_receiver) = channel::<knet::LogMsg>();
spawn(move || logging_thread(log_receiver));
let knet_handle = match knet::handle_new(our_hostid, Some(log_sender),
knet::LogLevel::Debug, knet::HandleFlags::NONE) {
Ok(h) => h,
Err(e) => {
println!("Error from handle_new: {e}");
return Err(e);
}
};
// Make sure we use the build-tree plugins if LD_LIBRRAY_PATH points to them
unsafe {
set_plugin_path(knet_handle);
}
if let Err(e) = knet::host_add(knet_handle, other_hostid) {
println!("Error from host_add: {e}");
return Err(e);
}
if let Err(e) = knet::host_set_name(knet_handle, other_hostid, name) {
println!("Error from host_set_name: {e}");
return Err(e);
}
Ok(knet_handle)
}
// Called by the ACL tests to get a free port for a dynamic link
fn setup_dynamic_link(handle: knet::Handle, hostid: &knet::HostId, link: u8,
lowest_port: u16) -> Result<()>
{
let mut src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
for p in lowest_port..=65535 {
src_addr.set_port(p);
if let Err(e) = knet::link_set_config(handle, hostid, link,
knet::TransportId::Udp,
&src_addr,
None,
knet::LinkFlags::NONE) {
if let Some(os_err) = e.raw_os_error() {
if os_err != libc::EADDRINUSE {
println!("Error from link_set_config(dyn): {e}");
return Err(e);
}
// In use - try the next port number
}
} else {
println!("Dynamic link - Using port {p}");
return Ok(())
}
}
Err(Error::new(ErrorKind::Other, "No ports available"))
}
// This is the bit that configures two links on two handles that talk to each other
// while also making sure they don't clash with anything else on the system
fn setup_links(handle1: knet::Handle, hostid1: &knet::HostId,
handle2: knet::Handle, hostid2: &knet::HostId) -> Result<u16>
{
let mut src_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
let mut dst_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0);
for p in 1025..=65534 {
src_addr.set_port(p);
dst_addr.set_port(p+1);
if let Err(e) = knet::link_set_config(handle1, hostid2, 0,
knet::TransportId::Udp,
&src_addr,
Some(&dst_addr),
knet::LinkFlags::NONE) {
if let Some(os_err) = e.raw_os_error() {
if os_err != libc::EADDRINUSE {
println!("Error from link_set_config(1): {e}");
return Err(e);
}
// In use - try the next port number
} else {
return Err(Error::new(ErrorKind::Other, "Error returned from link_set_config(1) was not an os_error"));
}
} else {
// Now try the other handle
if let Err(e) = knet::link_set_config(handle2, hostid1, 0,
knet::TransportId::Udp,
&dst_addr,
Some(&src_addr),
knet::LinkFlags::NONE) {
if let Some(os_err) = e.raw_os_error() {
if os_err != libc::EADDRINUSE {
println!("Error from link_set_config(2): {e}");
return Err(e);
} else {
// In use - clear handle1 and try next pair of ports
knet::link_clear_config(handle1, hostid2, 0)?;
}
} else {
return Err(Error::new(ErrorKind::Other, "Error returned from link_set_config(1) was not an os_error"));
}
}
println!("Bound to ports {} & {}",p, p+1);
return Ok(p+2)
}
}
Err(Error::new(ErrorKind::Other, "No ports available"))
}
// Finish configuring links
fn configure_link(knet_handle: knet::Handle, our_hostid: &knet::HostId, other_hostid: &knet::HostId) -> Result<()>
{
if let Err(e) = knet::handle_enable_sock_notify(knet_handle, our_hostid.to_u16() as u64, Some(sock_notify_fn)) {
println!("Error from handle_enable_sock_notify: {e}");
return Err(e);
}
if let Err(e) = knet::link_enable_status_change_notify(knet_handle, our_hostid.to_u16() as u64, Some(link_notify_fn)) {
println!("Error from handle_enable_link_notify: {e}");
return Err(e);
}
if let Err(e) = knet::host_enable_status_change_notify(knet_handle, our_hostid.to_u16() as u64, Some(host_notify_fn)) {
println!("Error from handle_enable_host_notify: {e}");
return Err(e);
}
if let Err(e) = knet::handle_enable_filter(knet_handle, our_hostid.to_u16() as u64, Some(filter_fn)) {
println!("Error from handle_enable_filter: {e}");
return Err(e);
}
if let Err(e) = knet::handle_enable_pmtud_notify(knet_handle, our_hostid.to_u16() as u64, Some(pmtud_fn)) {
println!("Error from handle_enable_pmtud_notify: {e}");
return Err(e);
}
if let Err(e) = knet::handle_enable_onwire_ver_notify(knet_handle, our_hostid.to_u16() as u64, Some(onwire_fn)) {
println!("Error from handle_enable_onwire_ver_notify: {e}");
return Err(e);
}
match knet::handle_add_datafd(knet_handle, 0, CHANNEL) {
Ok((fd,chan)) => {
println!("Added datafd, fd={fd}, channel={chan}");
},
Err(e) => {
println!("Error from add_datafd: {e}");
return Err(e);
}
}
if let Err(e) = knet::handle_crypto_rx_clear_traffic(knet_handle, knet::RxClearTraffic::Allow) {
println!("Error from handle_crypto_rx_clear_traffic: {e}");
return Err(e);
}
if let Err(e) = knet::link_set_enable(knet_handle, other_hostid, 0, true) {
println!("Error from set_link_enable(true): {e}");
return Err(e);
}
if let Err(e) = knet::link_set_ping_timers(knet_handle, other_hostid, 0,
500, 1000, 1024) {
println!("Error from set_link_ping_timers: {e}");
return Err(e);
}
match knet::link_get_ping_timers(knet_handle, other_hostid, 0) {
Ok((a,b,c)) => {
if a != 500 || b != 1000 || c != 1024 {
println!("get_link_ping_timers returned wrong values {a}, {b},{c} (s/b 500,1000,1024)");
return Err(Error::new(ErrorKind::Other, "Error in ping timers"));
}
},
Err(e) => {
println!("Error from set_link_ping_timers: {e}");
return Err(e);
}
}
if let Err(e) = knet::handle_setfwd(knet_handle, true) {
println!("Error from setfwd(true): {e}");
return Err(e);
}
// Check status
let data_fd =
match knet::handle_get_datafd(knet_handle, CHANNEL) {
Ok(f) => {
println!("got datafd {f} for channel");
f
}
Err(e) => {
println!("Error from handle_get_datafd: {e}");
return Err(e);
}
};
match knet::handle_get_channel(knet_handle, data_fd) {
Ok(c) =>
if c != CHANNEL {
println!("handle_get_channel returned wrong channel ID: {c}, {CHANNEL}");
return Err(Error::new(ErrorKind::Other, "Error in handle_get_channel"));
}
Err(e) => {
println!("Error from handle_get_channel: {e}");
return Err(e);
}
}
match knet::link_get_enable(knet_handle, other_hostid, 0) {
Ok(b) => if !b {
println!("link not enabled (according to link_get_enable()");
},
Err(e) => {
println!("Error from link_get_enable: {e}");
return Err(e);
}
}
Ok(())
}
fn recv_stuff(handle: knet::Handle, host: knet::HostId) -> Result<()>
{
let buf = [0u8; 1024];
loop {
match knet::recv(handle, &buf, CHANNEL) {
Ok(len) => {
let recv_len = len as usize;
if recv_len == 0 {
break; // EOF??
} else {
let s = String::from_utf8(buf[0..recv_len].to_vec()).unwrap();
println!("recvd on {}: {} {:?} {} ", host, recv_len, &buf[0..recv_len], s);
if s == *"QUIT" {
println!("got QUIT on {host}, exitting");
break;
}
}
}
Err(e) => {
if e.kind() == ErrorKind::WouldBlock {
thread::sleep(get_scaled_tmo(100));
} else {
println!("recv failed: {e}");
return Err(e);
}
}
}
}
Ok(())
}
fn close_handle(handle: knet::Handle, remnode: u16) -> Result<()>
{
let other_hostid = knet::HostId::new(remnode);
if let Err(e) = knet::handle_setfwd(handle, false) {
println!("Error from setfwd 1 (false): {e}");
return Err(e);
}
let data_fd =
match knet::handle_get_datafd(handle, CHANNEL) {
Ok(f) => {
println!("got datafd {f} for channel");
f
}
Err(e) => {
println!("Error from handle_get_datafd: {e}");
return Err(e);
}
};
if let Err(e) = knet::handle_remove_datafd(handle, data_fd) {
println!("Error from handle_remove_datafd: {e}");
return Err(e);
}
if let Err(e) = knet::link_set_enable(handle, &other_hostid, 0, false) {
println!("Error from set_link_enable(false): {e}");
return Err(e);
}
if let Err(e) = knet::link_clear_config(handle, &other_hostid, 0) {
println!("clear config failed: {e}");
return Err(e);
}
if let Err(e) = knet::host_remove(handle, &other_hostid) {
println!("host remove failed: {e}");
return Err(e);
}
if let Err(e) = knet::handle_free(handle) {
println!("handle_free failed: {e}");
return Err(e);
}
Ok(())
}
fn set_compress(handle: knet::Handle) -> Result<()>
{
let compress_config = knet::CompressConfig {
compress_model: "zlib".to_string(),
compress_threshold : 10,
compress_level: 1,
};
if let Err(e) = knet::handle_compress(handle, &compress_config) {
println!("Error from handle_compress: {e}");
Err(e)
} else {
Ok(())
}
}
fn set_crypto(handle: knet::Handle) -> Result<()>
{
let private_key = [55u8; 2048];
// Add some crypto
let crypto_config = knet::CryptoConfig {
crypto_model: "openssl".to_string(),
crypto_cipher_type: "aes256".to_string(),
crypto_hash_type: "sha256".to_string(),
private_key: &private_key,
};
if let Err(e) = knet::handle_crypto_set_config(handle, &crypto_config, 1) {
println!("Error from handle_crypto_set_config: {e}");
return Err(e);
}
if let Err(e) = knet::handle_crypto_use_config(handle, 1) {
println!("Error from handle_crypto_use_config: {e}");
return Err(e);
}
if let Err(e) = knet::handle_crypto_rx_clear_traffic(handle, knet::RxClearTraffic::Disallow) {
println!("Error from handle_crypto_rx_clear_traffic: {e}");
return Err(e);
}
Ok(())
}
fn send_messages(handle: knet::Handle, send_quit: bool) -> Result<()>
{
let mut buf : [u8; 20] = [b'0'; 20];
for i in 0..10 {
buf[i as usize + 1] = i + b'0';
match knet::send(handle, &buf, CHANNEL) {
Ok(len) => {
if len as usize != buf.len() {
println!("sent {} bytes instead of {}", len, buf.len());
}
},
Err(e) => {
println!("send failed: {e}");
return Err(e);
}
}
}
let s = String::from("SYNC TEST").into_bytes();
if let Err(e) = knet::send_sync(handle, &s, CHANNEL) {
println!("send_sync failed: {e}");
return Err(e);
}
if send_quit {
// Sleep to allow messages to calm down before we tell the RX thread to quit
thread::sleep(get_scaled_tmo(3000));
let b = String::from("QUIT").into_bytes();
match knet::send(handle, &b, CHANNEL) {
Ok(len) => {
if len as usize != b.len() {
println!("sent {} bytes instead of {}", len, b.len());
}
},
Err(e) => {
println!("send failed: {e}");
return Err(e);
}
}
}
Ok(())
}
fn test_link_host_list(handle: knet::Handle) -> Result<()>
{
match knet::host_get_host_list(handle) {
Ok(hosts) => {
for i in &hosts {
print!("host {i}: links: ");
match knet::link_get_link_list(handle, i) {
Ok(ll) => {
for j in ll {
print!(" {j}");
}
},
Err(e) => {
println!("link_get_link_list failed: {e}");
return Err(e);
}
}
println!();
}
}
Err(e) => {
println!("link_get_host_list failed: {e}");
return Err(e);
}
}
Ok(())
}
// Try some metadata calls
fn test_metadata_calls(handle: knet::Handle, host: &knet::HostId) -> Result<()>
{
if let Err(e) = knet::handle_set_threads_timer_res(handle, 190000) {
println!("knet_handle_set_threads_timer_res failed: {e:?}");
return Err(e);
}
match knet::handle_get_threads_timer_res(handle) {
Ok(v) => {
if v != 190000 {
println!("knet_handle_get_threads_timer_res returned wrong value {v}");
}
},
Err(e) => {
println!("knet_handle_set_threads_timer_res failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::handle_pmtud_set(handle, 1000) {
println!("knet_handle_pmtud_set failed: {e:?}");
return Err(e);
}
match knet::handle_pmtud_get(handle) {
Ok(v) => {
if v != 1000 {
println!("knet_handle_pmtud_get returned wrong value {v} (ALLOWED)");
// Don't fail on this, it might not have been set yet
}
},
Err(e) => {
println!("knet_handle_pmtud_get failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::handle_pmtud_setfreq(handle, 1000) {
println!("knet_handle_pmtud_setfreq failed: {e:?}");
return Err(e);
}
match knet::handle_pmtud_getfreq(handle) {
Ok(v) => {
if v != 1000 {
println!("knet_handle_pmtud_getfreq returned wrong value {v}");
}
},
Err(e) => {
println!("knet_handle_pmtud_getfreq failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::handle_set_transport_reconnect_interval(handle, 100) {
println!("knet_handle_set_transport_reconnect_interval failed: {e:?}");
return Err(e);
}
match knet::handle_get_transport_reconnect_interval(handle) {
Ok(v) => {
if v != 100 {
println!("knet_handle_get_transport_reconnect_interval {v}");
}
},
Err(e) => {
println!("knet_handle_get_transport_reconnect_interval failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::link_set_pong_count(handle, host, 0, 4) {
println!("knet_link_set_pong_count failed: {e:?}");
return Err(e);
}
match knet::link_get_pong_count(handle, host, 0) {
Ok(v) => {
if v != 4 {
println!("knet_link_get_pong_count returned wrong value {v}");
}
},
Err(e) => {
println!("knet_link_get_pong_count failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::host_set_policy(handle, host, knet::LinkPolicy::Active) {
println!("knet_host_set_policy failed: {e:?}");
return Err(e);
}
match knet::host_get_policy(handle, host) {
Ok(v) => {
if v != knet::LinkPolicy::Active {
println!("knet_host_get_policy returned wrong value {v}");
}
},
Err(e) => {
println!("knet_host_get_policy failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::link_set_priority(handle, host, 0, 5) {
println!("knet_link_set_priority failed: {e:?}");
return Err(e);
}
match knet::link_get_priority(handle, host, 0) {
Ok(v) => {
if v != 5 {
println!("knet_link_get_priority returned wrong value {v}");
}
},
Err(e) => {
println!("knet_link_get_priority failed: {e:?}");
return Err(e);
}
}
let name = match knet::host_get_name_by_host_id(handle, host) {
Ok(n) => {
println!("Returned host name is {n}");
n
},
Err(e) => {
println!("knet_host_get_name_by_host_id failed: {e:?}");
return Err(e);
}
};
match knet::host_get_id_by_host_name(handle, &name) {
Ok(n) => {
println!("Returned host id is {n}");
if n != *host {
println!("Returned host id is not 2");
return Err(Error::new(ErrorKind::Other, "Error in get_id_by_host_name"));
}
},
Err(e) => {
println!("knet_host_get_id_by_host_name failed: {e:?}");
return Err(e);
}
}
match knet::link_get_config(handle, host, 0) {
Ok((t, s, d, _f)) => {
println!("Got link config: {}, {:?}, {:?}", t.to_string(),s,d);
},
Err(e) => {
println!("knet_link_get_config failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::handle_set_host_defrag_bufs(handle, 4, 32, 25, knet::DefragReclaimPolicy::Absolute) {
println!("handle_config_set_host_defrag_bufs failed: {e:?}");
return Err(e);
}
match knet::handle_get_host_defrag_bufs(handle) {
Ok((min, max, shrink, policy)) => {
if min != 4 || max != 32 ||
shrink != 25 || policy != knet::DefragReclaimPolicy::Absolute {
println!("handle_config_get_host_defrag_bufs returned bad values");
println!("Got {min},{max},{shrink},{policy}. expected 4,32,2,Absolute");
} else {
println!("Defrag params correct: {min},{max},{shrink},{policy}");
}
}
Err(e) => {
println!("handle_config_get_host_defrag_bufs failed: {e:?}");
return Err(e);
}
}
// Can't set this to anything different
if let Err(e) = knet::handle_set_onwire_ver(handle, 1) {
println!("knet_link_set_onwire_ver failed: {e:?}");
return Err(e);
}
match knet::handle_get_onwire_ver(handle, host) {
Ok((min, max, ver)) => {
println!("get_onwire_ver: Got onwire ver: {min}/{max}/{ver}");
},
Err(e) => {
println!("knet_link_get_onwire_ver failed: {e:?}");
return Err(e);
}
}
// Logging
match knet::log_get_subsystem_name(3) {
Ok(n) => println!("subsystem name for 3 is {n}"),
Err(e) => {
println!("knet_log_get_subsystem_name failed: {e:?}");
return Err(e);
}
}
match knet::log_get_subsystem_id("TX") {
Ok(n) => println!("subsystem ID for TX is {n}"),
Err(e) => {
println!("knet_log_get_subsystem_id failed: {e:?}");
return Err(e);
}
}
match knet::log_get_loglevel_id("DEBUG") {
Ok(n) => println!("loglevel ID for DEBUG is {n}"),
Err(e) => {
println!("knet_log_get_loglevel_id failed: {e:?}");
return Err(e);
}
}
+ match knet::log_get_loglevel_id("TRACE") {
+ Ok(n) => println!("loglevel ID for TRACE is {n}"),
+ Err(e) => {
+ println!("knet_log_get_loglevel_id (Trace) failed: {e:?}");
+ return Err(e);
+ }
+ }
+
match knet::log_get_loglevel_name(1) {
Ok(n) => println!("loglevel name for 1 is {n}"),
Err(e) => {
println!("knet_log_get_loglevel_name failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::log_set_loglevel(handle, knet::SubSystem::Handle , knet::LogLevel::Debug) {
println!("knet_log_set_loglevel failed: {e:?}");
return Err(e);
}
match knet::log_get_loglevel(handle, knet::SubSystem::Handle) {
Ok(n) => println!("loglevel for Handle is {n}"),
Err(e) => {
println!("knet_log_get_loglevel failed: {e:?}");
return Err(e);
}
}
Ok(())
}
fn test_acl(handle: knet::Handle, host: &knet::HostId, low_port: u16) -> Result<()>
{
if let Err(e) = knet::handle_enable_access_lists(handle, true) {
println!("Error from handle_enable_access_lists: {e:?}");
return Err(e);
}
// Dynamic link for testing ACL APIs (it never gets used)
if let Err(e) = setup_dynamic_link(handle, host, 1, low_port) {
println!("Error from link_set_config (dynamic): {e}");
return Err(e);
}
// These ACLs are nonsense on stilts
if let Err(e) = knet::link_add_acl(handle, host, 1,
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8003_u16),
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8003_u16),
knet::AclCheckType::Address, knet::AclAcceptReject::Reject) {
println!("Error from link_add_acl: {e:?}");
return Err(e);
}
if let Err(e) = knet::link_insert_acl(handle, host, 1,
0,
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8004_u16),
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 2)), 8004_u16),
knet::AclCheckType::Address, knet::AclAcceptReject::Reject) {
println!("Error from link_add_acl: {e:?}");
return Err(e);
}
if let Err(e) = knet::link_rm_acl(handle, host, 1,
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8003_u16),
&SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8003_u16),
knet::AclCheckType::Address, knet::AclAcceptReject::Reject) {
println!("Error from link_rm_acl: {e:?}");
return Err(e);
}
if let Err(e) = knet::link_clear_acl(handle, host, 1) {
println!("Error from link_clear_acl: {e:?}");
return Err(e);
}
// Get rid of this link before it messes things up
if let Err(e) = knet::link_clear_config(handle, host, 1) {
println!("clear config (dynamic) failed: {e}");
return Err(e);
}
if let Err(e) = knet::handle_enable_access_lists(handle, false) {
println!("Error from handle_enable_access_lists: {e:?}");
return Err(e);
}
Ok(())
}
fn main() -> Result<()>
{
// Start with some non-handle information
match knet::get_crypto_list() {
Ok(l) => {
print!("Crypto models:");
for i in &l {
print!(" {}", i.name);
}
println!();
}
Err(e) => {
println!("link_get_crypto_list failed: {e:?}");
return Err(e);
}
}
match knet::get_compress_list() {
Ok(l) => {
print!("Compress models:");
for i in &l {
print!(" {}", i.name);
}
println!();
}
Err(e) => {
println!("link_get_compress_list failed: {e:?}");
return Err(e);
}
}
match knet::get_transport_list() {
Ok(l) => {
print!("Transports:");
for i in &l {
print!(" {}", i.name);
}
println!();
}
Err(e) => {
println!("link_get_transport_list failed: {e:?}");
return Err(e);
}
}
let host1 = knet::HostId::new(1);
let host2 = knet::HostId::new(2);
// Now test traffic
let handle1 = setup_node(&host1, &host2, "host2")?;
let handle2 = setup_node(&host2, &host1, "host1")?;
let low_port = setup_links(handle1, &host1, handle2, &host2)?;
configure_link(handle1, &host1, &host2)?;
configure_link(handle2, &host2, &host1)?;
// Copy stuff for the threads
let handle1_clone = handle1;
let handle2_clone = handle2;
let host1_clone = host1;
let host2_clone = host2;
// Wait for links to start
thread::sleep(get_scaled_tmo(10000));
test_link_host_list(handle1)?;
test_link_host_list(handle2)?;
// Start recv threads for each handle
let thread_handles = vec![
spawn(move || recv_stuff(handle1_clone, host1_clone)),
spawn(move || recv_stuff(handle2_clone, host2_clone))
];
send_messages(handle1, false)?;
send_messages(handle2, false)?;
thread::sleep(get_scaled_tmo(3000));
set_crypto(handle1)?;
set_crypto(handle2)?;
set_compress(handle1)?;
set_compress(handle2)?;
thread::sleep(get_scaled_tmo(3000));
send_messages(handle1, true)?;
send_messages(handle2, true)?;
test_acl(handle1, &host2, low_port)?;
// Wait for recv threads to finish
for handle in thread_handles {
if let Err(error) = handle.join() {
println!("thread join error: {error:?}");
}
}
// Try some statses
match knet::handle_get_stats(handle1) {
Ok(s) => println!("handle stats: {s}"),
Err(e) => {
println!("handle_get_stats failed: {e:?}");
return Err(e);
}
}
match knet::host_get_status(handle1, &host2) {
Ok(s) => println!("host status: {s}"),
Err(e) => {
println!("host_get_status failed: {e:?}");
return Err(e);
}
}
match knet::link_get_status(handle1, &host2, 0) {
Ok(s) => println!("link status: {s}"),
Err(e) => {
println!("link_get_status failed: {e:?}");
return Err(e);
}
}
if let Err(e) = knet::handle_clear_stats(handle1, knet::ClearStats::Handle) {
println!("handle_clear_stats failed: {e:?}");
return Err(e);
}
test_metadata_calls(handle1, &knet::HostId::new(2))?;
close_handle(handle1, 2)?;
close_handle(handle2, 1)?;
// Sleep to see if log thread dies
thread::sleep(get_scaled_tmo(3000));
Ok(())
}
diff --git a/libknet/libknet.h b/libknet/libknet.h
index a34d394e..8454406c 100644
--- a/libknet/libknet.h
+++ b/libknet/libknet.h
@@ -1,2699 +1,2700 @@
/*
* Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
* Federico Simoncelli <fsimon@kronosnet.org>
*
* This software licensed under LGPL-2.0+
*/
#ifndef __LIBKNET_H__
#define __LIBKNET_H__
#include <stdint.h>
#include <time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <limits.h>
/**
* @file libknet.h
* @brief kronosnet API include file
* @copyright Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
*
* Kronosnet is an advanced VPN system for High Availability applications.
*/
#define KNET_API_VER 2
/*
* libknet limits
*/
/** typedef for a knet node */
typedef uint16_t knet_node_id_t;
/*
* Maximum number of hosts
*/
#define KNET_MAX_HOST 65536
/*
* Maximum number of links between 2 hosts
*/
#define KNET_MAX_LINK 8
/*
* Maximum packet size that should be written to datafd
* see knet_handle_new for details
*/
#define KNET_MAX_PACKET_SIZE 65536
/*
* Buffers used for pretty logging
* host is used to store both ip addresses and hostnames
*/
#define KNET_MAX_HOST_LEN 256
#define KNET_MAX_PORT_LEN 6
/*
* Some notifications can be generated either on TX or RX
*/
#define KNET_NOTIFY_TX 0
#define KNET_NOTIFY_RX 1
/*
* Link flags
*/
/*
* Where possible, set traffic priority to high.
* On Linux this sets the TOS to INTERACTIVE (6),
* see tc-prio(8) for more infomation
*/
#define KNET_LINK_FLAG_TRAFFICHIPRIO (1ULL << 0)
/*
* Handle flags
*/
/*
* Use privileged operations during socket setup.
*/
#define KNET_HANDLE_FLAG_PRIVILEGED (1ULL << 0)
/*
* threads timer resolution (see knet_handle_set_threads_timer_res below)
*/
#define KNET_THREADS_TIMER_RES 200000
/**
* Opaque handle for this knet connection, created with knet_handle_new() and
* freed with knet_handle_free()
*/
typedef struct knet_handle *knet_handle_t;
/*
* Handle structs/API calls
*/
/**
* knet_handle_new
*
* @brief create a new instance of a knet handle
*
* host_id - Each host in a knet is identified with a unique
* ID. when creating a new handle local host_id
* must be specified (0 to UINT16_MAX are all valid).
* It is the user's responsibility to check that the value
* is unique, or bad things might happen.
*
* log_fd - Write file descriptor. If set to a value > 0, it will be used
* to write log packets from libknet to the application.
* Setting to 0 will disable logging from libknet.
* It is possible to enable logging at any given time (see logging API).
* Make sure to either read from this filedescriptor properly and/or
* mark it O_NONBLOCK, otherwise if the fd becomes full, libknet could
* block.
* It is strongly encouraged to use pipes (ex: pipe(2) or pipe2(2)) for
* logging fds due to the atomic nature of writes between fds.
* See also libknet test suite for reference and guidance.
* The caller is responsible for management of the FD. eg. knet will not
* close it when knet_handle_free(3) is called
*
* default_log_level -
* If logfd is specified, it will initialize all subsystems to log
* at default_log_level value. (see logging API)
*
* flags - bitwise OR of some of the following flags:
* KNET_HANDLE_FLAG_PRIVILEGED: use privileged operations setting up the
* communication sockets. If disabled, failure to acquire large
* enough socket buffers is ignored but logged. Inadequate buffers
* lead to poor performance.
*
* @return
* on success, a new knet_handle_t is returned.
* on failure, NULL is returned and errno is set.
* knet-specific errno values:
* ENAMETOOLONG - socket buffers couldn't be set big enough and KNET_HANDLE_FLAG_PRIVILEGED was specified
* ERANGE - buffer size readback returned unexpected type
*/
knet_handle_t knet_handle_new(knet_node_id_t host_id,
int log_fd,
uint8_t default_log_level,
uint64_t flags);
/**
* knet_handle_free
*
* @brief Destroy a knet handle, free all resources
*
* knet_h - pointer to knet_handle_t
*
* @return
* knet_handle_free returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_free(knet_handle_t knet_h);
/**
* knet_handle_set_threads_timer_res
*
* @brief Change internal thread timer resolution
*
* knet_h - pointer to knet_handle_t
*
* timeres - some threads inside knet will use usleep(timeres)
* to check if any activity has to be performed, or wait
* for the next cycle. 'timeres' (expressed in nano seconds)
* defines this interval, with a default of KNET_THREADS_TIMER_RES
* (200000).
* The lower this value is, the more often knet will perform
* those checks and allows a more (time) precise execution of
* some operations (for example ping/pong), at the cost of higher
* CPU usage.
* Accepted values:
* 0 - reset timer res to default
* 1 - 999 invalid (as it would cause 100% CPU spinning on some
* epoll operations)
* 1000 or higher - valid
*
* Unless you know exactly what you are doing, stay away from
* changing the default or seek written and notarized approval
* from the knet developer team.
*
* @return
* knet_handle_set_threads_timer_res returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_threads_timer_res(knet_handle_t knet_h,
useconds_t timeres);
/**
* knet_handle_get_threads_timer_res
*
* @brief Get internal thread timer resolutions
*
* knet_h - pointer to knet_handle_t
*
* timeres - current timer res value
*
* @return
* knet_handle_set_threads_timer_res returns
* 0 on success and timerres will contain the current value
* -1 on error and errno is set.
*/
int knet_handle_get_threads_timer_res(knet_handle_t knet_h,
useconds_t *timeres);
/**
* knet_handle_enable_sock_notify
*
* @brief Register a callback to receive socket events
*
* knet_h - pointer to knet_handle_t
*
* sock_notify_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* sock_notify_fn
* A callback function that is invoked every time
* a socket in the datafd pool will report an error (-1)
* or an end of read (0) (see socket.7).
* This function MUST NEVER block or add substantial delays.
* The callback is invoked in an internal unlocked area
* to allow calls to knet_handle_add_datafd/knet_handle_remove_datafd
* to swap/replace the bad fd.
* if both err and errno are 0, it means that the socket
* has received a 0 byte packet (EOF?).
* The callback function must either remove the fd from knet
* (by calling knet_handle_remove_fd()) or dup a new fd in its place.
* Failure to do this can cause problems.
*
* @return
* knet_handle_enable_sock_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_sock_notify(knet_handle_t knet_h,
void *sock_notify_fn_private_data,
void (*sock_notify_fn) (
void *private_data,
int datafd,
int8_t channel,
uint8_t tx_rx,
int error,
int errorno)); /* sorry! can't call it errno ;) */
#define KNET_DATAFD_MAX 32
/**
* knet_handle_add_datafd
*
* @brief Install a file descriptor for communication
*
* IMPORTANT: In order to add datafd to knet, knet_handle_enable_sock_notify
* _MUST_ be set and be able to handle both errors (-1) and
* 0 bytes read / write from the provided datafd.
* On read error (< 0) from datafd, the socket is automatically
* removed from polling to avoid spinning on dead sockets.
* It is safe to call knet_handle_remove_datafd even on sockets
* that have been removed.
*
* knet_h - pointer to knet_handle_t
*
* *datafd - read/write file descriptor.
* knet will read data here to send to the other hosts
* and will write data received from the network.
* Each data packet can be of max size KNET_MAX_PACKET_SIZE!
* Applications using knet_send/knet_recv will receive a
* proper error if the packet size is not within boundaries.
* Applications using their own functions to write to the
* datafd should NOT write more than KNET_MAX_PACKET_SIZE.
*
* Please refer to handle.c on how to set up a socketpair.
*
* datafd can be 0, and knet_handle_add_datafd will create a properly
* populated socket pair the same way as ping_test, or a value
* higher than 0. A negative number will return an error.
* On exit knet_handle_free will take care to cleanup the
* socketpair only if they have been created by knet_handle_add_datafd.
*
* It is possible to pass either sockets or normal fds.
* User provided datafd will be marked as non-blocking and close-on-exec.
*
* *channel - This value is analogous to the tag in VLAN tagging.
* A negative value will auto-allocate a channel.
* Setting a value between 0 and 31 will try to allocate that
* specific channel (unless already in use).
*
* It is possible to add up to 32 datafds but be aware that each
* one of them must have a receiving end on the other host.
*
* Example:
* hostA channel 0 will be delivered to datafd on hostB channel 0
* hostA channel 1 to hostB channel 1.
*
* Each channel must have a unique file descriptor.
*
* If your application could have 2 channels on one host and one
* channel on another host, then you can use dst_host_filter
* to manipulate channel values on TX and RX.
*
* @return
* knet_handle_add_datafd returns
* @retval 0 on success,
* *datafd will be populated with a socket if the original value was 0
* or if a specific fd was set, the value is untouched.
* *channel will be populated with a channel number if the original value
* was negative or the value is untouched if a specific channel
* was requested.
*
* @retval -1 on error and errno is set.
* *datafd and *channel are untouched or empty.
*/
int knet_handle_add_datafd(knet_handle_t knet_h, int *datafd, int8_t *channel);
/**
* knet_handle_remove_datafd
*
* @brief Remove a file descriptor from knet
*
* knet_h - pointer to knet_handle_t
*
* datafd - file descriptor to remove.
* NOTE that if the socket/fd was created by knet_handle_add_datafd,
* the socket will be closed by libknet.
*
* @return
* knet_handle_remove_datafd returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_remove_datafd(knet_handle_t knet_h, int datafd);
/**
* knet_handle_get_channel
*
* @brief Get the channel associated with a file descriptor
*
* knet_h - pointer to knet_handle_t
*
* datafd - get the channel associated to this datafd
*
* *channel - will contain the result
*
* @return
* knet_handle_get_channel returns
* @retval 0 on success
* and *channel will contain the result
* @retval -1 on error and errno is set.
* and *channel content is meaningless
*/
int knet_handle_get_channel(knet_handle_t knet_h, const int datafd, int8_t *channel);
/**
* knet_handle_get_datafd
*
* @brief Get the file descriptor associated with a channel
*
* knet_h - pointer to knet_handle_t
*
* channel - get the datafd associated to this channel
*
* *datafd - will contain the result
*
* @return
* knet_handle_get_datafd returns
* @retval 0 on success
* and *datafd will contain the results
* @retval -1 on error and errno is set.
* and *datafd content is meaningless
*/
int knet_handle_get_datafd(knet_handle_t knet_h, const int8_t channel, int *datafd);
/**
* knet_recv
*
* @brief Receive data from knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to buffer to store the received data
*
* buff_len - buffer length
*
* channel - channel number
*
* @return
* knet_recv is a commodity function to wrap iovec operations
* around a socket. It returns a call to readv(2).
*/
ssize_t knet_recv(knet_handle_t knet_h,
char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_send
*
* @brief Send data to knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to the buffer of data to send
*
* buff_len - length of data to send
*
* channel - channel number
*
* @return
* knet_send is a commodity function to wrap iovec operations
* around a socket. It returns a call to writev(2).
*/
ssize_t knet_send(knet_handle_t knet_h,
const char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_send_sync
*
* @brief Synchronously send data to knet nodes
*
* knet_h - pointer to knet_handle_t
*
* buff - pointer to the buffer of data to send
*
* buff_len - length of data to send
*
* channel - data channel to use (see knet_handle_add_datafd(3))
*
* All knet RX/TX operations are async for performance reasons.
* There are applications that might need a sync version of data
* transmission and receive errors in case of failure to deliver
* to another host.
* knet_send_sync bypasses the whole TX async layer and delivers
* data directly to the link layer, and returns errors accordingly.
* knet_send_sync sends only one packet to one host at a time.
* It does NOT support multiple destinations or multicast packets.
* Decision is still based on dst_host_filter_fn.
*
* @return
* knet_send_sync returns 0 on success and -1 on error.
* In addition to normal sendmmsg errors, knet_send_sync can fail
* due to:
*
* @retval ECANCELED - data forward is disabled
* @retval EFAULT - dst_host_filter fatal error
* @retval EINVAL - dst_host_filter did not provide dst_host_ids_entries on unicast pckts
* @retval E2BIG - dst_host_filter did return more than one dst_host_ids_entries on unicast pckts
* @retval ENOMSG - received unknown message type
* @retval EHOSTDOWN - unicast pckt cannot be delivered because dest host is not connected yet
* @retval ECHILD - crypto failed
* @retval EAGAIN - sendmmsg was unable to send all messages and there was no progress during retry
* @retval ENETDOWN - a packet filter was not installed (necessary for knet_send_sync, but not knet_send)
*/
int knet_send_sync(knet_handle_t knet_h,
const char *buff,
const size_t buff_len,
const int8_t channel);
/**
* knet_handle_enable_filter
*
* @brief install a filter to route packets
*
* knet_h - pointer to knet_handle_t
*
* dst_host_filter_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* dst_host_filter_fn -
* is a callback function that is invoked every time
* a packet hits datafd (see knet_handle_new(3)).
* the function allows users to tell libknet where the
* packet has to be delivered.
*
* const unsigned char *outdata - is a pointer to the
* current packet
* ssize_t outdata_len - length of the above data
* uint8_t tx_rx - filter is called on tx or rx
* (KNET_NOTIFY_TX, KNET_NOTIFY_RX)
* knet_node_id_t this_host_id - host_id processing the packet
* knet_node_id_t src_host_id - host_id that generated the
* packet
* knet_node_id_t *dst_host_ids - array of KNET_MAX_HOST knet_node_id_t
* where to store the destinations
* size_t *dst_host_ids_entries - number of hosts to send the message
*
* dst_host_filter_fn should return
* -1 on error, packet is discarded.
* 0 packet is unicast and should be sent to dst_host_ids and there are
* dst_host_ids_entries in the buffer.
* 1 packet is broadcast/multicast and is sent all hosts.
* contents of dst_host_ids and dst_host_ids_entries are ignored.
*
* @return
* knet_handle_enable_filter returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_filter(knet_handle_t knet_h,
void *dst_host_filter_fn_private_data,
int (*dst_host_filter_fn) (
void *private_data,
const unsigned char *outdata,
ssize_t outdata_len,
uint8_t tx_rx,
knet_node_id_t this_host_id,
knet_node_id_t src_host_id,
int8_t *channel,
knet_node_id_t *dst_host_ids,
size_t *dst_host_ids_entries));
/**
* knet_handle_setfwd
*
* @brief Start packet forwarding
*
* knet_h - pointer to knet_handle_t
*
* enable - set to 1 to allow data forwarding, 0 to disable data forwarding.
*
* @return
* knet_handle_setfwd returns
* 0 on success
* -1 on error and errno is set.
*
* By default data forwarding is off and no traffic will pass through knet until
* it is set on.
*/
int knet_handle_setfwd(knet_handle_t knet_h, unsigned int enabled);
/**
* knet_handle_enable_access_lists
*
* @brief Enable or disable usage of access lists (default: off)
*
* knet_h - pointer to knet_handle_t
*
* enable - set to 1 to use access lists, 0 to disable access_lists.
*
* @return
* knet_handle_enable_access_lists returns
* 0 on success
* -1 on error and errno is set.
*
* access lists are bound to links. There are 2 types of links:
* 1) point to point, where both source and destinations are well known
* at configuration time.
* 2) open links, where only the source is known at configuration time.
*
* knet will automatically generate access lists for point to point links.
*
* For open links, knet provides 4 API calls to manipulate access lists:
* knet_link_add_acl(3), knet_link_rm_acl(3), knet_link_insert_acl(3)
* and knet_link_clear_acl(3).
* Those API calls will work exclusively on open links as they
* are of no use on point to point links.
*
* knet will not enforce any access list unless specifically enabled by
* knet_handle_enable_access_lists(3).
*
* From a security / programming perspective we recommend:
* - create the knet handle
* - enable access lists
* - configure hosts and links
* - configure access lists for open links
*/
int knet_handle_enable_access_lists(knet_handle_t knet_h, unsigned int enabled);
#define KNET_PMTUD_DEFAULT_INTERVAL 60
/**
* knet_handle_pmtud_setfreq
*
* @brief Set the interval between PMTUd scans
*
* knet_h - pointer to knet_handle_t
*
* interval - define the interval in seconds between PMTUd scans
* range from 1 to 86400 (24h)
*
* @return
* knet_handle_pmtud_setfreq returns
* 0 on success
* -1 on error and errno is set.
*
* default interval is 60.
*/
int knet_handle_pmtud_setfreq(knet_handle_t knet_h, unsigned int interval);
/**
* knet_handle_pmtud_getfreq
*
* @brief Get the interval between PMTUd scans
*
* knet_h - pointer to knet_handle_t
*
* interval - pointer where to store the current interval value
*
* @return
* knet_handle_pmtud_setfreq returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_pmtud_getfreq(knet_handle_t knet_h, unsigned int *interval);
/**
* knet_handle_enable_pmtud_notify
*
* @brief install a callback to receive PMTUd changes
*
* knet_h - pointer to knet_handle_t
*
* pmtud_notify_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* pmtud_notify_fn
* is a callback function that is invoked every time
* a path MTU size change is detected.
* The function allows libknet to notify the user
* of data MTU, that's the max value that can be send
* onwire without fragmentation. The data MTU will always
* be lower than real link MTU because it accounts for
* protocol overhead, knet packet header and (if configured)
* crypto overhead,
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_handle_enable_pmtud_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_pmtud_notify(knet_handle_t knet_h,
void *pmtud_notify_fn_private_data,
void (*pmtud_notify_fn) (
void *private_data,
unsigned int data_mtu));
/**
* knet_handle_pmtud_set
*
* @brief Set the current interface MTU
*
* knet_h - pointer to knet_handle_t
*
* iface_mtu - current interface MTU, value 0 to 65535. 0 will
* re-enable automatic MTU discovery.
* In a setup with multiple interfaces, please specify
* the lowest MTU between the selected intefaces.
* knet will automatically adjust this value for
* all headers overhead and set the correct data_mtu.
* data_mtu can be retrivied with knet_handle_pmtud_get(3)
* or applications will receive a pmtud_notify event
* if enabled via knet_handle_enable_pmtud_notify(3).
*
* @return
* knet_handle_pmtud_set returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_pmtud_set(knet_handle_t knet_h,
unsigned int iface_mtu);
/**
* knet_handle_pmtud_get
*
* @brief Get the current data MTU
*
* knet_h - pointer to knet_handle_t
*
* data_mtu - pointer where to store data_mtu
*
* @return
* knet_handle_pmtud_get returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_pmtud_get(knet_handle_t knet_h,
unsigned int *data_mtu);
#define KNET_MIN_KEY_LEN 128
#define KNET_MAX_KEY_LEN 4096
/**
* Structure passed into knet_handle_set_crypto_config() to determine
* the crypto options to use for the current communications handle
*/
struct knet_handle_crypto_cfg {
/** Model to use. nss, openssl, etc */
char crypto_model[16];
/** Cipher type name for encryption. aes 256 etc */
char crypto_cipher_type[16];
/** Hash type for digest. sha512 etc */
char crypto_hash_type[16];
/** Private key */
unsigned char private_key[KNET_MAX_KEY_LEN];
/** Length of private key */
unsigned int private_key_len;
};
/**
* knet_handle_crypto_set_config
*
* @brief set up packet cryptographic signing & encryption
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_crypto_cfg -
* pointer to a knet_handle_crypto_cfg structure
*
* crypto_model should contain the model name.
* Currently "openssl", "nss" and "gcrypt" are supported.
* Setting to "none" will disable crypto.
*
* crypto_cipher_type
* should contain the cipher algo name.
* It can be set to "none" to disable
* encryption.
* Currently supported by "nss" model:
* "aes128", "aes192" and "aes256".
* "openssl" model supports more modes and it strictly
* depends on the openssl build. See: EVP_get_cipherbyname
* openssl API call for details.
*
* crypto_hash_type
* should contain the hashing algo name.
* It can be set to "none" to disable
* hashing.
* Currently supported by "nss" model:
* "md5", "sha1", "sha256", "sha384" and "sha512".
* "openssl" model supports more modes and it strictly
* depends on the openssl build. See: EVP_get_digestbyname
* openssl API call for details.
*
* private_key will contain the private shared key.
* It has to be at least KNET_MIN_KEY_LEN long.
*
* private_key_len
* length of the provided private_key.
*
* config_num - knet supports 2 concurrent sets of crypto configurations,
* to allow runtime change of crypto config and keys.
* On RX both configurations will be used sequentially
* in an attempt to decrypt/validate a packet (when 2 are available).
* Note that this might slow down performance during a reconfiguration.
* See also knet_handle_crypto_rx_clear_traffic(3) to enable / disable
* processing of clear (unencrypted) traffic.
* For TX, the user needs to specify which configuration to use via
* knet_handle_crypto_use_config(3).
* config_num accepts 0, 1 or 2 as the value. 0 should be used when
* all crypto is being disabled.
* Calling knet_handle_crypto_set_config(3) twice with
* the same config_num will REPLACE the configuration and
* NOT activate the second key. If the configuration is currently in use
* EBUSY will be returned. See also knet_handle_crypto_use_config(3).
* The correct sequence to perform a runtime rekey / reconfiguration
* is:
* - knet_handle_crypto_set_config(..., 1). -> first time config, will use config1
* - knet_handle_crypto_use_config(..., 1). -> switch TX to config 1
* - knet_handle_crypto_set_config(..., 2). -> install config2 and use it only for RX
* - knet_handle_crypto_use_config(..., 2). -> switch TX to config 2
* - knet_handle_crypto_set_config(..., 1). -> with a "none"/"none"/"none" configuration to
* release the resources previously allocated
* The application is responsible for synchronizing calls on the nodes
* to make sure the new config is in place before switching the TX configuration.
* Failure to do so will result in knet being unable to talk to some of the nodes.
*
* Implementation notes/current limitations:
* - enabling crypto, will increase latency as packets have
* to processed.
* - enabling crypto might reduce the overall throughtput
* due to crypto data overhead.
* - private/public key encryption/hashing is not currently
* planned.
* - crypto key must be the same for all hosts in the same
* knet instance / configX.
* - it is safe to call knet_handle_crypto_set_config multiple times at runtime.
* The last config will be used.
* IMPORTANT: a call to knet_handle_crypto_set_config can fail due to:
* 1) failure to obtain locking
* 2) errors to initializing the crypto level.
* This can happen even in subsequent calls to knet_handle_crypto_set_config(3).
* A failure in crypto init will restore the previous crypto configuration if any.
*
* @return
* knet_handle_crypto_set_config returns:
* @retval 0 on success
* @retval -1 on error and errno is set.
* @retval -2 on crypto subsystem initialization error. No errno is provided at the moment (yet).
*/
int knet_handle_crypto_set_config(knet_handle_t knet_h,
struct knet_handle_crypto_cfg *knet_handle_crypto_cfg,
uint8_t config_num);
#define KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC 0
#define KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC 1
/**
* knet_handle_crypto_rx_clear_traffic
*
* @brief enable or disable RX processing of clear (unencrypted) traffic
*
* knet_h - pointer to knet_handle_t
*
* value - KNET_CRYPTO_RX_ALLOW_CLEAR_TRAFFIC or KNET_CRYPTO_RX_DISALLOW_CLEAR_TRAFFIC
*
* @return
* knet_handle_crypto_use_config returns:
* @retval 0 on success
* @retval -1 on error and errno is set.
*/
int knet_handle_crypto_rx_clear_traffic(knet_handle_t knet_h, uint8_t value);
/**
* knet_handle_crypto_use_config
*
* @brief specify crypto configuration to use for TX
*
* knet_h - pointer to knet_handle_t
*
* config_num - 1|2 use configuration 1 or 2, 0 for clear (unencrypted) traffic.
*
* @return
* knet_handle_crypto_use_config returns:
* @retval 0 on success
* @retval -1 on error and errno is set.
*/
int knet_handle_crypto_use_config(knet_handle_t knet_h,
uint8_t config_num);
#define KNET_COMPRESS_THRESHOLD 100
/**
* Structure passed into knet_handle_compress()
* to tell knet what type of compression to use
* for this communiction
*/
struct knet_handle_compress_cfg {
/** Compression library to use, bzip2 etc... */
char compress_model[16];
/** Threshold. Packets smaller than this will not be compressed */
uint32_t compress_threshold;
/** Passed into the compression library as an indication of the level of compression to apply */
int compress_level;
};
/**
* knet_handle_compress
*
* @brief Set up packet compression
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_compress_cfg -
* pointer to a knet_handle_compress_cfg structure
*
* compress_model contains the model name.
* See "compress_level" for the list of accepted values.
* Setting the value to "none" disables compression.
*
* compress_threshold
* tells the transmission thread to NOT compress
* any packets that are smaller than the value
* indicated. Default 100 bytes.
* Set to 0 to reset to the default.
* Set to 1 to compress everything.
* Max accepted value is KNET_MAX_PACKET_SIZE.
*
* compress_level is the "level" parameter for most models:
* zlib: 0 (no compression), 1 (minimal) .. 9 (max compression).
* lz4: 1 (max compression)... 9 (fastest compression).
* lz4hc: 1 (min compression) ... LZ4HC_MAX_CLEVEL (16) or LZ4HC_CLEVEL_MAX (12)
* depending on the version of lz4hc libknet was built with.
* lzma: 0 (minimal) .. 9 (max compression)
* bzip2: 1 (minimal) .. 9 (max compression)
* For lzo2 it selects the algorithm to use:
* 1 : lzo1x_1_compress (default)
* 11 : lzo1x_1_11_compress
* 12 : lzo1x_1_12_compress
* 15 : lzo1x_1_15_compress
* 999: lzo1x_999_compress
* Other values select the default algorithm.
* Please refer to the documentation of the respective
* compression library for guidance about setting this
* value.
*
* Implementation notes:
* - it is possible to enable/disable compression at any time.
* - nodes can be using a different compression algorithm at any time.
* - knet does NOT implement the compression algorithm directly. it relies
* on external libraries for this functionality. Please read
* the libraries man pages to figure out which algorithm/compression
* level is best for the data you are planning to transmit.
*
* @return
* knet_handle_compress returns
* 0 on success
* -1 on error and errno is set. EINVAL means that either the model or the
* level are not supported.
*/
int knet_handle_compress(knet_handle_t knet_h,
struct knet_handle_compress_cfg *knet_handle_compress_cfg);
/**
* Detailed stats for this knet handle as returned by knet_handle_get_stats()
*/
struct knet_handle_stats {
/** Size of the structure. set this to sizeof(struct knet_handle_stats) before calling */
size_t size;
/** Number of uncompressed packets sent */
uint64_t tx_uncompressed_packets;
/** Number of compressed packets sent */
uint64_t tx_compressed_packets;
/** Number of bytes sent (as if uncompressed, ie actual data bytes) */
uint64_t tx_compressed_original_bytes;
/** Number of bytes sent on the wire after compression */
uint64_t tx_compressed_size_bytes;
/** Average(mean) time take to compress transmitted packets */
uint64_t tx_compress_time_ave;
/** Minimum time taken to compress transmitted packets */
uint64_t tx_compress_time_min;
/** Maximum time taken to compress transmitted packets */
uint64_t tx_compress_time_max;
/** Number of times the compression attempt failed for some reason */
uint64_t tx_failed_to_compress;
/** Number of packets where the compressed size was no smaller than the original */
uint64_t tx_unable_to_compress;
/** Number of compressed packets received */
uint64_t rx_compressed_packets;
/** Number of bytes received - after decompression */
uint64_t rx_compressed_original_bytes;
/** Number of compressed bytes received before decompression */
uint64_t rx_compressed_size_bytes;
/** Average(mean) time take to decompress received packets */
uint64_t rx_compress_time_ave;
/** Minimum time take to decompress received packets */
uint64_t rx_compress_time_min;
/** Maximum time take to decompress received packets */
uint64_t rx_compress_time_max;
/** Number of times decompression failed */
uint64_t rx_failed_to_decompress;
/** Number of encrypted packets sent */
uint64_t tx_crypt_packets;
/** Cumulative byte overhead of encrypted traffic */
uint64_t tx_crypt_byte_overhead;
/** Average(mean) time take to encrypt packets in usecs */
uint64_t tx_crypt_time_ave;
/** Minimum time take to encrypto packets in usecs */
uint64_t tx_crypt_time_min;
/** Maximum time take to encrypto packets in usecs */
uint64_t tx_crypt_time_max;
/** Number of encrypted packets received */
uint64_t rx_crypt_packets;
/** Average(mean) time take to decrypt received packets */
uint64_t rx_crypt_time_ave;
/** Minimum time take to decrypt received packets in usecs */
uint64_t rx_crypt_time_min;
/** Maximum time take to decrypt received packets in usecs */
uint64_t rx_crypt_time_max;
};
/**
* knet_handle_get_stats
*
* @brief Get statistics for compression & crypto
*
* knet_h - pointer to knet_handle_t
*
* knet_handle_stats
* pointer to a knet_handle_stats structure
*
* struct_size
* size of knet_handle_stats structure to allow
* for backwards compatibility. libknet will only
* copy this much data into the stats structure
* so that older callers will not get overflowed if
* new fields are added.
*
* @return
* 0 on success
* -1 on error and errno is set.
*
*/
int knet_handle_get_stats(knet_handle_t knet_h, struct knet_handle_stats *stats, size_t struct_size);
/*
* Tell knet_handle_clear_stats whether to clear just the handle stats
* or all of them.
*/
#define KNET_CLEARSTATS_HANDLE_ONLY 1
#define KNET_CLEARSTATS_HANDLE_AND_LINK 2
/**
* knet_handle_clear_stats
*
* @brief Clear knet stats, link and/or handle
*
* knet_h - pointer to knet_handle_t
*
* clear_option - Which stats to clear, must be one of
*
* KNET_CLEARSTATS_HANDLE_ONLY or
* KNET_CLEARSTATS_HANDLE_AND_LINK
*
* @return
* 0 on success
* -1 on error and errno is set.
*
*/
int knet_handle_clear_stats(knet_handle_t knet_h, int clear_option);
/**
* Structure returned from get_crypto_list() containing
* information about the installed cryptographic systems
*/
struct knet_crypto_info {
/** Name of the crypto library/ openssl, nss,etc .. */
const char *name;
/** Properties - currently unused */
uint8_t properties;
/** Currently unused padding */
char pad[256];
};
/**
* knet_get_crypto_list
*
* @brief Get a list of supported crypto libraries
*
* crypto_list - array of struct knet_crypto_info *
* If NULL then only the number of structs is returned in crypto_list_entries
* to allow the caller to allocate sufficient space.
* libknet does not allow more than 256 crypto methods at the moment.
* it is safe to allocate 256 structs to avoid calling
* knet_get_crypto_list twice.
*
* crypto_list_entries - returns the number of structs in crypto_list
*
* @return
* knet_get_crypto_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_crypto_list(struct knet_crypto_info *crypto_list,
size_t *crypto_list_entries);
/**
* Structure returned from get_compress_list() containing
* information about the installed compression systems
*/
struct knet_compress_info {
/** Name of the compression type bzip2, lz4, etc.. */
const char *name;
/** Properties - currently unused */
uint8_t properties;
/** Currently unused padding */
char pad[256];
};
/**
* knet_get_compress_list
*
* @brief Get a list of support compression types
*
* compress_list - array of struct knet_compress_info *
* If NULL then only the number of structs is returned in compress_list_entries
* to allow the caller to allocate sufficient space.
* libknet does not allow more than 256 compress methods at the moment.
* it is safe to allocate 256 structs to avoid calling
* knet_get_compress_list twice.
*
* compress_list_entries - returns the number of structs in compress_list
*
* @return
* knet_get_compress_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_compress_list(struct knet_compress_info *compress_list,
size_t *compress_list_entries);
/**
* knet_handle_enable_onwire_ver_notify
*
* @brief install a callback to receive onwire changes
*
* knet_h - pointer to knet_handle_t
*
* onwire_ver_notify_fn_private_data
* void pointer to data that can be used to identify
* the callback.
*
* onwire_ver_notify_fn
* is a callback function that is invoked every time
* an onwire version change is detected.
* The function allows libknet to notify the user
* of onwire version changes.
* onwire_min_ver - minimum onwire version supported
* onwire_max_ver - maximum onwire version supported
* onwire_ver - currently onwire version in use
* This function MUST NEVER block or add substantial delays.
*
* NOTE: the callback function will be invoked upon install to
* immediately notify the user of the current configuration.
* During startup, it is safer to use onwire_min_ver and
* onwire_ver on subsequent calls.
*
* @return
* knet_handle_enable_onwire_ver_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_enable_onwire_ver_notify(knet_handle_t knet_h,
void *onwire_ver_notify_fn_private_data,
void (*onwire_ver_notify_fn) (
void *private_data,
uint8_t onwire_min_ver,
uint8_t onwire_max_ver,
uint8_t onwire_ver));
/**
* knet_handle_get_onwire_ver
*
* @brief get onwire protocol version information
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* onwire_min_ver - minimum onwire version supported by local node.
* this value is set to 0 for remote nodes.
*
* onwire_max_ver - maximum onwire version supported by local or
* remote node.
*
* onwire_ver - currently onwire version in use by local or
* remote node.
*
* @return
* knet_handle_get_onwire_ver returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_get_onwire_ver(knet_handle_t knet_h,
knet_node_id_t host_id,
uint8_t *onwire_min_ver,
uint8_t *onwire_max_ver,
uint8_t *onwire_ver);
/**
* knet_handle_set_onwire_ver
*
* @brief force onwire protocol version
*
* knet_h - pointer to knet_handle_t
*
* onwire_ver - onwire version to use.
* reset to 0 to allow knet to detect
* automatically the highest version.
*
* @return
* knet_handle_get_onwire_ver returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_onwire_ver(knet_handle_t knet_h,
uint8_t onwire_ver);
/*
* defrag buffer configuration defaults
*/
#define KNET_MIN_DEFRAG_BUFS_DEFAULT 32
#define KNET_MAX_DEFRAG_BUFS_DEFAULT 1024
#define KNET_SHRINK_THRESHOLD_DEFAULT 25
/**
* reclaim_policy for defrag buffers
*/
typedef enum {
RECLAIM_POLICY_AVERAGE = 0,
RECLAIM_POLICY_ABSOLUTE = 1 /* default */
} defrag_bufs_reclaim_policy_t;
/**
* knet_handle_get_host_defrag_bufs
*
* @brief Return the defrag buffers parameters for hosts
*
* knet_h - pointer to knet_handle_t
*
* min_defrag_bufs - minimum defrag buffers for each host
*
* max_defrag_bufs - maximum defrag buffers for each host
*
* shrink_threshold - define buffer usage threshold in %
* below which buffers will be shrunk.
* This is measured over the last
* usage_samples_timespan, as an
* average of usage_samples.
*
* reclaim_policy - define how % threshold is calculated.
*
* @return
* knet_handle_set_host_defrag_bufs returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_get_host_defrag_bufs(knet_handle_t knet_h,
uint16_t *min_defrag_bufs,
uint16_t *max_defrag_bufs,
uint8_t *shrink_threshold,
defrag_bufs_reclaim_policy_t *reclaim_policy);
/**
* knet_handle_set_host_defrag_bufs
*
* @brief configure defrag buffers parameters per host
*
* knet_h - pointer to knet_handle_t
*
* min_defrag_bufs - minimum defrag buffers for each host,
* This should be a power of 2.
*
* max_defrag_bufs - maximum defrag buffers for each host,
* This should be a power of 2.
*
* shrink_threshold - define buffer usage threshold in %
* below which buffers will be shrunk.
* This is measured over the last
* usage_samples_timespan, as an
* average of usage_samples.
* Only values less than or equal to 50% are accepted.
*
* reclaim_policy - define how % threshold is calculated.
*
* @note The defrag buffer parameters are global to a handle
* but the buffers themselves are allocated and shrunk per-host.
*
* @return
* knet_handle_set_host_defrag_bufs returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_host_defrag_bufs(knet_handle_t knet_h,
uint16_t min_defrag_bufs,
uint16_t max_defrag_bufs,
uint8_t shrink_threshold,
defrag_bufs_reclaim_policy_t reclaim_policy);
/*
* host structs/API calls
*/
/**
* knet_host_add
*
* @brief Add a new host ID to knet
*
* knet_h - pointer to knet_handle_t
*
* host_id - each host in a knet is identified with a unique ID
* (see also knet_handle_new(3))
*
* @return
* knet_host_add returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_add(knet_handle_t knet_h, knet_node_id_t host_id);
/**
* knet_host_remove
*
* @brief Remove a host ID from knet
*
* knet_h - pointer to knet_handle_t
*
* host_id - each host in a knet is identified with a unique ID
* (see also knet_handle_new(3))
*
* @return
* knet_host_remove returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_remove(knet_handle_t knet_h, knet_node_id_t host_id);
/**
* knet_host_set_name
*
* @brief Set the name of a knet host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* name - this name will be used for pretty logging and eventually
* search for hosts (see also knet_handle_host_get_name(2) and knet_handle_host_get_id(3)).
* Only up to KNET_MAX_HOST_LEN - 1 bytes will be accepted and
* name has to be unique for each host.
*
* @return
* knet_host_set_name returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_set_name(knet_handle_t knet_h, knet_node_id_t host_id,
const char *name);
/**
* knet_host_get_name_by_host_id
*
* @brief Get the name of a host given its ID
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* name - pointer to a preallocated buffer of at least size KNET_MAX_HOST_LEN
* where the current host name will be stored
* (as set by knet_host_set_name or default by knet_host_add)
*
* @return
* knet_host_get_name_by_host_id returns:
* 0 on success
* -1 on error and errno is set (name is left untouched)
*/
int knet_host_get_name_by_host_id(knet_handle_t knet_h, knet_node_id_t host_id,
char *name);
/**
* knet_host_get_id_by_host_name
*
* @brief Get the ID of a host given its name
*
* knet_h - pointer to knet_handle_t
*
* name - name to lookup, max len KNET_MAX_HOST_LEN
*
* host_id - where to store the result
*
* @return
* knet_host_get_id_by_host_name returns:
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_id_by_host_name(knet_handle_t knet_h, const char *name,
knet_node_id_t *host_id);
/**
* knet_host_get_host_list
*
* @brief Get a list of hosts known to knet
*
* knet_h - pointer to knet_handle_t
*
* host_ids - array of at lest KNET_MAX_HOST size
*
* host_ids_entries -
* number of entries writted in host_ids
*
* @return
* knet_host_get_host_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_host_list(knet_handle_t knet_h,
knet_node_id_t *host_ids, size_t *host_ids_entries);
/*
* define switching policies
*/
#define KNET_LINK_POLICY_PASSIVE 0
#define KNET_LINK_POLICY_ACTIVE 1
#define KNET_LINK_POLICY_RR 2
/**
* knet_host_set_policy
*
* @brief Set the switching policy for a host's links
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* policy - there are currently 3 kind of simple switching policies
* based on link configuration.
* KNET_LINK_POLICY_PASSIVE - the active link with the highest
* priority (highest number) will be used.
* if one or more active links share
* the same priority, the one with
* lowest link_id will be used.
*
* KNET_LINK_POLICY_ACTIVE - all active links will be used
* simultaneously to send traffic.
* link priority is ignored.
*
* KNET_LINK_POLICY_RR - round-robin policy, every packet
* will be send on a different active
* link.
*
* @return
* knet_host_set_policy returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_set_policy(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t policy);
/**
* knet_host_get_policy
*
* @brief Get the switching policy for a host's links
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* policy - will contain the current configured switching policy.
* Default is passive when creating a new host.
*
* @return
* knet_host_get_policy returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_policy(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t *policy);
/**
* knet_host_enable_status_change_notify
*
* @brief Install a callback to get host status change events
*
* knet_h - pointer to knet_handle_t
*
* host_status_change_notify_fn_private_data -
* void pointer to data that can be used to identify
* the callback
*
* host_status_change_notify_fn -
* is a callback function that is invoked every time
* there is a change in the host status.
* host status is identified by:
* - reachable, this host can send/receive data to/from host_id
* - remote, 0 if the host_id is connected locally or 1 if
* the there is one or more knet host(s) in between.
* NOTE: re-switching is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* - external, 0 if the host_id is configured locally or 1 if
* it has been added from remote nodes config.
* NOTE: dynamic topology is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_host_status_change_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_enable_status_change_notify(knet_handle_t knet_h,
void *host_status_change_notify_fn_private_data,
void (*host_status_change_notify_fn) (
void *private_data,
knet_node_id_t host_id,
uint8_t reachable,
uint8_t remote,
uint8_t external));
/*
* define host status structure for quick lookup
* struct is in flux as more stats will be added soon
*
* reachable host_id can be seen either directly connected
* or via another host_id
*
* remote 0 = node is connected locally, 1 is visible via
* via another host_id
*
* external 0 = node is configured/known locally,
* 1 host_id has been received via another host_id
*/
/**
* status of a knet host, returned from knet_host_get_status()
*/
struct knet_host_status {
/** Whether the host is currently reachable */
uint8_t reachable;
/** Whether the host is a remote node (not currently implemented) */
uint8_t remote;
/** Whether the host is external (not currently implemented) */
uint8_t external;
/* add host statistics */
};
/**
* knet_host_get_status
*
* @brief Get the status of a host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* status - pointer to knet_host_status struct
*
* @return
* knet_handle_pmtud_get returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_host_get_status(knet_handle_t knet_h, knet_node_id_t host_id,
struct knet_host_status *status);
/*
* link structs/API calls
*
* every host allocated/managed by knet_host_* has
* KNET_MAX_LINK structures to define the network
* paths that connect 2 hosts.
*
* Each link is identified by a link_id that has a
* values between 0 and KNET_MAX_LINK - 1.
*
* KNOWN LIMITATIONS:
*
* - let's assume the scenario where two hosts are connected
* with any number of links. link_id must match on both sides.
* If host_id 0 link_id 0 is configured to connect IP1 to IP2 and
* host_id 0 link_id 1 is configured to connect IP3 to IP4,
* host_id 1 link_id 0 _must_ connect IP2 to IP1 and likewise
* host_id 1 link_id 1 _must_ connect IP4 to IP3.
* We might be able to lift this restriction in future, by using
* other data to determine src/dst link_id, but for now, deal with it.
*/
/*
* commodity functions to convert strings to sockaddr and viceversa
*/
/**
* knet_strtoaddr
*
* @brief Convert a hostname string to an address
*
* host - IPaddr/hostname to convert
* be aware only the first IP address will be returned
* in case a hostname resolves to multiple IP
*
* port - port to connect to
*
* ss - sockaddr_storage where to store the converted data
*
* sslen - len of the sockaddr_storage
*
* @return
* knet_strtoaddr returns same error codes as getaddrinfo
*
*/
int knet_strtoaddr(const char *host, const char *port,
struct sockaddr_storage *ss, socklen_t sslen);
/**
* knet_addrtostr
*
* @brief Convert an address to a host name
*
* ss - sockaddr_storage to convert
*
* sslen - len of the sockaddr_storage
*
* host - IPaddr/hostname where to store data
* (recommended size: KNET_MAX_HOST_LEN)
*
* port - port buffer where to store data
* (recommended size: KNET_MAX_PORT_LEN)
*
* @return
* knet_strtoaddr returns same error codes as getnameinfo
*/
int knet_addrtostr(const struct sockaddr_storage *ss, socklen_t sslen,
char *addr_buf, size_t addr_buf_size,
char *port_buf, size_t port_buf_size);
#define KNET_TRANSPORT_LOOPBACK 0
#define KNET_TRANSPORT_UDP 1
#define KNET_TRANSPORT_SCTP 2
#define KNET_MAX_TRANSPORTS UINT8_MAX
/*
* The Loopback transport is only valid for connections to localhost, the host
* with the same node_id specified in knet_handle_new(). Only one link of this
* type is allowed. Data sent down a LOOPBACK link will be copied directly from
* the knet send datafd to the knet receive datafd so the application must be set
* up to take data from that socket at least as often as it is sent or deadlocks
* could occur. If used, a LOOPBACK link must be the only link configured to the
* local host.
*/
/**
* Transport information returned from knet_get_transport_list()
*/
struct knet_transport_info {
/** Transport name. UDP, SCTP, etc... */
const char *name;
/** value that can be used for knet_link_set_config() */
uint8_t id;
/** currently unused */
uint8_t properties;
/** currently unused */
char pad[256];
};
/**
* knet_get_transport_list
*
* @brief Get a list of the transports support by this build of knet
*
* transport_list - an array of struct transport_info that must be
* at least of size struct transport_info * KNET_MAX_TRANSPORTS
*
* transport_list_entries - pointer to a size_t where to store how many transports
* are available in this build of libknet.
*
* @return
* knet_get_transport_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_get_transport_list(struct knet_transport_info *transport_list,
size_t *transport_list_entries);
/**
* knet_get_transport_name_by_id
*
* @brief Get a transport name from its ID number
*
* transport - one of the KNET_TRANSPORT_xxx constants
*
* @return
* knet_get_transport_name_by_id returns:
*
* @retval pointer to the name on success or
* @retval NULL on error and errno is set.
*/
const char *knet_get_transport_name_by_id(uint8_t transport);
/**
* knet_get_transport_id_by_name
*
* @brief Get a transport ID from its name
*
* name - transport name (UDP/SCTP/etc)
*
* @return
* knet_get_transport_name_by_id returns:
*
* @retval KNET_MAX_TRANSPORTS on error and errno is set accordingly
* @retval KNET_TRANSPORT_xxx on success.
*/
uint8_t knet_get_transport_id_by_name(const char *name);
#define KNET_TRANSPORT_DEFAULT_RECONNECT_INTERVAL 1000
/**
* knet_handle_set_transport_reconnect_interval
*
* @brief Set the interval between transport attempts to reconnect a failed link
*
* knet_h - pointer to knet_handle_t
*
* msecs - milliseconds
*
* @return
* knet_handle_set_transport_reconnect_interval returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_set_transport_reconnect_interval(knet_handle_t knet_h, uint32_t msecs);
/**
* knet_handle_get_transport_reconnect_interval
*
* @brief Get the interval between transport attempts to reconnect a failed link
*
* knet_h - pointer to knet_handle_t
*
* msecs - milliseconds
*
* @return
* knet_handle_get_transport_reconnect_interval returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_handle_get_transport_reconnect_interval(knet_handle_t knet_h, uint32_t *msecs);
/**
* knet_link_set_config
*
* @brief Configure the link to a host
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* transport - one of the KNET_TRANSPORT_xxx constants
*
* src_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dst_addr - sockaddr_storage that can be either IPv4 or IPv6
* this can be null if we don't know the incoming
* IP address/port and the link will remain quiet
* till the node on the other end will initiate a
* connection
*
* flags - KNET_LINK_FLAG_*
*
* @return
* knet_link_set_config returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint64_t flags);
/**
* knet_link_get_config
*
* @brief Get the link configutation information
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* transport - see knet_link_set_config(3)
*
* src_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dst_addr - sockaddr_storage that can be either IPv4 or IPv6
*
* dynamic - 0 if dst_addr is static or 1 if dst_addr is dynamic.
* In case of 1, dst_addr can be NULL and it will be left
* untouched.
*
* flags - KNET_LINK_FLAG_*
*
* @return
* knet_link_get_config returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *transport,
struct sockaddr_storage *src_addr,
struct sockaddr_storage *dst_addr,
uint8_t *dynamic,
uint64_t *flags);
/**
* knet_link_clear_config
*
* @brief Clear link information and disconnect the link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* @return
* knet_link_clear_config returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id);
/*
* Access lists management for open links
* see also knet_handle_enable_access_lists(3)
*/
/**
* check_type_t
* @brief address type enum for knet access lists
*
* CHECK_TYPE_ADDRESS is the equivalent of a single entry / IP address.
* for example: 10.1.9.3
* and the entry is stored in ss1. ss2 can be NULL.
*
* CHECK_TYPE_MASK is used to configure network/netmask.
* for example: 192.168.0.0/24
* the network is stored in ss1 and the netmask in ss2.
*
* CHECK_TYPE_RANGE defines a value / range of ip addresses.
* for example: 172.16.0.1-172.16.0.10
* the start is stored in ss1 and the end in ss2.
*
* Please be aware that the above examples refer only to IP based protocols.
* Other protocols might use ss1 and ss2 in slightly different ways.
* At the moment knet only supports IP based protocol, though that might change
* in the future.
*/
typedef enum {
CHECK_TYPE_ADDRESS,
CHECK_TYPE_MASK,
CHECK_TYPE_RANGE
} check_type_t;
/**
* check_acceptreject_t
*
* @brief enum for accept/reject in knet access lists
*
* accept or reject incoming packets defined in the access list entry
*/
typedef enum {
CHECK_ACCEPT,
CHECK_REJECT
} check_acceptreject_t;
/**
* knet_link_add_acl
*
* @brief Add access list entry to an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* IMPORTANT: the order in which access lists are added is critical and it
* is left to the user to add them in the right order. knet
* will not attempt to logically sort them.
*
* For example:
* 1 - accept from 10.0.0.0/8
* 2 - reject from 10.0.0.1/32
*
* is not the same as:
*
* 1 - reject from 10.0.0.1/32
* 2 - accept from 10.0.0.0/8
*
* In the first example, rule number 2 will never match because
* packets from 10.0.0.1 will be accepted by rule number 1.
*
* @return
* knet_link_add_acl returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_add_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_insert_acl
*
* @brief Insert access list entry to an open link at given index
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* index - insert at position "index" where 0 is the first entry and -1
* appends to the current list.
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* @return
* knet_link_insert_acl returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_insert_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
int index,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_rm_acl
*
* @brief Remove access list entry from an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* ss1 / ss2 / type / acceptreject - see typedef definitions for details
*
* IMPORTANT: the data passed to this API call must match exactly that passed
* to knet_link_add_acl(3).
*
* @return
* knet_link_rm_acl returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_rm_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct sockaddr_storage *ss1,
struct sockaddr_storage *ss2,
check_type_t type, check_acceptreject_t acceptreject);
/**
* knet_link_clear_acl
*
* @brief Remove all access list entries from an open link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* @return
* knet_link_clear_acl returns
* 0 on success.
* -1 on error and errno is set.
*/
int knet_link_clear_acl(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id);
/**
* knet_link_set_enable
*
* @brief Enable traffic on a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* enabled - 0 disable the link, 1 enable the link
*
* @return
* knet_link_set_enable returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int enabled);
/**
* knet_link_get_enable
*
* @brief Find out whether a link is enabled or not
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* enabled - 0 disable the link, 1 enable the link
*
* @return
* knet_link_get_enable returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
unsigned int *enabled);
#define KNET_LINK_DEFAULT_PING_INTERVAL 1000 /* 1 second */
#define KNET_LINK_DEFAULT_PING_TIMEOUT 2000 /* 2 seconds */
#define KNET_LINK_DEFAULT_PING_PRECISION 2048 /* samples */
/**
* knet_link_set_ping_timers
*
* @brief Set the ping timers for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* interval - specify the ping interval in milliseconds.
*
* timeout - if no pong is received within this time,
* the link is declared dead, in milliseconds.
* NOTE: in future it will be possible to set timeout to 0
* for an autocalculated timeout based on interval, pong_count
* and latency. The API already accept 0 as value and it will
* return ENOSYS / -1. Once the automatic calculation feature
* will be implemented, this call will only return EINVAL
* for incorrect values.
*
* precision - how many values of latency are used to calculate
* the average link latency (see also knet_link_get_status(3))
*
* @return
* knet_link_set_ping_timers returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t interval, time_t timeout, unsigned int precision);
/**
* knet_link_get_ping_timers
*
* @brief Get the ping timers for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* interval - ping interval
*
* timeout - if no pong is received within this time,
* the link is declared dead
*
* precision - how many values of latency are used to calculate
* the average link latency (see also knet_link_get_status(3))
*
* @return
* knet_link_get_ping_timers returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
time_t *interval, time_t *timeout, unsigned int *precision);
#define KNET_LINK_DEFAULT_PONG_COUNT 5
/**
* knet_link_set_pong_count
*
* @brief Set the pong count for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* pong_count - how many valid ping/pongs before a link is marked UP.
* default: 5, value should be > 0
*
* @return
* knet_link_set_pong_count returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t pong_count);
/**
* knet_link_get_pong_count
*
* @brief Get the pong count for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* pong_count - how many valid ping/pongs before a link is marked UP.
* default: 5, value should be > 0
*
* @return
* knet_link_get_pong_count returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *pong_count);
/**
* knet_link_set_priority
*
* @brief Set the priority for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* priority - specify the switching priority for this link
* see also knet_host_set_policy
*
* @return
* knet_link_set_priority returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t priority);
/**
* knet_link_get_priority
*
* @brief Get the priority for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* priority - gather the switching priority for this link
* see also knet_host_set_policy
*
* @return
* knet_link_get_priority returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
uint8_t *priority);
/**
* knet_link_get_link_list
*
* @brief Get a list of links connecting a host
*
* knet_h - pointer to knet_handle_t
*
* link_ids - array of at lest KNET_MAX_LINK size
* with the list of configured links for a certain host.
*
* link_ids_entries -
* number of entries contained in link_ids
*
* @return
* knet_link_get_link_list returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id,
uint8_t *link_ids, size_t *link_ids_entries);
/*
* define link status structure for quick lookup
*
* src/dst_{ipaddr,port} strings are filled by
* getnameinfo(3) when configuring the link.
* if the link is dynamic (see knet_link_set_config(3))
* dst_ipaddr/port will contain ipaddr/port of the currently
* connected peer or "Unknown" if it was not possible
* to determine the ipaddr/port at runtime.
*
* enabled see also knet_link_set/get_enable.
*
* connected the link is connected to a peer and ping/pong traffic
* is flowing.
*
* dynconnected the link has dynamic ip on the other end, and
* we can see the other host is sending pings to us.
*
* pong_last if the link is down, this value tells us how long
* ago this link was active. A value of 0 means that the link
* has never been active.
*
* knet_link_stats structure that contains details statistics for the link
*/
#define MAX_LINK_EVENTS 16
/**
* Stats for a knet link
* returned from knet_link_get_status() as part of a knet_link_status structure
* link stats are 'onwire', ie they indicate the number of actual bytes/packets
* sent including overheads, not just data packets.
*/
struct knet_link_stats {
/** Number of data packets sent */
uint64_t tx_data_packets;
/** Number of data packets received */
uint64_t rx_data_packets;
/** Number of data bytes sent */
uint64_t tx_data_bytes;
/** Number of data bytes received */
uint64_t rx_data_bytes;
/** Number of ping packets sent */
uint64_t rx_ping_packets;
/** Number of ping packets received */
uint64_t tx_ping_packets;
/** Number of ping bytes sent */
uint64_t rx_ping_bytes;
/** Number of ping bytes received */
uint64_t tx_ping_bytes;
/** Number of pong packets sent */
uint64_t rx_pong_packets;
/** Number of pong packets received */
uint64_t tx_pong_packets;
/** Number of pong bytes sent */
uint64_t rx_pong_bytes;
/** Number of pong bytes received */
uint64_t tx_pong_bytes;
/** Number of pMTU packets sent */
uint64_t rx_pmtu_packets;
/** Number of pMTU packets received */
uint64_t tx_pmtu_packets;
/** Number of pMTU bytes sent */
uint64_t rx_pmtu_bytes;
/** Number of pMTU bytes received */
uint64_t tx_pmtu_bytes;
/* These are only filled in when requested
ie. they are not collected in realtime */
/** Total of all packets sent */
uint64_t tx_total_packets;
/** Total of all packets received */
uint64_t rx_total_packets;
/** Total number of bytes sent */
uint64_t tx_total_bytes;
/** Total number of bytes received */
uint64_t rx_total_bytes;
/** Total number of errors that occurred while sending */
uint64_t tx_total_errors;
/** Total number of retries that occurred while sending */
uint64_t tx_total_retries;
/** Total number of errors that occurred while sending pMTU packets */
uint32_t tx_pmtu_errors;
/** Total number of retries that occurred while sending pMTU packets */
uint32_t tx_pmtu_retries;
/** Total number of errors that occurred while sending ping packets */
uint32_t tx_ping_errors;
/** Total number of retries that occurred while sending ping packets */
uint32_t tx_ping_retries;
/** Total number of errors that occurred while sending pong packets */
uint32_t tx_pong_errors;
/** Total number of retries that occurred while sending pong packets */
uint32_t tx_pong_retries;
/** Total number of errors that occurred while sending data packets */
uint32_t tx_data_errors;
/** Total number of retries that occurred while sending data packets */
uint32_t tx_data_retries;
/** Minimum latency measured in usecs */
uint32_t latency_min;
/** Maximum latency measured in usecs */
uint32_t latency_max;
/** Average(mean) latency measured in usecs */
uint32_t latency_ave;
/** Number of samples used to calculate latency */
uint32_t latency_samples;
/** How many times the link has gone down */
uint32_t down_count;
/** How many times the link has come up */
uint32_t up_count;
/**
* A circular buffer of time_t structs collecting the history
* of up events on this link.
* The index indicates current/last event.
* it is safe to walk back the history by decreasing the index
*/
time_t last_up_times[MAX_LINK_EVENTS];
/**
* A circular buffer of time_t structs collecting the history
* of down events on this link.
* The index indicates current/last event.
* it is safe to walk back the history by decreasing the index
*/
time_t last_down_times[MAX_LINK_EVENTS];
/** Index of last element in the last_up_times[] array */
int8_t last_up_time_index;
/** Index of last element in the last_down_times[] array */
int8_t last_down_time_index;
/* Always add new stats at the end */
};
/**
* Status of a knet link as returned from knet_link_get_status()
*/
struct knet_link_status {
/** Size of the structure for ABI checking, set this to sizeof(knet_link_status) before calling knet_link_get_status() */
size_t size;
/** Local IP address as a string*/
char src_ipaddr[KNET_MAX_HOST_LEN];
/** Local IP port as a string */
char src_port[KNET_MAX_PORT_LEN];
/** Remote IP address as a string */
char dst_ipaddr[KNET_MAX_HOST_LEN];
/** Remote IP port as a string*/
char dst_port[KNET_MAX_PORT_LEN];
/** Link is configured and admin enabled for traffic */
uint8_t enabled;
/** Link is connected for data (local view) */
uint8_t connected;
/** Link has been activated by remote dynip */
uint8_t dynconnected;
/** Timestamp of the past pong received */
struct timespec pong_last;
/** Currently detected MTU on this link */
unsigned int mtu;
/**
* Contains the size of the IP protocol, knet headers and
* crypto headers (if configured). This value is filled in
* ONLY after the first PMTUd run on that given link,
* and can change if link configuration or crypto configuration
* changes at runtime.
* WARNING: in general mtu + proto_overhead might or might
* not match the output of ifconfig mtu due to crypto
* requirements to pad packets to some specific boundaries.
*/
unsigned int proto_overhead;
/** Link statistics */
struct knet_link_stats stats;
};
/**
* knet_link_get_status
*
* @brief Get the status (and statistics) for a link
*
* knet_h - pointer to knet_handle_t
*
* host_id - see knet_host_add(3)
*
* link_id - see knet_link_set_config(3)
*
* status - pointer to knet_link_status struct
*
* struct_size - max size of knet_link_status - allows library to
* add fields without ABI change. Returned structure
* will be truncated to this length and .size member
* indicates the full size.
*
* @return
* knet_link_get_status returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id,
struct knet_link_status *status, size_t struct_size);
/**
* knet_link_enable_status_change_notify
*
* @brief Install a callback to get a link status change events
*
* knet_h - pointer to knet_handle_t
*
* host_status_change_notify_fn_private_data -
* void pointer to data that can be used to identify
* the callback
*
* host_status_change_notify_fn -
* is a callback function that is invoked every time
* there is a change in a link status.
* host status is identified by:
* - connected, 0 if the link has been disconnected, 1 if the link
* is connected.
* - remote, 0 if the host_id is connected locally or 1 if
* the there is one or more knet host(s) in between.
* NOTE: re-switching is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* - external, 0 if the host_id is configured locally or 1 if
* it has been added from remote nodes config.
* NOTE: dynamic topology is NOT currently implemented,
* but this is ready for future and can avoid
* an API/ABI breakage later on.
* This function MUST NEVER block or add substantial delays.
*
* @return
* knet_host_status_change_notify returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_link_enable_status_change_notify(knet_handle_t knet_h,
void *link_status_change_notify_fn_private_data,
void (*link_status_change_notify_fn) (
void *private_data,
knet_node_id_t host_id,
uint8_t link_id,
uint8_t connected,
uint8_t remote,
uint8_t external));
/*
* logging structs/API calls
*/
/*
* libknet is composed of several subsystems. In order
* to easily distinguish log messages coming from different
* places, each subsystem has its own ID.
*
* 0-19 config/management
* 20-39 internal threads
* 40-59 transports
* 60-69 crypto implementations
*/
#define KNET_SUB_COMMON 0 /* common.c */
#define KNET_SUB_HANDLE 1 /* handle.c alloc/dealloc config changes */
#define KNET_SUB_HOST 2 /* host add/del/modify */
#define KNET_SUB_LISTENER 3 /* listeners add/del/modify... */
#define KNET_SUB_LINK 4 /* link add/del/modify */
#define KNET_SUB_TRANSPORT 5 /* Transport common */
#define KNET_SUB_CRYPTO 6 /* crypto.c config generic layer */
#define KNET_SUB_COMPRESS 7 /* compress.c config generic layer */
#define KNET_SUB_FILTER 19 /* allocated for users to log from dst_filter */
#define KNET_SUB_DSTCACHE 20 /* switching thread (destination cache handling) */
#define KNET_SUB_HEARTBEAT 21 /* heartbeat thread */
#define KNET_SUB_PMTUD 22 /* Path MTU Discovery thread */
#define KNET_SUB_TX 23 /* send to link thread */
#define KNET_SUB_RX 24 /* recv from link thread */
#define KNET_SUB_TRANSP_BASE 40 /* Base log level for transports */
#define KNET_SUB_TRANSP_LOOPBACK (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_LOOPBACK)
#define KNET_SUB_TRANSP_UDP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_UDP)
#define KNET_SUB_TRANSP_SCTP (KNET_SUB_TRANSP_BASE + KNET_TRANSPORT_SCTP)
#define KNET_SUB_NSSCRYPTO 60 /* crypto_nss.c */
#define KNET_SUB_OPENSSLCRYPTO 61 /* crypto_openssl.c */
#define KNET_SUB_GCRYPTCRYPTO 62 /* crypto_gcrypt.c */
#define KNET_SUB_ZLIBCOMP 70 /* compress_zlib.c */
#define KNET_SUB_LZ4COMP 71 /* compress_lz4.c */
#define KNET_SUB_LZ4HCCOMP 72 /* compress_lz4.c */
#define KNET_SUB_LZO2COMP 73 /* compress_lzo.c */
#define KNET_SUB_LZMACOMP 74 /* compress_lzma.c */
#define KNET_SUB_BZIP2COMP 75 /* compress_bzip2.c */
#define KNET_SUB_ZSTDCOMP 76 /* compress_zstd.c */
#define KNET_SUB_UNKNOWN UINT8_MAX - 1
#define KNET_MAX_SUBSYSTEMS UINT8_MAX
/*
* Convert between subsystem IDs and names
*/
/**
* knet_log_get_subsystem_name
*
* @brief Get a logging system name from its numeric ID
*
* @return
* returns internal name of the subsystem or "common"
*/
const char *knet_log_get_subsystem_name(uint8_t subsystem);
/**
* knet_log_get_subsystem_id
*
* @brief Get a logging system ID from its name
*
* @return
* returns internal ID of the subsystem or KNET_SUB_COMMON
*/
uint8_t knet_log_get_subsystem_id(const char *name);
/*
- * 4 log levels are enough for everybody
+ * 5 log levels are enough for everybody
*/
#define KNET_LOG_ERR 0 /* unrecoverable errors/conditions */
#define KNET_LOG_WARN 1 /* recoverable errors/conditions */
#define KNET_LOG_INFO 2 /* info, link up/down, config changes.. */
#define KNET_LOG_DEBUG 3
+#define KNET_LOG_TRACE 4
/*
* Convert between log level values and names
*/
/**
* knet_log_get_loglevel_name
*
* @brief Get a logging level name from its numeric ID
*
* @return
* returns internal name of the log level or "ERROR" for unknown values
*/
const char *knet_log_get_loglevel_name(uint8_t level);
/**
* knet_log_get_loglevel_id
*
* @brief Get a logging level ID from its name
*
* @return
* returns internal log level ID or KNET_LOG_ERR for invalid names
*/
uint8_t knet_log_get_loglevel_id(const char *name);
/*
* every log message is composed by a text message
* and message level/subsystem IDs.
* In order to make debugging easier it is possible to send those packets
* straight to stdout/stderr (see knet_bench.c stdout option).
*/
#define KNET_MAX_LOG_MSG_SIZE 254
#if KNET_MAX_LOG_MSG_SIZE > PIPE_BUF
#error KNET_MAX_LOG_MSG_SIZE cannot be bigger than PIPE_BUF for guaranteed system atomic writes
#endif
/**
* Structure of a log message sent to the logging fd
*/
struct knet_log_msg {
/** Text of the log message */
char msg[KNET_MAX_LOG_MSG_SIZE];
/** Subsystem that sent this message. KNET_SUB_* */
uint8_t subsystem;
/** Logging level of this message. KNET_LOG_* */
uint8_t msglevel;
/** Pointer to the handle generating the log message */
knet_handle_t knet_h;
};
/**
* knet_log_set_loglevel
*
* @brief Set the logging level for a subsystem
*
* knet_h - same as above
*
* subsystem - same as above
*
* level - same as above
*
* knet_log_set_loglevel allows fine control of log levels by subsystem.
* See also knet_handle_new for defaults.
*
* @return
* knet_log_set_loglevel returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t level);
/**
* knet_log_get_loglevel
*
* @brief Get the logging level for a subsystem
*
* knet_h - same as above
*
* subsystem - same as above
*
* level - same as above
*
* @return
* knet_log_get_loglevel returns
* 0 on success
* -1 on error and errno is set.
*/
int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t *level);
#endif
diff --git a/libknet/logging.c b/libknet/logging.c
index 04dba429..00836a5a 100644
--- a/libknet/logging.c
+++ b/libknet/logging.c
@@ -1,275 +1,276 @@
/*
* Copyright (C) 2010-2023 Red Hat, Inc. All rights reserved.
*
* Author: Fabio M. Di Nitto <fabbione@kronosnet.org>
*
* This software licensed under LGPL-2.0+
*/
#include "config.h"
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include "internals.h"
#include "logging.h"
#include "threads_common.h"
static struct pretty_names subsystem_names[KNET_MAX_SUBSYSTEMS] =
{
{ "common", KNET_SUB_COMMON },
{ "handle", KNET_SUB_HANDLE },
{ "host", KNET_SUB_HOST },
{ "listener", KNET_SUB_LISTENER },
{ "link", KNET_SUB_LINK },
{ "transport", KNET_SUB_TRANSPORT },
{ "crypto", KNET_SUB_CRYPTO },
{ "compress", KNET_SUB_COMPRESS },
{ "filter", KNET_SUB_FILTER },
{ "dstcache", KNET_SUB_DSTCACHE },
{ "heartbeat", KNET_SUB_HEARTBEAT },
{ "pmtud", KNET_SUB_PMTUD },
{ "tx", KNET_SUB_TX },
{ "rx", KNET_SUB_RX },
{ "loopback", KNET_SUB_TRANSP_LOOPBACK },
{ "udp", KNET_SUB_TRANSP_UDP },
{ "sctp", KNET_SUB_TRANSP_SCTP },
{ "nsscrypto", KNET_SUB_NSSCRYPTO },
{ "opensslcrypto", KNET_SUB_OPENSSLCRYPTO },
{ "gcryptcrypto", KNET_SUB_GCRYPTCRYPTO },
{ "zlibcomp", KNET_SUB_ZLIBCOMP },
{ "lz4comp", KNET_SUB_LZ4COMP },
{ "lz4hccomp", KNET_SUB_LZ4HCCOMP },
{ "lzo2comp", KNET_SUB_LZO2COMP },
{ "lzmacomp", KNET_SUB_LZMACOMP },
{ "bzip2comp", KNET_SUB_BZIP2COMP },
{ "zstdcomp", KNET_SUB_ZSTDCOMP },
{ "unknown", KNET_SUB_UNKNOWN } /* unknown MUST always be last in this array */
};
const char *knet_log_get_subsystem_name(uint8_t subsystem)
{
unsigned int i;
for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) {
if (subsystem_names[i].val == KNET_SUB_UNKNOWN) {
break;
}
if (subsystem_names[i].val == subsystem) {
errno = 0;
return subsystem_names[i].name;
}
}
return "unknown";
}
uint8_t knet_log_get_subsystem_id(const char *name)
{
unsigned int i;
for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) {
if (subsystem_names[i].val == KNET_SUB_UNKNOWN) {
break;
}
if (strcasecmp(name, subsystem_names[i].name) == 0) {
errno = 0;
return subsystem_names[i].val;
}
}
return KNET_SUB_UNKNOWN;
}
static int is_valid_subsystem(uint8_t subsystem)
{
unsigned int i;
for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) {
if ((subsystem != KNET_SUB_UNKNOWN) &&
(subsystem_names[i].val == KNET_SUB_UNKNOWN)) {
break;
}
if (subsystem_names[i].val == subsystem) {
return 0;
}
}
return -1;
}
-static struct pretty_names loglevel_names[KNET_LOG_DEBUG + 1] =
+static struct pretty_names loglevel_names[KNET_LOG_TRACE + 1] =
{
{ "ERROR", KNET_LOG_ERR },
{ "WARNING", KNET_LOG_WARN },
{ "info", KNET_LOG_INFO },
- { "debug", KNET_LOG_DEBUG }
+ { "debug", KNET_LOG_DEBUG },
+ { "trace", KNET_LOG_TRACE }
};
const char *knet_log_get_loglevel_name(uint8_t level)
{
unsigned int i;
- for (i = 0; i <= KNET_LOG_DEBUG; i++) {
+ for (i = 0; i <= KNET_LOG_TRACE; i++) {
if (loglevel_names[i].val == level) {
errno = 0;
return loglevel_names[i].name;
}
}
return "ERROR";
}
uint8_t knet_log_get_loglevel_id(const char *name)
{
unsigned int i;
- for (i = 0; i <= KNET_LOG_DEBUG; i++) {
+ for (i = 0; i <= KNET_LOG_TRACE; i++) {
if (strcasecmp(name, loglevel_names[i].name) == 0) {
errno = 0;
return loglevel_names[i].val;
}
}
return KNET_LOG_ERR;
}
int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t level)
{
int savederrno = 0;
if (!_is_valid_handle(knet_h)) {
return -1;
}
if (is_valid_subsystem(subsystem) < 0) {
errno = EINVAL;
return -1;
}
- if (level > KNET_LOG_DEBUG) {
+ if (level > KNET_LOG_TRACE) {
errno = EINVAL;
return -1;
}
savederrno = get_global_wrlock(knet_h);
if (savederrno) {
log_err(knet_h, subsystem, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
knet_h->log_levels[subsystem] = level;
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = 0;
return 0;
}
int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem,
uint8_t *level)
{
int savederrno = 0;
if (!_is_valid_handle(knet_h)) {
return -1;
}
if (is_valid_subsystem(subsystem) < 0) {
errno = EINVAL;
return -1;
}
if (!level) {
errno = EINVAL;
return -1;
}
savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock);
if (savederrno) {
log_err(knet_h, subsystem, "Unable to get write lock: %s",
strerror(savederrno));
errno = savederrno;
return -1;
}
*level = knet_h->log_levels[subsystem];
pthread_rwlock_unlock(&knet_h->global_rwlock);
errno = 0;
return 0;
}
void log_msg(knet_handle_t knet_h, uint8_t subsystem, uint8_t msglevel,
const char *fmt, ...)
{
va_list ap;
struct knet_log_msg msg;
size_t byte_cnt = 0;
int len;
int retry_loop = 0;
if ((!knet_h) ||
(subsystem == KNET_MAX_SUBSYSTEMS) ||
(msglevel > knet_h->log_levels[subsystem]))
return;
if (knet_h->logfd <= 0)
goto out;
memset(&msg, 0, sizeof(struct knet_log_msg));
msg.subsystem = subsystem;
msg.msglevel = msglevel;
msg.knet_h = knet_h;
va_start(ap, fmt);
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
vsnprintf(msg.msg, sizeof(msg.msg), fmt, ap);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
va_end(ap);
retry:
while (byte_cnt < sizeof(struct knet_log_msg)) {
len = write(knet_h->logfd, &msg, sizeof(struct knet_log_msg) - byte_cnt);
if (len <= 0) {
if (errno == EAGAIN) {
struct timeval tv;
/*
* those 3 lines are the equivalent of usleep(1)
* but usleep makes some static code analizers very
* unhappy.
*
* this version is somewhat stolen from gnulib
* nanosleep implementation
*/
tv.tv_sec = 0;
tv.tv_usec = 1;
select(0, NULL, NULL, NULL, &tv);
retry_loop++;
/*
* arbitrary amount of retries.
* tested with fun_log_bench, 10 retries was never hit
*/
if (retry_loop >= 100) {
goto out;
}
goto retry;
}
goto out;
}
byte_cnt += len;
}
out:
return;
}
diff --git a/libknet/logging.h b/libknet/logging.h
index 1e2f38f3..31a4d801 100644
--- a/libknet/logging.h
+++ b/libknet/logging.h
@@ -1,38 +1,41 @@
/*
* Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
* Federico Simoncelli <fsimon@kronosnet.org>
*
* This software licensed under LGPL-2.0+
*/
#ifndef __KNET_LOGGING_H__
#define __KNET_LOGGING_H__
#include "internals.h"
typedef void log_msg_t(knet_handle_t knet_h, uint8_t subsystem, uint8_t msglevel,
const char *fmt, ...) __attribute__((format(printf, 4, 5)));
#ifdef KNET_MODULE
#define LOG_MSG (*log_msg)
#else
#define LOG_MSG log_msg
#endif
log_msg_t LOG_MSG;
#define log_err(knet_h, subsys, fmt, args...) \
LOG_MSG(knet_h, subsys, KNET_LOG_ERR, fmt, ##args)
#define log_warn(knet_h, subsys, fmt, args...) \
LOG_MSG(knet_h, subsys, KNET_LOG_WARN, fmt, ##args)
#define log_info(knet_h, subsys, fmt, args...) \
LOG_MSG(knet_h, subsys, KNET_LOG_INFO, fmt, ##args)
#define log_debug(knet_h, subsys, fmt, args...) \
LOG_MSG(knet_h, subsys, KNET_LOG_DEBUG, fmt, ##args)
+#define log_trace(knet_h, subsys, fmt, args...) \
+ LOG_MSG(knet_h, subsys, KNET_LOG_TRACE, fmt, ##args)
+
#endif
diff --git a/libknet/tests/api_knet_log_get_loglevel_name.c b/libknet/tests/api_knet_log_get_loglevel_name.c
index 0d51d720..592fda42 100644
--- a/libknet/tests/api_knet_log_get_loglevel_name.c
+++ b/libknet/tests/api_knet_log_get_loglevel_name.c
@@ -1,47 +1,54 @@
/*
* Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
*
* This software licensed under GPL-2.0+
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libknet.h"
#include "test-common.h"
static void test(void)
{
const char *res;
printf("Testing knet_log_get_loglevel_name normal lookup\n");
res = knet_log_get_loglevel_name(KNET_LOG_DEBUG);
if (strcmp(res, "debug")) {
printf("knet_log_get_loglevel_name failed to get correct log level name. got: %s expected: debug\n",
res);
exit(FAIL);
}
+ res = knet_log_get_loglevel_name(KNET_LOG_TRACE);
+ if (strcmp(res, "trace")) {
+ printf("knet_log_get_loglevel_name failed to get correct log level name. got: %s expected: debug\n",
+ res);
+ exit(FAIL);
+ }
+
printf("Testing knet_log_get_loglevel_name bad lookup\n");
- res = knet_log_get_loglevel_name(KNET_LOG_DEBUG+1);
+ res = knet_log_get_loglevel_name(KNET_LOG_TRACE+1);
if (strcmp(res, "ERROR")) {
printf("knet_log_get_loglevel_name failed to get correct log level name. got: %s expected: ERROR\n",
res);
exit(FAIL);
}
}
int main(int argc, char *argv[])
{
test();
return PASS;
}
diff --git a/libknet/tests/api_knet_log_set_loglevel.c b/libknet/tests/api_knet_log_set_loglevel.c
index e8cc1582..5a4ba04f 100644
--- a/libknet/tests/api_knet_log_set_loglevel.c
+++ b/libknet/tests/api_knet_log_set_loglevel.c
@@ -1,64 +1,69 @@
/*
* Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
*
* Authors: Fabio M. Di Nitto <fabbione@kronosnet.org>
*
* This software licensed under GPL-2.0+
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "libknet.h"
#include "internals.h"
#include "test-common.h"
static void test(void)
{
knet_handle_t knet_h1, knet_h[2];
int res;
int logfds[2];
printf("Test knet_log_set_loglevel incorrect knet_h\n");
if ((!knet_log_set_loglevel(NULL, KNET_SUB_COMMON, KNET_LOG_DEBUG)) || (errno != EINVAL)) {
printf("knet_log_set_loglevel accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno));
exit(FAIL);
}
setup_logpipes(logfds);
knet_h1 = knet_handle_start(logfds, KNET_LOG_INFO, knet_h);
printf("Test knet_log_set_loglevel incorrect subsystem\n");
FAIL_ON_SUCCESS(knet_log_set_loglevel(knet_h1, KNET_SUB_UNKNOWN - 1, KNET_LOG_DEBUG), EINVAL);
printf("Test knet_log_set_loglevel incorrect log level\n");
- FAIL_ON_SUCCESS(knet_log_set_loglevel(knet_h1, KNET_SUB_UNKNOWN, KNET_LOG_DEBUG + 1), EINVAL);
+ FAIL_ON_SUCCESS(knet_log_set_loglevel(knet_h1, KNET_SUB_UNKNOWN, KNET_LOG_TRACE + 1), EINVAL);
printf("Test knet_log_set_loglevel with valid parameters\n");
if (knet_h1->log_levels[KNET_SUB_UNKNOWN] != KNET_LOG_INFO) {
printf("knet_handle_new did not init log_levels correctly?\n");
CLEAN_EXIT(FAIL);
}
FAIL_ON_ERR(knet_log_set_loglevel(knet_h1, KNET_SUB_UNKNOWN, KNET_LOG_DEBUG));
if (knet_h1->log_levels[KNET_SUB_UNKNOWN] != KNET_LOG_DEBUG) {
- printf("knet_log_set_loglevel did not set log level correctly\n");
+ printf("knet_log_set_loglevel did not set log level to DEBUG correctly\n");
+ CLEAN_EXIT(FAIL);
+ }
+ FAIL_ON_ERR(knet_log_set_loglevel(knet_h1, KNET_SUB_UNKNOWN, KNET_LOG_TRACE));
+ if (knet_h1->log_levels[KNET_SUB_UNKNOWN] != KNET_LOG_TRACE) {
+ printf("knet_log_set_loglevel did not set log level to TRACE correctly\n");
CLEAN_EXIT(FAIL);
}
CLEAN_EXIT(CONTINUE);
}
int main(int argc, char *argv[])
{
test();
return PASS;
}
diff --git a/libknet/transport_udp.c b/libknet/transport_udp.c
index ac74445a..bb4b800a 100644
--- a/libknet/transport_udp.c
+++ b/libknet/transport_udp.c
@@ -1,486 +1,486 @@
/*
* Copyright (C) 2016-2023 Red Hat, Inc. All rights reserved.
*
* Author: Christine Caulfield <ccaulfie@redhat.com>
*
* This software licensed under LGPL-2.0+
*/
#include "config.h"
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#if defined (IP_RECVERR) || defined (IPV6_RECVERR)
#include <linux/errqueue.h>
#endif
#include "libknet.h"
#include "compat.h"
#include "host.h"
#include "link.h"
#include "logging.h"
#include "common.h"
#include "netutils.h"
#include "transport_common.h"
#include "transport_udp.h"
#include "transports.h"
#include "threads_common.h"
typedef struct udp_handle_info {
struct qb_list_head links_list;
} udp_handle_info_t;
typedef struct udp_link_info {
struct qb_list_head list;
struct sockaddr_storage local_address;
int socket_fd;
int on_epoll;
} udp_link_info_t;
int udp_transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link)
{
int err = 0, savederrno = 0;
int sock = -1;
struct epoll_event ev;
udp_link_info_t *info;
udp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_UDP];
#if defined (IP_RECVERR) || defined (IPV6_RECVERR)
int value;
#endif
/*
* Only allocate a new link if the local address is different
*/
qb_list_for_each_entry(info, &handle_info->links_list, list) {
if (memcmp(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage)) == 0) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Re-using existing UDP socket for new link");
kn_link->outsock = info->socket_fd;
kn_link->transport_link = info;
kn_link->transport_connected = 1;
return 0;
}
}
info = malloc(sizeof(udp_link_info_t));
if (!info) {
err = -1;
goto exit_error;
}
memset(info, 0, sizeof(udp_link_info_t));
sock = socket(kn_link->src_addr.ss_family, SOCK_DGRAM, 0);
if (sock < 0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to create listener socket: %s",
strerror(savederrno));
goto exit_error;
}
if (_configure_transport_socket(knet_h, sock, &kn_link->src_addr, kn_link->flags, "UDP") < 0) {
savederrno = errno;
err = -1;
goto exit_error;
}
#ifdef IP_RECVERR
if (kn_link->src_addr.ss_family == AF_INET) {
value = 1;
if (setsockopt(sock, SOL_IP, IP_RECVERR, &value, sizeof(value)) <0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s",
strerror(savederrno));
goto exit_error;
}
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "IP_RECVERR enabled on socket: %i", sock);
}
#else
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "IP_RECVERR not available in this build/platform");
#endif
#ifdef IPV6_RECVERR
if (kn_link->src_addr.ss_family == AF_INET6) {
value = 1;
if (setsockopt(sock, SOL_IPV6, IPV6_RECVERR, &value, sizeof(value)) <0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set RECVERR on socket: %s",
strerror(savederrno));
goto exit_error;
}
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "IPV6_RECVERR enabled on socket: %i", sock);
}
#else
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "IPV6_RECVERR not available in this build/platform");
#endif
if (bind(sock, (struct sockaddr *)&kn_link->src_addr, sockaddr_len(&kn_link->src_addr))) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to bind listener socket: %s",
strerror(savederrno));
goto exit_error;
}
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = sock;
if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, sock, &ev)) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to add listener to epoll pool: %s",
strerror(savederrno));
goto exit_error;
}
info->on_epoll = 1;
if (_set_fd_tracker(knet_h, sock, KNET_TRANSPORT_UDP, 0, sockaddr_len(&kn_link->src_addr), info) < 0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s",
strerror(savederrno));
goto exit_error;
}
memmove(&info->local_address, &kn_link->src_addr, sizeof(struct sockaddr_storage));
info->socket_fd = sock;
qb_list_add(&info->list, &handle_info->links_list);
kn_link->outsock = sock;
kn_link->transport_link = info;
kn_link->transport_connected = 1;
exit_error:
if (err) {
if (info) {
if (info->on_epoll) {
epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, sock, &ev);
}
free(info);
}
if (sock >= 0) {
close(sock);
}
}
errno = savederrno;
return err;
}
int udp_transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link)
{
int err = 0, savederrno = 0;
int found = 0;
struct knet_host *host;
int link_idx;
udp_link_info_t *info = kn_link->transport_link;
struct epoll_event ev;
for (host = knet_h->host_head; host != NULL; host = host->next) {
for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) {
if (&host->link[link_idx] == kn_link)
continue;
if (host->link[link_idx].transport_link == info) {
found = 1;
break;
}
}
}
if (found) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "UDP socket %d still in use", info->socket_fd);
savederrno = EBUSY;
err = -1;
goto exit_error;
}
if (info->on_epoll) {
memset(&ev, 0, sizeof(struct epoll_event));
ev.events = EPOLLIN;
ev.data.fd = info->socket_fd;
if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_DEL, info->socket_fd, &ev) < 0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to remove UDP socket from epoll poll: %s",
strerror(errno));
goto exit_error;
}
info->on_epoll = 0;
}
if (_set_fd_tracker(knet_h, info->socket_fd, KNET_MAX_TRANSPORTS, 0, sockaddr_len(&kn_link->src_addr), NULL) < 0) {
savederrno = errno;
err = -1;
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Unable to set fd tracker: %s",
strerror(savederrno));
goto exit_error;
}
close(info->socket_fd);
qb_list_del(&info->list);
free(kn_link->transport_link);
exit_error:
errno = savederrno;
return err;
}
int udp_transport_free(knet_handle_t knet_h)
{
udp_handle_info_t *handle_info;
if (!knet_h->transports[KNET_TRANSPORT_UDP]) {
errno = EINVAL;
return -1;
}
handle_info = knet_h->transports[KNET_TRANSPORT_UDP];
/*
* keep it here while we debug list usage and such
*/
if (!qb_list_empty(&handle_info->links_list)) {
log_err(knet_h, KNET_SUB_TRANSP_UDP, "Internal error. handle list is not empty");
return -1;
}
free(handle_info);
knet_h->transports[KNET_TRANSPORT_UDP] = NULL;
return 0;
}
int udp_transport_init(knet_handle_t knet_h)
{
udp_handle_info_t *handle_info;
if (knet_h->transports[KNET_TRANSPORT_UDP]) {
errno = EEXIST;
return -1;
}
handle_info = malloc(sizeof(udp_handle_info_t));
if (!handle_info) {
return -1;
}
memset(handle_info, 0, sizeof(udp_handle_info_t));
knet_h->transports[KNET_TRANSPORT_UDP] = handle_info;
qb_list_init(&handle_info->links_list);
return 0;
}
#if defined (IP_RECVERR) || defined (IPV6_RECVERR)
static int read_errs_from_sock(knet_handle_t knet_h, int sockfd)
{
int err = 0, savederrno = 0;
int got_err = 0;
char buffer[1024];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
struct sock_extended_err *sock_err;
struct icmphdr icmph;
struct sockaddr_storage remote;
struct sockaddr_storage *origin;
char addr_str[KNET_MAX_HOST_LEN];
char port_str[KNET_MAX_PORT_LEN];
char addr_remote_str[KNET_MAX_HOST_LEN];
char port_remote_str[KNET_MAX_PORT_LEN];
iov.iov_base = &icmph;
iov.iov_len = sizeof(icmph);
msg.msg_name = (void*)&remote;
msg.msg_namelen = sizeof(remote);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_flags = 0;
msg.msg_control = buffer;
msg.msg_controllen = sizeof(buffer);
for (;;) {
err = recvmsg(sockfd, &msg, MSG_ERRQUEUE);
savederrno = errno;
if (err < 0) {
if (!got_err) {
errno = savederrno;
return -1;
} else {
return 0;
}
}
got_err = 1;
for (cmsg = CMSG_FIRSTHDR(&msg);cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_RECVERR)) ||
((cmsg->cmsg_level == SOL_IPV6 && (cmsg->cmsg_type == IPV6_RECVERR)))) {
sock_err = (struct sock_extended_err*)(void *)CMSG_DATA(cmsg);
if (sock_err) {
switch (sock_err->ee_origin) {
case SO_EE_ORIGIN_NONE: /* no origin */
case SO_EE_ORIGIN_LOCAL: /* local source (EMSGSIZE) */
if (sock_err->ee_errno == EMSGSIZE || sock_err->ee_errno == EPERM) {
if (pthread_mutex_lock(&knet_h->kmtu_mutex) != 0) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Unable to get mutex lock");
knet_h->kernel_mtu = 0;
break;
} else {
knet_h->kernel_mtu = sock_err->ee_info;
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "detected kernel MTU: %u", knet_h->kernel_mtu);
pthread_mutex_unlock(&knet_h->kmtu_mutex);
}
force_pmtud_run(knet_h, KNET_SUB_TRANSP_UDP, 0, 0);
}
/*
* those errors are way too noisy
*/
break;
case SO_EE_ORIGIN_ICMP: /* ICMP */
case SO_EE_ORIGIN_ICMP6: /* ICMP6 */
origin = (struct sockaddr_storage *)(void *)SO_EE_OFFENDER(sock_err);
if (knet_addrtostr(origin, sizeof(*origin),
addr_str, KNET_MAX_HOST_LEN,
port_str, KNET_MAX_PORT_LEN) < 0) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from unknown source: %s", strerror(sock_err->ee_errno));
} else {
if (knet_addrtostr(&remote, sizeof(remote),
addr_remote_str, KNET_MAX_HOST_LEN,
port_remote_str, KNET_MAX_PORT_LEN) < 0) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from %s: %s destination unknown", addr_str, strerror(sock_err->ee_errno));
} else {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Received ICMP error from %s: %s %s", addr_str, strerror(sock_err->ee_errno), addr_remote_str);
if ((sock_err->ee_errno == ECONNREFUSED) || /* knet is not running on the other node */
(sock_err->ee_errno == ECONNABORTED) || /* local kernel closed the socket */
(sock_err->ee_errno == ENONET) || /* network does not exist */
(sock_err->ee_errno == ENETUNREACH) || /* network unreachable */
(sock_err->ee_errno == EHOSTUNREACH) || /* host unreachable */
(sock_err->ee_errno == EHOSTDOWN) || /* host down (from kernel/net/ipv4/icmp.c */
(sock_err->ee_errno == ENETDOWN)) { /* network down */
struct knet_host *host = NULL;
struct knet_link *kn_link = NULL;
int link_idx, found = 0;
for (host = knet_h->host_head; host != NULL; host = host->next) {
for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) {
kn_link = &host->link[link_idx];
if (kn_link->outsock == sockfd) {
if (!cmpaddr(&remote, &kn_link->dst_addr)) {
found = 1;
break;
}
}
}
if (found) {
break;
}
}
if ((host) && (kn_link) &&
(kn_link->status.connected)) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Setting down host %u link %i", host->host_id, kn_link->link_id);
/*
* setting transport_connected = 0 will trigger
* thread_heartbeat link_down process.
*
* the process terminates calling into transport_link_down
* below that will set transport_connected = 1
*/
kn_link->transport_connected = 0;
}
}
}
}
break;
}
} else {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "No data in MSG_ERRQUEUE");
}
}
}
}
}
#else
static int read_errs_from_sock(knet_handle_t knet_h, int sockfd)
{
return 0;
}
#endif
transport_sock_error_t udp_transport_rx_sock_error(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno)
{
if (recv_errno == EAGAIN) {
read_errs_from_sock(knet_h, sockfd);
}
return KNET_TRANSPORT_SOCK_ERROR_IGNORE;
}
transport_sock_error_t udp_transport_tx_sock_error(knet_handle_t knet_h, int sockfd, int subsys, int recv_err, int recv_errno)
{
if (recv_err < 0) {
- log_debug(knet_h, KNET_SUB_TRANSP_UDP, "tx_sock_error, subsys=%d, recv_err=%d, recv_errno=%d", subsys, recv_err, recv_errno);
+ log_trace(knet_h, KNET_SUB_TRANSP_UDP, "tx_sock_error, subsys=%d, recv_err=%d, recv_errno=%d", subsys, recv_err, recv_errno);
if ((recv_errno == EMSGSIZE) || ((recv_errno == EPERM) && ((subsys == KNET_SUB_TX) || (subsys == KNET_SUB_PMTUD)))) {
read_errs_from_sock(knet_h, sockfd);
return KNET_TRANSPORT_SOCK_ERROR_IGNORE;
}
if ((recv_errno == EINVAL) || (recv_errno == EPERM) ||
(recv_errno == ENETUNREACH) || (recv_errno == ENETDOWN) ||
(recv_errno == EHOSTUNREACH)) {
#ifdef DEBUG
if ((recv_errno == ENETUNREACH) || (recv_errno == ENETDOWN)) {
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Sock: %d is unreachable.", sockfd);
}
#endif
return KNET_TRANSPORT_SOCK_ERROR_INTERNAL;
}
if ((recv_errno == ENOBUFS) || (recv_errno == EAGAIN)) {
#ifdef DEBUG
log_debug(knet_h, KNET_SUB_TRANSP_UDP, "Sock: %d is overloaded. Slowing TX down", sockfd);
#endif
usleep(knet_h->threads_timer_res / 16);
} else {
read_errs_from_sock(knet_h, sockfd);
}
return KNET_TRANSPORT_SOCK_ERROR_RETRY;
}
return KNET_TRANSPORT_SOCK_ERROR_IGNORE;
}
transport_rx_isdata_t udp_transport_rx_is_data(knet_handle_t knet_h, int sockfd, struct knet_mmsghdr *msg)
{
if (msg->msg_len == 0)
return KNET_TRANSPORT_RX_NOT_DATA_CONTINUE;
return KNET_TRANSPORT_RX_IS_DATA;
}
int udp_transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link)
{
kn_link->status.dynconnected = 1;
return 0;
}
int udp_transport_link_is_down(knet_handle_t knet_h, struct knet_link *kn_link)
{
/*
* see comments about handling ICMP error messages
*/
kn_link->transport_connected = 1;
return 0;
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Feb 25, 3:36 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464879
Default Alt Text
(206 KB)

Event Timeline