pcap/
device.rs

1use std::{convert::TryFrom, net::IpAddr, ptr};
2
3use bitflags::bitflags;
4
5#[cfg(target_os = "windows")]
6use windows_sys::Win32::Networking::WinSock;
7
8use crate::{
9    capture::{Active, Capture},
10    cstr_to_string, raw, Error,
11};
12
13bitflags! {
14    /// Network device flags.
15    pub struct IfFlags: u32 {
16        /// Set if the device is a loopback interface
17        const LOOPBACK = raw::PCAP_IF_LOOPBACK;
18        /// Set if the device is up
19        const UP = raw::PCAP_IF_UP;
20        /// Set if the device is running
21        const RUNNING = raw::PCAP_IF_RUNNING;
22        /// Set if the device is a wireless interface; this includes IrDA as well as radio-based
23        /// networks such as IEEE 802.15.4 and IEEE 802.11, so it doesn't just mean Wi-Fi
24        const WIRELESS = raw::PCAP_IF_WIRELESS;
25    }
26}
27
28impl From<u32> for IfFlags {
29    fn from(flags: u32) -> Self {
30        IfFlags::from_bits_truncate(flags)
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35/// Indication of whether the adapter is connected or not; for wireless interfaces, "connected"
36/// means "associated with a network".
37pub enum ConnectionStatus {
38    /// It's unknown whether the adapter is connected or not
39    Unknown,
40    /// The adapter is connected
41    Connected,
42    /// The adapter is disconnected
43    Disconnected,
44    /// The notion of "connected" and "disconnected" don't apply to this interface; for example, it
45    /// doesn't apply to a loopback device
46    NotApplicable,
47}
48
49impl From<u32> for ConnectionStatus {
50    fn from(flags: u32) -> Self {
51        match flags & raw::PCAP_IF_CONNECTION_STATUS {
52            raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN => ConnectionStatus::Unknown,
53            raw::PCAP_IF_CONNECTION_STATUS_CONNECTED => ConnectionStatus::Connected,
54            raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED => ConnectionStatus::Disconnected,
55            raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE => ConnectionStatus::NotApplicable,
56            // DeviceFlags::CONNECTION_STATUS should be a 2-bit mask which means that the four
57            // values should cover all the possibilities.
58            // GRCOV_EXCL_START
59            _ => unreachable!(),
60            // GRCOV_EXCL_STOP
61        }
62    }
63}
64
65#[derive(Debug, Clone)]
66pub struct DeviceFlags {
67    pub if_flags: IfFlags,
68    pub connection_status: ConnectionStatus,
69}
70
71impl From<u32> for DeviceFlags {
72    fn from(flags: u32) -> Self {
73        DeviceFlags {
74            if_flags: flags.into(),
75            connection_status: flags.into(),
76        }
77    }
78}
79
80impl DeviceFlags {
81    pub fn empty() -> Self {
82        DeviceFlags {
83            if_flags: IfFlags::empty(),
84            connection_status: ConnectionStatus::Unknown,
85        }
86    }
87
88    pub fn contains(&self, if_flags: IfFlags) -> bool {
89        self.if_flags.contains(if_flags)
90    }
91
92    pub fn is_loopback(&self) -> bool {
93        self.contains(IfFlags::LOOPBACK)
94    }
95
96    pub fn is_up(&self) -> bool {
97        self.contains(IfFlags::UP)
98    }
99
100    pub fn is_running(&self) -> bool {
101        self.contains(IfFlags::RUNNING)
102    }
103
104    pub fn is_wireless(&self) -> bool {
105        self.contains(IfFlags::WIRELESS)
106    }
107}
108
109#[derive(Debug, Clone)]
110/// A network device name and pcap's description of it.
111pub struct Device {
112    /// The name of the interface
113    pub name: String,
114    /// A textual description of the interface, if available
115    pub desc: Option<String>,
116    /// Addresses associated with this interface
117    pub addresses: Vec<Address>,
118    /// Interface flags
119    pub flags: DeviceFlags,
120}
121
122impl Device {
123    fn new(
124        name: String,
125        desc: Option<String>,
126        addresses: Vec<Address>,
127        flags: DeviceFlags,
128    ) -> Device {
129        Device {
130            name,
131            desc,
132            addresses,
133            flags,
134        }
135    }
136
137    /// Opens a `Capture<Active>` on this device.
138    pub fn open(self) -> Result<Capture<Active>, Error> {
139        Capture::from_device(self)?.open()
140    }
141
142    /// Returns the default Device suitable for captures according to pcap_findalldevs,
143    /// or an error from pcap. Note that there may be no suitable devices.
144    pub fn lookup() -> Result<Option<Device>, Error> {
145        unsafe {
146            Device::with_all_devs(|all_devs| {
147                let dev = all_devs;
148                Ok(if !dev.is_null() {
149                    Some(Device::try_from(&*dev)?)
150                } else {
151                    None
152                })
153            })
154        }
155    }
156
157    /// Returns a vector of `Device`s known by pcap via pcap_findalldevs.
158    pub fn list() -> Result<Vec<Device>, Error> {
159        unsafe {
160            Device::with_all_devs(|all_devs| {
161                let mut devices = vec![];
162                let mut dev = all_devs;
163                while !dev.is_null() {
164                    devices.push(Device::try_from(&*dev)?);
165                    dev = (*dev).next;
166                }
167                Ok(devices)
168            })
169        }
170    }
171
172    unsafe fn with_all_devs<T, F>(func: F) -> Result<T, Error>
173    where
174        F: FnOnce(*mut raw::pcap_if_t) -> Result<T, Error>,
175    {
176        let all_devs = Error::with_errbuf(|err| {
177            let mut all_devs: *mut raw::pcap_if_t = ptr::null_mut();
178            if raw::pcap_findalldevs(&mut all_devs, err) != 0 {
179                return Err(Error::new(err));
180            }
181            Ok(all_devs)
182        })?;
183        let result = func(all_devs);
184        raw::pcap_freealldevs(all_devs);
185        result
186    }
187}
188
189impl From<&str> for Device {
190    fn from(name: &str) -> Self {
191        Device::new(name.into(), None, Vec::new(), DeviceFlags::empty())
192    }
193}
194
195impl TryFrom<&raw::pcap_if_t> for Device {
196    type Error = Error;
197
198    fn try_from(dev: &raw::pcap_if_t) -> Result<Self, Error> {
199        Ok(Device::new(
200            unsafe { cstr_to_string(dev.name)?.ok_or(Error::InvalidString)? },
201            unsafe { cstr_to_string(dev.description)? },
202            unsafe { Address::new_vec(dev.addresses) },
203            DeviceFlags::from(dev.flags),
204        ))
205    }
206}
207
208#[derive(Debug, Clone)]
209/// Address information for an interface
210pub struct Address {
211    /// The address
212    pub addr: IpAddr,
213    /// Network mask for this address
214    pub netmask: Option<IpAddr>,
215    /// Broadcast address for this address
216    pub broadcast_addr: Option<IpAddr>,
217    /// P2P destination address for this address
218    pub dst_addr: Option<IpAddr>,
219}
220
221impl Address {
222    unsafe fn new_vec(mut ptr: *const raw::pcap_addr_t) -> Vec<Address> {
223        let mut vec = Vec::new();
224        while !ptr.is_null() {
225            if let Some(addr) = Address::new(ptr) {
226                vec.push(addr);
227            }
228            ptr = (*ptr).next;
229        }
230        vec
231    }
232
233    unsafe fn new(ptr: *const raw::pcap_addr_t) -> Option<Address> {
234        Self::convert_sockaddr((*ptr).addr).map(|addr| Address {
235            addr,
236            netmask: Self::convert_sockaddr((*ptr).netmask),
237            broadcast_addr: Self::convert_sockaddr((*ptr).broadaddr),
238            dst_addr: Self::convert_sockaddr((*ptr).dstaddr),
239        })
240    }
241
242    #[cfg(not(windows))]
243    unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
244        if ptr.is_null() {
245            return None;
246        }
247
248        match (*ptr).sa_family as i32 {
249            libc::AF_INET => {
250                let ptr: *const libc::sockaddr_in = std::mem::transmute(ptr);
251                Some(IpAddr::V4(u32::from_be((*ptr).sin_addr.s_addr).into()))
252            }
253
254            libc::AF_INET6 => {
255                let ptr: *const libc::sockaddr_in6 = std::mem::transmute(ptr);
256                Some(IpAddr::V6((*ptr).sin6_addr.s6_addr.into()))
257            }
258
259            _ => None,
260        }
261    }
262
263    #[cfg(windows)]
264    unsafe fn convert_sockaddr(ptr: *const libc::sockaddr) -> Option<IpAddr> {
265        if ptr.is_null() {
266            return None;
267        }
268
269        match (*ptr).sa_family as u32 {
270            WinSock::AF_INET => {
271                let ptr: *const WinSock::SOCKADDR_IN = std::mem::transmute(ptr);
272                let addr: [u8; 4] = ((*ptr).sin_addr.S_un.S_addr).to_ne_bytes();
273                Some(IpAddr::from(addr))
274            }
275            WinSock::AF_INET6 => {
276                let ptr: *const WinSock::SOCKADDR_IN6 = std::mem::transmute(ptr);
277                let addr = (*ptr).sin6_addr.u.Byte;
278                Some(IpAddr::from(addr))
279            }
280
281            _ => None,
282        }
283    }
284}
285
286#[cfg(test)]
287mod tests {
288    use std::ffi::CString;
289
290    use crate::raw::testmod::{as_pcap_t, RAWMTX};
291
292    use super::*;
293
294    #[cfg(not(windows))]
295    enum Sockaddr {
296        SockaddrIn(libc::sockaddr_in),
297        SockaddrIn6(libc::sockaddr_in6),
298    }
299
300    #[cfg(windows)]
301    enum Sockaddr {
302        SockaddrIn(WinSock::SOCKADDR_IN),
303        SockaddrIn6(WinSock::SOCKADDR_IN6),
304    }
305
306    impl Sockaddr {
307        fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
308            match self {
309                Sockaddr::SockaddrIn(ref mut sin) => sin as *mut _ as _,
310                Sockaddr::SockaddrIn6(ref mut sin6) => sin6 as *mut _ as _,
311            }
312        }
313
314        fn set_family(&mut self, family: u16) {
315            // Annoyingly this differs between Linux (u16) and Mac (u8).
316            #[cfg(not(windows))]
317            let family = family as libc::sa_family_t;
318
319            match self {
320                Sockaddr::SockaddrIn(ref mut sin) => sin.sin_family = family,
321                Sockaddr::SockaddrIn6(ref mut sin6) => sin6.sin6_family = family,
322            }
323        }
324    }
325
326    static IF1_NAME: &str = "if1";
327    static IF2_NAME: &str = "if2";
328    static IF1_DESC: &str = "if1 desc";
329    static IF2_DESC: &str = "if2 desc";
330
331    fn devs() -> Vec<raw::pcap_if_t> {
332        let mut devs = vec![
333            raw::pcap_if_t {
334                next: std::ptr::null_mut(),
335                name: CString::new(IF1_NAME).unwrap().into_raw(),
336                description: CString::new(IF1_DESC).unwrap().into_raw(),
337                addresses: std::ptr::null_mut(),
338                flags: (raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP),
339            },
340            raw::pcap_if_t {
341                next: std::ptr::null_mut(),
342                name: CString::new(IF2_NAME).unwrap().into_raw(),
343                description: CString::new(IF2_DESC).unwrap().into_raw(),
344                addresses: std::ptr::null_mut(),
345                flags: 0,
346            },
347        ];
348        devs[0].next = &mut devs[1];
349        devs
350    }
351
352    trait InetAddressV4 {
353        fn new() -> Self;
354        fn set_addr(&mut self, addr: u32);
355    }
356
357    #[cfg(not(windows))]
358    impl InetAddressV4 for libc::sockaddr_in {
359        fn new() -> Self {
360            let mut addr: Self = unsafe { std::mem::zeroed() };
361            addr.sin_family = libc::AF_INET as libc::sa_family_t;
362            addr
363        }
364
365        fn set_addr(&mut self, addr: u32) {
366            self.sin_addr.s_addr = addr;
367        }
368    }
369
370    #[cfg(windows)]
371    impl InetAddressV4 for WinSock::SOCKADDR_IN {
372        fn new() -> Self {
373            let mut addr: Self = unsafe { std::mem::zeroed() };
374            // The cast is only necessary due to a bug in windows_sys@v0.36.1
375            addr.sin_family = WinSock::AF_INET as u16;
376            addr
377        }
378
379        fn set_addr(&mut self, addr: u32) {
380            self.sin_addr.S_un.S_addr = addr;
381        }
382    }
383
384    fn sockaddr_ipv4() -> Sockaddr {
385        #[cfg(not(windows))]
386        let mut addr: libc::sockaddr_in = InetAddressV4::new();
387        #[cfg(windows)]
388        let mut addr: WinSock::SOCKADDR_IN = InetAddressV4::new();
389
390        addr.sin_port = 1075;
391        addr.set_addr(0x0A000042_u32.to_be());
392
393        Sockaddr::SockaddrIn(addr)
394    }
395
396    trait InetAddressV6 {
397        fn new() -> Self;
398        fn set_octet(&mut self, index: usize, octet: u8);
399    }
400
401    #[cfg(not(windows))]
402    impl InetAddressV6 for libc::sockaddr_in6 {
403        fn new() -> Self {
404            let mut addr: Self = unsafe { std::mem::zeroed() };
405            addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
406            addr.sin6_addr.s6_addr[0] = 0xFE;
407            addr.sin6_addr.s6_addr[1] = 0x80;
408            addr
409        }
410
411        fn set_octet(&mut self, index: usize, octet: u8) {
412            self.sin6_addr.s6_addr[index] = octet;
413        }
414    }
415
416    #[cfg(windows)]
417    impl InetAddressV6 for WinSock::SOCKADDR_IN6 {
418        fn new() -> Self {
419            let mut addr: Self = unsafe { std::mem::zeroed() };
420            // The cast is only necessary due to a bug in windows_sys@v0.36.1
421            addr.sin6_family = WinSock::AF_INET6 as u16;
422            unsafe {
423                addr.sin6_addr.u.Byte[0] = 0xFE;
424                addr.sin6_addr.u.Byte[1] = 0x80;
425            }
426            addr
427        }
428
429        fn set_octet(&mut self, index: usize, octet: u8) {
430            unsafe { self.sin6_addr.u.Byte[index] = octet };
431        }
432    }
433
434    fn sockaddr_ipv6() -> Sockaddr {
435        #[cfg(not(windows))]
436        let mut addr: libc::sockaddr_in6 = InetAddressV6::new();
437        #[cfg(windows)]
438        let mut addr: WinSock::SOCKADDR_IN6 = InetAddressV6::new();
439
440        addr.sin6_port = 1075;
441        addr.set_octet(15, 0x42);
442
443        Sockaddr::SockaddrIn6(addr)
444    }
445
446    impl From<&mut Sockaddr> for raw::pcap_addr_t {
447        fn from(value: &mut Sockaddr) -> Self {
448            raw::pcap_addr_t {
449                next: std::ptr::null_mut(),
450                addr: value.as_mut_ptr(),
451                netmask: std::ptr::null_mut(),
452                broadaddr: std::ptr::null_mut(),
453                dstaddr: std::ptr::null_mut(),
454            }
455        }
456    }
457
458    #[test]
459    fn test_device_flags() {
460        let flags = DeviceFlags::from(
461            raw::PCAP_IF_LOOPBACK | raw::PCAP_IF_UP | raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE,
462        );
463
464        assert!(flags.is_loopback());
465        assert!(flags.is_up());
466        assert!(flags.contains(IfFlags::LOOPBACK | IfFlags::UP));
467
468        assert!(!flags.is_running());
469        assert!(!flags.is_wireless());
470
471        assert_ne!(flags.connection_status, ConnectionStatus::Unknown);
472        assert_ne!(flags.connection_status, ConnectionStatus::Connected);
473        assert_ne!(flags.connection_status, ConnectionStatus::Disconnected);
474        assert_eq!(flags.connection_status, ConnectionStatus::NotApplicable);
475
476        assert!(!format!("{flags:?}").is_empty());
477    }
478
479    #[test]
480    fn test_connection_status() {
481        let flags = raw::PCAP_IF_CONNECTION_STATUS_UNKNOWN;
482        assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Unknown);
483
484        let flags = raw::PCAP_IF_CONNECTION_STATUS_CONNECTED;
485        assert_eq!(ConnectionStatus::from(flags), ConnectionStatus::Connected);
486
487        let flags = raw::PCAP_IF_CONNECTION_STATUS_DISCONNECTED;
488        assert_eq!(
489            ConnectionStatus::from(flags),
490            ConnectionStatus::Disconnected
491        );
492
493        let flags = raw::PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE;
494        assert_eq!(
495            ConnectionStatus::from(flags),
496            ConnectionStatus::NotApplicable
497        );
498    }
499
500    #[test]
501    fn test_into_capture() {
502        let _m = RAWMTX.lock();
503
504        let mut dummy: isize = 777;
505        let pcap = as_pcap_t(&mut dummy);
506
507        let ctx = raw::pcap_create_context();
508        ctx.expect().return_once_st(move |_, _| pcap);
509
510        let ctx = raw::pcap_activate_context();
511        ctx.expect()
512            .withf_st(move |arg1| *arg1 == pcap)
513            .return_once(|_| 0);
514
515        let ctx = raw::pcap_close_context();
516        ctx.expect()
517            .withf_st(move |ptr| *ptr == pcap)
518            .return_once(|_| {});
519
520        let device: Device = "device".into();
521        let _capture: Capture<Active> = device.clone().open().unwrap();
522
523        assert!(!format!("{device:?}").is_empty());
524    }
525
526    #[test]
527    fn test_lookup() {
528        let _m = RAWMTX.lock();
529
530        let ctx = raw::pcap_findalldevs_context();
531        ctx.expect().return_once_st(move |arg1, _| {
532            unsafe { *arg1 = std::ptr::null_mut() };
533            0
534        });
535
536        let ctx = raw::pcap_freealldevs_context();
537        ctx.expect().return_once(move |_| {});
538
539        let device = Device::lookup().unwrap();
540        assert!(device.is_none());
541
542        let mut devs = devs();
543        let mut addrs = sockaddr_ipv4();
544        let mut pcap_addr = (&mut addrs).into();
545        devs[0].addresses = &mut pcap_addr;
546        let devs_ptr = devs.as_mut_ptr();
547
548        let ctx = raw::pcap_findalldevs_context();
549        ctx.checkpoint();
550        ctx.expect().return_once_st(move |arg1, _| {
551            unsafe { *arg1 = devs_ptr };
552            0
553        });
554
555        let ctx = raw::pcap_freealldevs_context();
556        ctx.checkpoint();
557        ctx.expect().return_once(move |_| {});
558
559        let device = Device::lookup().unwrap().unwrap();
560        assert_eq!(&device.name, IF1_NAME);
561        assert_eq!(&device.desc.unwrap(), IF1_DESC);
562        assert_eq!(device.addresses.len(), 1);
563        assert!(device.addresses[0].addr.is_ipv4());
564
565        let ctx = raw::pcap_findalldevs_context();
566        ctx.checkpoint();
567        ctx.expect().return_once_st(move |_, _| -1);
568
569        let ctx = raw::pcap_freealldevs_context();
570        ctx.checkpoint();
571
572        let result = Device::lookup();
573        assert!(result.is_err());
574    }
575
576    #[test]
577    fn test_list() {
578        let _m = RAWMTX.lock();
579
580        let ctx = raw::pcap_findalldevs_context();
581        ctx.expect().return_once_st(move |arg1, _| {
582            unsafe { *arg1 = std::ptr::null_mut() };
583            0
584        });
585
586        let ctx = raw::pcap_freealldevs_context();
587        ctx.expect().return_once(move |_| {});
588
589        let devices = Device::list().unwrap();
590        assert!(devices.is_empty());
591
592        let mut devs = devs();
593        let mut ipv4s = sockaddr_ipv4();
594        let mut ipv6s = sockaddr_ipv6();
595        let mut pcap_addr: raw::pcap_addr_t = (&mut ipv4s).into();
596        let mut pcap_addr6: raw::pcap_addr_t = (&mut ipv6s).into();
597        pcap_addr.next = &mut pcap_addr6;
598        devs[1].addresses = &mut pcap_addr;
599        let devs_ptr = devs.as_mut_ptr();
600
601        let ctx = raw::pcap_findalldevs_context();
602        ctx.checkpoint();
603        ctx.expect().return_once_st(move |arg1, _| {
604            unsafe { *arg1 = devs_ptr };
605            0
606        });
607
608        let ctx = raw::pcap_freealldevs_context();
609        ctx.checkpoint();
610        ctx.expect().return_once(move |_| {});
611
612        let devices = Device::list().unwrap();
613        assert_eq!(devices.len(), devs.len());
614
615        assert_eq!(&devices[0].name, IF1_NAME);
616        assert_eq!(devices[0].desc.as_ref().unwrap(), IF1_DESC);
617        assert_eq!(devices[0].addresses.len(), 0);
618
619        assert_eq!(&devices[1].name, IF2_NAME);
620        assert_eq!(devices[1].desc.as_ref().unwrap(), IF2_DESC);
621        assert_eq!(devices[1].addresses.len(), 2);
622        assert!(devices[1].addresses[0].addr.is_ipv4());
623        assert!(devices[1].addresses[1].addr.is_ipv6());
624
625        let ctx = raw::pcap_findalldevs_context();
626        ctx.checkpoint();
627        ctx.expect().return_once_st(move |_, _| -1);
628
629        let ctx = raw::pcap_freealldevs_context();
630        ctx.checkpoint();
631
632        let result = Device::list();
633        assert!(result.is_err());
634    }
635
636    #[test]
637    fn test_address_ipv4() {
638        let mut addr = sockaddr_ipv4();
639        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
640
641        let address = unsafe { Address::new(&pcap_addr) }.unwrap();
642
643        assert!(address.addr.is_ipv4());
644        assert_eq!(address.addr.to_string(), "10.0.0.66");
645
646        assert!(address.netmask.is_none());
647        assert!(address.broadcast_addr.is_none());
648        assert!(address.dst_addr.is_none());
649
650        assert!(!format!("{address:?}").is_empty());
651    }
652
653    #[test]
654    fn test_address_family() {
655        let mut addr = sockaddr_ipv4();
656
657        #[cfg(not(windows))]
658        addr.set_family(libc::AF_IPX as u16);
659        #[cfg(windows)]
660        addr.set_family(WinSock::AF_IPX);
661
662        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
663
664        let address = unsafe { Address::new(&pcap_addr) };
665        assert!(address.is_none());
666    }
667
668    #[test]
669    fn test_address_ipv6() {
670        let mut addr = sockaddr_ipv6();
671        let pcap_addr: raw::pcap_addr_t = (&mut addr).into();
672
673        let address = unsafe { Address::new(&pcap_addr) }.unwrap();
674
675        assert!(address.addr.is_ipv6());
676        assert_eq!(address.addr.to_string(), "fe80::42");
677
678        assert!(address.netmask.is_none());
679        assert!(address.broadcast_addr.is_none());
680        assert!(address.dst_addr.is_none());
681
682        assert!(!format!("{address:?}").is_empty());
683    }
684}