diff --git a/libknet/bindings/rust/tests/src/bin/knet-test.rs b/libknet/bindings/rust/tests/src/bin/knet-test.rs index 24f15e6a..f27b3cd8 100644 --- a/libknet/bindings/rust/tests/src/bin/knet-test.rs +++ b/libknet/bindings/rust/tests/src/bin/knet-test.rs @@ -1,921 +1,921 @@ // Testing the Knet Rust APIs // // Copyright (c) 2021 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}; const CHANNEL: i8 = 1; // Dirty C function to set the plugin path for testing (only) extern { fn set_plugin_path(knet_h: knet::Handle); } // 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); match txrx { knet::TxRx::Tx => { knet::FilterDecision::Multicast } 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), + 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) { + 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) { + 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) { + 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, + 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) { + 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(time::Duration::from_millis(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); } } } // Sleep to allow messages to calm down before we remove the filter thread::sleep(time::Duration::from_millis(3000)); if let Err(e) = knet::handle_enable_filter(handle, 0, None) { println!("Error from handle_enable_filter (disable): {}", 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 { 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); } } // 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) { + 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(time::Duration::from_millis(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(time::Duration::from_millis(3000)); set_crypto(handle1)?; set_crypto(handle2)?; set_compress(handle1)?; set_compress(handle2)?; thread::sleep(time::Duration::from_millis(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(time::Duration::from_millis(3000)); Ok(()) } diff --git a/libnozzle/bindings/rust/src/nozzle_bindings.rs b/libnozzle/bindings/rust/src/nozzle_bindings.rs index f0b0e715..523e5a9f 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 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)] 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)?; + 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)?; + 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)?; + 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(ðer_addr, &mut c_mac)?; + 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)?; + 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/tests/src/bin/nozzle-test.rs b/libnozzle/bindings/rust/tests/src/bin/nozzle-test.rs index 3e84f287..e8bf37cd 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. // // 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) { + 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") { + 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") { + 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") { + 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") { + 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")?; + 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") { + 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(()) }