• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::marker::Unpin;
16 use std::mem::size_of;
17 use std::time::Duration;
18 use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
19 use zerocopy::{AsBytes, FromBytes};
20 use zerocopy_derive::{AsBytes, FromBytes, FromZeroes};
21 
22 type Result<A> = std::result::Result<A, std::io::Error>;
23 
24 /// Represents the global header of a pcap capture file.
25 ///
26 /// This struct defines the global header that appears at the beginning of a
27 /// pcap capture file. It contains metadata about the capture, such as the
28 /// file format version, the data link type, and the maximum snapshot length.
29 ///
30 /// # File Header format
31 /// ```text
32 ///                         1                   2                   3
33 ///     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
34 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35 ///  0 |                          Magic Number                         |
36 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37 ///  4 |          Major Version        |         Minor Version         |
38 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 ///  8 |                           Reserved1                           |
40 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 /// 12 |                           Reserved2                           |
42 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 /// 16 |                            SnapLen                            |
44 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 /// 20 | FCS |f|0 0 0 0 0 0 0 0 0 0 0 0|         LinkType              |
46 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 /// ```
48 ///
49 /// * `magic`: A magic number that identifies the file format.
50 /// * `version_major`: The major version number of the file format.
51 /// * `version_minor`: The minor version number of the file format.
52 /// * `thiszone`: The time zone offset of the capture.
53 /// * `sigfigs`: The accuracy of the timestamps.
54 /// * `snaplen`: The maximum number of bytes captured from each packet.
55 /// * `linktype`: The data link type of the network interface used to capture the packets.
56 #[repr(C)]
57 #[derive(AsBytes, FromBytes, FromZeroes)]
58 /// Represents the global header of a pcap capture file.
59 pub struct FileHeader {
60     /// Magic number identifying the file format.
61     pub magic: u32,
62     /// Major version of the pcap format.
63     pub version_major: u16,
64     /// Minor version of the pcap format.
65     pub version_minor: u16,
66     /// Time zone offset.
67     pub thiszone: i32,
68     /// Timestamp accuracy.
69     pub sigfigs: u32,
70     /// Maximum packet length in bytes.
71     pub snaplen: u32,
72     /// Data link type of packets.
73     pub linktype: u32,
74 }
75 
76 impl FileHeader {
77     const MAGIC: u32 = 0xa1b2c3d4;
78     const VERSION_MAJOR: u16 = 2u16;
79     const VERSION_MINOR: u16 = 4u16;
80     const RESERVED_1: i32 = 0;
81     const RESERVED_2: u32 = 0;
82     const SNAP_LEN: u32 = u32::MAX;
83 }
84 
85 impl Default for FileHeader {
default() -> Self86     fn default() -> Self {
87         FileHeader {
88             magic: FileHeader::MAGIC,
89             version_major: FileHeader::VERSION_MAJOR,
90             version_minor: FileHeader::VERSION_MINOR,
91             thiszone: FileHeader::RESERVED_1,
92             sigfigs: FileHeader::RESERVED_2,
93             snaplen: FileHeader::SNAP_LEN,
94             linktype: LinkType::Null as u32,
95         }
96     }
97 }
98 
99 /// Represents the link layer header type of a pcap capture.
100 ///
101 /// This enum defines the different link layer types that can be used in a
102 /// pcap capture file. These values specify the format of the link-layer
103 /// header that precedes the network layer (e.g., IP) header in each packet.
104 ///
105 /// For a complete list of supported link types and their descriptions,
106 /// refer to the tcpdump documentation:
107 /// https://www.tcpdump.org/linktypes.html
108 #[repr(u32)]
109 pub enum LinkType {
110     /// Null link type (BSD loopback)
111     Null = 0,
112     /// Ethernet
113     Ethernet = 1,
114     /// Radiotap link-layer information followed by an 802.11
115     /// header. Radiotap is used with mac80211_hwsim networking.
116     Ieee80211RadioTap = 127,
117     /// Bluetooth HCI UART transport layer
118     BluetoothHciH4WithPhdr = 201,
119     /// Ultra-wideband controller interface protocol
120     FiraUci = 299,
121 }
122 
123 impl From<LinkType> for u32 {
from(val: LinkType) -> Self124     fn from(val: LinkType) -> Self {
125         val as u32
126     }
127 }
128 
129 /// Represents the header prepended to each packet in a pcap capture file.
130 ///
131 /// This struct defines the header that precedes each packet in a pcap
132 /// capture file. It provides information about the timestamp and length
133 /// of the captured packet.
134 ///
135 /// # Fields
136 /// ```text
137 ///                        1                   2                   3
138 ///    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
139 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
140 ///  0 |                      Timestamp (Seconds)                      |
141 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142 ///  4 |            Timestamp (Microseconds or nanoseconds)            |
143 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
144 ///  8 |                    Captured Packet Length                     |
145 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
146 /// 12 |                    Original Packet Length                     |
147 ///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 /// 16 /                                                               /
149 ///    /                          Packet Data                          /
150 ///    /                        variable length                        /
151 ///    /                                                               /
152 ///    +---------------------------------------------------------------+
153 /// ```
154 ///
155 /// * `tv_sec`:  The seconds component of the timestamp.
156 /// * `tv_usec`: The microseconds component of the timestamp.
157 /// * `caplen`: The number of bytes of packet data actually captured and saved in the file.
158 /// * `len`: The original length of the packet on the network.
159 //
160 #[repr(C)]
161 #[derive(AsBytes, FromBytes, FromZeroes)]
162 /// Represents the header prepended to each packet in a pcap capture file.
163 pub struct PacketHeader {
164     /// Timestamp of the captured packet (seconds).
165     pub tv_sec: u32,
166     /// Timestamp of the captured packet (microseconds).
167     pub tv_usec: u32,
168     /// Number of bytes captured from the packet.
169     pub caplen: u32,
170     /// Original length of the packet on the network.
171     pub len: u32,
172 }
173 
174 /// Reads a pcap file header from the given reader.
175 ///
176 /// # Arguments
177 ///
178 /// * `reader` - A reader to read the header from.
179 ///
180 /// # Returns
181 ///
182 /// * `Ok(FileHeader)` - If the header was successfully read.
183 /// * `Err(std::io::Error)` - If an error occurred while reading or parsing the header.
read_file_header(mut reader: impl AsyncRead + Unpin) -> Result<FileHeader>184 pub async fn read_file_header(mut reader: impl AsyncRead + Unpin) -> Result<FileHeader> {
185     let mut header_bytes = [0u8; size_of::<FileHeader>()];
186     reader.read_exact(&mut header_bytes).await?;
187     let header = FileHeader::read_from(&header_bytes[..]).ok_or(std::io::Error::new(
188         std::io::ErrorKind::InvalidData,
189         "Failed to parse pcap file header",
190     ))?;
191     if header.magic != FileHeader::MAGIC {
192         return Err(std::io::Error::new(
193             std::io::ErrorKind::InvalidData,
194             format!("Invalid magic in pcap file 0x{:x}", header.magic),
195         ));
196     }
197     Ok(header)
198 }
199 
200 /// Reads a pcap record from the given reader.
201 /// A record consists of a packet header (`PacketHeader`) and the packet data itself.
202 ///
203 /// # Arguments
204 ///
205 /// * `reader` - A reader to read the record from.
206 ///
207 /// # Returns
208 ///
209 /// * `Ok((PacketHeader, Vec<u8>))` - If the record was successfully read.
210 /// * `Err(std::io::Error)` - If an error occurred while reading or parsing the record.
read_record(mut reader: impl AsyncRead + Unpin) -> Result<(PacketHeader, Vec<u8>)>211 pub async fn read_record(mut reader: impl AsyncRead + Unpin) -> Result<(PacketHeader, Vec<u8>)> {
212     let mut pkt_hdr_bytes = [0u8; std::mem::size_of::<PacketHeader>()];
213     reader.read_exact(&mut pkt_hdr_bytes).await?;
214     let pkt_hdr = PacketHeader::read_from(&pkt_hdr_bytes[..]).ok_or(std::io::Error::new(
215         std::io::ErrorKind::InvalidData,
216         "Failed to parse pcap record header",
217     ))?;
218     let mut packet_data = vec![0u8; pkt_hdr.caplen as usize];
219     reader.read_exact(&mut packet_data).await?;
220     Ok((pkt_hdr, packet_data))
221 }
222 
223 /// Writes the header of a pcap file to the output writer.
224 ///
225 /// This function writes the global header of a pcap file to the provided
226 /// asynchronous writer. It returns the size of the header written.
227 ///
228 /// # Arguments
229 ///
230 /// * `link_type` - The link type of the network interface used to capture the packets.
231 /// * `output` - The asynchronous writer to write the header to.
232 ///
233 /// # Returns
234 ///
235 /// A `Result` containing the size of the header in bytes on success,
236 /// or a `std::io::Error` on failure.
write_file_header( link_type: LinkType, mut output: impl AsyncWrite + Unpin, ) -> Result<usize>237 pub async fn write_file_header(
238     link_type: LinkType,
239     mut output: impl AsyncWrite + Unpin,
240 ) -> Result<usize> {
241     // https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html#name-file-header
242     let header = FileHeader { linktype: link_type as u32, ..Default::default() };
243     output.write_all(header.as_bytes()).await?;
244     Ok(size_of::<FileHeader>())
245 }
246 
247 /// Appends a single packet record to the output writer.
248 ///
249 /// This function writes a packet record to the provided asynchronous writer,
250 /// including the packet header and the packet data itself. It returns the
251 /// total number of bytes written to the writer.
252 ///
253 /// # Arguments
254 ///
255 /// * `timestamp` - The timestamp of the packet.
256 /// * `output` - The asynchronous writer to write the record to.
257 /// * `packet` - The packet data as a byte slice.
258 ///
259 /// # Returns
260 ///
261 /// A `Result` containing the total number of bytes written on success,
262 /// or a `std::io::Error` on failure.
write_record( timestamp: Duration, mut output: impl AsyncWrite + Unpin, packet: &[u8], ) -> Result<usize>263 pub async fn write_record(
264     timestamp: Duration,
265     mut output: impl AsyncWrite + Unpin,
266     packet: &[u8],
267 ) -> Result<usize> {
268     // https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html#name-packet-record
269     let pkt_len = packet.len();
270     let pkt_hdr_len = size_of::<PacketHeader>();
271     let header = PacketHeader {
272         tv_sec: timestamp.as_secs() as u32,
273         tv_usec: timestamp.subsec_micros(),
274         caplen: pkt_len as u32,
275         len: pkt_len as u32,
276     };
277     let mut bytes = Vec::<u8>::with_capacity(pkt_hdr_len + pkt_len);
278     bytes.extend(header.as_bytes());
279     bytes.extend(packet);
280     output.write_all(&bytes).await?;
281     Ok(pkt_hdr_len + pkt_len)
282 }
283