1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Helper functions to access block contents (depending in linktype)

mod exported_pdu;
mod pcap_nflog;

pub use crate::data::exported_pdu::*;
pub use crate::data::pcap_nflog::*;
use crate::linktype::Linktype;
use crate::read_u32_e;
use nom::number::streaming::{be_u16, be_u64};
use nom::{do_parse, named};

pub const ETHERTYPE_IPV4: u16 = 0x0800;
pub const ETHERTYPE_IPV6: u16 = 0x86dd;

/// Contents of a pcap/pcap-ng block. This can be network data, USB, etc.
#[derive(Clone, Debug)]
pub enum PacketData<'a> {
    L2(&'a [u8]),
    L3(u16, &'a [u8]),
    L4(u8, &'a [u8]),

    Unsupported(&'a [u8]),
}

/// Get packet data for LINKTYPE_NULL (0)
///
/// BSD loopback encapsulation; the link layer header is a 4-byte field, in host byte order,
/// containing a value of 2 for IPv4 packets, a value of either 24, 28, or 30 for IPv6 packets, a
/// value of 7 for OSI packets, or a value of 23 for IPX packets. All of the IPv6 values correspond
/// to IPv6 packets; code reading files should check for all of them.
///
/// Note that ``host byte order'' is the byte order of the machine on which the packets are
/// captured; if a live capture is being done, ``host byte order'' is the byte order of the machine
/// capturing the packets, but if a ``savefile'' is being read, the byte order is not necessarily
/// that of the machine reading the capture file.
pub fn get_packetdata_null(i: &[u8], caplen: usize) -> Option<PacketData> {
    // debug!("data.len: {}, caplen: {}", packet.data.len(), packet.header.caplen);
    if i.len() < caplen || caplen < 4 {
        None
    } else {
        let vers = read_u32_e!(i, false);
        let ethertype = match vers {
            2 => ETHERTYPE_IPV4,
            24 | 28 | 30 => ETHERTYPE_IPV6,
            _ => 0,
        };
        Some(PacketData::L3(ethertype, &i[4..caplen]))
    }
}

/// Get packet data for LINKTYPE_ETHERNET (1)
///
/// IEEE 802.3 Ethernet (10Mb, 100Mb, 1000Mb, and up); the 10MB in the DLT_ name is historical.
pub fn get_packetdata_ethernet(i: &[u8], caplen: usize) -> Option<PacketData> {
    if i.len() < caplen || caplen == 0 {
        None
    } else {
        Some(PacketData::L2(&i[..caplen]))
    }
}

/// Get packet data for LINKTYPE_RAW (101)
///
/// Raw IP; the packet begins with an IPv4 or IPv6 header, with the "version" field of the header
/// indicating whether it's an IPv4 or IPv6 header.
pub fn get_packetdata_raw(i: &[u8], caplen: usize) -> Option<PacketData> {
    if i.len() < caplen || caplen == 0 {
        None
    } else {
        let vers = i[0] >> 4;
        let ethertype = match vers {
            4 => ETHERTYPE_IPV4,
            6 => ETHERTYPE_IPV6,
            _ => 0,
        };
        Some(PacketData::L3(ethertype, &i[..caplen]))
    }
}

/// Get packet data for LINKTYPE_LINUX_SLL (113)
///
/// See http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
pub fn get_packetdata_linux_sll(i: &[u8], caplen: usize) -> Option<PacketData> {
    if i.len() < caplen || caplen == 0 {
        None
    } else {
        match parse_sll_header(i) {
            Err(_) => None,
            Ok((rem, sll)) => {
                match sll.arphrd_type {
                    778 /* ARPHRD_IPGRE */ => Some(PacketData::L4(47, rem)),
                    803 /* ARPHRD_IEEE80211_RADIOTAP */ |
                    824 /* ARPHRD_NETLINK */ => None,
                    _ => Some(PacketData::L3(sll.proto, rem)),
                }
            }
        }
    }
}

struct SLLHeader {
    _packet_type: u16,
    arphrd_type: u16,
    _ll_addr_len: u16,
    _ll_addr: u64,
    proto: u16,
}

named! {
    parse_sll_header<SLLHeader>,
    do_parse!(
        _packet_type: be_u16 >>
        arphrd_type: be_u16 >>
        _ll_addr_len: be_u16 >>
        _ll_addr: be_u64 >>
        proto: be_u16 >>
        (SLLHeader {
                _packet_type,
                arphrd_type,
                _ll_addr_len,
                _ll_addr,
                proto,
            })
    )
}

/// Get packet data for LINKTYPE_IPV4 (228)
///
/// Raw IPv4; the packet begins with an IPv4 header.
pub fn get_packetdata_ipv4(i: &[u8], _caplen: usize) -> Option<PacketData> {
    Some(PacketData::L3(ETHERTYPE_IPV4, i))
}

/// Get packet data for LINKTYPE_IPV6 (229)
///
/// Raw IPv4; the packet begins with an IPv6 header.
pub fn get_packetdata_ipv6(i: &[u8], _caplen: usize) -> Option<PacketData> {
    Some(PacketData::L3(ETHERTYPE_IPV6, i))
}

/// Get packet data, depending on linktype.
///
/// Get packet data, depending on linktype.
///
/// Returns None if data could not be extracted (for ex, inner parsing error). If linktype is not
/// supported, `PacketData::Unsupported` is used.
pub fn get_packetdata(i: &[u8], linktype: Linktype, caplen: usize) -> Option<PacketData> {
    match linktype {
        Linktype::NULL => get_packetdata_null(i, caplen),
        Linktype::ETHERNET => get_packetdata_ethernet(i, caplen),
        Linktype::RAW => get_packetdata_raw(i, caplen),
        Linktype::LINUX_SLL => get_packetdata_linux_sll(i, caplen),
        Linktype::IPV4 => get_packetdata_ipv4(i, caplen),
        Linktype::IPV6 => get_packetdata_ipv6(i, caplen),
        Linktype::NFLOG => get_packetdata_nflog(i, caplen),
        Linktype::WIRESHARK_UPPER_PDU => get_packetdata_wireshark_upper_pdu(i, caplen),
        _ => Some(PacketData::Unsupported(i)),
    }
}