[wlan][mlme] Add Keep Alive frame construction

Test=Included

Change-Id: Iaf60ee93c20fc4b9073dc850d61a041a10757aaa
diff --git a/lib/rust/wlan-common/src/buffer_writer.rs b/lib/rust/wlan-common/src/buffer_writer.rs
index 11bde35..6163984 100644
--- a/lib/rust/wlan-common/src/buffer_writer.rs
+++ b/lib/rust/wlan-common/src/buffer_writer.rs
@@ -4,9 +4,11 @@
 
 use {
     failure::{Error, ensure, format_err},
-    zerocopy::{ByteSliceMut, LayoutVerified, Unaligned},
+    zerocopy::{self, LayoutVerified, Unaligned},
 };
 
+pub use zerocopy::ByteSliceMut as ByteSliceMut;
+
 pub struct BufferWriter<B: ByteSliceMut> {
     buf: B,
     written_bytes: usize,
diff --git a/lib/rust/wlan-common/src/mac.rs b/lib/rust/wlan-common/src/mac.rs
index 461b8c5..8a1b0a1 100644
--- a/lib/rust/wlan-common/src/mac.rs
+++ b/lib/rust/wlan-common/src/mac.rs
@@ -15,17 +15,19 @@
 
 // IEEE Std 802.11-2016, 9.2.4.1.3
 // Frame types:
-const FRAME_TYPE_MGMT: u16 = 0;
-const FRAME_TYPE_DATA: u16 = 2;
+pub const FRAME_TYPE_MGMT: u16 = 0;
+pub const FRAME_TYPE_DATA: u16 = 2;
 // Management subtypes:
-const MGMT_SUBTYPE_ASSOC_RESP: u16 = 0x01;
-const MGMT_SUBTYPE_BEACON: u16 = 0x08;
-const MGMT_SUBTYPE_AUTH: u16 = 0x0B;
+pub const MGMT_SUBTYPE_ASSOC_RESP: u16 = 0x01;
+pub const MGMT_SUBTYPE_BEACON: u16 = 0x08;
+pub const MGMT_SUBTYPE_AUTH: u16 = 0x0B;
 // Data subtypes:
-const DATA_SUBTYPE_DATA: u16 = 0x00;
-const DATA_SUBTYPE_QOS_DATA: u16 = 0x08;
+pub const DATA_SUBTYPE_DATA: u16 = 0x00;
+pub const DATA_SUBTYPE_NULL_DATA: u16 = 0x04;
+pub const DATA_SUBTYPE_QOS_DATA: u16 = 0x08;
 
 // IEEE Std 802.11-2016, 9.2.4.1.3, Table 9-1
+const BITMASK_NULL: u16 = 1 << 2;
 const BITMASK_QOS: u16 = 1 << 3;
 
 // IEEE Std 802.11-2016, 9.2.4.1.1
@@ -166,6 +168,18 @@
         LittleEndian::read_u16(&self.seq_ctrl)
     }
 
+    pub fn set_frame_ctrl(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.frame_ctrl, val)
+    }
+
+    pub fn set_duration(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.duration, val)
+    }
+
+    pub fn set_seq_ctrl(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.seq_ctrl, val)
+    }
+
     /// Returns the length in bytes of a mgmt header including all its fixed and optional
     /// fields (if they are present).
     pub fn len(has_ht_ctrl: bool) -> usize {
@@ -201,6 +215,18 @@
         LittleEndian::read_u16(&self.seq_ctrl)
     }
 
+    pub fn set_frame_ctrl(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.frame_ctrl, val)
+    }
+
+    pub fn set_duration(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.duration, val)
+    }
+
+    pub fn set_seq_ctrl(&mut self, val: u16) {
+        LittleEndian::write_u16(&mut self.seq_ctrl, val)
+    }
+
     /// Returns the length in bytes of a data header including all its fixed and optional
     /// fields (if they are present).
     pub fn len(has_addr4: bool, has_qos_ctrl: bool, has_ht_ctrl: bool) -> usize {
diff --git a/lib/rust/wlan-mlme/src/client.rs b/lib/rust/wlan-mlme/src/client.rs
new file mode 100644
index 0000000..4af031a
--- /dev/null
+++ b/lib/rust/wlan-mlme/src/client.rs
@@ -0,0 +1,51 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use {
+    failure::Error,
+    wlan_common::{mac, buffer_writer::{BufferWriter, ByteSliceMut}},
+};
+
+type MacAddr = [u8; 6];
+
+/// Fills a given buffer with a null-data frame which will be send from a client to an AP.
+/// Fails if the given buffer is too short.
+/// Returns the amount of bytes written to the buffer.
+pub fn write_keep_alive_resp_frame<B: ByteSliceMut>(buf: B, bssid: MacAddr,
+                                                    client_addr: MacAddr, seq_ctrl: u16)
+    -> Result<usize, Error>
+{
+    let (mut data_hdr, w) = BufferWriter::new(buf).reserve_zeroed::<mac::DataHdr>()?;
+    let mut fc = mac::FrameControl(0);
+    fc.set_frame_type(mac::FRAME_TYPE_DATA);
+    fc.set_frame_subtype(mac::DATA_SUBTYPE_NULL_DATA);
+    fc.set_to_ds(true);
+    data_hdr.set_frame_ctrl(fc.value());
+    data_hdr.addr1 = bssid;
+    data_hdr.addr2 = client_addr;
+    data_hdr.addr3 = bssid;
+    data_hdr.set_seq_ctrl(seq_ctrl);
+    Ok(w.written_bytes())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn keep_alive_resp_frame() {
+        let mut buf = [3u8; 25];
+        let written_bytes = write_keep_alive_resp_frame(&mut buf[..], [1; 6], [2; 6], 42)
+            .expect("failed writing frame");
+        assert_eq!(24, written_bytes);
+        assert_eq!([0b01001000, 0b1, // Frame Control
+                    0, 0,  // Duration
+                    1, 1, 1, 1, 1, 1,  // addr1
+                    2, 2, 2, 2, 2, 2,  // addr2
+                    1, 1, 1, 1, 1, 1,  // addr3
+                    42, 0, // Sequence Control
+                    3 // Trailing bytes
+                   ], buf);
+    }
+}
\ No newline at end of file
diff --git a/lib/rust/wlan-mlme/src/lib.rs b/lib/rust/wlan-mlme/src/lib.rs
index 83c17e4..0a146e6 100644
--- a/lib/rust/wlan-mlme/src/lib.rs
+++ b/lib/rust/wlan-mlme/src/lib.rs
@@ -7,4 +7,5 @@
 
 pub use wlan_common as common;
 
-pub mod auth;
\ No newline at end of file
+pub mod auth;
+pub mod client;
\ No newline at end of file