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"); + } + */ +}