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 #![allow(clippy::empty_line_after_doc_comments)]
16
17 use std::fmt;
18
19 use anyhow::{anyhow, Context};
20 use netsim_packets::ieee80211::MacAddress;
21 use netsim_packets::mac80211_hwsim::{self, HwsimAttr, HwsimAttrChild::*, TxRate, TxRateFlag};
22 use netsim_packets::netlink::NlAttrHdr;
23 use pdl_runtime::Packet;
24 use std::option::Option;
25
26 /// Parse or Build the Hwsim attributes into a set.
27 ///
28 /// Hwsim attributes are used to exchange data between kernel's
29 /// mac80211_hwsim subsystem and a user space process and include:
30 ///
31 /// HWSIM_ATTR_ADDR_TRANSMITTER,
32 /// HWSIM_ATTR_ADDR_RECEIVER,
33 /// HWSIM_ATTR_FRAME,
34 /// HWSIM_ATTR_FLAGS,
35 /// HWSIM_ATTR_RX_RATE,
36 /// HWSIM_ATTR_SIGNAL,
37 /// HWSIM_ATTR_COOKIE,
38 /// HWSIM_ATTR_FREQ (optional)
39 /// HWSIM_ATTR_TX_INFO (new use)
40 /// HWSIM_ATTR_TX_INFO_FLAGS (new use)
41
42 /// Aligns a length to the specified alignment boundary (`NLA_ALIGNTO`).
43 ///
44 /// # Arguments
45 ///
46 /// * `array_length`: The length in bytes to be aligned.
47 ///
48 /// # Returns
49 ///
50 /// * The aligned length, which is a multiple of `NLA_ALIGNTO`.
51 ///
nla_align(array_length: usize) -> usize52 fn nla_align(array_length: usize) -> usize {
53 const NLA_ALIGNTO: usize = 4;
54 array_length.wrapping_add(NLA_ALIGNTO - 1) & !(NLA_ALIGNTO - 1)
55 }
56
57 #[derive(Default)]
58 pub struct HwsimAttrSetBuilder {
59 transmitter: Option<MacAddress>,
60 receiver: Option<MacAddress>,
61 frame: Option<Vec<u8>>,
62 flags: Option<u32>,
63 rx_rate_idx: Option<u32>,
64 signal: Option<u32>,
65 cookie: Option<u64>,
66 freq: Option<u32>,
67 tx_info: Option<Vec<TxRate>>,
68 tx_info_flags: Option<Vec<TxRateFlag>>,
69 attributes: Vec<u8>,
70 }
71
72 #[derive(Debug)]
73 pub struct HwsimAttrSet {
74 pub transmitter: Option<MacAddress>,
75 pub receiver: Option<MacAddress>,
76 pub frame: Option<Vec<u8>>,
77 pub flags: Option<u32>,
78 pub rx_rate_idx: Option<u32>,
79 pub signal: Option<u32>,
80 pub cookie: Option<u64>,
81 pub freq: Option<u32>,
82 pub tx_info: Option<Vec<TxRate>>,
83 pub tx_info_flags: Option<Vec<TxRateFlag>>,
84 pub attributes: Vec<u8>,
85 }
86
87 /// Builder pattern for each of the HWSIM_ATTR used in conjunction
88 /// with the HwsimAttr packet formats defined in `mac80211_hwsim.pdl`
89 ///
90 /// Used during `parse` or to create new HwsimCmd packets containing
91 /// an attributes vector.
92 ///
93 /// The attributes field will contain the raw bytes in NLA format
94 /// in the order of method calls.
95 impl HwsimAttrSetBuilder {
96 // Add packet to the attributes vec and pad for proper NLA
97 // alignment. This provides for to_bytes for a HwsimMsg for
98 // packets constructed by the Builder.
99
extend_attributes<P: Packet>(&mut self, packet: P)100 fn extend_attributes<P: Packet>(&mut self, packet: P) {
101 let mut vec: Vec<u8> = packet.encode_to_vec().unwrap();
102 let nla_padding = nla_align(vec.len()) - vec.len();
103 vec.extend(vec![0; nla_padding]);
104 self.attributes.extend(vec);
105 }
106
transmitter(&mut self, transmitter: &[u8; 6]) -> &mut Self107 pub fn transmitter(&mut self, transmitter: &[u8; 6]) -> &mut Self {
108 self.extend_attributes(mac80211_hwsim::HwsimAttrAddrTransmitter {
109 address: *transmitter,
110 nla_m: 0,
111 nla_o: 0,
112 });
113 self.transmitter = Some(MacAddress::from(transmitter));
114 self
115 }
116
receiver(&mut self, receiver: &[u8; 6]) -> &mut Self117 pub fn receiver(&mut self, receiver: &[u8; 6]) -> &mut Self {
118 self.extend_attributes(mac80211_hwsim::HwsimAttrAddrReceiver {
119 address: *receiver,
120 nla_m: 0,
121 nla_o: 0,
122 });
123 self.receiver = Some(MacAddress::from(receiver));
124 self
125 }
126
frame(&mut self, frame: &[u8]) -> &mut Self127 pub fn frame(&mut self, frame: &[u8]) -> &mut Self {
128 self.extend_attributes(mac80211_hwsim::HwsimAttrFrame {
129 data: (*frame).to_vec(),
130 nla_m: 0,
131 nla_o: 0,
132 });
133 self.frame = Some(frame.to_vec());
134 self
135 }
136
flags(&mut self, flags: u32) -> &mut Self137 pub fn flags(&mut self, flags: u32) -> &mut Self {
138 self.extend_attributes(mac80211_hwsim::HwsimAttrFlags { flags, nla_m: 0, nla_o: 0 });
139 self.flags = Some(flags);
140 self
141 }
142
rx_rate(&mut self, rx_rate_idx: u32) -> &mut Self143 pub fn rx_rate(&mut self, rx_rate_idx: u32) -> &mut Self {
144 self.extend_attributes(mac80211_hwsim::HwsimAttrRxRate { rx_rate_idx, nla_m: 0, nla_o: 0 });
145 self.rx_rate_idx = Some(rx_rate_idx);
146 self
147 }
148
signal(&mut self, signal: u32) -> &mut Self149 pub fn signal(&mut self, signal: u32) -> &mut Self {
150 self.extend_attributes(mac80211_hwsim::HwsimAttrSignal { signal, nla_m: 0, nla_o: 0 });
151 self.signal = Some(signal);
152 self
153 }
154
cookie(&mut self, cookie: u64) -> &mut Self155 pub fn cookie(&mut self, cookie: u64) -> &mut Self {
156 self.extend_attributes(mac80211_hwsim::HwsimAttrCookie { cookie, nla_m: 0, nla_o: 0 });
157 self.cookie = Some(cookie);
158 self
159 }
160
freq(&mut self, freq: u32) -> &mut Self161 pub fn freq(&mut self, freq: u32) -> &mut Self {
162 self.extend_attributes(mac80211_hwsim::HwsimAttrFreq { freq, nla_m: 0, nla_o: 0 });
163 self.freq = Some(freq);
164 self
165 }
166
tx_info(&mut self, tx_info: &[TxRate]) -> &mut Self167 pub fn tx_info(&mut self, tx_info: &[TxRate]) -> &mut Self {
168 self.extend_attributes(mac80211_hwsim::HwsimAttrTxInfo {
169 tx_rates: (*tx_info).to_vec(),
170 nla_m: 0,
171 nla_o: 0,
172 });
173 self.tx_info = Some(tx_info.to_vec());
174 self
175 }
176
tx_info_flags(&mut self, tx_rate_flags: &[TxRateFlag]) -> &mut Self177 pub fn tx_info_flags(&mut self, tx_rate_flags: &[TxRateFlag]) -> &mut Self {
178 self.extend_attributes(mac80211_hwsim::HwsimAttrTxInfoFlags {
179 tx_rate_flags: (*tx_rate_flags).to_vec(),
180 nla_m: 0,
181 nla_o: 0,
182 });
183 self.tx_info_flags = Some(tx_rate_flags.to_vec());
184 self
185 }
186
build(self) -> anyhow::Result<HwsimAttrSet>187 pub fn build(self) -> anyhow::Result<HwsimAttrSet> {
188 Ok(HwsimAttrSet {
189 transmitter: self.transmitter,
190 receiver: self.receiver,
191 cookie: self.cookie,
192 flags: self.flags,
193 rx_rate_idx: self.rx_rate_idx,
194 signal: self.signal,
195 frame: self.frame,
196 freq: self.freq,
197 tx_info: self.tx_info,
198 tx_info_flags: self.tx_info_flags,
199 attributes: self.attributes,
200 })
201 }
202 }
203
204 impl fmt::Display for HwsimAttrSet {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 write!(f, "{{ ")?;
207 self.transmitter.map(|v| write!(f, "transmitter: {}, ", v));
208 self.receiver.map(|v| write!(f, "receiver: {}, ", v));
209 self.cookie.map(|v| write!(f, "cookie: {}, ", v));
210 self.flags.map(|v| write!(f, "flags: {}, ", v));
211 self.rx_rate_idx.map(|v| write!(f, "rx_rate_idx: {}, ", v));
212 self.signal.map(|v| write!(f, "signal: {}, ", v));
213 self.frame.as_ref().map(|v| write!(f, "frame: {:?}, ", &v));
214 self.freq.map(|v| write!(f, "freq: {}, ", v));
215 self.tx_info.as_ref().map(|v| write!(f, "tx_info: {:?}, ", &v));
216 self.tx_info_flags.as_ref().map(|v| write!(f, "tx_info_flags: {:?}, ", &v));
217 write!(f, "}}")?;
218 Ok(())
219 }
220 }
221
222 impl HwsimAttrSet {
223 /// Creates a new `HwsimAttrSetBuilder` with default settings, ready for configuring attributes.
224 ///
225 /// # Returns
226 ///
227 /// * A new `HwsimAttrSetBuilder` instance, initialized with default values.
228 ///
229 /// # Examples
230 ///
231 /// ```rust
232 /// let mut builder = HwsimAttrSetBuilder::builder();
233 /// builder.signal(42).cookie(32); // Example attribute configuration
234 /// let attr_set = builder.build();
235 /// ```
builder() -> HwsimAttrSetBuilder236 pub fn builder() -> HwsimAttrSetBuilder {
237 HwsimAttrSetBuilder::default()
238 }
239
240 /// Parse and validates the attributes from a HwsimMsg command.
parse(attributes: &[u8]) -> anyhow::Result<HwsimAttrSet>241 pub fn parse(attributes: &[u8]) -> anyhow::Result<HwsimAttrSet> {
242 Self::parse_with_frame_transmitter(attributes, Option::None, Option::None)
243 }
244 /// Parse and validates the attributes from a HwsimMsg command.
245 /// Update frame and transmitter if provided.
parse_with_frame_transmitter( attributes: &[u8], frame: Option<&[u8]>, transmitter: Option<&[u8; 6]>, ) -> anyhow::Result<HwsimAttrSet>246 pub fn parse_with_frame_transmitter(
247 attributes: &[u8],
248 frame: Option<&[u8]>,
249 transmitter: Option<&[u8; 6]>,
250 ) -> anyhow::Result<HwsimAttrSet> {
251 let mut index: usize = 0;
252 let mut builder = HwsimAttrSet::builder();
253 while index < attributes.len() {
254 // Parse a generic netlink attribute to get the size
255 let nla_hdr =
256 NlAttrHdr::decode_full(&attributes[index..index + 4]).context("NlAttrHdr")?;
257 let nla_len = nla_hdr.nla_len as usize;
258 // Now parse a single attribute at a time from the
259 // attributes to allow padding per attribute.
260 let hwsim_attr = HwsimAttr::decode_full(&attributes[index..index + nla_len])?;
261 match hwsim_attr.specialize().context("HwsimAttr")? {
262 HwsimAttrAddrTransmitter(child) => {
263 builder.transmitter(transmitter.unwrap_or(child.address()))
264 }
265 HwsimAttrAddrReceiver(child) => builder.receiver(&child.address),
266 HwsimAttrFrame(child) => builder.frame(frame.unwrap_or(&child.data)),
267 HwsimAttrFlags(child) => builder.flags(child.flags),
268 HwsimAttrRxRate(child) => builder.rx_rate(child.rx_rate_idx),
269 HwsimAttrSignal(child) => builder.signal(child.signal),
270 HwsimAttrCookie(child) => builder.cookie(child.cookie),
271 HwsimAttrFreq(child) => builder.freq(child.freq),
272 HwsimAttrTxInfo(child) => builder.tx_info(&child.tx_rates),
273 HwsimAttrTxInfoFlags(child) => builder.tx_info_flags(&child.tx_rate_flags),
274 _ => {
275 return Err(anyhow!(
276 "Invalid attribute message: {:?}",
277 hwsim_attr.nla_type as u32
278 ))
279 }
280 };
281 // Manually step through the attribute bytes aligning as
282 // we go because netlink aligns each attribute which isn't
283 // a feature of PDL parser.
284 index += nla_align(nla_len);
285 }
286 builder.build()
287 }
288 }
289
290 #[cfg(test)]
291 mod tests {
292 use super::*;
293 use anyhow::Context;
294 use anyhow::Error;
295 use netsim_packets::ieee80211::parse_mac_address;
296 use netsim_packets::mac80211_hwsim::{HwsimCmd, HwsimMsg};
297
298 // Validate `HwsimAttrSet` attribute parsing from byte vector.
299 #[test]
test_attr_set_parse()300 fn test_attr_set_parse() {
301 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
302 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
303 assert_eq!(hwsim_msg.hwsim_hdr().hwsim_cmd, HwsimCmd::Frame);
304 let attrs = HwsimAttrSet::parse(hwsim_msg.attributes()).unwrap();
305
306 // Validate each attribute parsed
307 assert_eq!(attrs.transmitter, MacAddress::try_from(11670786u64).ok());
308 assert!(attrs.receiver.is_none());
309 assert!(attrs.frame.is_some());
310 assert_eq!(attrs.flags, Some(2));
311 assert!(attrs.rx_rate_idx.is_none());
312 assert!(attrs.signal.is_none());
313 assert_eq!(attrs.cookie, Some(201));
314 assert_eq!(attrs.freq, Some(2422));
315 assert!(attrs.tx_info.is_some());
316 }
317
318 // Validate the contents of the `attributes` bytes constructed by
319 // the Builder by matching with the bytes containing the input
320 // attributes. Confirms attribute order, packet format and
321 // padding.
322 #[test]
test_attr_set_attributes()323 fn test_attr_set_attributes() {
324 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
325 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
326 assert_eq!(hwsim_msg.hwsim_hdr().hwsim_cmd, HwsimCmd::Frame);
327 let attrs = HwsimAttrSet::parse(hwsim_msg.attributes()).unwrap();
328 assert_eq!(&attrs.attributes, hwsim_msg.attributes());
329 }
330
331 /// Validate changing frame and transmitter during the parse.
332 /// 1. Check if reinserting the same values results in identical bytes.
333 /// 2. Insert modified values, parse to bytes, and parse back again to check
334 /// if the round trip values are identical.
335 #[test]
test_attr_set_parse_with_frame_transmitter() -> Result<(), Error>336 fn test_attr_set_parse_with_frame_transmitter() -> Result<(), Error> {
337 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
338 let hwsim_msg = HwsimMsg::decode_full(&packet)?;
339 assert_eq!(hwsim_msg.hwsim_hdr().hwsim_cmd, HwsimCmd::Frame);
340 let attrs = HwsimAttrSet::parse(hwsim_msg.attributes())?;
341 let transmitter: [u8; 6] = attrs.transmitter.context("transmitter")?.into();
342 let mod_attrs = HwsimAttrSet::parse_with_frame_transmitter(
343 hwsim_msg.attributes(),
344 attrs.frame.as_deref(),
345 Some(&transmitter),
346 )?;
347
348 assert_eq!(attrs.attributes, mod_attrs.attributes);
349
350 // Change frame and transmitter.
351 let mod_frame = Some(vec![0, 1, 2, 3]);
352 let mod_transmitter: Option<[u8; 6]> =
353 Some(parse_mac_address("00:0b:85:71:20:ce").context("transmitter")?.into());
354
355 let mod_attrs = HwsimAttrSet::parse_with_frame_transmitter(
356 &attrs.attributes,
357 mod_frame.as_deref(),
358 mod_transmitter.as_ref(),
359 )?;
360
361 let parsed_attrs = HwsimAttrSet::parse(&mod_attrs.attributes)?;
362 assert_eq!(parsed_attrs.transmitter, mod_transmitter.map(|t| MacAddress::from(&t)));
363 assert_eq!(parsed_attrs.frame, mod_frame);
364 Ok(())
365 }
366
367 #[test]
test_hwsim_attr_set_display()368 fn test_hwsim_attr_set_display() {
369 let packet: Vec<u8> = include!("test_packets/hwsim_cmd_frame.csv");
370 let hwsim_msg = HwsimMsg::decode_full(&packet).unwrap();
371 let attrs = HwsimAttrSet::parse(hwsim_msg.attributes()).unwrap();
372
373 let fmt_attrs = format!("{}", attrs);
374 assert!(fmt_attrs.contains("transmitter: 02:15:b2:00:00:00"));
375 assert!(fmt_attrs.contains("cookie: 201"));
376 }
377 }
378