diff --git a/libknet/bindings/rust/build.rs.in b/libknet/bindings/rust/build.rs.in index 712fcf59..00f39b90 100644 --- a/libknet/bindings/rust/build.rs.in +++ b/libknet/bindings/rust/build.rs.in @@ -1,11 +1,11 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // fn main() { println!("cargo:rustc-link-search=native=../../"); println!("cargo:rustc-link-lib=knet"); } diff --git a/libknet/bindings/rust/src/knet_bindings.rs b/libknet/bindings/rust/src/knet_bindings.rs index 68aba8f9..6c0c6d2c 100644 --- a/libknet/bindings/rust/src/knet_bindings.rs +++ b/libknet/bindings/rust/src/knet_bindings.rs @@ -1,2547 +1,2547 @@ // libknet interface for Rust -// Copyright (c) 2021-2022 Red Hat, Inc. +// Copyright (C) 2021-2022 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> = 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) -> 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::::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) { 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::())}; if msglen < 1 { unsafe { libc::close(knet_pipe); } // EOF on pipe, handle is closed. return; } if msglen == size_of::() 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, sock_notify_private_data: u64, filter_fn: Option, filter_private_data: u64, pmtud_notify_fn: Option, pmtud_notify_private_data: u64, onwire_notify_fn: Option, onwire_notify_private_data: u64, host_status_change_notify_fn: Option, host_status_change_notify_private_data: u64, link_status_change_notify_fn: Option, 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>, default_log_level: LogLevel, flags: HandleFlags) -> Result { // 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) -> 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 { 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 { 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 { 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 { 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) -> 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 { 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 { 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 { 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) -> 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 { 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::()); (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> { 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::::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> { 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::::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) -> 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 { 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 { 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 { 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> { 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::::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 { 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 { 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) -> 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> { 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::::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, Option, 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 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 { 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 as i64, c_timeout as i64, 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 { 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 { 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> { 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::::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) -> 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, pub last_down_times: Vec, } // 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 { let mut times_vec = Vec::::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 { 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::()); (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 { 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 { 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 { 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 { 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, } 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 } } pub fn to_u8(self: &LogLevel) -> u8 { match self { LogLevel::Err => 0, LogLevel::Warn => 1, LogLevel::Info => 2, LogLevel::Debug => 3, } } } 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"), } } } /// 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 { 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/src/sys/mod.rs b/libknet/bindings/rust/src/sys/mod.rs index 7fa0cbeb..ed06a36a 100644 --- a/libknet/bindings/rust/src/sys/mod.rs +++ b/libknet/bindings/rust/src/sys/mod.rs @@ -1,10 +1,10 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // #![allow(non_camel_case_types, non_snake_case, dead_code, improper_ctypes)] pub mod libknet; diff --git a/libknet/bindings/rust/tests/build.rs.in b/libknet/bindings/rust/tests/build.rs.in index 544a3c28..165ea41d 100644 --- a/libknet/bindings/rust/tests/build.rs.in +++ b/libknet/bindings/rust/tests/build.rs.in @@ -1,30 +1,30 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // extern crate pkg_config; fn main() { // Tell the compiler to use the build-tree libs & headers for compiling println!("cargo:rustc-link-search=native=../../../.libs/"); println!("cargo:rustc-link-lib=knet"); let lib = pkg_config::probe_library("libqb").unwrap(); cc::Build::new() .file("src/bin/set_plugin_path.c") .file("@ABSTOPLEVELSRC@/libknet/tests/test-common.c") // for find_plugins_path() .flag("-Wno-unused-parameter") // Needed for test-common.c to compile cleanly .include("@ABSTOPLEVELSRC@") // for config.h .include("@ABSTOPLEVELSRC@/libknet") // for internals.h .include("@ABSTOPLEVELSRC@/libknet/tests") // for test-common.h .include("@ABSTOPLEVELBUILD@") // for config.h .include("@ABSTOPLEVELBUILD@/libknet") // for internals.h .include("@ABSTOPLEVELBUILD@/libknet/tests") // for test-common.h .includes(lib.include_paths) .compile("set_plugin_path"); } diff --git a/libknet/bindings/rust/tests/src/bin/knet-test.rs b/libknet/bindings/rust/tests/src/bin/knet-test.rs index db975274..8ff88729 100644 --- a/libknet/bindings/rust/tests/src/bin/knet-test.rs +++ b/libknet/bindings/rust/tests/src/bin/knet-test.rs @@ -1,974 +1,974 @@ // Testing the Knet Rust APIs // -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 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 {}, datafd: {}, channel: {}, {}", private_data, datafd, 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 {}, MTU:{} ", private_data, 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::FilterDecision { println!("Filter ({}) called {} to {} from {}, channel: {}", private_data, txrx, this_host_id, src_host_id, 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) { 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 { let (log_sender, log_receiver) = channel::(); 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 { 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={}, channel={}", fd, 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 {}, {},{} (s/b 500,1000,1024)", a,b,c); 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 {} for channel", f); 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 {}, exitting", host); 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 {} for channel", f); 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 {}: links: ", i); 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 {} (ALLOWED)", v); // 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 {},{},{},{}. expected 4,32,2,Absolute", min, max, shrink, policy); } 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_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/libnozzle/bindings/rust/build.rs.in b/libnozzle/bindings/rust/build.rs.in index 0ad14f77..1ab78a1a 100644 --- a/libnozzle/bindings/rust/build.rs.in +++ b/libnozzle/bindings/rust/build.rs.in @@ -1,11 +1,11 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // fn main() { println!("cargo:rustc-link-search=native=../../../.libs"); println!("cargo:rustc-link-lib=dylib=nozzle"); } diff --git a/libnozzle/bindings/rust/src/nozzle_bindings.rs b/libnozzle/bindings/rust/src/nozzle_bindings.rs index 22295027..b9185f05 100644 --- a/libnozzle/bindings/rust/src/nozzle_bindings.rs +++ b/libnozzle/bindings/rust/src/nozzle_bindings.rs @@ -1,367 +1,367 @@ // libnozzle interface for Rust -// Copyright (c) 2021-2022 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // // For the code generated by bindgen use crate::sys::libnozzle as ffi; use std::io::{Result, Error, ErrorKind}; use std::os::raw::{c_char, c_void}; use std::ptr::{null_mut}; use libc::free; use std::fmt; /// A handle into the nozzle library. Returned from [open] and needed for all other calls #[derive(Copy, Clone, PartialEq, Eq)] pub struct Handle { nozzle_handle: ffi::nozzle_t, } const IFNAMSZ: usize = 16; /// Create a new tap device on the system pub fn open(devname: &mut String, updownpath: &str) -> Result { let mut c_devname: [c_char; IFNAMSZ] = [0; IFNAMSZ]; let mut c_updownpath: [c_char; libc::PATH_MAX as usize] = [0; libc::PATH_MAX as usize]; let c_devname_size = IFNAMSZ; crate::string_to_bytes(devname, &mut c_devname)?; crate::string_to_bytes(updownpath, &mut c_updownpath)?; let res = unsafe { ffi::nozzle_open(c_devname.as_mut_ptr(), c_devname_size, c_updownpath.as_ptr()) }; if res.is_null() { Err(Error::last_os_error()) } else { let temp = crate::string_from_bytes(c_devname.as_ptr(), IFNAMSZ)?; *devname = temp; Ok(Handle{nozzle_handle: res}) } } /// Deconfigure and destroy a nozzle device pub fn close(handle: Handle) -> Result<()> { let res = unsafe { ffi::nozzle_close(handle.nozzle_handle) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// Which script to run when [run_updown] is called pub enum Action { PreUp, Up, Down, PostDown } impl Action { pub fn to_u8(self: &Action) -> u8 { match self { Action::PreUp => 0, Action::Up => 1, Action::Down => 2, Action::PostDown => 3, } } } /// Run an up/down script before/after configuring a device. See [Action] pub fn run_updown(handle: Handle, action: Action) -> Result { let c_exec_string : *mut *mut ::std::os::raw::c_char = &mut [0;0].as_mut_ptr(); let c_action = action.to_u8(); let res = unsafe { ffi::nozzle_run_updown(handle.nozzle_handle, c_action, c_exec_string) }; match res { 0 => { unsafe { // This is unsafe because we deference a raw pointer let resstring = crate::string_from_bytes(*c_exec_string as *mut ::std::os::raw::c_char, libc::PATH_MAX as usize)?; free(*c_exec_string as *mut c_void); Ok(resstring) } }, -1 => Err(Error::last_os_error()), -2 => Err(Error::new(ErrorKind::Other, "error executing shell scripts")), _ => Err(Error::new(ErrorKind::Other, "unknown error returned from nozzle_tun_updown()")), } } /// Mark nozzle device as "up" pub fn set_up(handle: Handle) -> Result<()> { let res = unsafe { ffi::nozzle_set_up(handle.nozzle_handle) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// mark nozzle device as "down" pub fn set_down(handle: Handle) -> Result<()> { let res = unsafe { ffi::nozzle_set_down(handle.nozzle_handle) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } const IPADDR_CHAR_MAX: usize = 128; const PREFIX_CHAR_MAX: usize = 4; /// Add an ip address to a nozzle device. multiple addresses can be added to one device. /// The prefix is a the number that comes after the ip address when configuring: /// eg: 192.168.0.1/24 - the prefix is "24" pub fn add_ip(handle: Handle, ipaddr: &str, prefix: &str) -> Result<()> { let mut c_ipaddr: [c_char; IPADDR_CHAR_MAX] = [0; IPADDR_CHAR_MAX]; let mut c_prefix: [c_char; PREFIX_CHAR_MAX] = [0; PREFIX_CHAR_MAX]; crate::string_to_bytes(ipaddr, &mut c_ipaddr)?; crate::string_to_bytes(prefix, &mut c_prefix)?; let res = unsafe { ffi::nozzle_add_ip(handle.nozzle_handle, c_ipaddr.as_ptr(), c_prefix.as_ptr()) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// remove an ip address from a nozzle device pub fn del_ip(handle: Handle, ipaddr: &str, prefix: &str) -> Result<()> { let mut c_ipaddr: [c_char; IPADDR_CHAR_MAX] = [0; IPADDR_CHAR_MAX]; let mut c_prefix: [c_char; PREFIX_CHAR_MAX] = [0; PREFIX_CHAR_MAX]; crate::string_to_bytes(ipaddr, &mut c_ipaddr)?; crate::string_to_bytes(prefix, &mut c_prefix)?; let res = unsafe { ffi::nozzle_del_ip(handle.nozzle_handle, c_ipaddr.as_ptr(), c_prefix.as_ptr()) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// IpV4/IpV6 in the struct returned from [get_ips] pub enum Domain { IpV4, IpV6 } impl Domain { fn new(c_dom: i32) -> Domain { match c_dom { libc::AF_INET => Domain::IpV4, libc::AF_INET6 => Domain::IpV6, _ => Domain::IpV4, } } } impl fmt::Display for Domain { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Domain::IpV4 => write!(f, "IPv4"), Domain::IpV6 => write!(f, "IPv6"), } } } /// IP address info as returned from [get_ips] pub struct Ip { pub ipaddr: String, pub prefix: String, pub domain: Domain, } impl Ip { pub fn new(c_ip: &ffi::nozzle_ip) -> Ip { Ip { ipaddr: crate::string_from_bytes_safe(c_ip.ipaddr.as_ptr(), IPADDR_CHAR_MAX), prefix: crate::string_from_bytes_safe(c_ip.prefix.as_ptr(), PREFIX_CHAR_MAX), domain: Domain::new(c_ip.domain) } } } impl fmt::Display for Ip { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f,"{} {}/{}", self.domain, self.ipaddr, self.prefix)?; Ok(()) } } /// Return a Vec of Ip adressess attached to this device pub fn get_ips(handle: Handle) -> Result> { let mut c_ips : &mut ffi::nozzle_ip = &mut ffi::nozzle_ip{ipaddr: [0;129], prefix: [0;5], domain:0, next: null_mut()}; let res = unsafe { ffi::nozzle_get_ips(handle.nozzle_handle, &mut c_ips as *mut _ as *mut *mut ffi::nozzle_ip) }; let mut ipvec = Vec::::new(); if res == 0 { let mut ips : *mut ffi::nozzle_ip = c_ips; unsafe { while !ips.is_null() { ipvec.push(Ip::new(&*ips)); ips = (*ips).next; } } Ok(ipvec) } else { Err(Error::last_os_error()) } } /// Get the MTU of the device pub fn get_mtu(handle: Handle) -> Result { let res = unsafe { ffi::nozzle_get_mtu(handle.nozzle_handle) }; if res != -1 { Ok(res) } else { Err(Error::last_os_error()) } } /// Set the MTU of the device pub fn set_mtu(handle: Handle, new_mtu: i32) -> Result<()> { let res = unsafe { ffi::nozzle_set_mtu(handle.nozzle_handle, new_mtu) }; if res != -1 { Ok(()) } else { Err(Error::last_os_error()) } } /// Reset the device's MTU back to the default pub fn reset_mtu(handle: Handle) -> Result<()> { let res = unsafe { ffi::nozzle_reset_mtu(handle.nozzle_handle) }; if res != -1 { Ok(()) } else { Err(Error::last_os_error()) } } /// Returns the MAC address of the device pub fn get_mac(handle: Handle) -> Result { let mut c_mac: *mut c_char = null_mut(); let res = unsafe { ffi::nozzle_get_mac(handle.nozzle_handle, &mut c_mac) }; if res == 0 { let mac = crate::string_from_bytes(c_mac, 24_usize)?; // Needs to be 8byte aligned unsafe { free(c_mac as *mut c_void); }// Was created with strdup( Ok(mac) } else { Err(Error::last_os_error()) } } /// Setsthe MAC address of the device pub fn set_mac(handle: Handle, ether_addr: &str) -> Result<()> { let mut c_mac: [c_char; 24_usize] = [0; 24_usize]; // Needs to be 8byte aligned crate::string_to_bytes(ether_addr, &mut c_mac)?; let res = unsafe { ffi::nozzle_set_mac(handle.nozzle_handle, c_mac.as_ptr()) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// Reset the device's MAC address to the defaut pub fn reset_mac(handle: Handle) -> Result<()> { let res = unsafe { ffi::nozzle_reset_mac(handle.nozzle_handle) }; if res == 0 { Ok(()) } else { Err(Error::last_os_error()) } } /// Find the nozzle handle of a device by giving its name pub fn get_handle_by_name(devname: &str) -> Result { let mut c_devname: [c_char; IFNAMSZ] = [0; IFNAMSZ]; crate::string_to_bytes(devname, &mut c_devname)?; let res = unsafe { ffi::nozzle_get_handle_by_name(c_devname.as_ptr()) }; if !res.is_null() { Ok(Handle{nozzle_handle:res}) } else { Err(Error::last_os_error()) } } /// Return the name of the device pub fn get_name_by_handle(handle: Handle) -> Result { let res = unsafe { ffi::nozzle_get_name_by_handle(handle.nozzle_handle) }; if !res.is_null() { crate::string_from_bytes(res, IFNAMSZ) } else { Err(Error::last_os_error()) } } /// Return a unix FD for the device pub fn get_fd(handle: Handle) -> Result { let res = unsafe { ffi::nozzle_get_fd(handle.nozzle_handle) }; if res != -1 { Ok(res) } else { Err(Error::last_os_error()) } } diff --git a/libnozzle/bindings/rust/src/sys/mod.rs b/libnozzle/bindings/rust/src/sys/mod.rs index 6e9df599..11b2bcf6 100644 --- a/libnozzle/bindings/rust/src/sys/mod.rs +++ b/libnozzle/bindings/rust/src/sys/mod.rs @@ -1,10 +1,10 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // #![allow(non_camel_case_types, non_snake_case, dead_code, improper_ctypes)] pub mod libnozzle; diff --git a/libnozzle/bindings/rust/tests/build.rs.in b/libnozzle/bindings/rust/tests/build.rs.in index c9ebc9b2..d3574709 100644 --- a/libnozzle/bindings/rust/tests/build.rs.in +++ b/libnozzle/bindings/rust/tests/build.rs.in @@ -1,13 +1,13 @@ -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // fn main() { // Tell the compiler to use the build-tree libs & headers for compiling println!("cargo:rustc-link-search=native=../../../.libs/"); println!("cargo:rustc-link-lib=nozzle"); } diff --git a/libnozzle/bindings/rust/tests/src/bin/nozzle-test.rs b/libnozzle/bindings/rust/tests/src/bin/nozzle-test.rs index e8bf37cd..43bb7327 100644 --- a/libnozzle/bindings/rust/tests/src/bin/nozzle-test.rs +++ b/libnozzle/bindings/rust/tests/src/bin/nozzle-test.rs @@ -1,245 +1,245 @@ // Testing the Nozzle Rust APIs // -// Copyright (c) 2021 Red Hat, Inc. +// Copyright (C) 2021-2022 Red Hat, Inc. // // All rights reserved. // // Author: Christine Caulfield (ccaulfi@redhat.com) // use nozzle_bindings::nozzle_bindings as nozzle; use std::io::{Result, Error, ErrorKind, BufWriter, Write}; use std::fmt::Write as fmtwrite; use std::{thread, time}; use std::fs::File; use std::fs; use tempfile::tempdir; const SKIP: i32 = 77; fn main() -> Result<()> { // We must be root if unsafe { libc::geteuid() != 0 } { std::process::exit(SKIP); } // Run in a random tmpdir so we don't clash with other instances let tmp_path = tempdir()?; let tmp_dir = match tmp_path.path().to_str() { Some(td) => td, None => { println!("Error creating temp path for running"); return Err(Error::new(ErrorKind::Other, "Error creating temp path")); } }; std::env::set_current_dir(tmp_dir)?; // Let the OS generate a tap name let mut nozzle_name = String::from(""); let handle = match nozzle::open(&mut nozzle_name, tmp_dir) { Ok(h) => { println!("Opened device {}", nozzle_name); h }, Err(e) => { println!("Error from open: {}", e); return Err(e); } }; // Get default state for checking reset_* calls later let saved_mtu = match nozzle::get_mtu(handle) { Ok(m) => m, Err(e) => { println!("Error from get_mtu: {}", e); return Err(e); } }; let saved_mac = match nozzle::get_mac(handle) { Ok(m) => m, Err(e) => { println!("Error from get_mac: {}", e); return Err(e); } }; // Play with APIs if let Err(e) = nozzle::add_ip(handle, "192.160.100.1", "24") { println!("Error from add_ip: {}", e); return Err(e); } if let Err(e) = nozzle::add_ip(handle, "192.160.100.2", "24") { println!("Error from add_ip2: {}", e); return Err(e); } if let Err(e) = nozzle::add_ip(handle, "192.160.100.3", "24") { println!("Error from add_ip3: {}", e); return Err(e); } if let Err(e) = nozzle::set_mac(handle, "AA:00:04:00:22:01") { println!("Error from set_mac: {}", e); return Err(e); } if let Err(e) = nozzle::set_mtu(handle, 157) { println!("Error from set_mtu: {}", e); return Err(e); } if let Err(e) = nozzle::set_up(handle) { println!("Error from set_up: {}", e); return Err(e); } // Create the 'up' script so we can test the run_updown() function, let up_path = std::path::Path::new("up.d"); if let Err(e) = fs::create_dir_all(up_path) { eprintln!("Error creating up.d directory: {:?}", e); return Err(e); } let mut up_filename = String::new(); if let Err(e) = write!(up_filename, "up.d/{}", nozzle_name) { eprintln!("Error making up.d filename: {:?}", e); return Err(Error::new(ErrorKind::Other, "Error making up.d filename")); } match File::create(&up_filename) { Err(e) => { println!("Cannot create up.d file {}: {}", &up_filename, e); return Err(e); } Ok(fl) => { let mut f = BufWriter::new(fl); writeln!(f, "#!/bin/sh\necho 'This is a test of an \"Up\" script'")?; } } // A grotty way to do chmod, but normally this would be distributed by the sysadmin unsafe { let up_cstring = std::ffi::CString::new(up_filename.clone()).unwrap(); libc::chmod(up_cstring.as_ptr(), 0o700); } match nozzle::run_updown(handle, nozzle::Action::Up) { Ok(s) => println!("Returned from Up script: {}", s), Err(e) => { println!("Error from run_updown: {}", e); return Err(e); } } // Tidy up after ourself - remove the up.d/tapX file fs::remove_file(&up_filename)?; fs::remove_dir("up.d")?; match nozzle::get_ips(handle) { Ok(ips) => { print!("Got IPs:"); for i in ips { print!(" {}", i); } println!(); }, Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } match nozzle::get_mtu(handle) { Ok(m) => println!("Got mtu: {}", m), Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } match nozzle::get_mac(handle) { Ok(m) => println!("Got mac: {}", m), Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } match nozzle::get_fd(handle) { Ok(f) => println!("Got FD: {}", f), Err(e) => { println!("Error from get_fd: {}", e); return Err(e); } } match nozzle::get_handle_by_name(&nozzle_name) { Ok(h) => if h != handle { return Err(Error::new(ErrorKind::Other, "get_handle_by_name returned wrong value")); } Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } match nozzle::get_name_by_handle(handle) { Ok(n) => if n != nozzle_name { println!("n: {}, nozzle_name: {}", n, nozzle_name); return Err(Error::new(ErrorKind::Other, "get_name_by_handle returned wrong name")); } Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } // Wait a little while in case user wants to check with 'ip' command thread::sleep(time::Duration::from_millis(1000)); if let Err(e) = nozzle::del_ip(handle, "192.160.100.3", "24") { println!("Error from del_ip: {}", e); return Err(e); } if let Err(e) = nozzle::reset_mtu(handle) { println!("Error from reset_mtu: {}", e); return Err(e); } match nozzle::get_mtu(handle) { Ok(m) => { if m != saved_mtu { println!("Got default MTU of {}, not {}", m, saved_mtu); } } Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } if let Err(e) = nozzle::reset_mac(handle) { println!("Error from reset_mac: {}", e); return Err(e); } match nozzle::get_mac(handle) { Ok(m) => { if m != saved_mac { println!("Got default MAC of {}, not {}", m, saved_mac); } } Err(e) => { println!("Error from get_ips: {}", e); return Err(e); } } if let Err(e) = nozzle::set_down(handle){ println!("Error from set_down: {}", e); return Err(e); } if let Err(e) = nozzle::close(handle) { println!("Error from open: {}", e); return Err(e); } Ok(()) }