From 6995178903e6ca5f7490f02a30e7ad9169883980 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 02:00:10 +0800 Subject: [PATCH 1/9] netlink: use bool for condition flags Minor improvements for netlink by using bool for condition flags. Signed-off-by: Liu Jiang --- src/agent/netlink/src/lib.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index b12c2b54d..738ab7fed 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -44,8 +44,7 @@ macro_rules! sl { }; } -// define the struct, const, etc needed by -// netlink operations +// define the struct, const, etc needed by netlink operations pub type __s8 = libc::c_char; pub type __u8 = libc::c_uchar; @@ -1248,7 +1247,6 @@ impl RtnlHandle { }) } - // implement update{interface,routes}, list{interface, routes} fn send_message(&self, data: &mut [u8]) -> Result<()> { let mut sa: libc::sockaddr_nl = unsafe { mem::zeroed::() }; @@ -1340,22 +1338,22 @@ impl RtnlHandle { 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 dump_intr = false; + let mut done = false; while NLMSG_OK!(nlh, msglen) { + // 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; + dump_intr = true; } if (*nlh).nlmsg_type == NLMSG_DONE { - done = 1; + done = true; } if (*nlh).nlmsg_type == NLMSG_ERROR { @@ -1375,7 +1373,7 @@ impl RtnlHandle { lv.push(nlh); - if done == 1 { + if done { break; } @@ -1384,9 +1382,9 @@ 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; @@ -1412,7 +1410,7 @@ impl RtnlHandle { let (_sav, av) = self.dump_all_addresses(0)?; // got all the link message and address message - // into lv and av repectively, parse attributes + // into lv and av respectively, parse attributes for link in &lv { let nlh: *const nlmsghdr = *link; let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; @@ -1707,7 +1705,7 @@ impl RtnlHandle { ); } - // goog message + // good message if answer { // need to copy out data @@ -2474,6 +2472,7 @@ impl RtnlHandle { Ok(rt.clone()) } + pub fn handle_localhost(&mut self) -> Result<()> { let ifi = self.find_link_by_name("lo")?; From 4774814c73788c5a9b30ade18f106cb0fc256a90 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sat, 30 May 2020 23:10:59 +0800 Subject: [PATCH 2/9] netlink: get rid of dependency on rustjail The netlink crate is a library to communicate with Linux kenrel by using the netlink socket. It's generic enough to be reused by other clients. So get rid of dependency on the rustjail crate by: 1) normalize all pub interfaces to return Result, 2) add helpers to reduce duplicated code, 3) move parse_mac() into lib.rs, Signed-off-by: Liu Jiang --- src/agent/Cargo.lock | 1 - src/agent/netlink/Cargo.toml | 1 - src/agent/netlink/src/lib.rs | 109 +++++++++++++++++++-------------- src/agent/netlink/src/neigh.rs | 44 ++----------- 4 files changed, 70 insertions(+), 85 deletions(-) diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index c7c18e277..f763467a5 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -255,7 +255,6 @@ dependencies = [ "nix 0.17.0", "protobuf", "protocols", - "rustjail", "scan_fmt", "slog", "slog-async", diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml index 5dc6b5094..0e91983a4 100644 --- a/src/agent/netlink/Cargo.toml +++ b/src/agent/netlink/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" 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" diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index 738ab7fed..f10aa6088 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -15,7 +15,6 @@ extern crate libc; extern crate nix; extern crate protobuf; extern crate protocols; -extern crate rustjail; #[macro_use] extern crate slog; @@ -29,7 +28,6 @@ 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::fmt; @@ -37,6 +35,8 @@ use std::mem; use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; +type Result = std::result::Result; + // Convenience macro to obtain the scope logger macro_rules! sl { () => { @@ -1169,7 +1169,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( @@ -1182,7 +1182,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( @@ -1195,7 +1195,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( @@ -1216,7 +1216,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( @@ -1226,14 +1226,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 @@ -1268,9 +1268,10 @@ impl RtnlHandle { 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()); + return nix_last_os_err(); } } + Ok(()) } @@ -1296,7 +1297,7 @@ impl RtnlHandle { ); if rlen < 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::last())).into()); + return nix_last_os_err(); } // if rlen < 32768 { @@ -1311,16 +1312,16 @@ impl RtnlHandle { 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()); + return nix_last_os_err(); } if sa.nl_pid != 0 { // not our netlink message - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EBADMSG)).into()); + return nix_errno(Errno::EBADMSG); } if h.msg_flags & libc::MSG_TRUNC != 0 { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EBADMSG)).into()); + return nix_errno(Errno::EBADMSG); } v.resize(rlen as usize, 0); @@ -1362,13 +1363,11 @@ impl RtnlHandle { 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(), - ); + return nix_errno(Errno::from_i32(-(*el).error)); } lv.push(nlh); @@ -1393,9 +1392,10 @@ impl RtnlHandle { // 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)) } @@ -1563,20 +1563,10 @@ impl RtnlHandle { } fn find_link_by_hwaddr(&mut self, hwaddr: &str) -> Result { - let mut hw: Vec = vec![0; 6]; + let hw = parse_mac(hwaddr)?; + let p = hw.as_ptr() as *const u8 as *const libc::c_void; 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])?; - - hw[0] = hw0; - hw[1] = hw1; - hw[2] = hw2; - hw[3] = hw3; - hw[4] = hw4; - hw[5] = hw5; // dump out all links let (_slv, lv) = self.dump_all_links()?; @@ -1608,7 +1598,7 @@ impl RtnlHandle { if attrs[IFLA_ADDRESS as usize] as i64 != 0 { let a = RTA_DATA!(attrs[IFLA_ADDRESS as usize]) as *const libc::c_void; if libc::memcmp( - p as *const libc::c_void, + p, a, RTA_PAYLOAD!(attrs[IFLA_ADDRESS as usize]) as libc::size_t, ) == 0 @@ -1619,7 +1609,7 @@ impl RtnlHandle { } } - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::ENODEV)).into()); + nix_errno(Errno::ENODEV) } fn find_link_by_name(&mut self, name: &str) -> Result { @@ -1688,9 +1678,7 @@ impl RtnlHandle { 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; @@ -1700,9 +1688,7 @@ impl RtnlHandle { return Ok(Vec::new()); } - return Err( - ErrorKind::Nix(nix::Error::Sys(Errno::from_i32(-(*el).error))).into(), - ); + return nix_errno(Errno::from_i32(-(*el).error)); } // good message @@ -1723,7 +1709,7 @@ impl RtnlHandle { } if !(NLMSG_OK!(nlh, msglen)) { - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); + return nix_errno(Errno::EINVAL); } } } @@ -2042,7 +2028,7 @@ impl RtnlHandle { } } - Err(ErrorKind::ErrorCode("no name".to_string()).into()) + nix_errno(Errno::ENOENT) } pub fn list_routes(&mut self) -> Result> { @@ -2711,7 +2697,7 @@ unsafe fn format_address(addr: *const u8, len: u32) -> Result { */ } - return Err(ErrorKind::Nix(nix::Error::Sys(Errno::EINVAL)).into()); + nix_errno(Errno::EINVAL) } impl Drop for RtnlHandle { @@ -2740,13 +2726,15 @@ impl Default for RtRoute { } fn parse_cidripv4(s: &str) -> Result<(Vec, u8)> { - let (a0, a1, a2, a3, len) = scan_fmt!(s, "{}.{}.{}.{}/{}", u8, u8, u8, u8, u8)?; + let (a0, a1, a2, a3, len) = scan_fmt!(s, "{}.{}.{}.{}/{}", u8, u8, u8, u8, u8) + .map_err(|_| nix::Error::Sys(Errno::EINVAL))?; 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 (a0, a1, a2, a3) = + scan_fmt!(s, "{}.{}.{}.{}", u8, u8, u8, u8).map_err(|_| nix::Error::Sys(Errno::EINVAL))?; let ip: Vec = vec![a0, a1, a2, a3]; Ok(ip) @@ -2758,12 +2746,15 @@ fn parse_ipaddr(s: &str) -> Result> { } // v4 - Ok(Vec::from(Ipv4Addr::from_str(s)?.octets().as_ref())) + match Ipv4Addr::from_str(s) { + Ok(v) => Ok(Vec::from(v.octets().as_ref())), + Err(_e) => nix_errno(Errno::EINVAL), + } } fn parse_cider(s: &str) -> Result<(Vec, u8)> { let (addr, mask) = if s.contains("/") { - scan_fmt!(s, "{}/{}", String, u8)? + scan_fmt!(s, "{}/{}", String, u8).map_err(|_| nix::Error::Sys(Errno::EINVAL))? } else { (s.to_string(), 0) }; @@ -2771,6 +2762,24 @@ fn parse_cider(s: &str) -> Result<(Vec, u8)> { Ok((parse_ipaddr(addr.as_str())?, mask)) } +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]) + .map_err(|_| nix::Error::Sys(Errno::EINVAL))?; + + hw[0] = hw0; + hw[1] = hw1; + hw[2] = hw2; + hw[3] = hw3; + hw[4] = hw4; + hw[5] = hw5; + + Ok(hw) +} + impl From for RtRoute { fn from(r: Route) -> Self { // only handle ipv4 @@ -2849,6 +2858,16 @@ impl From for RtIPAddr { } } +#[inline] +fn nix_last_os_err() -> Result { + Err(nix::Error::Sys(Errno::last())) +} + +#[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}; diff --git a/src/agent/netlink/src/neigh.rs b/src/agent/netlink/src/neigh.rs index 6134f8a91..bb5096531 100644 --- a/src/agent/netlink/src/neigh.rs +++ b/src/agent/netlink/src/neigh.rs @@ -3,13 +3,8 @@ // 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 super::*; use protocols::types::ARPNeighbor; -use rustjail::errors::*; use std::mem; #[repr(C)] @@ -58,29 +53,19 @@ impl crate::RtnlHandle { 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()); + return nix_errno(Errno::EINVAL); } 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()); + return nix_errno(Errno::EINVAL); } + let dev = self.find_link_by_name(&neigh.device)?; + let mut v: Vec = vec![0; 2048]; unsafe { // init @@ -129,23 +114,6 @@ impl crate::RtnlHandle { } } -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; From bbd40203d4b20d5b499f61f058694177ea9150c1 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 02:29:56 +0800 Subject: [PATCH 3/9] netlink: group kata agent code into a dedicated file Group kata agent specific code into a dedicated file, so we could easily make it optional later. Signed-off-by: Liu Jiang --- src/agent/Cargo.lock | 5 +- src/agent/netlink/src/agent_handler.rs | 620 +++++++++++++++++++++++++ src/agent/netlink/src/lib.rs | 498 +++----------------- src/agent/netlink/src/neigh.rs | 219 --------- src/agent/rustjail/Cargo.toml | 2 +- 5 files changed, 677 insertions(+), 667 deletions(-) create mode 100644 src/agent/netlink/src/agent_handler.rs delete mode 100644 src/agent/netlink/src/neigh.rs diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index f763467a5..2c1eb6362 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -325,8 +325,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/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs new file mode 100644 index 000000000..84b50b21d --- /dev/null +++ b/src/agent/netlink/src/agent_handler.rs @@ -0,0 +1,620 @@ +// 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 protobuf::RepeatedField; +use protocols::types::{ARPNeighbor, IPAddress, IPFamily, Interface, Route}; + +// Convenience macro to obtain the scope logger +macro_rules! sl { + () => { + slog_scope::logger().new(o!("subsystem" => "netlink")) + }; +} + +use super::*; + +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::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()); + } + + 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()); + } + + 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)?; + + // 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()) + } + + pub fn list_interfaces(&mut self) -> Result> { + let mut ifaces: Vec = Vec::new(); + + 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 respectively, 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); + } + } + + Ok(ifaces) + } + + 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 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) + } + + 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<()> { + if neigh.toIPAddress.is_none() { + return nix_errno(Errno::EINVAL); + } + let to_ip = &neigh.toIPAddress.as_ref().unwrap().address; + if to_ip.is_empty() { + return nix_errno(Errno::EINVAL); + } + + let dev = self.find_link_by_name(&neigh.device)?; + + 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!(std::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(()) + } +} + +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, + } + } +} + +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, + } + } +} + +#[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 f10aa6088..d302fd8f3 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -9,7 +9,7 @@ #![allow(unused_parens)] #![allow(unused_unsafe)] -mod neigh; +mod agent_handler; extern crate libc; extern crate nix; @@ -26,8 +26,6 @@ extern crate slog_scope; extern crate scan_fmt; use nix::errno::Errno; -use protobuf::RepeatedField; -use protocols::types::{IPAddress, IPFamily, Interface, Route}; use std::clone::Clone; use std::default::Default; use std::fmt; @@ -1096,6 +1094,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 { @@ -1399,122 +1435,6 @@ impl RtnlHandle { Ok((slv, lv)) } - pub fn list_interfaces(&mut self) -> Result> { - let mut ifaces: Vec = Vec::new(); - - 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 respectively, 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); - } - } - - 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; @@ -1873,107 +1793,6 @@ impl RtnlHandle { 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()); - } - - 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()); - } - - 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]; let mut i = 0; @@ -2031,132 +1850,6 @@ impl RtnlHandle { 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; @@ -2432,33 +2125,6 @@ impl RtnlHandle { 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")?; @@ -2762,6 +2428,20 @@ fn parse_cider(s: &str) -> Result<(Vec, u8)> { Ok((parse_ipaddr(addr.as_str())?, mask)) } +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)) +} + fn parse_mac(hwaddr: &str) -> Result> { let mut hw: Vec = vec![0; 6]; @@ -2780,84 +2460,12 @@ fn parse_mac(hwaddr: &str) -> Result> { Ok(hw) } -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_last_os_err() -> Result { Err(nix::Error::Sys(Errno::last())) diff --git a/src/agent/netlink/src/neigh.rs b/src/agent/netlink/src/neigh.rs deleted file mode 100644 index bb5096531..000000000 --- a/src/agent/netlink/src/neigh.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2020 Ant Financial -// -// SPDX-License-Identifier: Apache-2.0 -// - -use super::*; -use protocols::types::ARPNeighbor; -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<()> { - if neigh.toIPAddress.is_none() { - return nix_errno(Errno::EINVAL); - } - - let to_ip = &neigh.toIPAddress.as_ref().unwrap().address; - if to_ip.is_empty() { - return nix_errno(Errno::EINVAL); - } - - let dev = self.find_link_by_name(&neigh.device)?; - - 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_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/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" From 710932df060183a478f9d496e05cad692505a9a2 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 03:17:12 +0800 Subject: [PATCH 4/9] netlink: use features for slog and agent handler Use features to enable/disable slog and agent handler on demand. This helps to reduce dependency chains if slog/agent handler is unused. Signed-off-by: Liu Jiang --- src/agent/Cargo.lock | 2 -- src/agent/netlink/Cargo.toml | 15 +++++++++------ src/agent/netlink/src/agent_handler.rs | 1 + src/agent/netlink/src/lib.rs | 25 +++++++++++++++++++------ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 2c1eb6362..867636659 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -257,8 +257,6 @@ dependencies = [ "protocols", "scan_fmt", "slog", - "slog-async", - "slog-json", "slog-scope", ] diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml index 0e91983a4..a14dc66f6 100644 --- a/src/agent/netlink/Cargo.toml +++ b/src/agent/netlink/Cargo.toml @@ -9,10 +9,13 @@ edition = "2018" [dependencies] libc = "0.2.58" nix = "0.17.0" -protobuf = "=2.14.0" -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" + +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 } scan_fmt = "0.2.3" + +[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 index 84b50b21d..a5260f448 100644 --- a/src/agent/netlink/src/agent_handler.rs +++ b/src/agent/netlink/src/agent_handler.rs @@ -9,6 +9,7 @@ use protobuf::RepeatedField; use protocols::types::{ARPNeighbor, IPAddress, IPFamily, Interface, Route}; +#[cfg(feature = "with-log")] // Convenience macro to obtain the scope logger macro_rules! sl { () => { diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index d302fd8f3..bd26243d1 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -9,17 +9,18 @@ #![allow(unused_parens)] #![allow(unused_unsafe)] -mod agent_handler; - extern crate libc; extern crate nix; + +#[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] @@ -33,8 +34,7 @@ use std::mem; use std::net::{Ipv4Addr, Ipv6Addr}; use std::str::FromStr; -type Result = std::result::Result; - +#[cfg(feature = "with-log")] // Convenience macro to obtain the scope logger macro_rules! sl { () => { @@ -42,6 +42,19 @@ macro_rules! sl { }; } +#[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; + +/// Specialized version of std::result::Result for Netlink related operations. +pub type Result = std::result::Result; + // define the struct, const, etc needed by netlink operations pub type __s8 = libc::c_char; From 3fe930b9464e3451bbe83b20426c50b3802d2e0c Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 03:41:39 +0800 Subject: [PATCH 5/9] netlink: get rid of dependency on scan_fmt The scan_fmt crate has dependency on other four crates, and it's trivial to use std library to implement the same logic. Get rid of scan_fmt to reduce the dependency chain. Signed-off-by: Liu Jiang --- src/agent/Cargo.lock | 1 - src/agent/netlink/Cargo.toml | 1 - src/agent/netlink/src/agent_handler.rs | 2 +- src/agent/netlink/src/lib.rs | 177 ++++++++++++++++--------- 4 files changed, 114 insertions(+), 67 deletions(-) diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 867636659..ffa4102a8 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -255,7 +255,6 @@ dependencies = [ "nix 0.17.0", "protobuf", "protocols", - "scan_fmt", "slog", "slog-scope", ] diff --git a/src/agent/netlink/Cargo.toml b/src/agent/netlink/Cargo.toml index a14dc66f6..5670889f0 100644 --- a/src/agent/netlink/Cargo.toml +++ b/src/agent/netlink/Cargo.toml @@ -14,7 +14,6 @@ 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 } -scan_fmt = "0.2.3" [features] with-log = ["slog", "slog-scope"] diff --git a/src/agent/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs index a5260f448..882ba99ad 100644 --- a/src/agent/netlink/src/agent_handler.rs +++ b/src/agent/netlink/src/agent_handler.rs @@ -428,7 +428,7 @@ impl super::RtnlHandle { // process lladdr if neigh.lladdr != "" { - let llabuf = parse_mac(&neigh.lladdr)?; + let llabuf = parse_mac_addr(&neigh.lladdr)?; addattr_var(nlh, NDA_LLADDR, llabuf.as_ptr() as *const u8, llabuf.len()); } diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index bd26243d1..96c6e282d 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -23,12 +23,7 @@ extern crate slog; #[cfg(feature = "with-log")] extern crate slog_scope; -#[macro_use] -extern crate scan_fmt; - use nix::errno::Errno; -use std::clone::Clone; -use std::default::Default; use std::fmt; use std::mem; use std::net::{Ipv4Addr, Ipv6Addr}; @@ -1496,7 +1491,7 @@ impl RtnlHandle { } fn find_link_by_hwaddr(&mut self, hwaddr: &str) -> Result { - let hw = parse_mac(hwaddr)?; + let hw = parse_mac_addr(hwaddr)?; let p = hw.as_ptr() as *const u8 as *const libc::c_void; unsafe { //parse out hwaddr in request @@ -2404,79 +2399,79 @@ impl Default for RtRoute { } } -fn parse_cidripv4(s: &str) -> Result<(Vec, u8)> { - let (a0, a1, a2, a3, len) = scan_fmt!(s, "{}.{}.{}.{}/{}", u8, u8, u8, u8, u8) - .map_err(|_| nix::Error::Sys(Errno::EINVAL))?; - let ip: Vec = vec![a0, a1, a2, a3]; - Ok((ip, len)) +pub struct RtIPAddr { + pub ip_family: __u8, + pub ip_mask: __u8, + pub addr: Vec, } -fn parse_ipv4(s: &str) -> Result> { - let (a0, a1, a2, a3) = - scan_fmt!(s, "{}.{}.{}.{}", u8, u8, u8, u8).map_err(|_| nix::Error::Sys(Errno::EINVAL))?; - 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())); +#[inline] +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)) } +} - // v4 +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), } } -fn parse_cider(s: &str) -> Result<(Vec, u8)> { - let (addr, mask) = if s.contains("/") { - scan_fmt!(s, "{}/{}", String, u8).map_err(|_| nix::Error::Sys(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 { - (s.to_string(), 0) - }; - - Ok((parse_ipaddr(addr.as_str())?, mask)) -} - -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; + parse_ipv4_addr(s) } - - Ok((family, ip_data)) } -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]) - .map_err(|_| nix::Error::Sys(Errno::EINVAL))?; - - hw[0] = hw0; - hw[1] = hw1; - hw[2] = hw2; - hw[3] = hw3; - hw[4] = hw4; - hw[5] = hw5; - - Ok(hw) +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 struct RtIPAddr { - pub ip_family: __u8, - pub ip_mask: __u8, - pub addr: Vec, +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)?, + ]) + } } #[inline] @@ -2491,9 +2486,10 @@ fn nix_errno(err: Errno) -> Result { #[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)); @@ -2504,4 +2500,57 @@ mod tests { NLMSG_ALIGN!(mem::size_of::() as libc::c_uint) ); } + + #[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(); + } } From 1b8c2cba607ff42ad3fecd8b0acba0dec35c55ad Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 11:19:54 +0800 Subject: [PATCH 6/9] netlink: refine interface to reduce unsafe code There are too much unsafe code in the netlink crate, we need to reduce unsafe code as much as possible. To achieve this, methods are classified as public interfaces and internal methods. All public interface of RtnlHandle has been reimplemented as safe code, only some public helper functions to manipulater Netlink message data structures are implemented as unsafe code. The code to parse IPv4/IPv6/MAC addresses has been moved to a dedicated file named parser.rs. Signed-off-by: Liu Jiang --- src/agent/Cargo.toml | 2 +- src/agent/netlink/src/agent_handler.rs | 465 +++---- src/agent/netlink/src/lib.rs | 1731 ++++++++++-------------- src/agent/netlink/src/parser.rs | 203 +++ 4 files changed, 1159 insertions(+), 1242 deletions(-) create mode 100644 src/agent/netlink/src/parser.rs diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 57664b862..ae55c21ac 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -9,7 +9,7 @@ oci = { path = "oci" } logging = { path = "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/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs index 882ba99ad..4c4307d99 100644 --- a/src/agent/netlink/src/agent_handler.rs +++ b/src/agent/netlink/src/agent_handler.rs @@ -31,14 +31,12 @@ impl super::RtnlHandle { 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 @@ -47,191 +45,175 @@ impl super::RtnlHandle { 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. - let mut v: Vec = vec![0; 2048]; + 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 { - 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.addattr32(IFLA_MTU, iface.mtu as u32); - (*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 + // if str is null terminated, use addattr_var. // otherwise, use addattr_str - addattr_var( - nlh, + nlh.addattr_var( IFLA_IFNAME, iface.name.as_ptr() as *const u8, iface.name.len(), ); - // addattr_str(nlh, IFLA_IFNAME, iface.name.as_str()); } self.rtnl_talk(v.as_mut_slice(), false)?; + // TODO: why the result is ignored here? 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()); } + /// 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)?; - // 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; + 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) }; - self.seq += 1; - (*nlh).nlmsg_seq = self.seq; + // 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; + 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)?; - } + 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)?; - unsafe { - // get link info - let (_slv, lv) = self.dump_all_links()?; + 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; + } - // get addrinfo - let (_sav, av) = self.dump_all_addresses(0)?; + 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; + } - // got all the link message and address message - // into lv and av respectively, parse attributes - for link in &lv { - let nlh: *const nlmsghdr = *link; - let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; + // 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)? }; - if (*nlh).nlmsg_type != RTM_NEWLINK && (*nlh).nlmsg_type != RTM_DELLINK { - continue; - } + // fill out some fields of Interface, + let mut iface: Interface = Interface::default(); - 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 { + // 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] as i64 != 0 { + 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] as i64 != 0 { + 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 = format_address(a, alen as u32)?; + 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; } - // get ip address info from av - let mut ads: Vec = Vec::new(); + 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; + } - 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; + // 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 (*alh).nlmsg_type != RTM_NEWADDR { - continue; + 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 mut tattr: *const rtattr = addrs[IFA_LOCAL as usize]; + if !addrs[IFA_ADDRESS as usize].is_null() { + tattr = addrs[IFA_ADDRESS as usize]; } - 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; + one.mask = format!("{}", ifa.ifa_prefixlen); + one.family = IPFamily::v4; + if ifa.ifa_family == libc::AF_INET6 as u8 { + one.family = IPFamily::v6; } - 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); + // Safe because parse_attrs() returns valid pointers. + unsafe { 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); + one.address = parser::format_address(a, alen as u32)?; } - } - iface.IPAddresses = RepeatedField::from_vec(ads); - ifaces.push(iface); + ads.push(one); + } } + + iface.IPAddresses = RepeatedField::from_vec(ads); + ifaces.push(iface); } Ok(ifaces) @@ -270,67 +252,63 @@ impl super::RtnlHandle { // attribute in dump request // Fix Me: think about othe tables, ipv6.. let mut rs: Vec = Vec::new(); + let (_srv, rv) = self.dump_all_routes()?; - unsafe { - let (_srv, rv) = self.dump_all_route_msgs()?; + // 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; + } - // 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; + // 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)? }; - if (*nlh).nlmsg_type != RTM_NEWROUTE && (*nlh).nlmsg_type != RTM_DELROUTE { - info!(sl!(), "not route message!"); + 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; } + } - 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(); + // 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 as i64 != 0 { + if !t.is_null() { 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); + rte.dest = + format!("{}/{}", parser::format_address(data, len)?, rtm.rtm_dst_len); } // gateway t = attrs[RTA_GATEWAY as usize]; - if t as i64 != 0 { + if !t.is_null() { let data: *const u8 = RTA_DATA!(t) as *const u8; let len = RTA_PAYLOAD!(t) as u32; - rte.gateway = format_address(data, len)?; + rte.gateway = parser::format_address(data, len)?; // for gateway, destination is 0.0.0.0 rte.dest = "0.0.0.0".to_string(); @@ -338,53 +316,35 @@ impl super::RtnlHandle { // source t = attrs[RTA_SRC as usize]; - - if t as i64 == 0 { + if t.is_null() { t = attrs[RTA_PREFSRC as usize]; } - - if t as i64 != 0 { + 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)?; - rte.source = format_address(data, len)?; - - if (*rtm).rtm_src_len != 0 { - rte.source = format!("{}/{}", rte.source.as_str(), (*rtm).rtm_src_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; + 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; + if !t.is_null() { + let data = &*(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); } + + rs.push(rte); } Ok(rs) @@ -399,60 +359,57 @@ impl super::RtnlHandle { } pub fn add_one_arp_neighbor(&mut self, neigh: &ARPNeighbor) -> Result<()> { - if neigh.toIPAddress.is_none() { - return nix_errno(Errno::EINVAL); - } - let to_ip = &neigh.toIPAddress.as_ref().unwrap().address; - if to_ip.is_empty() { - return nix_errno(Errno::EINVAL); - } + 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; 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; + 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; + 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); - 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 = parser::parse_mac_addr(&neigh.lladdr)?; - (*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_addr(&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)?; + // Safe because we have allocated enough buffer space. + unsafe { nlh.addattr_var(NDA_LLADDR, llabuf.as_ptr() as *const u8, llabuf.len()) }; } + 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_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(()) } } @@ -465,9 +422,8 @@ impl From for RtIPAddr { 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(); + let ip_mask = parser::parse_u8(ipi.mask.as_str(), 10).unwrap(); + let addr = parser::parse_ip_addr(ipi.address.as_ref()).unwrap(); Self { ip_family, @@ -492,21 +448,21 @@ impl From for RtRoute { 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(); + let (dst, mask) = parser::parse_cidr(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(); + let (src, mask) = parser::parse_cidr(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()) + Some(parser::parse_ip_addr(r.gateway.as_str()).unwrap()) }; /* @@ -618,4 +574,3 @@ mod tests { 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 96c6e282d..942a1b1ff 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -3,11 +3,14 @@ // SPDX-License-Identifier: Apache-2.0 // +//! 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 + #![allow(non_camel_case_types)] -#![allow(non_upper_case_globals)] -#![allow(dead_code)] -#![allow(unused_parens)] -#![allow(unused_unsafe)] extern crate libc; extern crate nix; @@ -24,10 +27,10 @@ extern crate slog; extern crate slog_scope; use nix::errno::Errno; +use std::borrow::Borrow; 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 @@ -46,12 +49,13 @@ macro_rules! info { #[cfg(feature = "with-agent-handler")] mod agent_handler; +pub mod parser; -/// Specialized version of std::result::Result for Netlink related operations. +pub const DEFAULT_NETLINK_BUF_SIZE: usize = 2048; + +/// Specialized std::result::Result for Netlink related operations. pub type Result = std::result::Result; -// define the struct, const, etc needed by netlink operations - pub type __s8 = libc::c_char; pub type __u8 = libc::c_uchar; pub type __s16 = libc::c_short; @@ -121,7 +125,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; @@ -280,13 +284,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] @@ -372,13 +374,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 @@ -764,14 +764,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] @@ -790,13 +788,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] @@ -849,25 +845,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] @@ -899,24 +891,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] @@ -928,13 +916,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] @@ -946,13 +932,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)] @@ -977,6 +961,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; @@ -1159,9 +1151,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, @@ -1191,10 +1211,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 @@ -1291,29 +1307,102 @@ impl RtnlHandle { }) } - 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 nix_last_os_err(); - } + let err = unsafe { libc::sendmsg(self.fd, &h as *const libc::msghdr, 0) }; + if err < 0 { + return nix_last_os_err(); } Ok(()) @@ -1321,60 +1410,53 @@ impl RtnlHandle { 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_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 nix_last_os_err(); - } - - // 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 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) + 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(); @@ -1382,36 +1464,35 @@ 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 nlh: &nlmsghdr = buf.borrow(); let mut dump_intr = false; let mut done = false; while NLMSG_OK!(nlh, msglen) { // Make sure we are interested in the message first. - if (*nlh).nlmsg_pid != self.local.nl_pid || (*nlh).nlmsg_seq != self.dump { + if nlh.nlmsg_pid != self.local.nl_pid || nlh.nlmsg_seq != self.dump { nlh = NLMSG_NEXT!(nlh, msglen); continue; } - if (*nlh).nlmsg_flags & NLM_F_DUMP_INTR > 0 { + if nlh.nlmsg_flags & NLM_F_DUMP_INTR > 0 { dump_intr = true; } - if (*nlh).nlmsg_type == NLMSG_DONE { + 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 nix_errno(Errno::EBADMSG); } - let el: *const nlmsgerr = NLMSG_DATA!(nlh) as *const nlmsgerr; - return nix_errno(Errno::from_i32(-(*el).error)); + // 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); @@ -1434,7 +1515,6 @@ impl RtnlHandle { } // still remain some bytes? - if msglen != 0 { return nix_errno(Errno::EINVAL); } @@ -1443,94 +1523,108 @@ impl RtnlHandle { Ok((slv, lv)) } - 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; + 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; + 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); - self.seq += 1; - self.dump = self.seq; - (*nlh).nlmsg_seq = self.seq; + ifi.ifi_family = libc::AF_UNSPEC as u8; - (*ifi).ifi_family = libc::AF_UNSPEC as u8; - - addattr32(nlh, IFLA_EXT_MASK, RTEXT_FILTER_VF); + // Safe because we have allocated enough buffer space. + unsafe { + nlh.addattr32(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 hw = parse_mac_addr(hwaddr)?; - let p = hw.as_ptr() as *const u8 as *const libc::c_void; + 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 + nlh.addattr32(RTA_TABLE, RT_TABLE_MAIN); + } - // dump out all links - let (_slv, lv) = self.dump_all_links()?; + self.send_message(v.as_mut_slice())?; - for link in &lv { - let nlh: *const nlmsghdr = *link; - let ifi: *const ifinfomsg = NLMSG_DATA!(nlh) as *const ifinfomsg; + self.recv_dump_message() + } - if (*nlh).nlmsg_type != RTM_NEWLINK && (*nlh).nlmsg_type != RTM_DELLINK { - continue; - } + 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_len < NLMSG_SPACE!(mem::size_of::()) { - info!( - sl!(), - "invalid nlmsg! nlmsg_len: {}, nlmsg_space: {}", - (*nlh).nlmsg_len, - NLMSG_SPACE!(mem::size_of::()) - ); - break; - } + 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; + } - 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, - 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 }); } } @@ -1540,162 +1634,134 @@ impl RtnlHandle { 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 }); + nlh.addattr_var(IFLA_IFNAME, name.as_ptr() as *const u8, name.len() + 1); + 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 }) } - 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; - } + 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.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; + self.rtnl_talk(v.as_mut_slice(), false).map(|_| ()) + } - while NLMSG_OK!(nlh, msglen) { - // not for us + 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)?; - if (*nlh).nlmsg_pid != self.local.nl_pid { - nlh = NLMSG_NEXT!(nlh, msglen); - continue; - } + for a in &av { + // Safe because dump_all_addresses returns valid pointers. + let nlh = unsafe { &**a }; + if nlh.nlmsg_type != RTM_NEWADDR { + continue; + } - if (*nlh).nlmsg_type == NLMSG_ERROR { - // error message, better to return - // error code in error messages + 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 (*nlh).nlmsg_len < NLMSG_LENGTH!(mem::size_of::()) { - // truncated - return nix_errno(Errno::EBADMSG); - } + // 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 el: *const nlmsgerr = NLMSG_DATA!(nlh) as *const nlmsgerr; + let rta: *const rtattr = IFA_RTA!(ifa) as *mut rtattr; + let rtalen = IFA_PAYLOAD!(nlh) as u32; - // this is ack. -_- - if (*el).error == 0 { - return Ok(Vec::new()); - } - - return nix_errno(Errno::from_i32(-(*el).error)); - } - - // good 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); + 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 { - return Ok(Vec::new()); - } - } + attrs[IFA_LOCAL as usize] + }; + getattr_var(t as *const rtattr) + }; - if !(NLMSG_OK!(nlh, msglen)) { - return nix_errno(Errno::EINVAL); - } - } - } - } - - 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; - } else { - (*ifi).ifi_flags &= !libc::IFF_UP as u32; + 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, + nlh.addattr_var( IFA_ADDRESS, addr.addr.as_ptr() as *const u8, addr.addr.len(), @@ -1708,7 +1774,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: &Vec) -> Result<()> { for a in addrs { self.delete_one_addr(ifinfo, a)?; } @@ -1716,266 +1782,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, - }); - } - } + nlh.addattr_var(IFA_ADDRESS, ip.addr.as_ptr() as *const u8, ip.addr.len()); + // don't know why need IFA_LOCAL, without it kernel returns -EINVAL... + nlh.addattr_var(IFA_LOCAL, ip.addr.as_ptr() as *const u8, ip.addr.len()); } - 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)?; - } + self.rtnl_talk(v.as_mut_slice(), false)?; Ok(()) } - 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)?); } } nix_errno(Errno::ENOENT) } - 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: &Vec) -> 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") { @@ -1992,144 +1967,102 @@ 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() { + let len = source.len(); 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_ptr() as *const u8, len); } else { - addattr_var( - nlh, - RTA_PREFSRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_PREFSRC, source.as_ptr() as *const u8, len); } } - 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() { + let len = dest.len(); + nlh.addattr_var(RTA_DST, dest.as_ptr() as *const u8, len); } - 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() { + let len = gateway.len(); + nlh.addattr_var(RTA_GATEWAY, gateway.as_ptr() as *const u8, len); } - 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() { + let len = source.len(); 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_ptr() as *const u8, len); } else { - addattr_var( - nlh, - RTA_PREFSRC, - r.source.as_ref().unwrap().as_ptr() as *const u8, - len, - ); + nlh.addattr_var(RTA_PREFSRC, source.as_ptr() as *const u8, len); } } - 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() { + let len = dest.len(); + nlh.addattr_var(RTA_DST, dest.as_ptr() as *const u8, len); } - 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() { + let len = gateway.len(); + nlh.addattr_var(RTA_GATEWAY, gateway.as_ptr() as *const u8, len); } - 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(()) } @@ -2138,19 +2071,40 @@ impl RtnlHandle { 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 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; } @@ -2160,106 +2114,111 @@ 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 { + pub unsafe fn addattr_var(&mut self, cat: u16, data: *const u8, len: usize) { + let mut rta: *mut rtattr = NLMSG_TAIL!(self) as *mut rtattr; + let alen = RTA_LENGTH!(len) as u16; - (*rta).rta_type = cat; - (*rta).rta_len = alen; + (*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, + ); + } + + self.nlmsg_len = NLMSG_ALIGN!(self.nlmsg_len) + RTA_ALIGN!(alen); + } + + 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); -} + 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 there's enough space 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 there's enough space 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 there's enough space 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 there's enough space 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 { @@ -2290,188 +2249,41 @@ unsafe fn getattr_size(rta: *const rtattr) -> u64 { panic!("impossible!"); } -unsafe fn getattr8(rta: *const rtattr) -> u8 { +pub unsafe fn getattr8(rta: *const rtattr) -> u8 { let alen = RTA_PAYLOAD!(rta); assert!(alen == 1); getattr_size(rta) as u8 } -unsafe fn getattr16(rta: *const rtattr) -> u16 { +pub unsafe fn getattr16(rta: *const rtattr) -> u16 { let alen = RTA_PAYLOAD!(rta); assert!(alen == 2); getattr_size(rta) as u16 } -unsafe fn getattr32(rta: *const rtattr) -> u32 { +pub unsafe fn getattr32(rta: *const rtattr) -> u32 { let alen = RTA_PAYLOAD!(rta); assert!(alen == 4); getattr_size(rta) as u32 } -unsafe fn getattr64(rta: *const rtattr) -> u64 { +pub unsafe fn getattr64(rta: *const rtattr) -> u64 { let alen = RTA_PAYLOAD!(rta); assert!(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; +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()); - } - */ - } - - nix_errno(Errno::EINVAL) -} - -impl Drop for RtnlHandle { - fn drop(&mut self) { - unsafe { - libc::close(self.fd); - } - } -} - -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, -} - -#[inline] -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)?, - ]) - } + v } #[inline] @@ -2500,57 +2312,4 @@ mod tests { NLMSG_ALIGN!(mem::size_of::() as libc::c_uint) ); } - - #[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(); - } } diff --git a/src/agent/netlink/src/parser.rs b/src/agent/netlink/src/parser.rs new file mode 100644 index 000000000..6b4d95bdb --- /dev/null +++ b/src/agent/netlink/src/parser.rs @@ -0,0 +1,203 @@ +// 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 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"); + } + */ +} From de23ec19434cadce1c0b7e52f419a23367dc725f Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 20:34:29 +0800 Subject: [PATCH 7/9] netlink: implement TryFrom instead of From to avoid unwrap() Implment `TryFrom for RtIPAddr` instead of From, so error code could be returned instead of unwrap(). Do the same for `TryFrom for RtRoute`. Signed-off-by: Liu Jiang --- src/agent/netlink/src/agent_handler.rs | 45 +++++++++++++++----------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/agent/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs index 4c4307d99..722837bfb 100644 --- a/src/agent/netlink/src/agent_handler.rs +++ b/src/agent/netlink/src/agent_handler.rs @@ -6,9 +6,13 @@ //! 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 { @@ -17,8 +21,6 @@ macro_rules! sl { }; } -use super::*; - impl super::RtnlHandle { pub fn update_interface(&mut self, iface: &Interface) -> Result { // the reliable way to find link is using hardware address @@ -41,7 +43,7 @@ impl super::RtnlHandle { // add new ip addresses in request for grpc_addr in &iface.IPAddresses { - let rtip = RtIPAddr::from(grpc_addr.clone()); + let rtip = RtIPAddr::try_from(grpc_addr.clone())?; self.add_one_address(&ifinfo, &rtip)?; } @@ -225,7 +227,7 @@ impl super::RtnlHandle { for grpcroute in rt { if grpcroute.gateway.as_str() == "" { - let r = RtRoute::from(grpcroute.clone()); + let r = RtRoute::try_from(grpcroute.clone())?; if r.index == -1 { continue; } @@ -235,7 +237,7 @@ impl super::RtnlHandle { for grpcroute in rt { if grpcroute.gateway.as_str() != "" { - let r = RtRoute::from(grpcroute.clone()); + let r = RtRoute::try_from(grpcroute.clone())?; if r.index == -1 { continue; } @@ -414,31 +416,35 @@ impl super::RtnlHandle { } } -impl From for RtIPAddr { - fn from(ipi: IPAddress) -> Self { +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).unwrap(); - let addr = parser::parse_ip_addr(ipi.address.as_ref()).unwrap(); + let ip_mask = parser::parse_u8(ipi.mask.as_str(), 10)?; + let addr = parser::parse_ip_addr(ipi.address.as_ref())?; - Self { + Ok(Self { ip_family, ip_mask, addr, - } + }) } } -impl From for RtRoute { - fn from(r: Route) -> Self { +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).unwrap(); + 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, @@ -448,21 +454,21 @@ impl From for RtRoute { 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()).unwrap(); + 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()).unwrap(); + 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()).unwrap()) + Some(parser::parse_ip_addr(r.gateway.as_str())?) }; /* @@ -472,7 +478,8 @@ impl From for RtRoute { (tdest, tdst_len) }; */ - Self { + + Ok(Self { dest, source, src_len, @@ -481,7 +488,7 @@ impl From for RtRoute { gateway, scope: r.scope as u8, protocol: RTPROTO_UNSPEC, - } + }) } } From f5cfd412e454f763f78cccf93e0fc16687d46ec6 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 20:50:52 +0800 Subject: [PATCH 8/9] netlink: fix an error in formatting MAC address When formatting MAC address, we should left-padding zeros instead of right-padding. Signed-off-by: Liu Jiang --- src/agent/netlink/src/parser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/agent/netlink/src/parser.rs b/src/agent/netlink/src/parser.rs index 6b4d95bdb..134f37007 100644 --- a/src/agent/netlink/src/parser.rs +++ b/src/agent/netlink/src/parser.rs @@ -104,11 +104,11 @@ pub unsafe fn format_address(addr: *const u8, len: u32) -> Result { let mut i = 1; let mut p = addr as i64; - a = format!("{:0<2X}", *(p as *const u8)); + 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()); + a.push_str(format!(":{:0>2X}", *(p as *const u8)).as_str()); } return Ok(a); @@ -188,7 +188,6 @@ mod tests { parse_mac_addr("FF:FF:FF:FF:FF").unwrap_err(); } - /* #[test] fn test_format_address() { let buf = [1u8, 2u8, 3u8, 4u8]; @@ -199,5 +198,4 @@ mod tests { let addr = unsafe { format_address(&buf as *const u8, 6).unwrap() }; assert_eq!(addr, "01:02:03:04:05:06"); } - */ } From 4e31bcf8b23107a4230ba373f008069a0b9d4e29 Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Mon, 1 Jun 2020 22:12:03 +0800 Subject: [PATCH 9/9] netlink: clean all clippy warnings Clean up all clippy warning. Also fix a bug in dealing with IFLA_IFNAME attribute. nlh.addattr_var(IFLA_IFNAME, name.as_ptr() as *const u8, name.len() + 1); The `name` is a rust String, which doesn't including the trailing '\0', so name.len() + 1 may cause invalid memory access. Signed-off-by: Liu Jiang --- src/agent/netlink/src/agent_handler.rs | 35 ++---- src/agent/netlink/src/lib.rs | 147 ++++++++++++++++--------- src/agent/netlink/src/parser.rs | 12 +- 3 files changed, 111 insertions(+), 83 deletions(-) diff --git a/src/agent/netlink/src/agent_handler.rs b/src/agent/netlink/src/agent_handler.rs index 722837bfb..a8fffb071 100644 --- a/src/agent/netlink/src/agent_handler.rs +++ b/src/agent/netlink/src/agent_handler.rs @@ -72,11 +72,7 @@ impl super::RtnlHandle { // if str is null terminated, use addattr_var. // otherwise, use addattr_str - nlh.addattr_var( - IFLA_IFNAME, - iface.name.as_ptr() as *const u8, - iface.name.len(), - ); + nlh.addattr_var(IFLA_IFNAME, iface.name.as_ref()); } self.rtnl_talk(v.as_mut_slice(), false)?; @@ -192,10 +188,11 @@ impl super::RtnlHandle { // 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].is_null() { - tattr = addrs[IFA_ADDRESS as usize]; - } + 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; @@ -221,7 +218,7 @@ impl super::RtnlHandle { Ok(ifaces) } - pub fn update_routes(&mut self, rt: &Vec) -> Result> { + pub fn update_routes(&mut self, rt: &[Route]) -> Result> { let rs = self.get_all_routes()?; self.delete_all_routes(&rs)?; @@ -245,7 +242,7 @@ impl super::RtnlHandle { } } - Ok(rt.clone()) + Ok(rt.to_owned()) } pub fn list_routes(&mut self) -> Result> { @@ -342,7 +339,7 @@ impl super::RtnlHandle { rte.device = self .get_name_by_index(*data) - .unwrap_or("unknown".to_string()); + .unwrap_or_else(|_| "unknown".to_string()); } } @@ -352,7 +349,7 @@ impl super::RtnlHandle { Ok(rs) } - pub fn add_arp_neighbors(&mut self, neighs: &Vec) -> Result<()> { + pub fn add_arp_neighbors(&mut self, neighs: &[ARPNeighbor]) -> Result<()> { for neigh in neighs { self.add_one_arp_neighbor(&neigh)?; } @@ -390,13 +387,13 @@ impl super::RtnlHandle { let llabuf = parser::parse_mac_addr(&neigh.lladdr)?; // Safe because we have allocated enough buffer space. - unsafe { nlh.addattr_var(NDA_LLADDR, llabuf.as_ptr() as *const u8, llabuf.len()) }; + 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_ptr() as *const u8, ip_data.len()) }; + unsafe { nlh.addattr_var(NDA_DST, ip_data.as_ref()) }; // process state if neigh.state != 0 { @@ -471,14 +468,6 @@ impl TryFrom for RtRoute { Some(parser::parse_ip_addr(r.gateway.as_str())?) }; - /* - let (dest, dst_len) = if gateway.is_some() { - (vec![0 as u8; 4], 0) - } else { - (tdest, tdst_len) - }; - */ - Ok(Self { dest, source, diff --git a/src/agent/netlink/src/lib.rs b/src/agent/netlink/src/lib.rs index 942a1b1ff..37268286f 100644 --- a/src/agent/netlink/src/lib.rs +++ b/src/agent/netlink/src/lib.rs @@ -11,6 +11,18 @@ //! - all fields have been correctly aligned #![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; @@ -28,6 +40,7 @@ extern crate slog_scope; use nix::errno::Errno; use std::borrow::Borrow; +use std::ffi::CString; use std::fmt; use std::mem; use std::os::unix::io::RawFd; @@ -246,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; @@ -456,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; @@ -1005,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; @@ -1302,7 +1315,7 @@ 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, }) } @@ -1412,7 +1425,7 @@ impl RtnlHandle { 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, }; @@ -1649,7 +1662,11 @@ impl RtnlHandle { // Safe because the data buffer should be big enough. unsafe { - nlh.addattr_var(IFLA_IFNAME, name.as_ptr() as *const u8, name.len() + 1); + if let Ok(cname) = CString::new(name.as_bytes()) { + nlh.addattr_var(IFLA_IFNAME, cname.as_bytes()); + } else { + return nix_errno(Errno::EINVAL); + } nlh.addattr32(IFLA_EXT_MASK, RTEXT_FILTER_VF | RTEXT_FILTER_SKIP_STATS); } @@ -1761,11 +1778,7 @@ impl RtnlHandle { // Safe because we have allocated a big enough data buffer. unsafe { - nlh.addattr_var( - IFA_ADDRESS, - addr.addr.as_ptr() as *const u8, - addr.addr.len(), - ); + nlh.addattr_var(IFA_ADDRESS, addr.addr.as_ref()); } // ignore EADDRNOTAVAIL here.. @@ -1774,7 +1787,7 @@ impl RtnlHandle { Ok(()) } - pub 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)?; } @@ -1799,9 +1812,9 @@ impl RtnlHandle { // Safe because we have allocated a big enough data buffer. unsafe { - nlh.addattr_var(IFA_ADDRESS, ip.addr.as_ptr() as *const u8, ip.addr.len()); + 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_ptr() as *const u8, ip.addr.len()); + nlh.addattr_var(IFA_LOCAL, ip.addr.as_ref()); } self.rtnl_talk(v.as_mut_slice(), false)?; @@ -1950,7 +1963,7 @@ impl RtnlHandle { Ok(rs) } - pub 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") { @@ -1991,22 +2004,19 @@ impl RtnlHandle { unsafe { if let Some(source) = r.source.as_ref() { - let len = source.len(); if r.src_len > 0 { - nlh.addattr_var(RTA_SRC, source.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_SRC, source.as_ref()); } else { - nlh.addattr_var(RTA_PREFSRC, source.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_PREFSRC, source.as_ref()); } } if let Some(dest) = r.dest.as_ref() { - let len = dest.len(); - nlh.addattr_var(RTA_DST, dest.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_DST, dest.as_ref()); } if let Some(gateway) = r.gateway.as_ref() { - let len = gateway.len(); - nlh.addattr_var(RTA_GATEWAY, gateway.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); } nlh.addattr32(RTA_OIF, r.index as u32); @@ -2040,22 +2050,19 @@ impl RtnlHandle { // Safe because we have allocated a big enough data buffer. unsafe { if let Some(source) = r.source.as_ref() { - let len = source.len(); if r.src_len > 0 { - nlh.addattr_var(RTA_SRC, source.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_SRC, source.as_ref()); } else { - nlh.addattr_var(RTA_PREFSRC, source.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_PREFSRC, source.as_ref()); } } if let Some(dest) = r.dest.as_ref() { - let len = dest.len(); - nlh.addattr_var(RTA_DST, dest.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_DST, dest.as_ref()); } if let Some(gateway) = r.gateway.as_ref() { - let len = gateway.len(); - nlh.addattr_var(RTA_GATEWAY, gateway.as_ptr() as *const u8, len); + nlh.addattr_var(RTA_GATEWAY, gateway.as_ref()); } nlh.addattr32(RTA_OIF, r.index as u32); @@ -2092,8 +2099,8 @@ impl Drop for RtnlHandle { /// Parse netlink attributes from raw buffer. /// -/// Safety: -/// Caller needs to ensure rta and rtalen are valid. +/// # Safety +/// Caller needs to ensure that rta and rtalen are valid. pub unsafe fn parse_attrs( mut rta: *const rtattr, mut rtalen: u32, @@ -2115,24 +2122,32 @@ pub unsafe fn parse_attrs( } impl nlmsghdr { - pub unsafe fn addattr_var(&mut self, cat: u16, data: *const u8, len: usize) { + /// 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!(len) as u16; + let alen = RTA_LENGTH!(data.len()) as u16; (*rta).rta_type = cat; (*rta).rta_len = alen; - if len > 0 { + if !data.is_empty() { libc::memcpy( RTA_DATA!(rta) as *mut libc::c_void, - data as *const libc::c_void, - len, + 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(); @@ -2151,6 +2166,10 @@ impl nlmsghdr { self.nlmsg_len = NLMSG_ALIGN!(self.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); @@ -2190,32 +2209,32 @@ impl nlmsghdr { /// Add a 8-bit attribute. /// - /// Safety: - /// Caller needs to ensure there's enough space to store the 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); } /// Add a 16-bit attribute. /// - /// Safety: - /// Caller needs to ensure there's enough space to store the 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); } /// Add a 32-bit attribute. /// - /// Safety: - /// Caller needs to ensure there's enough space to store the 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); } /// Add a 64-bit attribute. /// - /// Safety: - /// Caller needs to ensure there's enough space to store the 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); } @@ -2249,30 +2268,50 @@ unsafe fn getattr_size(rta: *const rtattr) -> u64 { panic!("impossible!"); } +/// 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 } +/// 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 } +/// 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 } +/// 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) } +/// 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; diff --git a/src/agent/netlink/src/parser.rs b/src/agent/netlink/src/parser.rs index 134f37007..0786c0396 100644 --- a/src/agent/netlink/src/parser.rs +++ b/src/agent/netlink/src/parser.rs @@ -37,12 +37,12 @@ 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))) + 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(); + let fields: Vec<&str> = s.split('/').collect(); if fields.len() != 2 { nix_errno(Errno::EINVAL) @@ -52,7 +52,7 @@ pub fn parse_ipv4_cidr(s: &str) -> Result<(Vec, u8)> { } pub fn parse_cidr(s: &str) -> Result<(Vec, u8)> { - let fields: Vec<&str> = s.split("/").collect(); + let fields: Vec<&str> = s.split('/').collect(); if fields.len() != 2 { nix_errno(Errno::EINVAL) @@ -62,7 +62,7 @@ pub fn parse_cidr(s: &str) -> Result<(Vec, u8)> { } pub fn parse_mac_addr(hwaddr: &str) -> Result> { - let fields: Vec<&str> = hwaddr.split(":").collect(); + let fields: Vec<&str> = hwaddr.split(':').collect(); if fields.len() != 6 { nix_errno(Errno::EINVAL) @@ -80,8 +80,8 @@ pub fn parse_mac_addr(hwaddr: &str) -> Result> { /// Format an IPv4/IPv6/MAC address. /// -/// Safety: -/// Caller needs to ensure addr and len are valid. +/// # 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 {