From 337a336edb8fc48848883a4c0d7a91e63f445f6f Mon Sep 17 00:00:00 2001 From: Ningyuan Wang Date: Thu, 7 Jul 2016 14:25:14 -0700 Subject: [PATCH] Initial commit for NL80211Packet This CL adds the initial implementation of class NL80211Packet as well as the corresponding unit tests. BUG=29454786 TEST=compile TEST=run unittests Change-Id: Ide5393e77650d157cc3045ffdaaf5b62615a5346 --- Android.mk | 4 +- net/nl80211_attribute.cpp | 79 +++++++------ net/nl80211_attribute.h | 25 ++-- net/nl80211_packet.cpp | 182 +++++++++++++++++++++++++++++ net/nl80211_packet.h | 136 ++++++++++++++++++++++ tests/nl80211_packet_unittest.cpp | 239 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 616 insertions(+), 49 deletions(-) create mode 100644 net/nl80211_packet.cpp create mode 100644 net/nl80211_packet.h create mode 100644 tests/nl80211_packet_unittest.cpp diff --git a/Android.mk b/Android.mk index 2154b42..bbe7dfd 100644 --- a/Android.mk +++ b/Android.mk @@ -66,7 +66,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := libwificond_nl LOCAL_CPPFLAGS := $(wificond_cpp_flags) LOCAL_SRC_FILES := \ - net/nl80211_attribute.cpp + net/nl80211_attribute.cpp \ + net/nl80211_packet.cpp LOCAL_SHARED_LIBRARIES := \ libbase include $(BUILD_STATIC_LIBRARY) @@ -109,6 +110,7 @@ LOCAL_SRC_FILES := \ tests/main.cpp \ tests/looper_backed_event_loop_unittest.cpp \ tests/nl80211_attribute_unittest.cpp \ + tests/nl80211_packet_unittest.cpp \ tests/server_unittest.cpp \ tests/shell_unittest.cpp LOCAL_STATIC_LIBRARIES := \ diff --git a/net/nl80211_attribute.cpp b/net/nl80211_attribute.cpp index b9d5a49..5ff9c9c 100644 --- a/net/nl80211_attribute.cpp +++ b/net/nl80211_attribute.cpp @@ -57,6 +57,33 @@ const vector& BaseNL80211Attr::GetConstData() const { return data_; } +bool BaseNL80211Attr::GetAttributeImpl(const uint8_t* buf, + size_t len, + int attr_id, + uint8_t** attr_start, + uint8_t** attr_end) { + // Skip the top level attribute header. + const uint8_t* ptr = buf; + const uint8_t* end_ptr = buf + len; + while (ptr + NLA_HDRLEN <= end_ptr) { + const nlattr* header = reinterpret_cast(ptr); + if (header->nla_type == attr_id) { + if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { + LOG(ERROR) << "Failed to get attribute: broken nl80211 atrribute."; + return false; + } + if (attr_start != nullptr && attr_end != nullptr) { + *attr_start = const_cast(ptr); + *attr_end = const_cast(ptr + NLA_ALIGN(header->nla_len)); + } + return true; + } + ptr += NLA_ALIGN(header->nla_len); + } + return false; +} + + // For NL80211Attr> NL80211Attr>::NL80211Attr(int id, const vector& raw_buffer) { @@ -125,29 +152,29 @@ void NL80211NestedAttr::AddAttribute(const BaseNL80211Attr& attribute) { void NL80211NestedAttr::AddFlagAttribute(int attribute_id) { // We only need to append a header for flag attribute. - nlattr flag_header; - flag_header.nla_type = attribute_id; - flag_header.nla_len = NLA_HDRLEN; - vector append_data( - reinterpret_cast(&flag_header), - reinterpret_cast(&flag_header) + sizeof(nlattr)); - append_data.resize(NLA_HDRLEN, 0); - // Append the data of |attribute| to |this|. - data_.insert(data_.end(), append_data.begin(), append_data.end()); - - nlattr* header = reinterpret_cast(data_.data()); - header->nla_len += NLA_HDRLEN; + // Make space for the new attribute. + data_.resize(data_.size() + NLA_HDRLEN, 0); + nlattr* flag_header = + reinterpret_cast(data_.data() + data_.size() - NLA_HDRLEN); + flag_header->nla_type = attribute_id; + flag_header->nla_len = NLA_HDRLEN; + nlattr* nl_header = reinterpret_cast(data_.data()); + nl_header->nla_len += NLA_HDRLEN; } bool NL80211NestedAttr::HasAttribute(int id) const { - return GetAttributeInternal(id, nullptr, nullptr); + return BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN, + data_.size() - NLA_HDRLEN, + id, nullptr, nullptr); } bool NL80211NestedAttr::GetAttribute(int id, NL80211NestedAttr* attribute) const { uint8_t* start = nullptr; uint8_t* end = nullptr; - if (!GetAttributeInternal(id, &start, &end) || + if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN, + data_.size() - NLA_HDRLEN, + id, &start, &end) || start == nullptr || end == nullptr) { return false; @@ -159,29 +186,5 @@ bool NL80211NestedAttr::GetAttribute(int id, return true; } -bool NL80211NestedAttr::GetAttributeInternal(int id, - uint8_t** start, - uint8_t** end) const { - // Skip the top level attribute header. - const uint8_t* ptr = data_.data() + NLA_HDRLEN; - const uint8_t* end_ptr = data_.data() + data_.size(); - while (ptr + NLA_HDRLEN <= end_ptr) { - const nlattr* header = reinterpret_cast(ptr); - if (header->nla_type == id) { - if (ptr + NLA_ALIGN(header->nla_len) > end_ptr) { - LOG(ERROR) << "Failed to get attribute: broken nl80211 atrribute."; - return false; - } - if (start != nullptr && end != nullptr) { - *start = const_cast(ptr); - *end = const_cast(ptr + NLA_ALIGN(header->nla_len)); - } - return true; - } - ptr += NLA_ALIGN(header->nla_len); - } - return false; -} - } // namespace wificond } // namespace android diff --git a/net/nl80211_attribute.h b/net/nl80211_attribute.h index 34f8403..2dbd914 100644 --- a/net/nl80211_attribute.h +++ b/net/nl80211_attribute.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef WIFICOND_NL80211_ATTRIBUTE_H_ -#define WIFICOND_NL80211_ATTRIBUTE_H_ +#ifndef WIFICOND_NET_NL80211_ATTRIBUTE_H_ +#define WIFICOND_NET_NL80211_ATTRIBUTE_H_ #include #include @@ -38,6 +38,15 @@ class BaseNL80211Attr { // This is used when we initialize a NL80211 attribute from an existing // buffer. virtual bool IsValid() const; + // A util helper function to find a specific sub attribute from a buffer. + // This buffer is supposed to be from a nested attribute or a nl80211 packet. + // |*start| and |*end| are the start and end pointers of buffer where + // |id| atrribute locates. + static bool GetAttributeImpl(const uint8_t* buf, + size_t len, + int attr_id, + uint8_t** attr_start, + uint8_t** attr_end); protected: BaseNL80211Attr() = default; @@ -157,7 +166,9 @@ class NL80211NestedAttr : public BaseNL80211Attr { bool GetAttribute(int id, NL80211Attr* attribute) const { uint8_t* start = nullptr; uint8_t* end = nullptr; - if (!GetAttributeInternal(id, &start, &end) || + if (!BaseNL80211Attr::GetAttributeImpl(data_.data() + NLA_HDRLEN, + data_.size() - NLA_HDRLEN, + id, &start, &end) || start == nullptr || end == nullptr) { return false; @@ -169,15 +180,9 @@ class NL80211NestedAttr : public BaseNL80211Attr { return true; } - private: - // |*start| and |*end| are the start and end pointers of buffer where - // |id| atrribute locates. - bool GetAttributeInternal(int id, - uint8_t** start, - uint8_t** end) const; }; } // namespace wificond } // namespace android -#endif // WIFICOND_NL80211_ATTRIBUTE_H_ +#endif // WIFICOND_NET_NL80211_ATTRIBUTE_H_ diff --git a/net/nl80211_packet.cpp b/net/nl80211_packet.cpp new file mode 100644 index 0000000..ad9726b --- /dev/null +++ b/net/nl80211_packet.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "net/nl80211_packet.h" + +#include + +using std::vector; + +namespace android { +namespace wificond { + +NL80211Packet::NL80211Packet(const vector& data) + : data_(data) { + data_ = data; +} + +NL80211Packet::NL80211Packet() { + // Initialize the netlink header and generic netlink header. + // NLMSG_HDRLEN and GENL_HDRLEN already include the padding size. + data_.resize(NLMSG_HDRLEN + GENL_HDRLEN, 0); + // Initialize length field. + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_len = data_.size(); + // Add NLM_F_REQUEST flag. + nl_header->nlmsg_flags = nl_header->nlmsg_flags | NLM_F_REQUEST; + + genlmsghdr* genl_header = + reinterpret_cast(data_.data() + NLMSG_HDRLEN); + genl_header->version = 1; + // genl_header->reserved is aready 0. + // genl_header->cmd will be set by SetCommand(). +} + +bool NL80211Packet::IsValid() const { + // Verify the size of packet. + if (data_.size() < NLMSG_HDRLEN) { + LOG(ERROR) << "Cannot retrieve netlink header."; + return false; + } + + // If type < NLMSG_MIN_TYPE, this should be a reserved control message, + // which doesn't carry a generic netlink header. + if (GetMessageType() >= NLMSG_MIN_TYPE) { + if (data_.size() < NLMSG_HDRLEN + GENL_HDRLEN) { + LOG(ERROR) << "Cannot retrieve generic netlink header."; + return false; + } + } + const nlmsghdr* nl_header = reinterpret_cast(data_.data()); + // Verify the netlink header. + if (data_.size() < nl_header->nlmsg_len || + nl_header->nlmsg_len < sizeof(nlmsghdr)) { + LOG(ERROR) << "Discarding incomplete / invalid message."; + return false; + } + return true; +} + +bool NL80211Packet::IsDump() const { + return GetFlags() & NLM_F_DUMP; +} + +bool NL80211Packet::IsMulti() const { + return GetFlags() & NLM_F_MULTI; +} + +uint8_t NL80211Packet::GetCommand() const { + const genlmsghdr* genl_header = reinterpret_cast( + data_.data() + NLMSG_HDRLEN); + return genl_header->cmd; +} + +uint16_t NL80211Packet::GetFlags() const { + const nlmsghdr* nl_header = reinterpret_cast(data_.data()); + return nl_header->nlmsg_flags; +} + +uint16_t NL80211Packet::GetMessageType() const { + const nlmsghdr* nl_header = reinterpret_cast(data_.data()); + return nl_header->nlmsg_type; +} + +uint32_t NL80211Packet::GetMessageSequence() const { + const nlmsghdr* nl_header = reinterpret_cast(data_.data()); + return nl_header->nlmsg_seq; +} + +uint32_t NL80211Packet::GetPortId() const { + const nlmsghdr* nl_header = reinterpret_cast(data_.data()); + return nl_header->nlmsg_pid; +} + +void NL80211Packet::SetCommand(uint8_t command) { + genlmsghdr* genl_header = reinterpret_cast( + data_.data() + NLMSG_HDRLEN); + genl_header->cmd = command; +} + +void NL80211Packet::SetFlags(uint16_t flags) { + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_flags = flags; +} + +void NL80211Packet::SetMessageType(uint16_t message_type) { + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_type = message_type; +} + +void NL80211Packet::SetMessageSequence(uint32_t message_sequence) { + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_seq = message_sequence; +} + +void NL80211Packet::SetPortId(uint32_t pid) { + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_pid = pid; +} + +void NL80211Packet::AddAttribute(const BaseNL80211Attr& attribute) { + const vector& append_data = attribute.GetConstData(); + // Append the data of |attribute| to |this|. + data_.insert(data_.end(), append_data.begin(), append_data.end()); + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + // We don't need to worry about padding for a nl80211 packet. + // Because as long as all sub attributes have padding, the payload is aligned. + nl_header->nlmsg_len += append_data.size(); +} + +void NL80211Packet::AddFlagAttribute(int attribute_id) { + // We only need to append a header for flag attribute. + // Make space for the new attribute. + data_.resize(data_.size() + NLA_HDRLEN, 0); + nlattr* flag_header = + reinterpret_cast(data_.data() + data_.size() - NLA_HDRLEN); + flag_header->nla_type = attribute_id; + flag_header->nla_len = NLA_HDRLEN; + nlmsghdr* nl_header = reinterpret_cast(data_.data()); + nl_header->nlmsg_len += NLA_HDRLEN; +} + +bool NL80211Packet::HasAttribute(int id) const { + return BaseNL80211Attr::GetAttributeImpl( + data_.data() + NLMSG_HDRLEN + GENL_HDRLEN, + data_.size() - NLMSG_HDRLEN - GENL_HDRLEN, + id, nullptr, nullptr); +} + +bool NL80211Packet::GetAttribute(int id, + NL80211NestedAttr* attribute) const { + uint8_t* start = nullptr; + uint8_t* end = nullptr; + if (!BaseNL80211Attr::GetAttributeImpl( + data_.data() + NLMSG_HDRLEN + GENL_HDRLEN, + data_.size() - NLMSG_HDRLEN - GENL_HDRLEN, + id, &start, &end) || + start == nullptr || + end == nullptr) { + return false; + } + *attribute = NL80211NestedAttr(vector(start, end)); + if (!attribute->IsValid()) { + return false; + } + return true; +} + +} // namespace wificond +} // namespace android diff --git a/net/nl80211_packet.h b/net/nl80211_packet.h new file mode 100644 index 0000000..8dd846d --- /dev/null +++ b/net/nl80211_packet.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WIFICOND_NET_NL80211_PACKET_H_ +#define WIFICOND_NET_NL80211_PACKET_H_ + +#include +#include + +#include +#include + +#include + +#include "net/nl80211_attribute.h" + +namespace android { +namespace wificond { + +// NL80211Packets are used to communicate with the cfg80211 kernel subsystem +// (via the nl80211 interface). An NL80211 packet is a type of generic netlink +// packet (i.e. it includes netlink and generic netlink headers). +// In order to simplify the design, we also allow this class to represent a +// few types of netlink control messages. In this way the API user is supposed to +// call IsValid() and GetMessageType() in the first place to avoid misuse of +// this class. +class NL80211Packet { + public: + // This is used for creating a NL80211Packet from buffer. + explicit NL80211Packet(const std::vector& data); + // This is used for creating an empty NL80211Packet to be filled later. + NL80211Packet(); + ~NL80211Packet() = default; + + // Returns whether a packet has consistent header fields. + bool IsValid() const; + + // Helper functions for Netlink flags. + + // Requesting a dump of a kernel obejct. + bool IsDump() const; + // Multipart messages are used to send lists or trees of objects. + // They are supposed to be parsed independently, and must not be reassembled. + // Multipart messages are terminated by NLMSG_DONE, which should be returned + // by GetMessageType(). + bool IsMulti() const; + + // Getter functions. + uint8_t GetCommand() const; + uint16_t GetFlags() const; + uint16_t GetMessageType() const; + uint32_t GetMessageSequence() const; + uint32_t GetPortId() const; + + // Setter functions. + + // In the contexet of nl80211 messages, + // |command| is one of |enum nl80211_commands| in nl80211.h + void SetCommand(uint8_t command); + // |flags| is set of flag bits described by NLM_F_* macros in netlink.h + void SetFlags(uint16_t flags); + // In the context of nl80211 messages, + // message type is a nl80211 message family id dynamiclly allocated by kernel. + // If this is a control message, it could be one of the following value: + // NLMSG_NOOP, NLMSG_ERROR, NLMSG_DONE, NLMSG_OVERRUN + void SetMessageType(uint16_t message_type); + void SetMessageSequence(uint32_t message_sequemce); + // Set nlmsg_pid in netlink header. + // nlmsg_pid is the sender process port ID. + // It is *not* associated with a process but a netlink socket. + // We should use sockaddr_nl.nl_pid from sender socket. + // This value should be 0 if message is from kernel. + // See man 7 netlink for details. + void SetPortId(uint32_t pid); + + void AddAttribute(const BaseNL80211Attr& attribute); + // For NLA_FLAG attribute + void AddFlagAttribute(int attribute_id); + + bool HasAttribute(int id) const; + bool GetAttribute(int id, NL80211NestedAttr* attribute) const; + + template + bool GetAttributeValue(int id, T* value) const { + std::vector empty_vec; + // All data in |attribute| created here will be overwritten by + // GetAttribute(). So we use an empty vector to initialize it, + // regardless of the fact that an empty buffer is not qualified + // for creating a valid attribute. + NL80211Attr attribute(empty_vec); + if (!GetAttribute(id, &attribute)) { + return false; + } + *value = attribute.GetValue(); + return true; + } + + template + bool GetAttribute(int id, NL80211Attr* attribute) const { + uint8_t* start = nullptr; + uint8_t* end = nullptr; + if (!BaseNL80211Attr::GetAttributeImpl( + data_.data() + NLMSG_HDRLEN + GENL_HDRLEN, + data_.size() - NLMSG_HDRLEN - GENL_HDRLEN, + id, &start, &end) || + start == nullptr || + end == nullptr) { + return false; + } + *attribute = NL80211Attr(std::vector(start, end)); + if (!attribute->IsValid()) { + return false; + } + return true; + } + + std::vector data_; +}; + +} // namespace wificond +} // namespace android + +#endif // WIFICOND_NET_NL80211_PACKET_H_ diff --git a/tests/nl80211_packet_unittest.cpp b/tests/nl80211_packet_unittest.cpp new file mode 100644 index 0000000..49274cb --- /dev/null +++ b/tests/nl80211_packet_unittest.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include + +#include "net/nl80211_attribute.h" +#include "net/nl80211_packet.h" + +using std::string; + +namespace android { +namespace wificond { + +namespace { + +const uint8_t kU8Value1 = 150; +const uint16_t kU16Value1 = 5000; +const uint32_t kU32Value1 = 500000; +const uint32_t kU32Value2 = 800000; +const uint32_t kPortId = 123; + +const uint8_t kGenNLCommand = 102; +const uint16_t kNLMsgType = 4000; +const uint32_t kNLMsgSequenceNumber = 70000; + +// NL80211 family id is dynamically allocated by kernel. +const uint16_t kNL80211FamilyId = 0x13; +const uint32_t kWiPhy = 0; +const uint32_t kExpectedIfIndex = 4; +const uint8_t kMacAddressBytes[] = { + 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f +}; + +const unsigned char kNL80211_CMD_ASSOCIATE[] = { + 0x58, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x33, 0x00, + 0x10, 0x00, 0x3a, 0x01, 0x48, 0x5d, 0x60, 0x77, + 0x2d, 0xcf, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, + 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x40, 0x07, + 0x01, 0x04, 0x00, 0x00, 0x01, 0xc0, 0x01, 0x08, + 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, + 0x32, 0x04, 0x30, 0x48, 0x60, 0x6c, 0x00, 0x00, +}; + +const uint32_t kExpectedCqmNotAcked = 50; + +const unsigned char kNL80211_CMD_NOTIFY_CQM[] = { + 0x3c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x06, 0x00, + 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00, + 0x0c, 0x00, 0x5e, 0x00, 0x08, 0x00, 0x04, 0x00, + 0x32, 0x00, 0x00, 0x00, +}; + +const uint32_t kNewStationExpectedGeneration = 275; + +const unsigned char kNL80211_CMD_NEW_STATION[] = { + 0x34, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x01, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x06, 0x00, + 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00, + 0x08, 0x00, 0x2e, 0x00, 0x13, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x15, 0x00, +}; + +} // namespace + +TEST(NL80211PacketTest, CanConstructValidNL80211Packet) { + NL80211Packet netlink_packet; + EXPECT_TRUE(netlink_packet.IsValid()); +} + +TEST(NL80211PacketTest, SetAndGetNL80211PacketHeaderFields) { + NL80211Packet netlink_packet; + netlink_packet.SetCommand(kGenNLCommand); + netlink_packet.SetMessageType(kNLMsgType); + netlink_packet.SetMessageSequence(kNLMsgSequenceNumber); + netlink_packet.SetFlags(NLM_F_MULTI); + netlink_packet.SetPortId(kPortId); + EXPECT_EQ(kGenNLCommand, netlink_packet.GetCommand()); + EXPECT_EQ(kNLMsgType, netlink_packet.GetMessageType()); + EXPECT_EQ(kNLMsgSequenceNumber, netlink_packet.GetMessageSequence()); + EXPECT_TRUE(netlink_packet.IsMulti()); + EXPECT_EQ(kPortId, netlink_packet.GetPortId()); +} + +TEST(NL80211PacketTest, AddAttributeToNL80211Packet) { + NL80211Packet netlink_packet; + NL80211Attr u8_attr(1, kU8Value1); + netlink_packet.AddAttribute(u8_attr); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_TRUE(netlink_packet.HasAttribute(1)); + uint8_t attr_value; + EXPECT_TRUE(netlink_packet.GetAttributeValue(1, &attr_value)); + EXPECT_EQ(attr_value, kU8Value1); +} + +TEST(NL80211PacketTest, AddMultipleAttributesToNL80211Packet) { + NL80211Packet netlink_packet; + NL80211Attr u8_attr(1, kU8Value1); + NL80211Attr u32_attr(2, kU32Value1); + NL80211Attr u16_attr(3, kU16Value1); + netlink_packet.AddAttribute(u8_attr); + netlink_packet.AddAttribute(u32_attr); + netlink_packet.AddAttribute(u16_attr); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_TRUE(netlink_packet.HasAttribute(1)); + EXPECT_TRUE(netlink_packet.HasAttribute(2)); + EXPECT_TRUE(netlink_packet.HasAttribute(3)); + uint8_t u8_attr_value; + uint32_t u32_attr_value; + uint16_t u16_attr_value; + EXPECT_TRUE(netlink_packet.GetAttributeValue(1, &u8_attr_value)); + EXPECT_TRUE(netlink_packet.GetAttributeValue(2, &u32_attr_value)); + EXPECT_TRUE(netlink_packet.GetAttributeValue(3, &u16_attr_value)); + EXPECT_EQ(u8_attr_value, kU8Value1); + EXPECT_EQ(u32_attr_value, kU32Value1); + EXPECT_EQ(u16_attr_value, kU16Value1); +} + +TEST(NL80211PacketTest, AddNestedAttributesToNL80211Packet) { + NL80211Packet netlink_packet; + NL80211NestedAttr nested_attr(1); + NL80211Attr u16_attr(2, kU16Value1); + NL80211Attr u32_attr_1(3, kU32Value1); + NL80211Attr u32_attr_2(4, kU32Value2); + nested_attr.AddAttribute(u16_attr); + nested_attr.AddAttribute(u32_attr_1); + netlink_packet.AddAttribute(nested_attr); + netlink_packet.AddAttribute(u32_attr_2); + EXPECT_TRUE(netlink_packet.HasAttribute(1)); + EXPECT_TRUE(netlink_packet.HasAttribute(4)); + // Attribute 2 and 3 are deeper nested. + // They should not be found from packet level. + EXPECT_FALSE(netlink_packet.HasAttribute(2)); + EXPECT_FALSE(netlink_packet.HasAttribute(3)); +} + +TEST(NL80211PacketTest, CanbotGetMissingAttributeFromNL80211Packet) { + NL80211Packet netlink_packet; + NL80211Attr u8_attr(1, kU8Value1); + netlink_packet.AddAttribute(u8_attr); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_FALSE(netlink_packet.HasAttribute(2)); + uint8_t attr_value; + EXPECT_FALSE(netlink_packet.GetAttributeValue(2, &attr_value)); +} + +TEST(NL80211PacketTest, ParseCMDAssociateTest) { + NL80211Packet netlink_packet(std::vector( + kNL80211_CMD_ASSOCIATE, + kNL80211_CMD_ASSOCIATE + sizeof(kNL80211_CMD_ASSOCIATE))); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_EQ(kNL80211FamilyId, netlink_packet.GetMessageType()); + EXPECT_EQ(NL80211_CMD_ASSOCIATE, netlink_packet.GetCommand()); + uint32_t value; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_WIPHY, &value)); + EXPECT_EQ(kWiPhy, value); + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_IFINDEX, &value)); + EXPECT_EQ(kExpectedIfIndex, value); + std::vector rawdata; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_FRAME, &rawdata)); + EXPECT_FALSE(rawdata.empty()); + +} + +TEST(NL80211PacketTest, ParseCMDNotifyCQMTest) { + NL80211Packet netlink_packet(std::vector( + kNL80211_CMD_NOTIFY_CQM, + kNL80211_CMD_NOTIFY_CQM + sizeof(kNL80211_CMD_NOTIFY_CQM))); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_EQ(kNL80211FamilyId, netlink_packet.GetMessageType()); + EXPECT_EQ(NL80211_CMD_NOTIFY_CQM, netlink_packet.GetCommand()); + uint32_t value; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_WIPHY, &value)); + EXPECT_EQ(kWiPhy, value); + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_IFINDEX, &value)); + EXPECT_EQ(kExpectedIfIndex, value); + std::vector mac; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_MAC, &mac)); + std::vector expected_mac( + kMacAddressBytes, + kMacAddressBytes + sizeof(kMacAddressBytes)); + EXPECT_EQ(expected_mac, mac); + NL80211NestedAttr nested(0); + EXPECT_TRUE(netlink_packet.GetAttribute(NL80211_ATTR_CQM, &nested)); + EXPECT_TRUE(nested.GetAttributeValue(NL80211_ATTR_CQM_PKT_LOSS_EVENT, &value)); + EXPECT_EQ(kExpectedCqmNotAcked, value); +} + +TEST(NL80211PacketTest, ParseCMDNewStation) { + NL80211Packet netlink_packet(std::vector( + kNL80211_CMD_NEW_STATION, + kNL80211_CMD_NEW_STATION + sizeof(kNL80211_CMD_NEW_STATION))); + EXPECT_TRUE(netlink_packet.IsValid()); + EXPECT_EQ(kNL80211FamilyId, netlink_packet.GetMessageType()); + EXPECT_EQ(NL80211_CMD_NEW_STATION, netlink_packet.GetCommand()); + uint32_t value; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_IFINDEX, &value)); + EXPECT_EQ(kExpectedIfIndex, value); + std::vector mac; + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_MAC, &mac)); + std::vector expected_mac( + kMacAddressBytes, + kMacAddressBytes + sizeof(kMacAddressBytes)); + EXPECT_EQ(expected_mac, mac); + NL80211NestedAttr nested(0); + EXPECT_TRUE(netlink_packet.GetAttribute(NL80211_ATTR_STA_INFO, &nested)); + EXPECT_TRUE(netlink_packet.GetAttributeValue(NL80211_ATTR_GENERATION, + &value)); + EXPECT_EQ(kNewStationExpectedGeneration, value); +} + +} // namespace wificond +} // namespace android -- 2.11.0