Skip to main content

sniffnet_packet_parser/
link_type.rs

1use pcap::Linktype;
2
3/// `DLT_RAW`; `pcap::Linktype` exposes no associated constant for it.
4const DLT_RAW: Linktype = Linktype(12);
5
6/// Link types this crate can parse.
7///
8/// [`LinkType::Unsupported`] is still parsed, as Ethernet, on the assumption
9/// that an unknown DLT is more often Ethernet-shaped than not.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum LinkType {
12    Null(Linktype),
13    Ethernet(Linktype),
14    RawIp(Linktype),
15    Loop(Linktype),
16    IPv4(Linktype),
17    IPv6(Linktype),
18    LinuxSll(Linktype),
19    LinuxSll2(Linktype),
20    Unsupported(Linktype),
21}
22
23impl LinkType {
24    #[must_use]
25    /// Convert a `pcap::Linktype` to a `LinkType`.
26    pub fn from_pcap(link_type: Linktype) -> Self {
27        match link_type {
28            Linktype::NULL => LinkType::Null(link_type),
29            Linktype::ETHERNET => LinkType::Ethernet(link_type),
30            DLT_RAW => LinkType::RawIp(link_type),
31            Linktype::LOOP => LinkType::Loop(link_type),
32            Linktype::IPV4 => LinkType::IPv4(link_type),
33            Linktype::IPV6 => LinkType::IPv6(link_type),
34            Linktype::LINUX_SLL => LinkType::LinuxSll(link_type),
35            Linktype::LINUX_SLL2 => LinkType::LinuxSll2(link_type),
36            _ => LinkType::Unsupported(link_type),
37        }
38    }
39
40    #[must_use]
41    /// Whether the link type is supported by this crate.
42    pub fn is_supported(self) -> bool {
43        !matches!(self, LinkType::Unsupported(_))
44    }
45
46    #[must_use]
47    /// Returns a human-readable description of the link type.
48    pub fn description(self) -> String {
49        match self {
50            LinkType::Null(l)
51            | LinkType::Ethernet(l)
52            | LinkType::RawIp(l)
53            | LinkType::Loop(l)
54            | LinkType::IPv4(l)
55            | LinkType::IPv6(l)
56            | LinkType::LinuxSll(l)
57            | LinkType::LinuxSll2(l)
58            | LinkType::Unsupported(l) => {
59                format!(
60                    "{}{}",
61                    l.get_name().unwrap_or_else(|_| l.0.to_string()),
62                    if let Ok(desc) = l.get_description() {
63                        format!(" ({desc})")
64                    } else {
65                        String::new()
66                    }
67                )
68            }
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_link_type_from_pcap_supported() {
79        for link_type in [
80            Linktype::NULL,
81            Linktype::ETHERNET,
82            DLT_RAW,
83            Linktype::LOOP,
84            Linktype::IPV4,
85            Linktype::IPV6,
86            Linktype::LINUX_SLL,
87            Linktype::LINUX_SLL2,
88        ] {
89            let parsed = LinkType::from_pcap(link_type);
90            assert!(parsed.is_supported(), "{link_type:?} should be supported");
91        }
92    }
93
94    #[test]
95    fn test_link_type_from_pcap_unsupported() {
96        let parsed = LinkType::from_pcap(Linktype(0x7fff));
97        assert_eq!(parsed, LinkType::Unsupported(Linktype(0x7fff)));
98        assert!(!parsed.is_supported());
99    }
100}