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