Lines
100 %
Functions
69.88 %
use std::{convert::TryFrom, net::IpAddr, ptr};
use bitflags::bitflags;
#[cfg(target_os = "windows")]
use windows_sys::Win32::Networking::WinSock;
use crate::{
capture::{Active, Capture},
cstr_to_string, raw, Error,
};
bitflags! {
/// Network device flags.
pub struct IfFlags: u32 {
/// Set if the device is a loopback interface
const LOOPBACK = raw::PCAP_IF_LOOPBACK;
/// Set if the device is up
const UP = raw::PCAP_IF_UP;
/// Set if the device is running
const RUNNING = raw::PCAP_IF_RUNNING;
/// Set if the device is a wireless interface; this includes IrDA as well as radio-based
/// networks such as IEEE 802.15.4 and IEEE 802.11, so it doesn't just mean Wi-Fi
const WIRELESS = raw::PCAP_IF_WIRELESS;
}
impl From<u32> for IfFlags {
fn from(flags: u32) -> Self {
IfFlags::from_bits_truncate(flags)
#[derive(Debug, Clone, PartialEq, Eq)]
/// Indication of whether the adapter is connected or not; for wireless interfaces, "connected"
/// means "associated with a network".
pub enum ConnectionStatus {
/// It's unknown whether the adapter is connected or not
Unknown,
/// The adapter is connected
Connected,
/// The adapter is disconnected
Disconnected,
/// The notion of "connected" and "disconnected" don't apply to this interface; for example, it
/// doesn't apply to a loopback device
NotApplicable,
impl From<u32> for ConnectionStatus {
match flags & raw::PCAP_IF_CONNECTION_STATUS {
raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN => ConnectionStatus::Unknown,
raw::PCAP_IF_CONNECTION_STATUS_CONNECTED => ConnectionStatus::Connected,
raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED => ConnectionStatus::Disconnected,
raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE => ConnectionStatus::NotApplicable,
// DeviceFlags::CONNECTION_STATUS should be a 2-bit mask which means that the four
// values should cover all the possibilities.
// GRCOV_EXCL_START
_ => unreachable!(),
// GRCOV_EXCL_STOP
#[derive(Debug, Clone)]
pub struct DeviceFlags {
pub if_flags: IfFlags,
pub connection_status: ConnectionStatus,
impl From<u32> for DeviceFlags {
DeviceFlags {
if_flags: flags.into(),
connection_status: flags.into(),
impl DeviceFlags {
pub fn empty() -> Self {
if_flags: IfFlags::empty(),
connection_status: ConnectionStatus::Unknown,
pub fn contains(&self, if_flags: IfFlags) -> bool {
self.if_flags.contains(if_flags)
pub fn is_loopback(&self) -> bool {
self.contains(IfFlags::LOOPBACK)
pub fn is_up(&self) -> bool {
self.contains(IfFlags::UP)
pub fn is_running(&self) -> bool {
self.contains(IfFlags::RUNNING)
pub fn is_wireless(&self) -> bool {
self.contains(IfFlags::WIRELESS)
/// A network device name and pcap's description of it.
pub struct Device {
/// The name of the interface
pub name: String,
/// A textual description of the interface, if available
pub desc: Option<String>,
/// Addresses associated with this interface
pub addresses: Vec<Address>,
/// Interface flags
pub flags: DeviceFlags,
impl Device {
fn new(
name: String,
desc: Option<String>,
addresses: Vec<Address>,
flags: DeviceFlags,
) -> Device {
Device {
name,
desc,
addresses,
flags,
/// Opens a `Capture<Active>` on this device.
pub fn open(self) -> Result<Capture<Active>, Error> {
Capture::from_device(self)?.open()
/// Returns the default Device suitable for captures according to pcap_findalldevs,
/// or an error from pcap. Note that there may be no suitable devices.
pub fn lookup() -> Result<Option<Device>, Error> {
unsafe {
Device::with_all_devs(|all_devs| {
let dev = all_devs;
Ok(if !dev.is_null() {
Some(Device::try_from(&*dev)?)
} else {
None
})
/// Returns a vector of `Device`s known by pcap via pcap_findalldevs.
pub fn list() -> Result<Vec<Device>, Error> {
let mut devices = vec![];
let mut dev = all_devs;
while !dev.is_null() {
devices.push(Device::try_from(&*dev)?);
dev = (*dev).next;
Ok(devices)
unsafe fn with_all_devs<T, F>(func: F) -> Result<T, Error>
where
F: FnOnce(*mut raw::pcap_if_t) -> Result<T, Error>,
{
let all_devs = Error::with_errbuf(|err| {
let mut all_devs: *mut raw::pcap_if_t = ptr::null_mut();
if raw::pcap_findalldevs(&mut all_devs, err) != 0 {
return Err(Error::new(err));
Ok(all_devs)
})?;
let result = func(all_devs);
raw::pcap_freealldevs(all_devs);
result
impl From<&str> for Device {
fn from(name: &str) -> Self {
Device::new(name.into(), None, Vec::new(), DeviceFlags::empty())
impl TryFrom<&raw::pcap_if_t> for Device {
type Error = Error;
fn try_from(dev: &raw::pcap_if_t) -> Result<Self, Error> {
Ok(Device::new(
unsafe { cstr_to_string(dev.name)?.ok_or(Error::InvalidString)? },
unsafe { cstr_to_string(dev.description)? },
unsafe { Address::new_vec(dev.addresses) },
DeviceFlags::from(dev.flags),
))
/// Address information for an interface
pub struct Address {
/// The address
pub addr: IpAddr,
/// Network mask for this address
pub netmask: Option<IpAddr>,
/// Broadcast address for this address
pub broadcast_addr: Option<IpAddr>,
/// P2P destination address for this address
pub dst_addr: Option<IpAddr>,
impl Address {
unsafe fn new_vec(mut ptr: *const raw::pcap_addr_t) -> Vec<Address> {
let mut vec = Vec::new();
while !ptr.is_null() {
if let Some(addr) = Address::new(ptr) {
vec.push(addr);
ptr = (*ptr).next;
vec
unsafe fn new(ptr: *const raw::pcap_addr_t) -> Option<Address> {
Self::convert_sockaddr((*ptr).addr).map(|addr| Address {
addr,
netmask: Self::convert_sockaddr((*ptr).netmask),
broadcast_addr: Self::convert_sockaddr((*ptr).broadaddr),
dst_addr: Self::convert_sockaddr((*ptr).dstaddr),
#[cfg(not(windows))]
unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
if ptr.is_null() {
return None;
match (*ptr).sa_family as i32 {
libc::AF_INET => {
let ptr: *const libc::sockaddr_in = std::mem::transmute(ptr);
Some(IpAddr::V4(u32::from_be((*ptr).sin_addr.s_addr).into()))
libc::AF_INET6 => {
let ptr: *const libc::sockaddr_in6 = std::mem::transmute(ptr);
Some(IpAddr::V6((*ptr).sin6_addr.s6_addr.into()))
_ => None,
#[cfg(windows)]
match (*ptr).sa_family as u32 {
WinSock::AF_INET => {
let ptr: *const WinSock::SOCKADDR_IN = std::mem::transmute(ptr);
let addr: [u8; 4] = ((*ptr).sin_addr.S_un.S_addr).to_ne_bytes();
Some(IpAddr::from(addr))
WinSock::AF_INET6 => {
let ptr: *const WinSock::SOCKADDR_IN6 = std::mem::transmute(ptr);
let addr = (*ptr).sin6_addr.u.Byte;
#[cfg(test)]
mod tests {
use std::ffi::CString;
use crate::raw::testmod::{as_pcap_t, RAWMTX};
use super::*;
enum Sockaddr {
SockaddrIn(libc::sockaddr_in),
SockaddrIn6(libc::sockaddr_in6),
SockaddrIn(WinSock::SOCKADDR_IN),
SockaddrIn6(WinSock::SOCKADDR_IN6),
impl Sockaddr {
fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
match self {
Sockaddr::SockaddrIn(ref mut sin) => sin as *mut _ as _,
Sockaddr::SockaddrIn6(ref mut sin6) => sin6 as *mut _ as _,
fn set_family(&mut self, family: u16) {
// Annoyingly this differs between Linux (u16) and Mac (u8).
let family = family as libc::sa_family_t;
Sockaddr::SockaddrIn(ref mut sin) => sin.sin_family = family,
Sockaddr::SockaddrIn6(ref mut sin6) => sin6.sin6_family = family,
static IF1_NAME: &str = "if1";
static IF2_NAME: &str = "if2";
static IF1_DESC: &str = "if1 desc";
static IF2_DESC: &str = "if2 desc";
fn devs() -> Vec<raw::pcap_if_t> {
let mut devs = vec![
raw::pcap_if_t {
next: std::ptr::null_mut(),
name: CString::new(IF1_NAME).unwrap().into_raw(),
description: CString::new(IF1_DESC).unwrap().into_raw(),
addresses: std::ptr::null_mut(),
flags: (raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP),
},
name: CString::new(IF2_NAME).unwrap().into_raw(),
description: CString::new(IF2_DESC).unwrap().into_raw(),
flags: 0,
];
devs[0].next = &mut devs[1];
devs
trait InetAddressV4 {
fn new() -> Self;
fn set_addr(&mut self, addr: u32);
impl InetAddressV4 for libc::sockaddr_in {
fn new() -> Self {
let mut addr: Self = unsafe { std::mem::zeroed() };
addr.sin_family = libc::AF_INET as libc::sa_family_t;
addr
fn set_addr(&mut self, addr: u32) {
self.sin_addr.s_addr = addr;
impl InetAddressV4 for WinSock::SOCKADDR_IN {
// The cast is only necessary due to a bug in windows_sys@v0.36.1
addr.sin_family = WinSock::AF_INET as u16;
self.sin_addr.S_un.S_addr = addr;
fn sockaddr_ipv4() -> Sockaddr {
let mut addr: libc::sockaddr_in = InetAddressV4::new();
let mut addr: WinSock::SOCKADDR_IN = InetAddressV4::new();
addr.sin_port = 1075;
addr.set_addr(0x0A000042_u32.to_be());
Sockaddr::SockaddrIn(addr)
trait InetAddressV6 {
fn set_octet(&mut self, index: usize, octet: u8);
impl InetAddressV6 for libc::sockaddr_in6 {
addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
addr.sin6_addr.s6_addr[0] = 0xFE;
addr.sin6_addr.s6_addr[1] = 0x80;
fn set_octet(&mut self, index: usize, octet: u8) {
self.sin6_addr.s6_addr[index] = octet;
impl InetAddressV6 for WinSock::SOCKADDR_IN6 {
addr.sin6_family = WinSock::AF_INET6 as u16;
addr.sin6_addr.u.Byte[0] = 0xFE;
addr.sin6_addr.u.Byte[1] = 0x80;
unsafe { self.sin6_addr.u.Byte[index] = octet };
fn sockaddr_ipv6() -> Sockaddr {
let mut addr: libc::sockaddr_in6 = InetAddressV6::new();
let mut addr: WinSock::SOCKADDR_IN6 = InetAddressV6::new();
addr.sin6_port = 1075;
addr.set_octet(15, 0x42);
Sockaddr::SockaddrIn6(addr)
impl From<&mut Sockaddr> for raw::pcap_addr_t {
fn from(value: &mut Sockaddr) -> Self {
raw::pcap_addr_t {
addr: value.as_mut_ptr(),
netmask: std::ptr::null_mut(),
broadaddr: std::ptr::null_mut(),
dstaddr: std::ptr::null_mut(),
#[test]
fn test_device_flags() {
let flags = DeviceFlags::from(
raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP | raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
);
assert!(flags.is_loopback());
assert!(flags.is_up());
assert!(flags.contains(IfFlags::LOOPBACK | IfFlags::UP));
assert!(!flags.is_running());
assert!(!flags.is_wireless());
assert_ne!(flags.connection_status, ConnectionStatus::Unknown);
assert_ne!(flags.connection_status, ConnectionStatus::Connected);
assert_ne!(flags.connection_status, ConnectionStatus::Disconnected);
assert_eq!(flags.connection_status, ConnectionStatus::NotApplicable);
assert!(!format!("{:?}", flags).is_empty());
fn test_connection_status() {
let flags = raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN;
assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Unknown);
let flags = raw::PCAP_IF_CONNECTION_STATUS_CONNECTED;
assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Connected);
let flags = raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED;
assert_eq!(
ConnectionStatus::from(flags),
ConnectionStatus::Disconnected
let flags = raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
ConnectionStatus::NotApplicable
fn test_into_capture() {
let _m = RAWMTX.lock();
let mut dummy: isize = 777;
let pcap = as_pcap_t(&mut dummy);
let ctx = raw::pcap_create_context();
ctx.expect().return_once_st(move |_, _| pcap);
let ctx = raw::pcap_activate_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap)
.return_once(|_| 0);
let ctx = raw::pcap_close_context();
.withf_st(move |ptr| *ptr == pcap)
.return_once(|_| {});
let device: Device = "device".into();
let _capture: Capture<Active> = device.clone().open().unwrap();
assert!(!format!("{:?}", device).is_empty());
fn test_lookup() {
let ctx = raw::pcap_findalldevs_context();
ctx.expect().return_once_st(move |arg1, _| {
unsafe { *arg1 = std::ptr::null_mut() };
0
});
let ctx = raw::pcap_freealldevs_context();
ctx.expect().return_once(move |_| {});
let device = Device::lookup().unwrap();
assert!(device.is_none());
let mut devs = devs();
let mut addrs = sockaddr_ipv4();
let mut pcap_addr = (&mut addrs).into();
devs[0].addresses = &mut pcap_addr;
let devs_ptr = devs.as_mut_ptr();
ctx.checkpoint();
unsafe { *arg1 = devs_ptr };
let device = Device::lookup().unwrap().unwrap();
assert_eq!(&device.name, IF1_NAME);
assert_eq!(&device.desc.unwrap(), IF1_DESC);
assert_eq!(device.addresses.len(), 1);
assert!(device.addresses[0].addr.is_ipv4());
ctx.expect().return_once_st(move |_, _| -1);
let result = Device::lookup();
assert!(result.is_err());
fn test_list() {
let devices = Device::list().unwrap();
assert!(devices.is_empty());
let mut ipv4s = sockaddr_ipv4();
let mut ipv6s = sockaddr_ipv6();
let mut pcap_addr: raw::pcap_addr_t = (&mut ipv4s).into();
let mut pcap_addr6: raw::pcap_addr_t = (&mut ipv6s).into();
pcap_addr.next = &mut pcap_addr6;
devs[1].addresses = &mut pcap_addr;
assert_eq!(devices.len(), devs.len());
assert_eq!(&devices[0].name, IF1_NAME);
assert_eq!(devices[0].desc.as_ref().unwrap(), IF1_DESC);
assert_eq!(devices[0].addresses.len(), 0);
assert_eq!(&devices[1].name, IF2_NAME);
assert_eq!(devices[1].desc.as_ref().unwrap(), IF2_DESC);
assert_eq!(devices[1].addresses.len(), 2);
assert!(devices[1].addresses[0].addr.is_ipv4());
assert!(devices[1].addresses[1].addr.is_ipv6());
let result = Device::list();
fn test_address_ipv4() {
let mut addr = sockaddr_ipv4();
let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
let address = unsafe { Address::new(&pcap_addr) }.unwrap();
assert!(address.addr.is_ipv4());
assert_eq!(address.addr.to_string(), "10.0.0.66");
assert!(address.netmask.is_none());
assert!(address.broadcast_addr.is_none());
assert!(address.dst_addr.is_none());
assert!(!format!("{:?}", address).is_empty());
fn test_address_family() {
addr.set_family(libc::AF_IPX as u16);
addr.set_family(WinSock::AF_IPX);
let address = unsafe { Address::new(&pcap_addr) };
assert!(address.is_none());
fn test_address_ipv6() {
let mut addr = sockaddr_ipv6();
assert!(address.addr.is_ipv6());
assert_eq!(address.addr.to_string(), "fe80::42");