• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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::{
16     fs::File,
17     io::{Result, Write},
18     time::Duration,
19 };
20 macro_rules! be_vec {
21     ( $( $x:expr ),* ) => {
22          Vec::<u8>::new().iter().copied()
23          $( .chain($x.to_be_bytes()) )*
24          .collect()
25        };
26     }
27 
28 pub enum PacketDirection {
29     HostToController = 0,
30     ControllerToHost = 1,
31 }
32 
write_pcap_header(output: &mut File) -> Result<usize>33 pub fn write_pcap_header(output: &mut File) -> Result<usize> {
34     let linktype: u32 = 201; // LINKTYPE_BLUETOOTH_HCI_H4_WITH_PHDR
35 
36     // https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html#name-file-header
37     let header: Vec<u8> = be_vec![
38         0xa1b2c3d4u32, // magic number
39         2u16,          // major version
40         4u16,          // minor version
41         0u32,          // reserved 1
42         0u32,          // reserved 2
43         u32::MAX,      // snaplen
44         linktype
45     ];
46 
47     output.write_all(&header)?;
48     Ok(header.len())
49 }
50 
append_record( timestamp: Duration, output: &mut File, packet_direction: PacketDirection, packet_type: u32, packet: &[u8], ) -> Result<usize>51 pub fn append_record(
52     timestamp: Duration,
53     output: &mut File,
54     packet_direction: PacketDirection,
55     packet_type: u32,
56     packet: &[u8],
57 ) -> Result<usize> {
58     // Record (direciton, type, packet)
59     let record: Vec<u8> = be_vec![packet_direction as u32, packet_type as u8];
60 
61     // https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html#name-packet-record
62     let length = record.len() + packet.len();
63     let header: Vec<u8> = be_vec![
64         timestamp.as_secs() as u32, // seconds
65         timestamp.subsec_micros(),  // microseconds
66         length as u32,              // Captured Packet Length
67         length as u32               // Original Packet Length
68     ];
69     let mut bytes = Vec::<u8>::with_capacity(header.len() + length);
70     bytes.extend(&header);
71     bytes.extend(&record);
72     bytes.extend(packet);
73     output.write_all(&bytes)?;
74     output.flush()?;
75     Ok(header.len() + length)
76 }
77 
78 #[cfg(test)]
79 mod tests {
80     use std::{fs::File, io::Read, time::Duration};
81 
82     use crate::captures::pcap_util::{append_record, PacketDirection};
83 
84     use super::write_pcap_header;
85 
86     static EXPECTED: &[u8; 76] = include_bytes!("sample.pcap");
87 
88     #[test]
89     /// The test is done with the golden file sample.pcap with following packets:
90     /// Packet 1: HCI_EVT from Controller to Host (Sent Command Complete (LE Set Advertise Enable))
91     /// Packet 2: HCI_CMD from Host to Controller (Rcvd LE Set Advertise Enable) [250 milisecs later]
test_pcap_file()92     fn test_pcap_file() {
93         let mut temp_dir = std::env::temp_dir();
94         temp_dir.push("test.pcap");
95         if let Ok(mut file) = File::create(temp_dir.clone()) {
96             write_pcap_header(&mut file).unwrap();
97             append_record(
98                 Duration::from_secs(0),
99                 &mut file,
100                 PacketDirection::HostToController,
101                 4u32,
102                 &[14, 4, 1, 10, 32, 0],
103             )
104             .unwrap();
105             append_record(
106                 Duration::from_millis(250),
107                 &mut file,
108                 PacketDirection::ControllerToHost,
109                 1u32,
110                 &[10, 32, 1, 0],
111             )
112             .unwrap();
113         } else {
114             panic!("Cannot create temp file")
115         }
116         if let Ok(mut file) = File::open(temp_dir) {
117             let mut buffer = [0u8; 76];
118             #[allow(clippy::unused_io_amount)]
119             {
120                 file.read(&mut buffer).unwrap();
121             }
122             assert_eq!(&buffer, EXPECTED);
123         } else {
124             panic!("Cannot create temp file")
125         }
126     }
127 }
128