• 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 /// Produce PCAP Radiotap buffers from Hwsim Frames
16 ///
17 /// This module produces the Radiotap buffers used by PCAP and PcapNG
18 /// for logging 802.11 frames.
19 ///
20 /// See https://www.radiotap.org/
21 use crate::wifi::frame::Frame;
22 use anyhow::anyhow;
23 use log::info;
24 use netsim_packets::mac80211_hwsim::{HwsimCmd, HwsimMsg};
25 use pdl_runtime::Packet;
26 
27 #[repr(C, packed)]
28 struct RadiotapHeader {
29     version: u8,
30     pad: u8,
31     len: u16,
32     present: u32,
33     channel: ChannelInfo,
34     signal: u8,
35 }
36 
37 #[repr(C)]
38 struct ChannelInfo {
39     freq: u16,
40     flags: u16,
41 }
42 
43 #[allow(dead_code)]
44 #[derive(Debug)]
45 enum HwsimCmdEnum {
46     Unspec,
47     Register,
48     Frame(Box<Frame>),
49     TxInfoFrame,
50     NewRadio,
51     DelRadio,
52     GetRadio,
53     AddMacAddr,
54     DelMacAddr,
55 }
56 
parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum>57 fn parse_hwsim_cmd(packet: &[u8]) -> anyhow::Result<HwsimCmdEnum> {
58     let hwsim_msg = HwsimMsg::decode_full(packet)?;
59     match hwsim_msg.hwsim_hdr.hwsim_cmd {
60         HwsimCmd::Frame => {
61             let frame = Frame::parse(&hwsim_msg)?;
62             Ok(HwsimCmdEnum::Frame(Box::new(frame)))
63         }
64         HwsimCmd::TxInfoFrame => Ok(HwsimCmdEnum::TxInfoFrame),
65         _ => Err(anyhow!("Unknown HwsimMsg cmd={:?}", hwsim_msg.hwsim_hdr.hwsim_cmd)),
66     }
67 }
68 
into_pcap(packet: &[u8]) -> Option<Vec<u8>>69 pub fn into_pcap(packet: &[u8]) -> Option<Vec<u8>> {
70     match parse_hwsim_cmd(packet) {
71         Ok(HwsimCmdEnum::Frame(frame)) => frame_into_pcap(*frame).ok(),
72         Ok(_) => None,
73         Err(e) => {
74             info!("Failed to convert packet to pcap format. Err: {}. Packet: {:?}", e, &packet);
75             None
76         }
77     }
78 }
79 
frame_into_pcap(frame: Frame) -> anyhow::Result<Vec<u8>>80 pub fn frame_into_pcap(frame: Frame) -> anyhow::Result<Vec<u8>> {
81     // Create an instance of the RadiotapHeader with fields for
82     // Channel and Signal.  In the future add more fields from the
83     // Frame.
84 
85     let radiotap_hdr: RadiotapHeader = RadiotapHeader {
86         version: 0,
87         pad: 0,
88         len: (std::mem::size_of::<RadiotapHeader>() as u16),
89         present: (1 << 3 /* channel */ | 1 << 5/* signal dBm */),
90         channel: ChannelInfo { freq: frame.freq.unwrap_or(0) as u16, flags: 0 },
91         signal: frame.signal.unwrap_or(0) as u8,
92     };
93 
94     // Add the struct fields to the buffer manually in little-endian.
95     let mut buffer = Vec::<u8>::new();
96     buffer.push(radiotap_hdr.version);
97     buffer.push(radiotap_hdr.pad);
98     buffer.extend_from_slice(&radiotap_hdr.len.to_le_bytes());
99     buffer.extend_from_slice(&radiotap_hdr.present.to_le_bytes());
100     buffer.extend_from_slice(&radiotap_hdr.channel.freq.to_le_bytes());
101     buffer.extend_from_slice(&radiotap_hdr.channel.flags.to_le_bytes());
102     buffer.push(radiotap_hdr.signal);
103     buffer.extend_from_slice(&frame.data);
104 
105     Ok(buffer)
106 }
107 
108 #[test]
test_netlink_attr()109 fn test_netlink_attr() {
110     let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
111     assert!(parse_hwsim_cmd(&packet).is_ok());
112 
113     let tx_info_packet: Vec<u8> = include!("test_packets/hwsim_cmd_tx_info.csv");
114     assert!(parse_hwsim_cmd(&tx_info_packet).is_ok());
115 }
116 
117 #[test]
test_netlink_attr_response_packet()118 fn test_netlink_attr_response_packet() {
119     // Response packet may not contain transmitter, flags, tx_info, or cookie fields.
120     let response_packet: Vec<u8> =
121         include!("test_packets/hwsim_cmd_frame_response_no_transmitter_flags_tx_info.csv");
122     assert!(parse_hwsim_cmd(&response_packet).is_ok());
123 
124     let response_packet2: Vec<u8> = include!("test_packets/hwsim_cmd_frame_response_no_cookie.csv");
125     assert!(parse_hwsim_cmd(&response_packet2).is_ok());
126 }
127