diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index c7c18e277..ffa4102a8 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -255,11 +255,7 @@ dependencies = [ "nix 0.17.0", "protobuf", "protocols", - "rustjail", - "scan_fmt", "slog", - "slog-async", - "slog-json", "slog-scope", ] @@ -326,8 +322,9 @@ dependencies = [ [[package]] name = "path-absolutize" -version = "1.2.0" -source = "git+git://github.com/magiclen/path-absolutize.git?tag=v1.2.0#a8fea23c20192218bdbb141a0940d13ffba7ec86" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceeedc827d9a758b4641457683ced2f02d4252cc1bd8794c415ed20256114290" dependencies = [ "path-dedot", "slash-formatter", diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 97f6f3379..b4b7d7748 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -9,7 +9,7 @@ oci = { path = "oci" } logging = { path = "../../pkg/logging" } rustjail = { path = "rustjail" } protocols = { path = "protocols" } -netlink = { path = "netlink" } +netlink = { path = "netlink", features = ["with-log", "with-agent-handler"] } lazy_static = "1.3.0" error-chain = "0.12.1" ttrpc = { git = "https://github.com/containerd/ttrpc-rust.git", branch="0.3.0" } diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml index 5dc6b5094..5670889f0 100644 --- a/src/agent/netlink/Cargo.toml +++ b/src/agent/netlink/Cargo.toml @@ -9,11 +9,12 @@ edition = "2018" [dependencies] libc = "0.2.58" nix = "0.17.0" -protobuf = "=2.14.0" -rustjail = { path = "../rustjail" } -protocols = { path = "../protocols" } -slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"] } -slog-json = "2.3.0" -slog-async = "2.3.0" -slog-scope = "4.1.2" -scan_fmt = "0.2.3" + +protobuf = { version = "=2.14.0", optional = true } +protocols = { path = "../protocols", optional = true } +slog = { version = "2.5.2", features = ["dynamic-keys", "max_level_trace", "release_max_level_info"], optional = true } +slog-scope = { version = "4.1.2", optional = true } + +[features] +with-log = ["slog", "slog-scope"] +with-agent-handler = ["protobuf", "protocols"] diff --git a/src/agent/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs new file mode 100644 index 000000000..a8fffb071 --- /dev/null +++ b/src/agent/netlink/src/agent_handler.rs @@ -0,0 +1,572 @@ +// Copyright (c) 2020 Ant Financial +// Copyright (C) 2020 Alibaba Cloud. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! Dedicated Netlink interfaces for Kata agent protocol handler. + +use std::convert::TryFrom; + +use protobuf::RepeatedField; +use protocols::types::{ARPNeighbor, IPAddress, IPFamily, Interface, Route}; + +use super::*; + +#[cfg(feature = "with-log")] +// Convenience macro to obtain the scope logger +macro_rules! sl { + () => { + slog_scope::logger().new(o!("subsystem" => "netlink")) + }; +} + +impl super::RtnlHandle { + pub fn update_interface(&mut self, iface: &Interface) -> Result { + // the reliable way to find link is using hardware address + // as filter. However, hardware filter might not be supported + // by netlink, we may have to dump link list and the find the + // target link. filter using name or family is supported, but + // we cannot use that to find target link. + // let's try if hardware address filter works. -_- + + let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; + + // bring down interface if it is up + if ifinfo.ifi_flags & libc::IFF_UP as u32 != 0 { + self.set_link_status(&ifinfo, false)?; + } + + // delete all addresses associated with the link + let del_addrs: Vec = self.get_link_addresses(&ifinfo)?; + self.delete_all_addrs(&ifinfo, del_addrs.as_ref())?; + + // add new ip addresses in request + for grpc_addr in &iface.IPAddresses { + let rtip = RtIPAddr::try_from(grpc_addr.clone())?; + self.add_one_address(&ifinfo, &rtip)?; + } + + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + + // set name, set mtu, IFF_NOARP. in one rtnl_talk. + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; + nlh.nlmsg_type = RTM_NEWLINK; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + ifi.ifi_family = ifinfo.ifi_family; + ifi.ifi_type = ifinfo.ifi_type; + ifi.ifi_index = ifinfo.ifi_index; + if iface.raw_flags & libc::IFF_NOARP as u32 != 0 { + ifi.ifi_change |= libc::IFF_NOARP as u32; + ifi.ifi_flags |= libc::IFF_NOARP as u32; + } + + // Safe because we have allocated enough buffer space. + unsafe { + nlh.addattr32(IFLA_MTU, iface.mtu as u32); + + // if str is null terminated, use addattr_var. + // otherwise, use addattr_str + nlh.addattr_var(IFLA_IFNAME, iface.name.as_ref()); + } + + self.rtnl_talk(v.as_mut_slice(), false)?; + + // TODO: why the result is ignored here? + let _ = self.set_link_status(&ifinfo, true); + + Ok(iface.clone()) + } + + /// Delete this interface/link per request + pub fn remove_interface(&mut self, iface: &Interface) -> Result { + let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; + + self.set_link_status(&ifinfo, false)?; + + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + + // No attributes needed? + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; + nlh.nlmsg_type = RTM_DELLINK; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + ifi.ifi_family = ifinfo.ifi_family; + ifi.ifi_index = ifinfo.ifi_index; + ifi.ifi_type = ifinfo.ifi_type; + + self.rtnl_talk(v.as_mut_slice(), false)?; + + Ok(iface.clone()) + } + + pub fn list_interfaces(&mut self) -> Result> { + let mut ifaces: Vec = Vec::new(); + let (_slv, lv) = self.dump_all_links()?; + let (_sav, av) = self.dump_all_addresses(0)?; + + for link in &lv { + // Safe because dump_all_links() returns valid pointers. + let nlh = unsafe { &**link }; + if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { + continue; + } + + if nlh.nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", + nlh.nlmsg_len, + NLMSG_SPACE!(mem::size_of::()) + ); + break; + } + + // Safe because we have just validated available buffer space above. + let ifi = unsafe { &*(NLMSG_DATA!(nlh) as *const ifinfomsg) }; + let rta: *mut rtattr = IFLA_RTA!(ifi as *const ifinfomsg) as *mut rtattr; + let rtalen = IFLA_PAYLOAD!(nlh) as u32; + let attrs = unsafe { parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)? }; + + // fill out some fields of Interface, + let mut iface: Interface = Interface::default(); + + // Safe because parse_attrs() returns valid pointers. + unsafe { + if !attrs[IFLA_IFNAME as usize].is_null() { + let t = attrs[IFLA_IFNAME as usize]; + iface.name = String::from_utf8(getattr_var(t as *const rtattr))?; + } + + if !attrs[IFLA_MTU as usize].is_null() { + let t = attrs[IFLA_MTU as usize]; + iface.mtu = getattr32(t) as u64; + } + + if !attrs[IFLA_ADDRESS as usize].is_null() { + let alen = RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]); + let a: *const u8 = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const u8; + iface.hwAddr = parser::format_address(a, alen as u32)?; + } + } + + // get ip address info from av + let mut ads: Vec = Vec::new(); + for address in &av { + // Safe because dump_all_addresses() returns valid pointers. + let alh = unsafe { &**address }; + if alh.nlmsg_type != RTM_NEWADDR { + continue; + } + + let tlen = NLMSG_SPACE!(mem::size_of::()); + if alh.nlmsg_len < tlen { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", alh.nlmsg_len, tlen + ); + break; + } + + // Safe becahse we have checked avialable buffer space by NLMSG_SPACE above. + let ifa = unsafe { &*(NLMSG_DATA!(alh) as *const ifaddrmsg) }; + let arta: *mut rtattr = IFA_RTA!(ifa) as *mut rtattr; + let artalen = IFA_PAYLOAD!(alh) as u32; + + if ifa.ifa_index as u32 == ifi.ifi_index as u32 { + // found target addresses, parse attributes and fill out Interface + let addrs = unsafe { parse_attrs(arta, artalen, (IFA_MAX + 1) as usize)? }; + + // fill address field of Interface + let mut one: IPAddress = IPAddress::default(); + let tattr: *const rtattr = if !addrs[IFA_ADDRESS as usize].is_null() { + addrs[IFA_ADDRESS as usize] + } else { + addrs[IFA_LOCAL as usize] + }; + + one.mask = format!("{}", ifa.ifa_prefixlen); + one.family = IPFamily::v4; + if ifa.ifa_family == libc::AF_INET6 as u8 { + one.family = IPFamily::v6; + } + + // Safe because parse_attrs() returns valid pointers. + unsafe { + let a: *const u8 = RTA_DATA!(tattr) as *const u8; + let alen = RTA_PAYLOAD!(tattr); + one.address = parser::format_address(a, alen as u32)?; + } + + ads.push(one); + } + } + + iface.IPAddresses = RepeatedField::from_vec(ads); + ifaces.push(iface); + } + + Ok(ifaces) + } + + pub fn update_routes(&mut self, rt: &[Route]) -> Result> { + let rs = self.get_all_routes()?; + self.delete_all_routes(&rs)?; + + for grpcroute in rt { + if grpcroute.gateway.as_str() == "" { + let r = RtRoute::try_from(grpcroute.clone())?; + if r.index == -1 { + continue; + } + self.add_one_route(&r)?; + } + } + + for grpcroute in rt { + if grpcroute.gateway.as_str() != "" { + let r = RtRoute::try_from(grpcroute.clone())?; + if r.index == -1 { + continue; + } + self.add_one_route(&r)?; + } + } + + Ok(rt.to_owned()) + } + + pub fn list_routes(&mut self) -> Result> { + // currently, only dump routes from main table for ipv4 + // ie, rtmsg.rtmsg_family = AF_INET, set RT_TABLE_MAIN + // attribute in dump request + // Fix Me: think about othe tables, ipv6.. + let mut rs: Vec = Vec::new(); + let (_srv, rv) = self.dump_all_routes()?; + + // parse out routes and store in rs + for r in &rv { + // Safe because dump_all_routes() returns valid pointers. + let nlh = unsafe { &**r }; + if nlh.nlmsg_type != RTM_NEWROUTE && nlh.nlmsg_type != RTM_DELROUTE { + info!(sl!(), "not route message!"); + continue; + } + let tlen = NLMSG_SPACE!(mem::size_of::()); + if nlh.nlmsg_len < tlen { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", nlh.nlmsg_len, tlen + ); + break; + } + + // Safe because we have just validated available buffer space above. + let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; + if rtm.rtm_table != RT_TABLE_MAIN as u8 { + continue; + } + let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; + let rtalen = RTM_PAYLOAD!(nlh) as u32; + let attrs = unsafe { parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)? }; + + let t = attrs[RTA_TABLE as usize]; + if !t.is_null() { + // Safe because parse_attrs() returns valid pointers + let table = unsafe { getattr32(t) }; + if table != RT_TABLE_MAIN { + continue; + } + } + + // find source, destination, gateway, scope, and and device name + let mut t = attrs[RTA_DST as usize]; + let mut rte: Route = Route::default(); + + // Safe because parse_attrs() returns valid pointers + unsafe { + // destination + if !t.is_null() { + let data: *const u8 = RTA_DATA!(t) as *const u8; + let len = RTA_PAYLOAD!(t) as u32; + rte.dest = + format!("{}/{}", parser::format_address(data, len)?, rtm.rtm_dst_len); + } + + // gateway + t = attrs[RTA_GATEWAY as usize]; + if !t.is_null() { + let data: *const u8 = RTA_DATA!(t) as *const u8; + let len = RTA_PAYLOAD!(t) as u32; + rte.gateway = parser::format_address(data, len)?; + + // for gateway, destination is 0.0.0.0 + rte.dest = "0.0.0.0".to_string(); + } + + // source + t = attrs[RTA_SRC as usize]; + if t.is_null() { + t = attrs[RTA_PREFSRC as usize]; + } + if !t.is_null() { + let data: *const u8 = RTA_DATA!(t) as *const u8; + let len = RTA_PAYLOAD!(t) as u32; + rte.source = parser::format_address(data, len)?; + + if rtm.rtm_src_len != 0 { + rte.source = format!("{}/{}", rte.source.as_str(), rtm.rtm_src_len); + } + } + + // scope + rte.scope = rtm.rtm_scope as u32; + + // oif + t = attrs[RTA_OIF as usize]; + if !t.is_null() { + let data = &*(RTA_DATA!(t) as *const i32); + assert_eq!(RTA_PAYLOAD!(t), 4); + + rte.device = self + .get_name_by_index(*data) + .unwrap_or_else(|_| "unknown".to_string()); + } + } + + rs.push(rte); + } + + Ok(rs) + } + + pub fn add_arp_neighbors(&mut self, neighs: &[ARPNeighbor]) -> Result<()> { + for neigh in neighs { + self.add_one_arp_neighbor(&neigh)?; + } + + Ok(()) + } + + pub fn add_one_arp_neighbor(&mut self, neigh: &ARPNeighbor) -> Result<()> { + let to_ip = match neigh.toIPAddress.as_ref() { + None => return nix_errno(Errno::EINVAL), + Some(v) => { + if v.address.is_empty() { + return nix_errno(Errno::EINVAL); + } + v.address.as_ref() + } + }; + + let dev = self.find_link_by_name(&neigh.device)?; + + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ndm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ndmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(std::mem::size_of::()) as u32; + nlh.nlmsg_type = RTM_NEWNEIGH; + nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + self.assign_seqnum(nlh); + + ndm.ndm_family = libc::AF_UNSPEC as __u8; + ndm.ndm_state = IFA_F_PERMANENT as __u16; + // process lladdr + if neigh.lladdr != "" { + let llabuf = parser::parse_mac_addr(&neigh.lladdr)?; + + // Safe because we have allocated enough buffer space. + unsafe { nlh.addattr_var(NDA_LLADDR, llabuf.as_ref()) }; + } + + let (family, ip_data) = parser::parse_ip_addr_with_family(&to_ip)?; + ndm.ndm_family = family; + // Safe because we have allocated enough buffer space. + unsafe { nlh.addattr_var(NDA_DST, ip_data.as_ref()) }; + + // process state + if neigh.state != 0 { + ndm.ndm_state = neigh.state as __u16; + } + + // process flags + ndm.ndm_flags = (*ndm).ndm_flags | neigh.flags as __u8; + + // process dev + ndm.ndm_ifindex = dev.ifi_index; + + // send + self.rtnl_talk(v.as_mut_slice(), false)?; + + Ok(()) + } +} + +impl TryFrom for RtIPAddr { + type Error = nix::Error; + + fn try_from(ipi: IPAddress) -> std::result::Result { + let ip_family = if ipi.family == IPFamily::v4 { + libc::AF_INET + } else { + libc::AF_INET6 + } as __u8; + + let ip_mask = parser::parse_u8(ipi.mask.as_str(), 10)?; + let addr = parser::parse_ip_addr(ipi.address.as_ref())?; + + Ok(Self { + ip_family, + ip_mask, + addr, + }) + } +} + +impl TryFrom for RtRoute { + type Error = nix::Error; + + fn try_from(r: Route) -> std::result::Result { + // only handle ipv4 + + let index = { + let mut rh = RtnlHandle::new(NETLINK_ROUTE, 0)?; + match rh.find_link_by_name(r.device.as_str()) { + Ok(ifi) => ifi.ifi_index, + Err(_) => -1, + } + }; + + let (dest, dst_len) = if r.dest.is_empty() { + (Some(vec![0 as u8; 4]), 0) + } else { + let (dst, mask) = parser::parse_cidr(r.dest.as_str())?; + (Some(dst), mask) + }; + + let (source, src_len) = if r.source.is_empty() { + (None, 0) + } else { + let (src, mask) = parser::parse_cidr(r.source.as_str())?; + (Some(src), mask) + }; + + let gateway = if r.gateway.is_empty() { + None + } else { + Some(parser::parse_ip_addr(r.gateway.as_str())?) + }; + + Ok(Self { + dest, + source, + src_len, + dst_len, + index, + gateway, + scope: r.scope as u8, + protocol: RTPROTO_UNSPEC, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{RtnlHandle, NETLINK_ROUTE}; + use protocols::types::IPAddress; + use std::process::Command; + + fn clean_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { + // ip link delete dummy + Command::new("ip") + .args(&["link", "delete", dummy_name]) + .output() + .expect("prepare: failed to delete dummy"); + + // ip neigh del dev dummy ip + Command::new("ip") + .args(&["neigh", "del", dummy_name, ip]) + .output() + .expect("prepare: failed to delete neigh"); + } + + fn prepare_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { + clean_env_for_test_add_one_arp_neighbor(dummy_name, ip); + // modprobe dummy + Command::new("modprobe") + .arg("dummy") + .output() + .expect("failed to run modprobe dummy"); + + // ip link add dummy type dummy + Command::new("ip") + .args(&["link", "add", dummy_name, "type", "dummy"]) + .output() + .expect("failed to add dummy interface"); + + // ip addr add 192.168.0.2/16 dev dummy + Command::new("ip") + .args(&["addr", "add", "192.168.0.2/16", "dev", dummy_name]) + .output() + .expect("failed to add ip for dummy"); + + // ip link set dummy up; + Command::new("ip") + .args(&["link", "set", dummy_name, "up"]) + .output() + .expect("failed to up dummy"); + } + + #[test] + fn test_add_one_arp_neighbor() { + // skip_if_not_root + if !nix::unistd::Uid::effective().is_root() { + println!("INFO: skipping {} which needs root", module_path!()); + return; + } + + let mac = "6a:92:3a:59:70:aa"; + let to_ip = "169.254.1.1"; + let dummy_name = "dummy_for_arp"; + + prepare_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); + + let mut ip_address = IPAddress::new(); + ip_address.set_address(to_ip.to_string()); + + let mut neigh = ARPNeighbor::new(); + neigh.set_toIPAddress(ip_address); + neigh.set_device(dummy_name.to_string()); + neigh.set_lladdr(mac.to_string()); + neigh.set_state(0x80); + + let mut rtnl = RtnlHandle::new(NETLINK_ROUTE, 0).unwrap(); + + rtnl.add_one_arp_neighbor(&neigh).unwrap(); + + // ip neigh show dev dummy ip + let stdout = Command::new("ip") + .args(&["neigh", "show", "dev", dummy_name, to_ip]) + .output() + .expect("failed to show neigh") + .stdout; + + let stdout = std::str::from_utf8(&stdout).expect("failed to conveert stdout"); + + assert_eq!(stdout, format!("{} lladdr {} PERMANENT\n", to_ip, mac)); + + clean_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); + } +} diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index b12c2b54d..37268286f 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -3,40 +3,49 @@ // SPDX-License-Identifier: Apache-2.0 // -#![allow(non_camel_case_types)] -#![allow(non_upper_case_globals)] -#![allow(dead_code)] -#![allow(unused_parens)] -#![allow(unused_unsafe)] +//! Structs, consts to support Linux Netlink operations +//! +//! The netlink library makes heavy use of unsafe functions, we trust the Linux kernel and assume: +//! - all pointers/buffers generated by kernel are valid +//! - all offsets are within legal range +//! - all fields have been correctly aligned -mod neigh; +#![allow(non_camel_case_types)] +// NOTE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// By default, there are many warnings for incorrect alignment like: +// casting from `*mut u8` to a more-strictly-aligned pointer (`*mut nlmsghdr`) (1 < 4 bytes) +// +// The root cause is that we use a Vec buffer to receive netlink message from the kernel. +// The data buffer for a Vec may be 1-byte aligned, which doesn't match the alignment +// requirement for netlink message fields. The reason it works without failure now is that +// rust memory allocation is 16-byte aligned by default. +// +// But please do pay attention here, it's no guarantee that it will be 16-byte aligned, just works +// in this way. For safety, we should use something like the FamStruct from the vmm-sys-util crate. +#![allow(clippy::cast_ptr_alignment)] extern crate libc; extern crate nix; -extern crate protobuf; -extern crate protocols; -extern crate rustjail; +#[cfg(feature = "with-agent-handler")] +extern crate protobuf; +#[cfg(feature = "with-agent-handler")] +extern crate protocols; + +#[cfg(feature = "with-log")] #[macro_use] extern crate slog; -extern crate slog_async; -extern crate slog_json; +#[cfg(feature = "with-log")] extern crate slog_scope; -#[macro_use] -extern crate scan_fmt; - use nix::errno::Errno; -use protobuf::RepeatedField; -use protocols::types::{IPAddress, IPFamily, Interface, Route}; -use rustjail::errors::*; -use std::clone::Clone; -use std::default::Default; +use std::borrow::Borrow; +use std::ffi::CString; use std::fmt; use std::mem; -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::str::FromStr; +use std::os::unix::io::RawFd; +#[cfg(feature = "with-log")] // Convenience macro to obtain the scope logger macro_rules! sl { () => { @@ -44,8 +53,21 @@ macro_rules! sl { }; } -// define the struct, const, etc needed by -// netlink operations +#[cfg(not(feature = "with-log"))] +#[macro_export] +macro_rules! info { + ($l:expr, #$tag:expr, $($args:tt)*) => {}; + ($l:expr, $($args:tt)*) => {}; +} + +#[cfg(feature = "with-agent-handler")] +mod agent_handler; +pub mod parser; + +pub const DEFAULT_NETLINK_BUF_SIZE: usize = 2048; + +/// Specialized std::result::Result for Netlink related operations. +pub type Result = std::result::Result; pub type __s8 = libc::c_char; pub type __u8 = libc::c_uchar; @@ -116,7 +138,7 @@ pub const RTM_DELCHAIN: libc::c_ushort = 101; pub const RTM_GETCHAIN: libc::c_ushort = 102; pub const __RTM_MAX: libc::c_ushort = 103; -pub const RTM_MAX: libc::c_ushort = (((__RTM_MAX + 3) & !3) - 1); +pub const RTM_MAX: libc::c_ushort = ((__RTM_MAX + 3) & !3) - 1; pub const RTM_NR_MSGTYPES: libc::c_ushort = (RTM_MAX + 1) - RTM_BASE; pub const RTM_NR_FAMILIES: libc::c_ushort = RTM_NR_MSGTYPES >> 2; @@ -237,7 +259,7 @@ pub const RT_TABLE_COMPAT: libc::c_uint = 252; pub const RT_TABLE_DEFAULT: libc::c_uint = 253; pub const RT_TABLE_MAIN: libc::c_uint = 254; pub const RT_TABLE_LOCAL: libc::c_uint = 255; -pub const RT_TABLE_MAX: libc::c_uint = 0xffffffff; +pub const RT_TABLE_MAX: libc::c_uint = 0xffff_ffff; // rat_type c_ushort pub const RTA_UNSPEC: libc::c_ushort = 0; @@ -275,13 +297,11 @@ pub const RTA_MAX: libc::c_ushort = __RTA_MAX - 1; #[macro_export] macro_rules! RTM_RTA { - ($rtm: expr) => { - unsafe { - let mut p = $rtm as *mut rtmsg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - } - }; + ($rtm: expr) => {{ + let mut p = $rtm as *mut rtmsg as i64; + p += NLMSG_ALIGN!(mem::size_of::()) as i64; + p as *mut rtattr + }}; } #[macro_export] @@ -367,13 +387,11 @@ macro_rules! RTNH_SPACE { #[macro_export] macro_rules! RTNH_DATA { - ($rtnh: expr) => { - unsafe { - let mut p = $rtnh as *mut rtnexthop as i64; - p += RTNH_LENGTH!(0); - p as *mut rtattr - } - }; + ($rtnh: expr) => {{ + let mut p = $rtnh as *mut rtnexthop as i64; + p += RTNH_LENGTH!(0); + p as *mut rtattr + }}; } // RTA_VIA @@ -451,10 +469,10 @@ pub const RTAX_FASTOPEN_NO_COOKIE: libc::c_ushort = 17; pub const __RTAX_MAX: libc::c_ushort = 18; pub const RTAX_MAX: libc::c_ushort = __RTAX_MAX - 1; -pub const RTAX_FEATURE_ECN: libc::c_ushort = 1 << 0; -pub const RTAX_FEATURE_SACK: libc::c_ushort = 1 << 1; -pub const RTAX_FEATURE_TIMESTAMP: libc::c_ushort = 1 << 2; -pub const RTAX_FEATURE_ALLFRAG: libc::c_ushort = 1 << 3; +pub const RTAX_FEATURE_ECN: libc::c_ushort = 0x1; +pub const RTAX_FEATURE_SACK: libc::c_ushort = 0x2; +pub const RTAX_FEATURE_TIMESTAMP: libc::c_ushort = 0x4; +pub const RTAX_FEATURE_ALLFRAG: libc::c_ushort = 0x8; pub const RTAX_FEATURE_MASK: libc::c_ushort = RTAX_FEATURE_ECN | RTAX_FEATURE_SACK | RTAX_FEATURE_TIMESTAMP | RTAX_FEATURE_ALLFRAG; @@ -759,14 +777,12 @@ macro_rules! RTA_OK { #[macro_export] macro_rules! RTA_NEXT { - ($attr: expr, $len: expr) => { - unsafe { - $len -= RTA_ALIGN!((*$attr).rta_len) as u32; - let mut p = $attr as *mut libc::c_char as i64; - p += RTA_ALIGN!((*$attr).rta_len) as i64; - p as *mut rtattr - } - }; + ($attr: expr, $len: expr) => {{ + $len -= RTA_ALIGN!((*$attr).rta_len) as u32; + let mut p = $attr as *mut libc::c_char as i64; + p += RTA_ALIGN!((*$attr).rta_len) as i64; + p as *mut rtattr + }}; } #[macro_export] @@ -785,13 +801,11 @@ macro_rules! RTA_SPACE { #[macro_export] macro_rules! RTA_DATA { - ($attr: expr) => { - unsafe { - let mut p = $attr as *mut libc::c_char as i64; - p += RTA_LENGTH!(0) as i64; - p as *mut libc::c_char - } - }; + ($attr: expr) => {{ + let mut p = $attr as *mut libc::c_char as i64; + p += RTA_LENGTH!(0) as i64; + p as *mut libc::c_char + }}; } #[macro_export] @@ -844,25 +858,21 @@ macro_rules! NLMSG_SPACE { #[macro_export] macro_rules! NLMSG_DATA { - ($nlh: expr) => { - unsafe { - let mut p = $nlh as *mut nlmsghdr as i64; - p += NLMSG_LENGTH!(0) as i64; - p as *mut libc::c_void - } - }; + ($nlh: expr) => {{ + let mut p = $nlh as *const nlmsghdr as i64; + p += NLMSG_LENGTH!(0) as i64; + p as *mut libc::c_void + }}; } #[macro_export] macro_rules! NLMSG_NEXT { - ($nlh: expr, $len: expr) => { - unsafe { - $len -= NLMSG_ALIGN!((*$nlh).nlmsg_len) as u32; - let mut p = $nlh as *mut libc::c_char; - p = (p as i64 + NLMSG_ALIGN!((*$nlh).nlmsg_len) as i64) as *mut libc::c_char; - p as *mut nlmsghdr - } - }; + ($nlh: expr, $len: expr) => {{ + $len -= NLMSG_ALIGN!($nlh.nlmsg_len) as u32; + let mut p = $nlh as *const nlmsghdr as *mut libc::c_char; + p = (p as i64 + NLMSG_ALIGN!($nlh.nlmsg_len) as i64) as *mut libc::c_char; + unsafe { &mut *(p as *mut nlmsghdr) } + }}; } #[macro_export] @@ -894,24 +904,20 @@ macro_rules! RTA_TAIL { #[macro_export] macro_rules! NLMSG_TAIL { - ($msg: expr) => { - unsafe { - let mut p = $msg as *mut nlmsghdr as i64; - p += NLMSG_ALIGN!((*$msg).nlmsg_len) as i64; - p as *mut rtattr - } - }; + ($msg: expr) => {{ + let mut p = $msg as *mut nlmsghdr as i64; + p += NLMSG_ALIGN!((*$msg).nlmsg_len) as i64; + p as *mut rtattr + }}; } #[macro_export] macro_rules! IFA_RTA { - ($ifmsg: expr) => { - unsafe { - let mut p = $ifmsg as *mut ifaddrmsg as *mut libc::c_char; - p = (p as i64 + NLMSG_ALIGN!(mem::size_of::()) as i64) as *mut libc::c_char; - p as *mut rtattr - } - }; + ($ifmsg: expr) => {{ + let mut p = $ifmsg as *const ifaddrmsg as *const libc::c_char; + p = (p as i64 + NLMSG_ALIGN!(mem::size_of::()) as i64) as *mut libc::c_char; + p as *const rtattr + }}; } #[macro_export] @@ -923,13 +929,11 @@ macro_rules! IFA_PAYLOAD { #[macro_export] macro_rules! IFLA_RTA { - ($ifinfo: expr) => { - unsafe { - let mut p = $ifinfo as *mut ifinfomsg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - } - }; + ($ifinfo: expr) => {{ + let mut p = $ifinfo as *mut ifinfomsg as i64; + p += NLMSG_ALIGN!(mem::size_of::()) as i64; + p as *mut rtattr + }}; } #[macro_export] @@ -941,13 +945,11 @@ macro_rules! IFLA_PAYLOAD { #[macro_export] macro_rules! IFLA_STATS_RTA { - ($stats: expr) => { - unsafe { - let mut p = $stats as *mut if_stats_msg as i64; - p += NLMSG_ALIGN!(mem::size_of::()) as i64; - p as *mut rtattr - } - }; + ($stats: expr) => {{ + let mut p = $stats as *mut if_stats_msg as i64; + p += NLMSG_ALIGN!(mem::size_of::()) as i64; + p as *mut rtattr + }}; } #[repr(C)] @@ -972,6 +974,14 @@ impl Default for nlmsghdr { } } +impl Borrow for Vec { + fn borrow(&self) -> &nlmsghdr { + let ptr = self.as_ptr(); + assert_eq!(ptr.align_offset(std::mem::align_of::()), 0); + unsafe { &*(ptr as *const nlmsghdr) } + } +} + // nlmsg_flags pub const NLM_F_REQUEST: __u16 = 0x01; pub const NLM_F_MULTI: __u16 = 0x02; @@ -1008,10 +1018,10 @@ pub const NLMSG_OVERRUN: __u16 = 0x4; pub const NLMSG_MIN_TYPE: __u16 = 0x10; // IFLA_EXT_MASK -pub const RTEXT_FILTER_VF: __u32 = 1 << 0; -pub const RTEXT_FILTER_BRVLAN: __u32 = 1 << 1; -pub const RTEXT_FILTER_BRVLAN_COMPRESSED: __u32 = 1 << 2; -pub const RTEXT_FILTER_SKIP_STATS: __u32 = 1 << 3; +pub const RTEXT_FILTER_VF: __u32 = 0x1; +pub const RTEXT_FILTER_BRVLAN: __u32 = 0x2; +pub const RTEXT_FILTER_BRVLAN_COMPRESSED: __u32 = 0x4; +pub const RTEXT_FILTER_SKIP_STATS: __u32 = 0x8; // IFLA attr pub const IFLA_UNSPEC: __u16 = 0; @@ -1097,6 +1107,44 @@ pub const IFA_F_NOPREFIXROUTE: __u32 = 0x200; pub const IFA_F_MCAUTOJOIN: __u32 = 0x400; pub const IFA_F_STABLE_PRIVACY: __u32 = 0x800; +#[repr(C)] +#[derive(Copy)] +pub struct ndmsg { + ndm_family: __u8, + ndm_pad1: __u8, + ndm_pad: __u16, + ndm_ifindex: __s32, + ndm_state: __u16, + ndm_flags: __u8, + ndm_type: __u8, +} + +pub const NDA_UNSPEC: __u16 = 0; +pub const NDA_DST: __u16 = 1; +pub const NDA_LLADDR: __u16 = 2; +pub const NDA_CACHEINFO: __u16 = 3; +pub const NDA_PROBES: __u16 = 4; +pub const NDA_VLAN: __u16 = 5; +pub const NDA_PORT: __u16 = 6; +pub const NDA_VNI: __u16 = 7; +pub const NDA_IFINDEX: __u16 = 8; +pub const NDA_MASTER: __u16 = 9; +pub const NDA_LINK_NETNSID: __u16 = 10; +pub const NDA_SRC_VNI: __u16 = 11; +pub const __NDA_MAX: __u16 = 12; + +impl Clone for ndmsg { + fn clone(&self) -> Self { + Self { ..*self } + } +} + +impl Default for ndmsg { + fn default() -> Self { + unsafe { mem::zeroed::() } + } +} + #[repr(C)] #[derive(Copy)] pub struct nlmsgerr { @@ -1116,9 +1164,37 @@ impl Default for nlmsgerr { } } +pub const NETLINK_ROUTE: libc::c_int = 0; +pub const NETLINK_EXT_ACK: libc::c_int = 11; +pub const NETLINK_UEVENT: libc::c_int = 15; + +pub struct RtRoute { + pub dest: Option>, + pub source: Option>, + pub gateway: Option>, + pub index: i32, + pub scope: u8, + pub dst_len: u8, + pub src_len: u8, + pub protocol: u8, +} + +impl Default for RtRoute { + fn default() -> Self { + unsafe { mem::zeroed::() } + } +} + +pub struct RtIPAddr { + pub ip_family: __u8, + pub ip_mask: __u8, + pub addr: Vec, +} + +/// Handle to access the Linux Netlink subsystem. // #[derive(Copy)] pub struct RtnlHandle { - pub fd: libc::c_int, + pub fd: RawFd, local: libc::sockaddr_nl, seq: __u32, dump: __u32, @@ -1148,10 +1224,6 @@ impl fmt::Debug for RtnlHandle { } } -pub const NETLINK_ROUTE: libc::c_int = 0; -pub const NETLINK_EXT_ACK: libc::c_int = 11; -pub const NETLINK_UEVENT: libc::c_int = 15; - impl RtnlHandle { pub fn new(protocal: libc::c_int, group: u32) -> Result { // open netlink_route socket @@ -1170,7 +1242,7 @@ impl RtnlHandle { mem::size_of::() as libc::socklen_t; if tmpfd < 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } let mut err = libc::setsockopt( @@ -1183,7 +1255,7 @@ impl RtnlHandle { if err < 0 { libc::close(tmpfd); - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } err = libc::setsockopt( @@ -1196,7 +1268,7 @@ impl RtnlHandle { if err < 0 { libc::close(tmpfd); - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } libc::setsockopt( @@ -1217,7 +1289,7 @@ impl RtnlHandle { ); if err < 0 { libc::close(tmpfd); - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } err = libc::getsockname( @@ -1227,14 +1299,14 @@ impl RtnlHandle { ); if err < 0 { libc::close(tmpfd); - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } if sa.nl_family as i32 != libc::AF_NETLINK || addrlen as usize != mem::size_of::() { libc::close(tmpfd); - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); + return nix_errno(Errno::EINVAL); } tmpfd @@ -1243,95 +1315,161 @@ impl RtnlHandle { Ok(Self { fd, local: sa, - seq: unsafe { libc::time(0 as *mut libc::time_t) } as __u32, + seq: unsafe { libc::time(std::ptr::null_mut::()) } as __u32, dump: 0, }) } - // implement update{interface,routes}, list{interface, routes} - fn send_message(&self, data: &mut [u8]) -> Result<()> { + pub fn rtnl_talk(&mut self, data: &mut [u8], answer: bool) -> Result> { + if data.len() < std::mem::size_of::() { + return nix_errno(Errno::EINVAL); + } + // Safe because we have just validated buffer size above. + let nlh = unsafe { &mut *(data.as_mut_ptr() as *mut nlmsghdr) }; + if !answer { + nlh.nlmsg_flags |= NLM_F_ACK; + } + self.send_message(data)?; + + loop { + let buf = self.recv_message()?; + let mut msglen = buf.len() as u32; + if (msglen as usize) < std::mem::size_of::() { + return nix_errno(Errno::EBADMSG); + } + // Safe because we have just validated buffer size above. + let mut nlh = unsafe { &*(buf.as_ptr() as *const nlmsghdr) }; + + while NLMSG_OK!(nlh, msglen) { + if nlh.nlmsg_pid != self.local.nl_pid { + nlh = NLMSG_NEXT!(nlh, msglen); + continue; + } + + if nlh.nlmsg_type == NLMSG_ERROR { + if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { + // truncated + return nix_errno(Errno::EBADMSG); + } + + // Safe because we have just validated buffer size above. + let el = unsafe { &*(NLMSG_DATA!(nlh) as *const nlmsgerr) }; + + // this is ack. -_- + if el.error == 0 { + return Ok(Vec::new()); + } + + return nix_errno(Errno::from_i32(-el.error)); + } + + // good message + let mut result = Vec::new(); + if answer { + // need to copy out data + result.resize(nlh.nlmsg_len as usize, 0); + let dp: *mut libc::c_void = result.as_mut_ptr() as *mut libc::c_void; + // Safe because the source and destination buffers are valid. + unsafe { + libc::memcpy( + dp, + nlh as *const nlmsghdr as *const libc::c_void, + nlh.nlmsg_len as libc::size_t, + ) + }; + }; + + return Ok(result); + } + + if !(NLMSG_OK!(nlh, msglen)) { + return nix_errno(Errno::EINVAL); + } + } + } + + pub fn send_message(&self, data: &mut [u8]) -> Result<()> { + if data.len() < std::mem::size_of::() { + return nix_errno(Errno::EINVAL); + } + let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; + let mut h = unsafe { mem::zeroed::() }; + + // Safe because we have validated the data buffer size is bigger then nlmsghdr. + let nh = unsafe { &mut *(data.as_mut_ptr() as *mut nlmsghdr) }; + if nh.nlmsg_len as usize > data.len() { + return nix_errno(Errno::EINVAL); + } + let mut iov: libc::iovec = libc::iovec { + iov_base: nh as *mut nlmsghdr as *mut libc::c_void, + iov_len: nh.nlmsg_len as libc::size_t, + }; + + h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; + h.msg_namelen = mem::size_of::() as libc::socklen_t; + h.msg_iov = &mut iov as *mut libc::iovec; + h.msg_iovlen = 1; sa.nl_family = libc::AF_NETLINK as u16; - unsafe { - let nh = data.as_mut_ptr() as *mut nlmsghdr; - let mut iov: libc::iovec = libc::iovec { - iov_base: nh as *mut libc::c_void, - iov_len: (*nh).nlmsg_len as libc::size_t, - }; - - let mut h = mem::zeroed::(); - h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; - h.msg_namelen = mem::size_of::() as libc::socklen_t; - h.msg_iov = &mut iov as *mut libc::iovec; - h.msg_iovlen = 1; - - let err = libc::sendmsg(self.fd, &h as *const libc::msghdr, 0); - - if err < 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); - } + let err = unsafe { libc::sendmsg(self.fd, &h as *const libc::msghdr, 0) }; + if err < 0 { + return nix_last_os_err(); } + Ok(()) } pub fn recv_message(&self) -> Result> { let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; - + let mut h = unsafe { mem::zeroed::() }; let mut iov = libc::iovec { - iov_base: 0 as *mut libc::c_void, + iov_base: std::ptr::null_mut::(), iov_len: 0 as libc::size_t, }; - unsafe { - let mut h = mem::zeroed::(); - h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; - h.msg_namelen = mem::size_of::() as libc::socklen_t; - h.msg_iov = &mut iov as *mut libc::iovec; - h.msg_iovlen = 1; + h.msg_name = &mut sa as *mut libc::sockaddr_nl as *mut libc::c_void; + h.msg_namelen = mem::size_of::() as libc::socklen_t; + h.msg_iov = &mut iov as *mut libc::iovec; + h.msg_iovlen = 1; - let mut rlen = libc::recvmsg( + let mut rlen = unsafe { + libc::recvmsg( self.fd, &mut h as *mut libc::msghdr, libc::MSG_PEEK | libc::MSG_TRUNC, - ); + ) + }; - if rlen < 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); - } - - // if rlen < 32768 { - // rlen = 32768; - // } - - let mut v: Vec = vec![0; rlen as usize]; - // v.set_len(rlen as usize); - - iov.iov_base = v.as_mut_ptr() as *mut libc::c_void; - iov.iov_len = rlen as libc::size_t; - - rlen = libc::recvmsg(self.fd, &mut h as *mut libc::msghdr, 0); - if rlen < 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); - } - - if sa.nl_pid != 0 { - // not our netlink message - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EBADMSG)).into()); - } - - if h.msg_flags & libc::MSG_TRUNC != 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EBADMSG)).into()); - } - - v.resize(rlen as usize, 0); - - Ok(v) + if rlen < 0 { + return nix_last_os_err(); } + + let mut v: Vec = vec![0; rlen as usize]; + iov.iov_base = v.as_mut_ptr() as *mut libc::c_void; + iov.iov_len = rlen as libc::size_t; + + rlen = unsafe { libc::recvmsg(self.fd, &mut h as *mut libc::msghdr, 0) }; + if rlen < 0 { + return nix_last_os_err(); + } + + if sa.nl_pid != 0 { + // not our netlink message + return nix_errno(Errno::EBADMSG); + } + + if h.msg_flags & libc::MSG_TRUNC != 0 { + return nix_errno(Errno::EBADMSG); + } + + v.resize(rlen as usize, 0); + + Ok(v) } - unsafe fn recv_dump_message(&self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { + fn recv_dump_message(&self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { let mut slv: Vec> = Vec::new(); let mut lv: Vec<*const nlmsghdr> = Vec::new(); @@ -1339,43 +1477,40 @@ impl RtnlHandle { let buf = self.recv_message()?; let mut msglen = buf.len() as u32; - let mut nlh = buf.as_ptr() as *const nlmsghdr; - let mut dump_intr = 0; - let mut done = 0; + let mut nlh: &nlmsghdr = buf.borrow(); + let mut dump_intr = false; + let mut done = false; while NLMSG_OK!(nlh, msglen) { - if (*nlh).nlmsg_pid != self.local.nl_pid || (*nlh).nlmsg_seq != self.dump { + // Make sure we are interested in the message first. + if nlh.nlmsg_pid != self.local.nl_pid || nlh.nlmsg_seq != self.dump { nlh = NLMSG_NEXT!(nlh, msglen); continue; } - // got one nlmsg - if (*nlh).nlmsg_flags & NLM_F_DUMP_INTR > 0 { - dump_intr = 1; + if nlh.nlmsg_flags & NLM_F_DUMP_INTR > 0 { + dump_intr = true; } - if (*nlh).nlmsg_type == NLMSG_DONE { - done = 1; + if nlh.nlmsg_type == NLMSG_DONE { + done = true; } - if (*nlh).nlmsg_type == NLMSG_ERROR { - // error message, better to return - // error code in error messages - - if (*nlh).nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { + if nlh.nlmsg_type == NLMSG_ERROR { + // error message, better to return error code in error messages + if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { // truncated - return Err(ErrorKind::ErrorCode("truncated message".to_string()).into()); + return nix_errno(Errno::EBADMSG); } - let el: *const nlmsgerr = NLMSG_DATA!(nlh) as *const nlmsgerr; - return Err( - ErrorKind::Nix(nix::Error::Sys(Errno::from_i32(-(*el).error))).into(), - ); + // Safe because we have validated buffer size. + let el = unsafe { &*(NLMSG_DATA!(nlh) as *const nlmsgerr) }; + return nix_errno(Errno::from_i32(-el.error)); } lv.push(nlh); - if done == 1 { + if done { break; } @@ -1384,410 +1519,266 @@ impl RtnlHandle { slv.push(buf); - if done == 1 { - if dump_intr == 1 { - info!(sl!(), "dump interuppted, maybe incomplete"); + if done { + if dump_intr { + info!(sl!(), "dump interrupted, maybe incomplete"); } break; } // still remain some bytes? - if msglen != 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); + return nix_errno(Errno::EINVAL); } } + Ok((slv, lv)) } - pub fn list_interfaces(&mut self) -> Result> { - let mut ifaces: Vec = Vec::new(); + fn dump_all_links(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as i32) as __u32; + nlh.nlmsg_type = RTM_GETLINK; + nlh.nlmsg_flags = (NLM_F_DUMP | NLM_F_REQUEST) as __u16; + self.assign_seqnum(nlh); + + ifi.ifi_family = libc::AF_UNSPEC as u8; + + // Safe because we have allocated enough buffer space. unsafe { - // get link info - let (_slv, lv) = self.dump_all_links()?; - - // get addrinfo - let (_sav, av) = self.dump_all_addresses(0)?; - - // got all the link message and address message - // into lv and av repectively, parse attributes - for link in &lv { - let nlh: *const nlmsghdr = *link; - let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; - - if (*nlh).nlmsg_type != RTM_NEWLINK && (*nlh).nlmsg_type != RTM_DELLINK { - continue; - } - - if (*nlh).nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - (*nlh).nlmsg_len, - NLMSG_SPACE!(mem::size_of::()) - ); - break; - } - - let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; - - let attrs = parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)?; - - // fill out some fields of Interface, - let mut iface: Interface = Interface::default(); - - if attrs[IFLA_IFNAME as usize] as i64 != 0 { - let t = attrs[IFLA_IFNAME as usize]; - iface.name = String::from_utf8(getattr_var(t as *const rtattr))?; - } - - if attrs[IFLA_MTU as usize] as i64 != 0 { - let t = attrs[IFLA_MTU as usize]; - iface.mtu = getattr32(t) as u64; - } - - if attrs[IFLA_ADDRESS as usize] as i64 != 0 { - let alen = RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]); - let a: *const u8 = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const u8; - iface.hwAddr = format_address(a, alen as u32)?; - } - - // get ip address info from av - let mut ads: Vec = Vec::new(); - - for address in &av { - let alh: *const nlmsghdr = *address; - let ifa: *const ifaddrmsg = NLMSG_DATA!(alh) as *const ifaddrmsg; - let arta: *mut rtattr = IFA_RTA!(ifa) as *mut rtattr; - - if (*alh).nlmsg_type != RTM_NEWADDR { - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if (*alh).nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - (*alh).nlmsg_len, - tlen - ); - break; - } - - let artalen = IFA_PAYLOAD!(alh) as u32; - - if (*ifa).ifa_index as u32 == (*ifi).ifi_index as u32 { - // found target addresses - // parse attributes and fill out Interface - let addrs = parse_attrs(arta, artalen, (IFA_MAX + 1) as usize)?; - // fill address field of Interface - let mut one: IPAddress = IPAddress::default(); - let mut tattr: *const rtattr = addrs[IFA_LOCAL as usize]; - if addrs[IFA_ADDRESS as usize] as i64 != 0 { - tattr = addrs[IFA_ADDRESS as usize]; - } - - one.mask = format!("{}", (*ifa).ifa_prefixlen); - let a: *const u8 = RTA_DATA!(tattr) as *const u8; - let alen = RTA_PAYLOAD!(tattr); - one.family = IPFamily::v4; - - if (*ifa).ifa_family == libc::AF_INET6 as u8 { - one.family = IPFamily::v6; - } - - // only handle IPv4 for now - // if (*ifa).ifa_family == libc::AF_INET as u8{ - one.address = format_address(a, alen as u32)?; - //} - - ads.push(one); - } - } - - iface.IPAddresses = RepeatedField::from_vec(ads); - ifaces.push(iface); - } + nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF); } - Ok(ifaces) - } - - unsafe fn dump_all_links(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut v: Vec = vec![0; 2048]; - let p = v.as_mut_ptr() as *mut libc::c_char; - let nlh: *mut nlmsghdr = p as *mut nlmsghdr; - let ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as i32) as __u32; - (*nlh).nlmsg_type = RTM_GETLINK; - (*nlh).nlmsg_flags = (NLM_F_DUMP | NLM_F_REQUEST) as __u16; - - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*ifi).ifi_family = libc::AF_UNSPEC as u8; - - addattr32(nlh, IFLA_EXT_MASK, RTEXT_FILTER_VF); - self.send_message(v.as_mut_slice())?; self.recv_dump_message() } - unsafe fn dump_all_addresses( + fn dump_all_addresses( &mut self, ifindex: __u32, ) -> Result<(Vec>, Vec<*const nlmsghdr>)> { let mut v: Vec = vec![0; 2048]; - let p = v.as_mut_ptr() as *mut libc::c_char; - let nlh: *mut nlmsghdr = p as *mut nlmsghdr; - let ifa: *mut ifaddrmsg = NLMSG_DATA!(nlh) as *mut ifaddrmsg; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()); - (*nlh).nlmsg_type = RTM_GETADDR; - (*nlh).nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()); + nlh.nlmsg_type = RTM_GETADDR; + nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + self.assign_seqnum(nlh); - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*ifa).ifa_family = libc::AF_UNSPEC as u8; - (*ifa).ifa_index = ifindex; + ifa.ifa_family = libc::AF_UNSPEC as u8; + ifa.ifa_index = ifindex; self.send_message(v.as_mut_slice())?; self.recv_dump_message() } - fn find_link_by_hwaddr(&mut self, hwaddr: &str) -> Result { - let mut hw: Vec = vec![0; 6]; + fn dump_all_routes(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { + let mut v: Vec = vec![0; 2048]; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; + nlh.nlmsg_type = RTM_GETROUTE; + nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + self.assign_seqnum(nlh); + + rtm.rtm_family = libc::AF_INET as u8; + rtm.rtm_table = RT_TABLE_MAIN as u8; + + // Safe because we have allocated enough buffer space. unsafe { - //parse out hwaddr in request - let p = hw.as_mut_ptr() as *mut u8; - let (hw0, hw1, hw2, hw3, hw4, hw5) = scan_fmt!(hwaddr, "{x}:{x}:{x}:{x}:{x}:{x}", - [hex u8], [hex u8], [hex u8], [hex u8], [hex u8], - [hex u8])?; + nlh.addattr32(RTA_TABLE, RT_TABLE_MAIN); + } - hw[0] = hw0; - hw[1] = hw1; - hw[2] = hw2; - hw[3] = hw3; - hw[4] = hw4; - hw[5] = hw5; + self.send_message(v.as_mut_slice())?; - // dump out all links - let (_slv, lv) = self.dump_all_links()?; + self.recv_dump_message() + } - for link in &lv { - let nlh: *const nlmsghdr = *link; - let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; + pub fn find_link_by_hwaddr(&mut self, hwaddr: &str) -> Result { + let hw = parser::parse_mac_addr(hwaddr)?; + let (_slv, lv) = self.dump_all_links()?; - if (*nlh).nlmsg_type != RTM_NEWLINK && (*nlh).nlmsg_type != RTM_DELLINK { - continue; - } + for link in &lv { + // Safe because dump_all_links() return valid pointers. + let nlh = unsafe { &**link }; + if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { + continue; + } - if (*nlh).nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - (*nlh).nlmsg_len, - NLMSG_SPACE!(mem::size_of::()) - ); - break; - } - - let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; + if nlh.nlmsg_len < NLMSG_SPACE!(mem::size_of::()) { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", + nlh.nlmsg_len, + NLMSG_SPACE!(mem::size_of::()) + ); + break; + } + let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; + let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; + let rtalen = IFLA_PAYLOAD!(nlh) as u32; + // Safe because IFLA_RTA and IFLA_PAYLOAD have validated the buffer. + unsafe { let attrs = parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)?; // find the target ifinfomsg - if attrs[IFLA_ADDRESS as usize] as i64 != 0 { + if !attrs[IFLA_ADDRESS as usize].is_null() { + let p = hw.as_ptr() as *const u8 as *const libc::c_void; let a = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const libc::c_void; - if libc::memcmp( - p as *const libc::c_void, - a, - RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]) as libc::size_t, - ) == 0 - { + let sz = RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]) as libc::size_t; + if libc::memcmp(p, a, sz) == 0 { return Ok(ifinfomsg { ..*ifi }); } } } } - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::ENODEV)).into()); + nix_errno(Errno::ENODEV) } - fn find_link_by_name(&mut self, name: &str) -> Result { - let mut v: Vec = vec![0; 2048]; + pub fn find_link_by_name(&mut self, name: &str) -> Result { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated enough buffer size. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; + nlh.nlmsg_type = RTM_GETLINK; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + ifi.ifi_family = libc::AF_UNSPEC as u8; + + // Safe because the data buffer should be big enough. unsafe { - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - (*nlh).nlmsg_type = RTM_GETLINK; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifi).ifi_family = libc::AF_UNSPEC as u8; - - addattr_var( - nlh, - IFLA_IFNAME, - name.as_ptr() as *const u8, - (name.len() + 1), - ); - - addattr32( - nlh, - IFLA_EXT_MASK, - RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS, - ); - - let mut retv = self.rtnl_talk(v.as_mut_slice(), true)?; - - nlh = retv.as_mut_ptr() as *mut nlmsghdr; - ifi = NLMSG_DATA!(nlh) as *mut ifinfomsg; - - return Ok(ifinfomsg { ..*ifi }); - } - } - - fn rtnl_talk(&mut self, data: &mut [u8], answer: bool) -> Result> { - unsafe { - let nlh: *mut nlmsghdr = data.as_mut_ptr() as *mut nlmsghdr; - if !answer { - (*nlh).nlmsg_flags |= NLM_F_ACK; - } - } - - self.send_message(data)?; - unsafe { - loop { - let buf = self.recv_message()?; - let mut msglen = buf.len() as u32; - let mut nlh = buf.as_ptr() as *const nlmsghdr; - - while NLMSG_OK!(nlh, msglen) { - // not for us - - if (*nlh).nlmsg_pid != self.local.nl_pid { - nlh = NLMSG_NEXT!(nlh, msglen); - continue; - } - - if (*nlh).nlmsg_type == NLMSG_ERROR { - // error message, better to return - // error code in error messages - - if (*nlh).nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { - // truncated - return Err( - ErrorKind::ErrorCode("truncated message".to_string()).into() - ); - } - - let el: *const nlmsgerr = NLMSG_DATA!(nlh) as *const nlmsgerr; - - // this is ack. -_- - if (*el).error == 0 { - return Ok(Vec::new()); - } - - return Err( - ErrorKind::Nix(nix::Error::Sys(Errno::from_i32(-(*el).error))).into(), - ); - } - - // goog message - if answer { - // need to copy out data - - let mut d: Vec = vec![0; (*nlh).nlmsg_len as usize]; - let dp: *mut libc::c_void = d.as_mut_ptr() as *mut libc::c_void; - libc::memcpy( - dp, - nlh as *const libc::c_void, - (*nlh).nlmsg_len as libc::size_t, - ); - return Ok(d); - } else { - return Ok(Vec::new()); - } - } - - if !(NLMSG_OK!(nlh, msglen)) { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); - } - } - } - } - - fn set_link_status(&mut self, ifinfo: &ifinfomsg, up: bool) -> Result<()> { - let mut v: Vec = vec![0; 2048]; - unsafe { - let p: *mut u8 = v.as_mut_ptr() as *mut u8; - let mut nlh: *mut nlmsghdr = p as *mut nlmsghdr; - let mut ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - (*nlh).nlmsg_type = RTM_NEWLINK; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifi).ifi_family = ifinfo.ifi_family; - (*ifi).ifi_type = ifinfo.ifi_type; - (*ifi).ifi_index = ifinfo.ifi_index; - - (*ifi).ifi_change |= libc::IFF_UP as u32; - - if up { - (*ifi).ifi_flags |= libc::IFF_UP as u32; + if let Ok(cname) = CString::new(name.as_bytes()) { + nlh.addattr_var(IFLA_IFNAME, cname.as_bytes()); } else { - (*ifi).ifi_flags &= !libc::IFF_UP as u32; + return nix_errno(Errno::EINVAL); + } + nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS); + } + + let ret_val = self.rtnl_talk(v.as_mut_slice(), true)?; + if ret_val.len() < std::mem::size_of::() { + return nix_errno(Errno::EBADMSG); + } + // Safe because we have just validated the returned buffer size. + let nlh = unsafe { &*(ret_val.as_ptr() as *const nlmsghdr) }; + if nlh.nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { + return nix_errno(Errno::EBADMSG); + } + // Safe because we have just validated the returned buffer size. + let ifi = unsafe { &*(NLMSG_DATA!(nlh) as *const ifinfomsg) }; + + Ok(ifinfomsg { ..*ifi }) + } + + pub fn set_link_status(&mut self, ifinfo: &ifinfomsg, up: bool) -> Result<()> { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated a big enough data buffer. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut u8 as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; + nlh.nlmsg_type = RTM_NEWLINK; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + ifi.ifi_family = ifinfo.ifi_family; + ifi.ifi_type = ifinfo.ifi_type; + ifi.ifi_index = ifinfo.ifi_index; + ifi.ifi_change |= libc::IFF_UP as u32; + if up { + ifi.ifi_flags |= libc::IFF_UP as u32; + } else { + ifi.ifi_flags &= !libc::IFF_UP as u32; + } + + self.rtnl_talk(v.as_mut_slice(), false).map(|_| ()) + } + + pub fn get_link_addresses(&mut self, ifinfo: &ifinfomsg) -> Result> { + let mut addrs: Vec = Vec::new(); + let (_sav, av) = self.dump_all_addresses(ifinfo.ifi_index as __u32)?; + + for a in &av { + // Safe because dump_all_addresses returns valid pointers. + let nlh = unsafe { &**a }; + if nlh.nlmsg_type != RTM_NEWADDR { + continue; + } + + let tlen = NLMSG_SPACE!(mem::size_of::()); + if nlh.nlmsg_len < tlen { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", nlh.nlmsg_len, tlen + ); + break; + } + + // Safe because we have just validate the buffer size above. + let ifa = unsafe { &*(NLMSG_DATA!(nlh) as *const ifaddrmsg) }; + if ifa.ifa_flags as u32 & IFA_F_SECONDARY != 0 { + continue; + } + + let rta: *const rtattr = IFA_RTA!(ifa) as *mut rtattr; + let rtalen = IFA_PAYLOAD!(nlh) as u32; + + if ifinfo.ifi_index as u32 == ifa.ifa_index { + // Safe because dump_all_addresses returns valid pointers. + let addr = unsafe { + let attrs = parse_attrs(rta, rtalen, (IFA_MAX + 1) as usize)?; + let t = if !attrs[IFA_ADDRESS as usize].is_null() { + attrs[IFA_ADDRESS as usize] + } else { + attrs[IFA_LOCAL as usize] + }; + getattr_var(t as *const rtattr) + }; + + addrs.push(RtIPAddr { + ip_family: ifa.ifa_family, + ip_mask: ifa.ifa_prefixlen, + addr, + }); } } - self.rtnl_talk(v.as_mut_slice(), false)?; - - Ok(()) + Ok(addrs) } - fn delete_one_addr(&mut self, ifinfo: &ifinfomsg, addr: &RtIPAddr) -> Result<()> { - let mut v: Vec = vec![0; 2048]; + pub fn delete_one_addr(&mut self, ifinfo: &ifinfomsg, addr: &RtIPAddr) -> Result<()> { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated a big enough data buffer. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; + nlh.nlmsg_type = RTM_DELADDR; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + ifa.ifa_family = addr.ip_family; + ifa.ifa_prefixlen = addr.ip_mask; + ifa.ifa_index = ifinfo.ifi_index as u32; + + // Safe because we have allocated a big enough data buffer. unsafe { - let p: *mut u8 = v.as_mut_ptr() as *mut u8; - - let mut nlh: *mut nlmsghdr = p as *mut nlmsghdr; - let mut ifa: *mut ifaddrmsg = NLMSG_DATA!(nlh) as *mut ifaddrmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - (*nlh).nlmsg_type = RTM_DELADDR; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifa).ifa_family = addr.ip_family; - (*ifa).ifa_prefixlen = addr.ip_mask; - (*ifa).ifa_index = ifinfo.ifi_index as u32; - - addattr_var( - nlh, - IFA_ADDRESS, - addr.addr.as_ptr() as *const u8, - addr.addr.len(), - ); + nlh.addattr_var(IFA_ADDRESS, addr.addr.as_ref()); } // ignore EADDRNOTAVAIL here.. @@ -1796,7 +1787,7 @@ impl RtnlHandle { Ok(()) } - fn delete_all_addrs(&mut self, ifinfo: &ifinfomsg, addrs: &Vec) -> Result<()> { + pub fn delete_all_addrs(&mut self, ifinfo: &ifinfomsg, addrs: &[RtIPAddr]) -> Result<()> { for a in addrs { self.delete_one_addr(ifinfo, a)?; } @@ -1804,493 +1795,175 @@ impl RtnlHandle { Ok(()) } - fn get_link_addresses(&mut self, ifinfo: &ifinfomsg) -> Result> { - let mut del_addrs: Vec = Vec::new(); + pub fn add_one_address(&mut self, ifinfo: &ifinfomsg, ip: &RtIPAddr) -> Result<()> { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated a big enough data buffer. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifa = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifaddrmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; + nlh.nlmsg_type = RTM_NEWADDR; + nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + self.assign_seqnum(nlh); + + ifa.ifa_family = ip.ip_family; + ifa.ifa_prefixlen = ip.ip_mask; + ifa.ifa_index = ifinfo.ifi_index as __u32; + + // Safe because we have allocated a big enough data buffer. unsafe { - let (_sav, av) = self.dump_all_addresses(ifinfo.ifi_index as __u32)?; - - for a in &av { - let nlh: *const nlmsghdr = *a; - let ifa: *const ifaddrmsg = NLMSG_DATA!(nlh) as *const ifaddrmsg; - - if (*nlh).nlmsg_type != RTM_NEWADDR { - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if (*nlh).nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - (*nlh).nlmsg_len, - tlen - ); - break; - } - - if (*ifa).ifa_flags as u32 & IFA_F_SECONDARY != 0 { - continue; - } - - let rta: *mut rtattr = IFA_RTA!(ifa) as *mut rtattr; - let rtalen = IFA_PAYLOAD!(nlh) as u32; - - if ifinfo.ifi_index as u32 == (*ifa).ifa_index { - let attrs = parse_attrs(rta, rtalen, (IFA_MAX + 1) as usize)?; - let mut t: *const rtattr = attrs[IFA_LOCAL as usize]; - - if attrs[IFA_ADDRESS as usize] as i64 != 0 { - t = attrs[IFA_ADDRESS as usize]; - } - - let addr = getattr_var(t as *const rtattr); - - del_addrs.push(RtIPAddr { - ip_family: (*ifa).ifa_family, - ip_mask: (*ifa).ifa_prefixlen, - addr, - }); - } - } - } - - Ok(del_addrs) - } - - fn add_one_address(&mut self, ifinfo: &ifinfomsg, ip: &RtIPAddr) -> Result<()> { - let mut v: Vec = vec![0; 2048]; - unsafe { - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut ifa: *mut ifaddrmsg = NLMSG_DATA!(nlh) as *mut ifaddrmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - (*nlh).nlmsg_type = RTM_NEWADDR; - (*nlh).nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifa).ifa_family = ip.ip_family; - (*ifa).ifa_prefixlen = ip.ip_mask; - (*ifa).ifa_index = ifinfo.ifi_index as __u32; - - addattr_var( - nlh, - IFA_ADDRESS, - ip.addr.as_ptr() as *const u8, - ip.addr.len(), - ); - // don't know why need IFA_LOCAL, without it - // kernel returns -EINVAL... - addattr_var(nlh, IFA_LOCAL, ip.addr.as_ptr() as *const u8, ip.addr.len()); - - self.rtnl_talk(v.as_mut_slice(), false)?; - } - - Ok(()) - } - - pub fn update_interface(&mut self, iface: &Interface) -> Result { - // the reliable way to find link is using hardware address - // as filter. However, hardware filter might not be supported - // by netlink, we may have to dump link list and the find the - // target link. filter using name or family is supported, but - // we cannot use that to find target link. - // let's try if hardware address filter works. -_- - - let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; - - // bring down interface if it is up - - if ifinfo.ifi_flags & libc::IFF_UP as u32 != 0 { - self.set_link_status(&ifinfo, false)?; - } - - // delete all addresses associated with the link - let del_addrs: Vec = self.get_link_addresses(&ifinfo)?; - - self.delete_all_addrs(&ifinfo, del_addrs.as_ref())?; - - // add new ip addresses in request - for grpc_addr in &iface.IPAddresses { - let rtip = RtIPAddr::from(grpc_addr.clone()); - self.add_one_address(&ifinfo, &rtip)?; - } - - // set name, set mtu, IFF_NOARP. in one rtnl_talk. - let mut v: Vec = vec![0; 2048]; - unsafe { - let p: *mut u8 = v.as_mut_ptr() as *mut u8; - let mut nlh: *mut nlmsghdr = p as *mut nlmsghdr; - let mut ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::() as u32) as __u32; - (*nlh).nlmsg_type = RTM_NEWLINK; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifi).ifi_family = ifinfo.ifi_family; - (*ifi).ifi_type = ifinfo.ifi_type; - (*ifi).ifi_index = ifinfo.ifi_index; - - if iface.raw_flags & libc::IFF_NOARP as u32 != 0 { - (*ifi).ifi_change |= libc::IFF_NOARP as u32; - (*ifi).ifi_flags |= libc::IFF_NOARP as u32; - } - - addattr32(nlh, IFLA_MTU, iface.mtu as u32); - - // if str is null terminated, use addattr_var - // otherwise, use addattr_str - addattr_var( - nlh, - IFLA_IFNAME, - iface.name.as_ptr() as *const u8, - iface.name.len(), - ); - // addattr_str(nlh, IFLA_IFNAME, iface.name.as_str()); + nlh.addattr_var(IFA_ADDRESS, ip.addr.as_ref()); + // don't know why need IFA_LOCAL, without it kernel returns -EINVAL... + nlh.addattr_var(IFA_LOCAL, ip.addr.as_ref()); } self.rtnl_talk(v.as_mut_slice(), false)?; - let _ = self.set_link_status(&ifinfo, true); - // test remove this link - // let _ = self.remove_interface(iface)?; - - Ok(iface.clone()) - //return Err(ErrorKind::Nix(nix::Error::Sys( - // Errno::EOPNOTSUPP)).into()); + Ok(()) } - fn remove_interface(&mut self, iface: &Interface) -> Result { - let ifinfo = self.find_link_by_hwaddr(iface.hwAddr.as_str())?; - self.set_link_status(&ifinfo, false)?; - - // delete this link per request - let mut v: Vec = vec![0; 2048]; - unsafe { - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - // No attributes needed? - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - (*nlh).nlmsg_type = RTM_DELLINK; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; - - (*ifi).ifi_family = ifinfo.ifi_family; - (*ifi).ifi_index = ifinfo.ifi_index; - (*ifi).ifi_type = ifinfo.ifi_type; - - self.rtnl_talk(v.as_mut_slice(), false)?; - } - - Ok(iface.clone()) - } - - fn get_name_by_index(&mut self, index: i32) -> Result { - let mut v: Vec = vec![0; 2048]; + pub fn get_name_by_index(&mut self, index: i32) -> Result { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; let mut i = 0; - unsafe { - while i < 5 { - i += 1; - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; - (*nlh).nlmsg_type = RTM_GETLINK; - (*nlh).nlmsg_flags = NLM_F_REQUEST; + while i < 5 { + i += 1; + // Safe because we have allocated enough buffer space. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as __u32; + nlh.nlmsg_type = RTM_GETLINK; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); - (*ifi).ifi_index = index; + ifi.ifi_index = index; - addattr32( - nlh, - IFLA_EXT_MASK, - RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS, - ); + unsafe { + nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS); + } - let mut retv = self.rtnl_talk(v.as_mut_slice(), true)?; + let mut retv = self.rtnl_talk(v.as_mut_slice(), true)?; + if retv.len() < std::mem::size_of::() { + return nix_errno(Errno::EBADMSG); + } - let nlh: *mut nlmsghdr = retv.as_mut_ptr() as *mut nlmsghdr; - let ifi: *mut ifinfomsg = NLMSG_DATA!(nlh) as *mut ifinfomsg; + let nlh = unsafe { &mut *(retv.as_mut_ptr() as *mut nlmsghdr) }; + if nlh.nlmsg_type != RTM_NEWLINK && nlh.nlmsg_type != RTM_DELLINK { + info!(sl!(), "wrong message!"); + continue; + } - if (*nlh).nlmsg_type != RTM_NEWLINK && (*nlh).nlmsg_type != RTM_DELLINK { - info!(sl!(), "wrong message!"); - continue; - } + let tlen = NLMSG_SPACE!(mem::size_of::()); + if nlh.nlmsg_len < tlen { + info!(sl!(), "corrupt message?"); + continue; + } - let tlen = NLMSG_SPACE!(mem::size_of::()); - if (*nlh).nlmsg_len < tlen { - info!(sl!(), "corrupt message?"); - continue; - } + let ifi = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut ifinfomsg) }; + let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; + let rtalen = IFLA_PAYLOAD!(nlh) as u32; - let rta: *mut rtattr = IFLA_RTA!(ifi) as *mut rtattr; - let rtalen = IFLA_PAYLOAD!(nlh) as u32; + let attrs = unsafe { parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)? }; - let attrs = parse_attrs(rta, rtalen, (IFLA_MAX + 1) as usize)?; - - let t = attrs[IFLA_IFNAME as usize]; - if t as i64 != 0 { - // we have a name - let tdata = getattr_var(t as *const rtattr); - return Ok(String::from_utf8(tdata)?); - } + let t = attrs[IFLA_IFNAME as usize]; + if !t.is_null() { + // we have a name + let tdata = unsafe { getattr_var(t) }; + return Ok(String::from_utf8(tdata)?); } } - Err(ErrorKind::ErrorCode("no name".to_string()).into()) + nix_errno(Errno::ENOENT) } - pub fn list_routes(&mut self) -> Result> { - // currently, only dump routes from main table for ipv4 - // ie, rtmsg.rtmsg_family = AF_INET, set RT_TABLE_MAIN - // attribute in dump request - // Fix Me: think about othe tables, ipv6.. - let mut rs: Vec = Vec::new(); - - unsafe { - let (_srv, rv) = self.dump_all_route_msgs()?; - - // parse out routes and store in rs - for r in &rv { - let nlh: *const nlmsghdr = *r; - let rtm: *const rtmsg = NLMSG_DATA!(nlh) as *const rtmsg; - - if (*nlh).nlmsg_type != RTM_NEWROUTE && (*nlh).nlmsg_type != RTM_DELROUTE { - info!(sl!(), "not route message!"); - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if (*nlh).nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", - (*nlh).nlmsg_len, - tlen - ); - break; - } - - let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; - - if (*rtm).rtm_table != RT_TABLE_MAIN as u8 { - continue; - } - - let rtalen = RTM_PAYLOAD!(nlh) as u32; - - let attrs = parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)?; - - let t = attrs[RTA_TABLE as usize]; - if t as i64 != 0 { - let table = getattr32(t); - if table != RT_TABLE_MAIN { - continue; - } - } - // find source, destination, gateway, scope, and - // and device name - - let mut t = attrs[RTA_DST as usize]; - let mut rte: Route = Route::default(); - - // destination - if t as i64 != 0 { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - rte.dest = format!("{}/{}", format_address(data, len)?, (*rtm).rtm_dst_len); - } - - // gateway - t = attrs[RTA_GATEWAY as usize]; - if t as i64 != 0 { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - rte.gateway = format_address(data, len)?; - - // for gateway, destination is 0.0.0.0 - rte.dest = "0.0.0.0".to_string(); - } - - // source - t = attrs[RTA_SRC as usize]; - - if t as i64 == 0 { - t = attrs[RTA_PREFSRC as usize]; - } - - if t as i64 != 0 { - let data: *const u8 = RTA_DATA!(t) as *const u8; - let len = RTA_PAYLOAD!(t) as u32; - - rte.source = format_address(data, len)?; - - if (*rtm).rtm_src_len != 0 { - rte.source = format!("{}/{}", rte.source.as_str(), (*rtm).rtm_src_len); - } - } - - // scope - rte.scope = (*rtm).rtm_scope as u32; - - // oif - t = attrs[RTA_OIF as usize]; - if t as i64 != 0 { - let data: *const i32 = RTA_DATA!(t) as *const i32; - assert_eq!(RTA_PAYLOAD!(t), 4); - - /* - - let mut n: Vec = vec![0; libc::IF_NAMESIZE]; - let np: *mut libc::c_char = n.as_mut_ptr() as *mut libc::c_char; - let tn = libc::if_indextoname(*data as u32, - np); - - if tn as i64 == 0 { - info!(sl!(), "no name?"); - } else { - info!(sl!(), "name(indextoname): {}", String::from_utf8(n)?); - } - // std::process::exit(-1); - */ - - rte.device = self - .get_name_by_index(*data) - .unwrap_or("unknown".to_string()); - } - - rs.push(rte); - } - } - - Ok(rs) - } - - unsafe fn dump_all_route_msgs(&mut self) -> Result<(Vec>, Vec<*const nlmsghdr>)> { - let mut v: Vec = vec![0; 2048]; - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut rtm: *mut rtmsg = NLMSG_DATA!(nlh) as *mut rtmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - (*nlh).nlmsg_type = RTM_GETROUTE; - (*nlh).nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*rtm).rtm_family = libc::AF_INET as u8; - (*rtm).rtm_table = RT_TABLE_MAIN as u8; - - addattr32(nlh, RTA_TABLE, RT_TABLE_MAIN); - - self.send_message(v.as_mut_slice())?; - - self.recv_dump_message() - } - - fn get_all_routes(&mut self) -> Result> { + pub fn get_all_routes(&mut self) -> Result> { let mut rs: Vec = Vec::new(); + let (_srv, rv) = self.dump_all_routes()?; - unsafe { - let (_srv, rv) = self.dump_all_route_msgs()?; + for r in &rv { + // Safe because dump_all_routes() return valid pointers. + let nlh = unsafe { &**r }; + let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; - for r in &rv { - let nlh: *const nlmsghdr = *r; - let rtm: *const rtmsg = NLMSG_DATA!(nlh) as *const rtmsg; - - if (*nlh).nlmsg_type != RTM_NEWROUTE && (*nlh).nlmsg_type != RTM_DELROUTE { - info!(sl!(), "not route message!"); - continue; - } - - let tlen = NLMSG_SPACE!(mem::size_of::()); - if (*nlh).nlmsg_len < tlen { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", - (*nlh).nlmsg_len, - tlen - ); - break; - } - - if (*rtm).rtm_table != RT_TABLE_MAIN as u8 { - continue; - } - - let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; - let rtalen = RTM_PAYLOAD!(nlh) as u32; - - let attrs = parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)?; - - let t = attrs[RTA_TABLE as usize]; - if t as i64 != 0 { - let table = getattr32(t); - if table != RT_TABLE_MAIN { - continue; - } - } - - // find source, destination, gateway, scope, and - // and device name - - let mut t = attrs[RTA_DST as usize]; - let mut rte: RtRoute = RtRoute::default(); - - rte.dst_len = (*rtm).rtm_dst_len; - rte.src_len = (*rtm).rtm_src_len; - rte.dest = None; - rte.protocol = (*rtm).rtm_protocol; - // destination - if t as i64 != 0 { - rte.dest = Some(getattr_var(t as *const rtattr)); - } - - // gateway - t = attrs[RTA_GATEWAY as usize]; - if t as i64 != 0 { - rte.gateway = Some(getattr_var(t as *const rtattr)); - if rte.dest.is_none() { - rte.dest = Some(vec![0 as u8; 4]); - } - } - - // source - t = attrs[RTA_SRC as usize]; - - if t as i64 == 0 { - t = attrs[RTA_PREFSRC as usize]; - } - - if t as i64 != 0 { - rte.source = Some(getattr_var(t as *const rtattr)); - } - - // scope - rte.scope = (*rtm).rtm_scope; - - // oif - t = attrs[RTA_OIF as usize]; - if t as i64 != 0 { - rte.index = getattr32(t as *const rtattr) as i32; - } - - rs.push(rte); + if nlh.nlmsg_type != RTM_NEWROUTE && nlh.nlmsg_type != RTM_DELROUTE { + info!(sl!(), "not route message!"); + continue; } + + let tlen = NLMSG_SPACE!(mem::size_of::()); + if nlh.nlmsg_len < tlen { + info!( + sl!(), + "invalid nlmsg! nlmsg_len: {}, nlmsg_spae: {}", nlh.nlmsg_len, tlen + ); + break; + } + + if rtm.rtm_table != RT_TABLE_MAIN as u8 { + continue; + } + + let rta: *mut rtattr = RTM_RTA!(rtm) as *mut rtattr; + let rtalen = RTM_PAYLOAD!(nlh) as u32; + + let attrs = unsafe { parse_attrs(rta, rtalen, (RTA_MAX + 1) as usize)? }; + + let t = attrs[RTA_TABLE as usize]; + if !t.is_null() { + let table = unsafe { getattr32(t) }; + if table != RT_TABLE_MAIN { + continue; + } + } + + // find source, destination, gateway, scope, and device name + let mut t = attrs[RTA_DST as usize]; + let mut rte: RtRoute = RtRoute::default(); + + rte.dst_len = (*rtm).rtm_dst_len; + rte.src_len = (*rtm).rtm_src_len; + rte.dest = None; + rte.protocol = (*rtm).rtm_protocol; + // destination + if !t.is_null() { + rte.dest = Some(unsafe { getattr_var(t as *const rtattr) }); + } + + // gateway + t = attrs[RTA_GATEWAY as usize]; + if !t.is_null() { + rte.gateway = Some(unsafe { getattr_var(t as *const rtattr) }); + if rte.dest.is_none() { + rte.dest = Some(vec![0 as u8; 4]); + } + } + + // source + t = attrs[RTA_SRC as usize]; + if t.is_null() { + t = attrs[RTA_PREFSRC as usize]; + } + if !t.is_null() { + rte.source = Some(unsafe { getattr_var(t as *const rtattr) }); + } + + // scope + rte.scope = rtm.rtm_scope; + + // oif + t = attrs[RTA_OIF as usize]; + if !t.is_null() { + rte.index = unsafe { getattr32(t as *const rtattr) as i32 }; + } + + rs.push(rte); } Ok(rs) } - fn delete_all_routes(&mut self, rs: &Vec) -> Result<()> { + pub fn delete_all_routes(&mut self, rs: &[RtRoute]) -> Result<()> { for r in rs { let name = self.get_name_by_index(r.index)?; if name.as_str().contains("lo") || name.as_str().contains("::1") { @@ -2307,191 +1980,138 @@ impl RtnlHandle { Ok(()) } - fn add_one_route(&mut self, r: &RtRoute) -> Result<()> { - let mut v: Vec = vec![0; 2048]; + pub fn add_one_route(&mut self, r: &RtRoute) -> Result<()> { + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated a big enough data buffer. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; + nlh.nlmsg_type = RTM_NEWROUTE; + nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + self.assign_seqnum(nlh); + + rtm.rtm_family = libc::AF_INET as u8; + rtm.rtm_table = RT_TABLE_MAIN as u8; + rtm.rtm_scope = RT_SCOPE_NOWHERE; + rtm.rtm_protocol = RTPROTO_BOOT; + rtm.rtm_scope = RT_SCOPE_UNIVERSE; + rtm.rtm_type = RTN_UNICAST; + + rtm.rtm_dst_len = r.dst_len; + rtm.rtm_src_len = r.src_len; + rtm.rtm_scope = r.scope; unsafe { - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut rtm: *mut rtmsg = NLMSG_DATA!(nlh) as *mut rtmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - (*nlh).nlmsg_type = RTM_NEWROUTE; - (*nlh).nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*rtm).rtm_family = libc::AF_INET as u8; - (*rtm).rtm_table = RT_TABLE_MAIN as u8; - (*rtm).rtm_scope = RT_SCOPE_NOWHERE; - (*rtm).rtm_protocol = RTPROTO_BOOT; - (*rtm).rtm_scope = RT_SCOPE_UNIVERSE; - (*rtm).rtm_type = RTN_UNICAST; - - (*rtm).rtm_dst_len = r.dst_len; - (*rtm).rtm_src_len = r.src_len; - (*rtm).rtm_scope = r.scope; - - if r.source.is_some() { - let len = r.source.as_ref().unwrap().len(); + if let Some(source) = r.source.as_ref() { if r.src_len > 0 { - addattr_var( - nlh, - RTA_SRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_SRC, source.as_ref()); } else { - addattr_var( - nlh, - RTA_PREFSRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_PREFSRC, source.as_ref()); } } - if r.dest.is_some() { - let len = r.dest.as_ref().unwrap().len(); - addattr_var( - nlh, - RTA_DST, - r.dest.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + if let Some(dest) = r.dest.as_ref() { + nlh.addattr_var(RTA_DST, dest.as_ref()); } - if r.gateway.is_some() { - let len = r.gateway.as_ref().unwrap().len(); - addattr_var( - nlh, - RTA_GATEWAY, - r.gateway.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + if let Some(gateway) = r.gateway.as_ref() { + nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); } - addattr32(nlh, RTA_OIF, r.index as u32); - - self.rtnl_talk(v.as_mut_slice(), false)?; + nlh.addattr32(RTA_OIF, r.index as u32); } + + self.rtnl_talk(v.as_mut_slice(), false)?; + Ok(()) } - fn delete_one_route(&mut self, r: &RtRoute) -> Result<()> { + pub fn delete_one_route(&mut self, r: &RtRoute) -> Result<()> { info!(sl!(), "delete route"); - let mut v: Vec = vec![0; 2048]; + let mut v: Vec = vec![0; DEFAULT_NETLINK_BUF_SIZE]; + // Safe because we have allocated a big enough data buffer. + let nlh = unsafe { &mut *(v.as_mut_ptr() as *mut nlmsghdr) }; + let rtm = unsafe { &mut *(NLMSG_DATA!(nlh) as *mut rtmsg) }; + + nlh.nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; + nlh.nlmsg_type = RTM_DELROUTE; + nlh.nlmsg_flags = NLM_F_REQUEST; + self.assign_seqnum(nlh); + + rtm.rtm_family = libc::AF_INET as u8; + rtm.rtm_table = RT_TABLE_MAIN as u8; + rtm.rtm_scope = RT_SCOPE_NOWHERE; + + rtm.rtm_dst_len = r.dst_len; + rtm.rtm_src_len = r.src_len; + rtm.rtm_scope = r.scope; + + // Safe because we have allocated a big enough data buffer. unsafe { - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut rtm: *mut rtmsg = NLMSG_DATA!(nlh) as *mut rtmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - (*nlh).nlmsg_type = RTM_DELROUTE; - (*nlh).nlmsg_flags = NLM_F_REQUEST; - - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*rtm).rtm_family = libc::AF_INET as u8; - (*rtm).rtm_table = RT_TABLE_MAIN as u8; - (*rtm).rtm_scope = RT_SCOPE_NOWHERE; - - (*rtm).rtm_dst_len = r.dst_len; - (*rtm).rtm_src_len = r.src_len; - (*rtm).rtm_scope = r.scope; - - if r.source.is_some() { - let len = r.source.as_ref().unwrap().len(); + if let Some(source) = r.source.as_ref() { if r.src_len > 0 { - addattr_var( - nlh, - RTA_SRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_SRC, source.as_ref()); } else { - addattr_var( - nlh, - RTA_PREFSRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_PREFSRC, source.as_ref()); } } - if r.dest.is_some() { - let len = r.dest.as_ref().unwrap().len(); - addattr_var( - nlh, - RTA_DST, - r.dest.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + if let Some(dest) = r.dest.as_ref() { + nlh.addattr_var(RTA_DST, dest.as_ref()); } - if r.gateway.is_some() { - let len = r.gateway.as_ref().unwrap().len(); - addattr_var( - nlh, - RTA_GATEWAY, - r.gateway.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + if let Some(gateway) = r.gateway.as_ref() { + nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); } - addattr32(nlh, RTA_OIF, r.index as u32); - self.rtnl_talk(v.as_mut_slice(), false)?; + nlh.addattr32(RTA_OIF, r.index as u32); } + self.rtnl_talk(v.as_mut_slice(), false)?; + Ok(()) } - pub fn update_routes(&mut self, rt: &Vec) -> Result> { - let rs = self.get_all_routes()?; - self.delete_all_routes(&rs)?; - - for grpcroute in rt { - if grpcroute.gateway.as_str() == "" { - let r = RtRoute::from(grpcroute.clone()); - if r.index == -1 { - continue; - } - self.add_one_route(&r)?; - } - } - - for grpcroute in rt { - if grpcroute.gateway.as_str() != "" { - let r = RtRoute::from(grpcroute.clone()); - if r.index == -1 { - continue; - } - self.add_one_route(&r)?; - } - } - - Ok(rt.clone()) - } pub fn handle_localhost(&mut self) -> Result<()> { let ifi = self.find_link_by_name("lo")?; self.set_link_status(&ifi, true) } + + fn assign_seqnum(&mut self, nlh: &mut nlmsghdr) { + self.seq += 1; + self.dump = self.seq; + nlh.nlmsg_seq = self.seq; + } } -unsafe fn parse_attrs( - mut rta: *mut rtattr, +impl Drop for RtnlHandle { + fn drop(&mut self) { + if self.fd >= 0 { + unsafe { + libc::close(self.fd); + } + self.fd = -1; + } + } +} + +/// Parse netlink attributes from raw buffer. +/// +/// # Safety +/// Caller needs to ensure that rta and rtalen are valid. +pub unsafe fn parse_attrs( + mut rta: *const rtattr, mut rtalen: u32, max: usize, ) -> Result> { - let mut attrs: Vec<*const rtattr> = vec![0 as *const rtattr; max as usize]; + let mut attrs: Vec<*const rtattr> = vec![std::ptr::null(); max as usize]; while RTA_OK!(rta, rtalen) { let rtype = (*rta).rta_type as usize; - if rtype < max && attrs[rtype] as i64 == 0 { + if rtype < max && attrs[rtype].is_null() { attrs[rtype] = rta as *const rtattr; } @@ -2501,106 +2121,123 @@ unsafe fn parse_attrs( Ok(attrs) } -unsafe fn addattr_var(mut nlh: *mut nlmsghdr, cat: u16, data: *const u8, len: usize) { - let mut rta: *mut rtattr = NLMSG_TAIL!(nlh) as *mut rtattr; - let alen = RTA_LENGTH!(len) as u16; +impl nlmsghdr { + /// Add an variable attribute to the netlink message. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr_var(&mut self, cat: u16, data: &[u8]) { + let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; + let alen = RTA_LENGTH!(data.len()) as u16; - (*rta).rta_type = cat; - (*rta).rta_len = alen; + (*rta).rta_type = cat; + (*rta).rta_len = alen; + + if !data.is_empty() { + libc::memcpy( + RTA_DATA!(rta) as *mut libc::c_void, + data.as_ptr() as *const libc::c_void, + data.len(), + ); + } + + self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!(alen); + } + + /// Add a string attribute to the netlink message. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr_str(&mut self, cat: u16, data: &str) { + let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; + let len = data.len(); + let alen = RTA_LENGTH!(len + 1) as u16; + let tp: *mut libc::c_void = RTA_DATA!(rta) as *mut libc::c_void; + + (*rta).rta_type = cat; + (*rta).rta_len = alen; - if len > 0 { libc::memcpy( - RTA_DATA!(rta) as *mut libc::c_void, - data as *const libc::c_void, - len, + tp, + data.as_ptr() as *const libc::c_void, + len as libc::size_t, ); + + self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!(alen); } - (*nlh).nlmsg_len = NLMSG_ALIGN!((*nlh).nlmsg_len) + RTA_ALIGN!(alen); -} + /// Add an 1/2/4/8 bytes attribute to the netlink message. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr_size(&mut self, cat: u16, val: u64, size: u8) { + assert_eq!(size == 1 || size == 2 || size == 4 || size == 8, true); -unsafe fn addattr_str(mut nlh: *mut nlmsghdr, cat: u16, data: &str) { - let mut rta: *mut rtattr = NLMSG_TAIL!(nlh) as *mut rtattr; - let len = data.len(); - let alen = RTA_LENGTH!(len + 1) as u16; - let tp: *mut libc::c_void = RTA_DATA!(rta) as *mut libc::c_void; + let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; + (*rta).rta_type = cat; - (*rta).rta_type = cat; - (*rta).rta_len = alen; + if size == 1 { + let data: *mut u8 = RTA_DATA!(rta) as *mut u8; + *data = val as u8; + let len = RTA_LENGTH!(1) as u16; + (*rta).rta_len = len; + } - libc::memcpy( - tp, - data.as_ptr() as *const libc::c_void, - len as libc::size_t, - ); + if size == 2 { + let data: *mut u16 = RTA_DATA!(rta) as *mut u16; + *data = val as u16; + let len = RTA_LENGTH!(2) as u16; + (*rta).rta_len = len; + } - (*nlh).nlmsg_len = NLMSG_ALIGN!((*nlh).nlmsg_len) + RTA_ALIGN!(alen); -} + if size == 4 { + let data: *mut u32 = RTA_DATA!(rta) as *mut u32; + *data = val as u32; + let len = RTA_LENGTH!(4) as u16; + (*rta).rta_len = len; + } -unsafe fn addattr_size(mut nlh: *mut nlmsghdr, cat: u16, val: u64, size: u8) { - assert_eq!(size == 1 || size == 2 || size == 4 || size == 8, true); + if size == 8 { + let data: *mut u64 = RTA_DATA!(rta) as *mut u64; + *data = val as u64; + let len = RTA_LENGTH!(8) as u16; + (*rta).rta_len = len; + } - let mut rta: *mut rtattr = NLMSG_TAIL!(nlh) as *mut rtattr; - (*rta).rta_type = cat; - - if size == 1 { - let data: *mut u8 = RTA_DATA!(rta) as *mut u8; - *data = val as u8; - let len = RTA_LENGTH!(1) as u16; - (*rta).rta_len = len; + self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!((*rta).rta_len); } - if size == 2 { - let data: *mut u16 = RTA_DATA!(rta) as *mut u16; - *data = val as u16; - let len = RTA_LENGTH!(2) as u16; - (*rta).rta_len = len; + /// Add a 8-bit attribute. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr8(&mut self, cat: u16, val: u8) { + self.addattr_size(cat, val as u64, 1); } - if size == 4 { - let data: *mut u32 = RTA_DATA!(rta) as *mut u32; - *data = val as u32; - let len = RTA_LENGTH!(4) as u16; - (*rta).rta_len = len; + /// Add a 16-bit attribute. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr16(&mut self, cat: u16, val: u16) { + self.addattr_size(cat, val as u64, 2); } - if size == 8 { - let data: *mut u64 = RTA_DATA!(rta) as *mut u64; - *data = val as u64; - let len = RTA_LENGTH!(8) as u16; - (*rta).rta_len = len; + /// Add a 32-bit attribute. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr32(&mut self, cat: u16, val: u32) { + self.addattr_size(cat, val as u64, 4); } - (*nlh).nlmsg_len = NLMSG_ALIGN!((*nlh).nlmsg_len) + RTA_ALIGN!((*rta).rta_len); -} - -unsafe fn addattr8(nlh: *mut nlmsghdr, cat: u16, val: u8) { - addattr_size(nlh, cat, val as u64, 1); -} - -unsafe fn addattr16(nlh: *mut nlmsghdr, cat: u16, val: u16) { - addattr_size(nlh, cat, val as u64, 2); -} - -unsafe fn addattr32(nlh: *mut nlmsghdr, cat: u16, val: u32) { - addattr_size(nlh, cat, val as u64, 4); -} - -unsafe fn addattr64(nlh: *mut nlmsghdr, cat: u16, val: u64) { - addattr_size(nlh, cat, val, 8); -} - -unsafe fn getattr_var(rta: *const rtattr) -> Vec { - assert_ne!(rta as i64, 0); - let data: *const libc::c_void = RTA_DATA!(rta) as *const libc::c_void; - let alen: usize = RTA_PAYLOAD!(rta) as usize; - - let mut v: Vec = vec![0; alen]; - let tp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; - - libc::memcpy(tp, data, alen as libc::size_t); - - v + /// Add a 64-bit attribute. + /// + /// # Safety + /// Caller needs to ensure that there are enough space after `self` to store the attribute. + pub unsafe fn addattr64(&mut self, cat: u16, val: u64) { + self.addattr_size(cat, val, 8); + } } unsafe fn getattr_size(rta: *const rtattr) -> u64 { @@ -2631,230 +2268,79 @@ unsafe fn getattr_size(rta: *const rtattr) -> u64 { panic!("impossible!"); } -unsafe fn getattr8(rta: *const rtattr) -> u8 { +/// Get a 8-bit attribute. +/// +/// # Safety +/// Caller needs to ensure that there are enough space after `rta` to read the attribute. +pub unsafe fn getattr8(rta: *const rtattr) -> u8 { let alen = RTA_PAYLOAD!(rta); - assert!(alen == 1); + assert_eq!(alen, 1); getattr_size(rta) as u8 } -unsafe fn getattr16(rta: *const rtattr) -> u16 { +/// Get a 16-bit attribute. +/// +/// # Safety +/// Caller needs to ensure that there are enough space after `rta` to read the attribute. +pub unsafe fn getattr16(rta: *const rtattr) -> u16 { let alen = RTA_PAYLOAD!(rta); - assert!(alen == 2); + assert_eq!(alen, 2); getattr_size(rta) as u16 } -unsafe fn getattr32(rta: *const rtattr) -> u32 { +/// Get a 32-bit attribute. +/// +/// # Safety +/// Caller needs to ensure that there are enough space after `rta` to read the attribute. +pub unsafe fn getattr32(rta: *const rtattr) -> u32 { let alen = RTA_PAYLOAD!(rta); - assert!(alen == 4); + assert_eq!(alen, 4); getattr_size(rta) as u32 } -unsafe fn getattr64(rta: *const rtattr) -> u64 { +/// Get a 64-bit attribute. +/// +/// # Safety +/// Caller needs to ensure that there are enough space after `rta` to read the attribute. +pub unsafe fn getattr64(rta: *const rtattr) -> u64 { let alen = RTA_PAYLOAD!(rta); - assert!(alen == 8); + assert_eq!(alen, 8); getattr_size(rta) } -unsafe fn format_address(addr: *const u8, len: u32) -> Result { - let mut a: String; - if len == 4 { - // ipv4 - let mut i = 1; - let mut p = addr as i64; +/// Get a variable length attribute. +/// +/// # Safety +/// Caller needs to ensure that there are enough space after `rta` to read the attribute. +pub unsafe fn getattr_var(rta: *const rtattr) -> Vec { + assert_ne!(rta as i64, 0); + let data: *const libc::c_void = RTA_DATA!(rta) as *const libc::c_void; + let alen: usize = RTA_PAYLOAD!(rta) as usize; - a = format!("{}", *(p as *const u8)); - while i < len { - p += 1; - i += 1; - a.push_str(format!(".{}", *(p as *const u8)).as_str()); - } + let mut v: Vec = vec![0; alen]; + let tp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; - return Ok(a); - } + libc::memcpy(tp, data, alen as libc::size_t); - if len == 6 { - // hwaddr - let mut i = 1; - let mut p = addr as i64; - - a = format!("{:0<2X}", *(p as *const u8)); - while i < len { - p += 1; - i += 1; - a.push_str(format!(":{:0<2X}", *(p as *const u8)).as_str()); - } - - return Ok(a); - } - - if len == 16 { - // ipv6 - let p = addr as *const u8 as *const libc::c_void; - let mut ar: [u8; 16] = [0; 16]; - let mut v: Vec = vec![0; 16]; - let dp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; - libc::memcpy(dp, p, 16); - - ar.copy_from_slice(v.as_slice()); - - return Ok(Ipv6Addr::from(ar).to_string()); - - /* - let l = len / 2; - - a = format!("{:0<4X}", *(p as *const u16)); - - while i < l { - p += 2; - i += 1; - a.push_str(format!(":{:0<4X}", *(p as *const u16)).as_str()); - } - */ - } - - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); + v } -impl Drop for RtnlHandle { - fn drop(&mut self) { - unsafe { - libc::close(self.fd); - } - } +#[inline] +fn nix_last_os_err() -> Result { + Err(nix::Error::Sys(Errno::last())) } -pub struct RtRoute { - pub dest: Option>, - pub source: Option>, - pub gateway: Option>, - pub index: i32, - pub scope: u8, - pub dst_len: u8, - pub src_len: u8, - pub protocol: u8, -} - -impl Default for RtRoute { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -fn parse_cidripv4(s: &str) -> Result<(Vec, u8)> { - let (a0, a1, a2, a3, len) = scan_fmt!(s, "{}.{}.{}.{}/{}", u8, u8, u8, u8, u8)?; - let ip: Vec = vec![a0, a1, a2, a3]; - Ok((ip, len)) -} - -fn parse_ipv4(s: &str) -> Result> { - let (a0, a1, a2, a3) = scan_fmt!(s, "{}.{}.{}.{}", u8, u8, u8, u8)?; - let ip: Vec = vec![a0, a1, a2, a3]; - - Ok(ip) -} - -fn parse_ipaddr(s: &str) -> Result> { - if let Ok(v6) = Ipv6Addr::from_str(s) { - return Ok(Vec::from(v6.octets().as_ref())); - } - - // v4 - Ok(Vec::from(Ipv4Addr::from_str(s)?.octets().as_ref())) -} - -fn parse_cider(s: &str) -> Result<(Vec, u8)> { - let (addr, mask) = if s.contains("/") { - scan_fmt!(s, "{}/{}", String, u8)? - } else { - (s.to_string(), 0) - }; - - Ok((parse_ipaddr(addr.as_str())?, mask)) -} - -impl From for RtRoute { - fn from(r: Route) -> Self { - // only handle ipv4 - - let index = { - let mut rh = RtnlHandle::new(NETLINK_ROUTE, 0).unwrap(); - match rh.find_link_by_name(r.device.as_str()) { - Ok(ifi) => ifi.ifi_index, - Err(_) => -1, - } - }; - - let (dest, dst_len) = if r.dest.is_empty() { - (Some(vec![0 as u8; 4]), 0) - } else { - let (dst, mask) = parse_cider(r.dest.as_str()).unwrap(); - (Some(dst), mask) - }; - - let (source, src_len) = if r.source.is_empty() { - (None, 0) - } else { - let (src, mask) = parse_cider(r.source.as_str()).unwrap(); - (Some(src), mask) - }; - - let gateway = if r.gateway.is_empty() { - None - } else { - Some(parse_ipaddr(r.gateway.as_str()).unwrap()) - }; - - /* - let (dest, dst_len) = if gateway.is_some() { - (vec![0 as u8; 4], 0) - } else { - (tdest, tdst_len) - }; - */ - Self { - dest, - source, - src_len, - dst_len, - index, - gateway, - scope: r.scope as u8, - protocol: RTPROTO_UNSPEC, - } - } -} - -pub struct RtIPAddr { - pub ip_family: __u8, - pub ip_mask: __u8, - pub addr: Vec, -} - -impl From for RtIPAddr { - fn from(ipi: IPAddress) -> Self { - let ip_family = if ipi.family == IPFamily::v4 { - libc::AF_INET - } else { - libc::AF_INET6 - } as __u8; - - let ip_mask = scan_fmt!(ipi.mask.as_str(), "{}", u8).unwrap(); - - let addr = parse_ipaddr(ipi.address.as_ref()).unwrap(); - - Self { - ip_family, - ip_mask, - addr, - } - } +#[inline] +fn nix_errno(err: Errno) -> Result { + Err(nix::Error::Sys(err)) } #[cfg(test)] mod tests { - use crate::{nlmsghdr, NLMSG_ALIGNTO, RTA_ALIGNTO, RTM_BASE}; + use super::*; use libc; use std::mem; + #[test] fn test_macro() { println!("{}", RTA_ALIGN!(10)); diff --git a/src/agent/netlink/src/neigh.rs b/src/agent/netlink/src/neigh.rs deleted file mode 100644 index 6134f8a91..000000000 --- a/src/agent/netlink/src/neigh.rs +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright (c) 2020 Ant Financial -// -// SPDX-License-Identifier: Apache-2.0 -// - -use crate::{ - __s32, __u16, __u8, addattr_var, ifinfomsg, nlmsghdr, parse_ipaddr, IFA_F_PERMANENT, - NLMSG_ALIGNTO, NLM_F_CREATE, NLM_F_EXCL, NLM_F_REQUEST, RTM_NEWNEIGH, -}; -use crate::{NLMSG_ALIGN, NLMSG_DATA, NLMSG_HDRLEN, NLMSG_LENGTH}; -use protocols::types::ARPNeighbor; -use rustjail::errors::*; -use std::mem; - -#[repr(C)] -#[derive(Copy)] -pub struct ndmsg { - ndm_family: __u8, - ndm_pad1: __u8, - ndm_pad: __u16, - ndm_ifindex: __s32, - ndm_state: __u16, - ndm_flags: __u8, - ndm_type: __u8, -} - -pub const NDA_UNSPEC: __u16 = 0; -pub const NDA_DST: __u16 = 1; -pub const NDA_LLADDR: __u16 = 2; -pub const NDA_CACHEINFO: __u16 = 3; -pub const NDA_PROBES: __u16 = 4; -pub const NDA_VLAN: __u16 = 5; -pub const NDA_PORT: __u16 = 6; -pub const NDA_VNI: __u16 = 7; -pub const NDA_IFINDEX: __u16 = 8; -pub const NDA_MASTER: __u16 = 9; -pub const NDA_LINK_NETNSID: __u16 = 10; -pub const NDA_SRC_VNI: __u16 = 11; -pub const __NDA_MAX: __u16 = 12; - -impl Clone for ndmsg { - fn clone(&self) -> Self { - Self { ..*self } - } -} - -impl Default for ndmsg { - fn default() -> Self { - unsafe { mem::zeroed::() } - } -} - -impl crate::RtnlHandle { - pub fn add_arp_neighbors(&mut self, neighs: &Vec) -> Result<()> { - for neigh in neighs { - self.add_one_arp_neighbor(&neigh)?; - } - - Ok(()) - } - pub fn add_one_arp_neighbor(&mut self, neigh: &ARPNeighbor) -> Result<()> { - let dev: ifinfomsg; - - match self.find_link_by_name(&neigh.device) { - Ok(d) => dev = d, - Err(e) => { - return Err(ErrorKind::ErrorCode(format!( - "Could not find link from device {}: {}", - neigh.device, e - )) - .into()); - } - } - - if neigh.toIPAddress.is_none() { - return Err(ErrorKind::ErrorCode("toIPAddress is required".to_string()).into()); - } - - let to_ip = &neigh.toIPAddress.as_ref().unwrap().address; - if to_ip.is_empty() { - return Err(ErrorKind::ErrorCode("toIPAddress.address is required".to_string()).into()); - } - - let mut v: Vec = vec![0; 2048]; - unsafe { - // init - let mut nlh: *mut nlmsghdr = v.as_mut_ptr() as *mut nlmsghdr; - let mut ndm: *mut ndmsg = NLMSG_DATA!(nlh) as *mut ndmsg; - - (*nlh).nlmsg_len = NLMSG_LENGTH!(mem::size_of::()) as u32; - (*nlh).nlmsg_type = RTM_NEWNEIGH; - (*nlh).nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; - - (*ndm).ndm_family = libc::AF_UNSPEC as __u8; - (*ndm).ndm_state = IFA_F_PERMANENT as __u16; - - // process lladdr - if neigh.lladdr != "" { - let llabuf = parse_mac(&neigh.lladdr)?; - - addattr_var(nlh, NDA_LLADDR, llabuf.as_ptr() as *const u8, llabuf.len()); - } - - // process destination - let (family, ip_data) = parse_addr(&to_ip)?; - (*ndm).ndm_family = family; - addattr_var(nlh, NDA_DST, ip_data.as_ptr() as *const u8, ip_data.len()); - - // process state - if neigh.state != 0 { - (*ndm).ndm_state = neigh.state as __u16; - } - - // process flags - (*ndm).ndm_flags = (*ndm).ndm_flags | neigh.flags as __u8; - - // process dev - (*ndm).ndm_ifindex = dev.ifi_index; - - // send - self.rtnl_talk(v.as_mut_slice(), false)?; - } - - Ok(()) - } -} - -fn parse_mac(hwaddr: &str) -> Result> { - let mut hw: Vec = vec![0; 6]; - - let (hw0, hw1, hw2, hw3, hw4, hw5) = scan_fmt!(hwaddr, "{x}:{x}:{x}:{x}:{x}:{x}", - [hex u8], [hex u8], [hex u8], [hex u8], [hex u8], - [hex u8])?; - - hw[0] = hw0; - hw[1] = hw1; - hw[2] = hw2; - hw[3] = hw3; - hw[4] = hw4; - hw[5] = hw5; - - Ok(hw) -} - -fn parse_addr(ip_address: &str) -> Result<(__u8, Vec)> { - let ip_data = parse_ipaddr(ip_address)?; - let family: __u8; - - // ipv6 - if ip_data.len() == 16 { - family = libc::AF_INET6 as __u8; - } else { - family = libc::AF_INET as __u8; - } - - Ok((family, ip_data)) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{RtnlHandle, NETLINK_ROUTE}; - use protocols::types::IPAddress; - use std::process::Command; - - fn clean_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { - // ip link delete dummy - Command::new("ip") - .args(&["link", "delete", dummy_name]) - .output() - .expect("prepare: failed to delete dummy"); - - // ip neigh del dev dummy ip - Command::new("ip") - .args(&["neigh", "del", dummy_name, ip]) - .output() - .expect("prepare: failed to delete neigh"); - } - - fn prepare_env_for_test_add_one_arp_neighbor(dummy_name: &str, ip: &str) { - clean_env_for_test_add_one_arp_neighbor(dummy_name, ip); - // modprobe dummy - Command::new("modprobe") - .arg("dummy") - .output() - .expect("failed to run modprobe dummy"); - - // ip link add dummy type dummy - Command::new("ip") - .args(&["link", "add", dummy_name, "type", "dummy"]) - .output() - .expect("failed to add dummy interface"); - - // ip addr add 192.168.0.2/16 dev dummy - Command::new("ip") - .args(&["addr", "add", "192.168.0.2/16", "dev", dummy_name]) - .output() - .expect("failed to add ip for dummy"); - - // ip link set dummy up; - Command::new("ip") - .args(&["link", "set", dummy_name, "up"]) - .output() - .expect("failed to up dummy"); - } - - #[test] - fn test_add_one_arp_neighbor() { - // skip_if_not_root - if !nix::unistd::Uid::effective().is_root() { - println!("INFO: skipping {} which needs root", module_path!()); - return; - } - - let mac = "6a:92:3a:59:70:aa"; - let to_ip = "169.254.1.1"; - let dummy_name = "dummy_for_arp"; - - prepare_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); - - let mut ip_address = IPAddress::new(); - ip_address.set_address(to_ip.to_string()); - - let mut neigh = ARPNeighbor::new(); - neigh.set_toIPAddress(ip_address); - neigh.set_device(dummy_name.to_string()); - neigh.set_lladdr(mac.to_string()); - neigh.set_state(0x80); - - let mut rtnl = RtnlHandle::new(NETLINK_ROUTE, 0).unwrap(); - - rtnl.add_one_arp_neighbor(&neigh).unwrap(); - - // ip neigh show dev dummy ip - let stdout = Command::new("ip") - .args(&["neigh", "show", "dev", dummy_name, to_ip]) - .output() - .expect("failed to show neigh") - .stdout; - - let stdout = std::str::from_utf8(&stdout).expect("failed to conveert stdout"); - - assert_eq!(stdout, format!("{} lladdr {} PERMANENT\n", to_ip, mac)); - - clean_env_for_test_add_one_arp_neighbor(dummy_name, to_ip); - } -} diff --git a/src/agent/netlink/src/parser.rs b/src/agent/netlink/src/parser.rs new file mode 100644 index 000000000..0786c0396 --- /dev/null +++ b/src/agent/netlink/src/parser.rs @@ -0,0 +1,201 @@ +// Copyright (c) 2019 Ant Financial +// +// SPDX-License-Identifier: Apache-2.0 + +//! Parser for IPv4/IPv6/MAC addresses. + +use std::net::{Ipv4Addr, Ipv6Addr}; +use std::str::FromStr; + +use super::{Errno, Result, __u8, nix_errno}; + +#[inline] +pub(crate) fn parse_u8(s: &str, radix: u32) -> Result { + if radix >= 2 && radix <= 36 { + u8::from_str_radix(s, radix).map_err(|_| nix::Error::Sys(Errno::EINVAL)) + } else { + u8::from_str(s).map_err(|_| nix::Error::Sys(Errno::EINVAL)) + } +} + +pub fn parse_ipv4_addr(s: &str) -> Result> { + match Ipv4Addr::from_str(s) { + Ok(v) => Ok(Vec::from(v.octets().as_ref())), + Err(_e) => nix_errno(Errno::EINVAL), + } +} + +pub fn parse_ip_addr(s: &str) -> Result> { + if let Ok(v6) = Ipv6Addr::from_str(s) { + Ok(Vec::from(v6.octets().as_ref())) + } else { + parse_ipv4_addr(s) + } +} + +pub fn parse_ip_addr_with_family(ip_address: &str) -> Result<(__u8, Vec)> { + if let Ok(v6) = Ipv6Addr::from_str(ip_address) { + Ok((libc::AF_INET6 as __u8, Vec::from(v6.octets().as_ref()))) + } else { + parse_ipv4_addr(ip_address).map(|v| (libc::AF_INET as __u8, v)) + } +} + +pub fn parse_ipv4_cidr(s: &str) -> Result<(Vec, u8)> { + let fields: Vec<&str> = s.split('/').collect(); + + if fields.len() != 2 { + nix_errno(Errno::EINVAL) + } else { + Ok((parse_ipv4_addr(fields[0])?, parse_u8(fields[1], 10)?)) + } +} + +pub fn parse_cidr(s: &str) -> Result<(Vec, u8)> { + let fields: Vec<&str> = s.split('/').collect(); + + if fields.len() != 2 { + nix_errno(Errno::EINVAL) + } else { + Ok((parse_ip_addr(fields[0])?, parse_u8(fields[1], 10)?)) + } +} + +pub fn parse_mac_addr(hwaddr: &str) -> Result> { + let fields: Vec<&str> = hwaddr.split(':').collect(); + + if fields.len() != 6 { + nix_errno(Errno::EINVAL) + } else { + Ok(vec![ + parse_u8(fields[0], 16)?, + parse_u8(fields[1], 16)?, + parse_u8(fields[2], 16)?, + parse_u8(fields[3], 16)?, + parse_u8(fields[4], 16)?, + parse_u8(fields[5], 16)?, + ]) + } +} + +/// Format an IPv4/IPv6/MAC address. +/// +/// # Safety +/// Caller needs to ensure that addr and len are valid. +pub unsafe fn format_address(addr: *const u8, len: u32) -> Result { + let mut a: String; + if len == 4 { + // ipv4 + let mut i = 1; + let mut p = addr as i64; + + a = format!("{}", *(p as *const u8)); + while i < len { + p += 1; + i += 1; + a.push_str(format!(".{}", *(p as *const u8)).as_str()); + } + + return Ok(a); + } + + if len == 6 { + // hwaddr + let mut i = 1; + let mut p = addr as i64; + + a = format!("{:0>2X}", *(p as *const u8)); + while i < len { + p += 1; + i += 1; + a.push_str(format!(":{:0>2X}", *(p as *const u8)).as_str()); + } + + return Ok(a); + } + + if len == 16 { + // ipv6 + let p = addr as *const u8 as *const libc::c_void; + let mut ar: [u8; 16] = [0; 16]; + let mut v: Vec = vec![0; 16]; + let dp: *mut libc::c_void = v.as_mut_ptr() as *mut libc::c_void; + libc::memcpy(dp, p, 16); + + ar.copy_from_slice(v.as_slice()); + + return Ok(Ipv6Addr::from(ar).to_string()); + } + + nix_errno(Errno::EINVAL) +} + +#[cfg(test)] +mod tests { + use super::*; + use libc; + + #[test] + fn test_ip_addr() { + let ip = parse_ipv4_addr("1.2.3.4").unwrap(); + assert_eq!(ip, vec![0x1u8, 0x2u8, 0x3u8, 0x4u8]); + parse_ipv4_addr("1.2.3.4.5").unwrap_err(); + parse_ipv4_addr("1.2.3-4").unwrap_err(); + parse_ipv4_addr("1.2.3.a").unwrap_err(); + parse_ipv4_addr("1.2.3.x").unwrap_err(); + parse_ipv4_addr("-1.2.3.4").unwrap_err(); + parse_ipv4_addr("+1.2.3.4").unwrap_err(); + + let (family, _) = parse_ip_addr_with_family("192.168.1.1").unwrap(); + assert_eq!(family, libc::AF_INET as __u8); + + let (family, ip) = + parse_ip_addr_with_family("2001:0db8:85a3:0000:0000:8a2e:0370:7334").unwrap(); + assert_eq!(family, libc::AF_INET6 as __u8); + assert_eq!(ip.len(), 16); + parse_ip_addr_with_family("2001:0db8:85a3:0000:0000:8a2e:0370:73345").unwrap_err(); + + let ip = parse_ip_addr("::1").unwrap(); + assert_eq!(ip[0], 0x0); + assert_eq!(ip[15], 0x1); + } + + #[test] + fn test_parse_cidr() { + let (_, mask) = parse_ipv4_cidr("1.2.3.4/31").unwrap(); + assert_eq!(mask, 31); + + parse_ipv4_cidr("1.2.3/4/31").unwrap_err(); + parse_ipv4_cidr("1.2.3.4/f").unwrap_err(); + parse_ipv4_cidr("1.2.3/8").unwrap_err(); + parse_ipv4_cidr("1.2.3.4.8").unwrap_err(); + + let (ip, mask) = parse_cidr("2001:db8:a::123/64").unwrap(); + assert_eq!(mask, 64); + assert_eq!(ip[0], 0x20); + assert_eq!(ip[15], 0x23); + } + + #[test] + fn test_parse_mac_addr() { + let mac = parse_mac_addr("FF:FF:FF:FF:FF:FE").unwrap(); + assert_eq!(mac.len(), 6); + assert_eq!(mac[0], 0xff); + assert_eq!(mac[5], 0xfe); + + parse_mac_addr("FF:FF:FF:FF:FF:FE:A0").unwrap_err(); + parse_mac_addr("FF:FF:FF:FF:FF:FX").unwrap_err(); + parse_mac_addr("FF:FF:FF:FF:FF").unwrap_err(); + } + + #[test] + fn test_format_address() { + let buf = [1u8, 2u8, 3u8, 4u8]; + let addr = unsafe { format_address(&buf as *const u8, 4).unwrap() }; + assert_eq!(addr, "1.2.3.4"); + + let buf = [1u8, 2u8, 3u8, 4u8, 5u8, 6u8]; + let addr = unsafe { format_address(&buf as *const u8, 6).unwrap() }; + assert_eq!(addr, "01:02:03:04:05:06"); + } +} diff --git a/src/agent/rustjail/Cargo.toml b/src/agent/rustjail/Cargo.toml index 9b50517a0..5b70ae876 100644 --- a/src/agent/rustjail/Cargo.toml +++ b/src/agent/rustjail/Cargo.toml @@ -22,4 +22,4 @@ slog = "2.5.2" slog-scope = "4.1.2" scan_fmt = "0.2" regex = "1.1" -path-absolutize = { git = "git://github.com/magiclen/path-absolutize.git", tag= "v1.2.0" } +path-absolutize = "1.2.0"