From 1b8c2cba607ff42ad3fecd8b0acba0dec35c55ad Mon Sep 17 00:00:00 2001 From: Liu Jiang Date: Sun, 31 May 2020 11:19:54 +0800 Subject: [PATCH] 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"); + } + */ +}