shill: Reorganizes Nl80211Message preparing to build a class hierarchy.
This is a reorganization of Nl80211Message so that breaking it into
an appropriate class hierarchy is clear and straight-forward. This CL
includes the following:
1. Moves NetlinkMessage encoding from |NetlinkSocket| up to
|Config80211| (because it makes more sense, there). This requires
exposing sequence number calculations through |Config80211| instead
of requiring access to a specific socket.
2. Breaks both the |Encode| and |InitFromNlmsg| methods into separate
header and body pieces (the header pieces will be broken-up into
their constituent parts when the NetlinkMessage class hierarchy is
created).
3. Changes the name of |message_type| into |command| to more closely
match the netlink data structures. Unfortunately, this is
necessary in order to add a _different_ |message_type| member (and
accessor) that is required by a different netlink data structure.
4. Adds a unittest for message encoding.
BUG=chromium-os:38221
TEST=unittest.
Change-Id: I2f01f3a4c6104bdd09498f143ce44518615e8150
Reviewed-on: https://gerrit.chromium.org/gerrit/44573
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/nl80211_message.cc b/nl80211_message.cc
index fe60011..60ea977 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -27,6 +27,7 @@
#include <ctype.h>
#include <endian.h>
#include <errno.h>
+#include <limits.h>
#include <linux/nl80211.h>
#include <net/if.h>
#include <netinet/in.h>
@@ -52,7 +53,6 @@
#include "shill/netlink_attribute.h"
#include "shill/netlink_socket.h"
#include "shill/scope_logger.h"
-#include "shill/wifi.h"
using base::Bind;
using base::LazyInstance;
@@ -70,15 +70,19 @@
LAZY_INSTANCE_INITIALIZER;
} // namespace
-const char Nl80211Message::kBogusMacAddress[] = "XX:XX:XX:XX:XX:XX";
-
const uint8_t Nl80211Frame::kMinimumFrameByteCount = 26;
const uint8_t Nl80211Frame::kFrameTypeMask = 0xfc;
-const uint32_t Nl80211Message::kIllegalMessage = 0;
+// TODO(wdg): These will go into NetlinkMessage when that class exists.
+const uint32_t Nl80211Message::kBroadcastSequenceNumber = 0;
+const uint16_t Nl80211Message::kIllegalMessageType = UINT16_MAX;
+
+const char Nl80211Message::kBogusMacAddress[] = "XX:XX:XX:XX:XX:XX";
const unsigned int Nl80211Message::kEthernetAddressBytes = 6;
map<uint16_t, string> *Nl80211Message::reason_code_string_ = NULL;
map<uint16_t, string> *Nl80211Message::status_code_string_ = NULL;
+uint16_t Nl80211Message::nl80211_message_type_ = kIllegalMessageType;
+
// The nl messages look like this:
//
@@ -108,11 +112,30 @@
// Nl80211Message
//
-void Nl80211Message::Print(int log_level) const {
- SLOG(WiFi, log_level) << StringPrintf("Message %s (%d)",
- message_type_string(),
- message_type());
- attributes_->Print(log_level, 1);
+bool Nl80211Message::InitAndStripHeader(ByteString *input) {
+ if (!input) {
+ LOG(ERROR) << "NULL input";
+ return false;
+ }
+ // Read the nlmsghdr.
+ nlmsghdr *header = reinterpret_cast<nlmsghdr *>(input->GetData());
+ message_type_ = header->nlmsg_type;
+ flags_ = header->nlmsg_flags;
+ sequence_number_ = header->nlmsg_seq;
+
+ // Strip the nlmsghdr.
+ input->RemovePrefix(NLMSG_ALIGN(sizeof(struct nlmsghdr)));
+
+ // Read the genlmsghdr.
+ genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(input->GetData());
+ if (command_ != gnlh->cmd) {
+ LOG(WARNING) << "This object thinks it's a " << command_
+ << " but the message thinks it's a " << gnlh->cmd;
+ }
+
+ // Strip the genlmsghdr.
+ input->RemovePrefix(NLMSG_ALIGN(sizeof(struct genlmsghdr)));
+ return true;
}
bool Nl80211Message::InitFromNlmsg(const nlmsghdr *const_msg) {
@@ -120,23 +143,19 @@
LOG(ERROR) << "Null |msg| parameter";
return false;
}
+ ByteString message(reinterpret_cast<const unsigned char *>(const_msg),
+ const_msg->nlmsg_len);
- // Netlink header.
- sequence_number_ = const_msg->nlmsg_seq;
- SLOG(WiFi, 6) << "NL Message " << sequence_number() << " <===";
-
- // Casting away constness, here, since the libnl code doesn't properly label
- // their stuff as const (even though it is).
- nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
-
- // Genl message header.
- genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(nlmsg_data(msg));
+ if (!InitAndStripHeader(&message)) {
+ return false;
+ }
// Attributes.
// Parse the attributes from the nl message payload into the 'tb' array.
nlattr *tb[NL80211_ATTR_MAX + 1];
- nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
- genlmsg_attrlen(gnlh, 0), NULL);
+ nla_parse(tb, NL80211_ATTR_MAX,
+ reinterpret_cast<nlattr *>(message.GetData()), message.GetLength(),
+ NULL);
for (int i = 0; i < NL80211_ATTR_MAX + 1; ++i) {
if (tb[i]) {
@@ -345,6 +364,13 @@
return true;
}
+void Nl80211Message::Print(int log_level) const {
+ SLOG(WiFi, log_level) << StringPrintf("Message %s (%d)",
+ command_string(),
+ command());
+ attributes_->Print(log_level, 1);
+}
+
// static
void Nl80211Message::PrintBytes(int log_level, const unsigned char *buf,
size_t num_bytes) {
@@ -607,42 +633,61 @@
return match->second;
}
-ByteString Nl80211Message::Encode(uint16_t nlmsg_type) const {
+ByteString Nl80211Message::EncodeHeader(uint32_t sequence_number,
+ uint16_t nlmsg_type) {
+ ByteString result;
+ sequence_number_ = sequence_number;
+ if (sequence_number_ == kBroadcastSequenceNumber) {
+ LOG(ERROR) << "Couldn't get a legal sequence number";
+ return result;
+ }
+
// Build netlink header.
nlmsghdr header;
size_t nlmsghdr_with_pad = NLMSG_ALIGN(sizeof(header));
header.nlmsg_len = nlmsghdr_with_pad;
header.nlmsg_type = nlmsg_type;
header.nlmsg_flags = NLM_F_REQUEST;
- header.nlmsg_seq = sequence_number();
+ header.nlmsg_seq = sequence_number_;
header.nlmsg_pid = getpid();
- // Build genl message header.
- genlmsghdr genl_header;
- size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
- header.nlmsg_len += genlmsghdr_with_pad;
- genl_header.cmd = message_type();
- genl_header.version = 1;
- genl_header.reserved = 0;
-
- // Assemble attributes (padding is included by AttributeList::Encode).
- ByteString attribute_bytes = attributes_->Encode();
- header.nlmsg_len += attribute_bytes.GetLength();
-
- // Now that we know the total message size, build the output ByteString.
- ByteString result;
-
// Netlink header + pad.
result.Append(ByteString(reinterpret_cast<unsigned char *>(&header),
sizeof(header)));
result.Resize(nlmsghdr_with_pad); // Zero-fill pad space (if any).
- // Genl message header + pad.
- result.Append(ByteString(reinterpret_cast<unsigned char *>(&genl_header),
- sizeof(genl_header)));
- result.Resize(nlmsghdr_with_pad + genlmsghdr_with_pad); // Zero-fill.
+ // Build and append the genl message header.
+ genlmsghdr genl_header;
+ genl_header.cmd = command();
+ genl_header.version = 1;
+ genl_header.reserved = 0;
- // Attributes including pad.
+ ByteString genl_header_string(
+ reinterpret_cast<unsigned char *>(&genl_header), sizeof(genl_header));
+ size_t genlmsghdr_with_pad = NLMSG_ALIGN(sizeof(genl_header));
+ genl_header_string.Resize(genlmsghdr_with_pad); // Zero-fill.
+
+ nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
+ pheader->nlmsg_len += genlmsghdr_with_pad;
+ result.Append(genl_header_string);
+ return result;
+}
+
+ByteString Nl80211Message::Encode(uint32_t sequence_number,
+ uint16_t nlmsg_type) {
+ ByteString result(EncodeHeader(sequence_number, nlmsg_type));
+ if (result.GetLength() == 0) {
+ LOG(ERROR) << "Couldn't encode message header.";
+ return result;
+ }
+
+ // Build and append attributes (padding is included by
+ // AttributeList::Encode).
+ ByteString attribute_bytes = attributes_->Encode();
+
+ // Need to re-calculate |header| since |Append|, above, moves the data.
+ nlmsghdr *pheader = reinterpret_cast<nlmsghdr *>(result.GetData());
+ pheader->nlmsg_len += attribute_bytes.GetLength();
result.Append(attribute_bytes);
return result;
@@ -991,13 +1036,13 @@
bool doit = false;
map<uint8_t, bool>::const_iterator node;
- node = need_to_print.find(message.message_type());
+ node = need_to_print.find(message.command());
if (node != need_to_print.end())
doit = node->second;
if (doit) {
LOG(INFO) << "@@const unsigned char "
- << "k" << message.message_type_string()
+ << "k" << message.command_string()
<< "[] = {";
int payload_bytes = nlmsg_datalen(msg);
@@ -1010,7 +1055,7 @@
<< + rawdata[i] << ",";
}
LOG(INFO) << "};";
- need_to_print[message.message_type()] = false;
+ need_to_print[message.command()] = false;
}
}