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/callback80211_metrics.cc b/callback80211_metrics.cc
index 0583165..c47cc7c 100644
--- a/callback80211_metrics.cc
+++ b/callback80211_metrics.cc
@@ -20,10 +20,10 @@
 
 void Callback80211Metrics::Config80211MessageCallback(
     const Nl80211Message &message) {
-  SLOG(WiFi, 3) << "Received " << message.message_type_string()
-                << " (" << + message.message_type() << ")";
+  SLOG(WiFi, 3) << "Received " << message.command_string()
+                << " (" << + message.command() << ")";
   if (metrics_ &&
-      message.message_type() == DeauthenticateMessage::kCommand) {
+      message.command() == DeauthenticateMessage::kCommand) {
     Metrics::WiFiDisconnectByWhom by_whom =
         message.const_attributes()->IsFlagAttributeTrue(
             NL80211_ATTR_DISCONNECTED_BY_AP) ?
diff --git a/config80211.cc b/config80211.cc
index 4e0b9e4..c37ad71 100644
--- a/config80211.cc
+++ b/config80211.cc
@@ -146,17 +146,15 @@
     LOG(ERROR) << "Message is NULL.";
     return false;
   }
-  uint32 sequence_number = message->sequence_number();
-  if (!sequence_number) {
-    sequence_number = sock_->GetSequenceNumber();
-    message->set_sequence_number(sequence_number);
-  }
+
+  ByteString message_string = message->Encode(this->GetSequenceNumber(),
+                                              sock_->family_id());
 
   SLOG(WiFi, 6) << "NL Message " << message->sequence_number()
                 << " Sending ===>";
   message->Print(6);
 
-  if (!sock_->SendMessage(message)) {
+  if (!sock_->SendMessage(message_string)) {
     LOG(ERROR) << "Failed to send nl80211 message.";
     return false;
   }
@@ -164,12 +162,13 @@
     LOG(INFO) << "Handler for message was null.";
     return true;
   }
-  if (ContainsKey(message_handlers_, sequence_number)) {
+  if (ContainsKey(message_handlers_, message->sequence_number())) {
     LOG(ERROR) << "Sent message, but already had a handler for this message?";
     return false;
   }
-  message_handlers_[sequence_number] = handler;
-  LOG(INFO) << "Sent nl80211 message with sequence number: " << sequence_number;
+  message_handlers_[message->sequence_number()] = handler;
+  LOG(INFO) << "Sent nl80211 message with sequence number: "
+            << message->sequence_number();
   return true;
 }
 
@@ -231,6 +230,11 @@
   wifi_state_ = new_state;
 }
 
+uint32_t Config80211::GetSequenceNumber() {
+  return sock_ ?
+      sock_->GetSequenceNumber() : Nl80211Message::kBroadcastSequenceNumber;
+}
+
 bool Config80211::SubscribeToEvents(EventType type) {
   bool it_worked = true;
   if (!ContainsKey(subscribed_events_, type)) {
diff --git a/config80211.h b/config80211.h
index 5e0d55d..ff072b6 100644
--- a/config80211.h
+++ b/config80211.h
@@ -112,6 +112,10 @@
   // subscription requests or down.
   void SetWifiState(WifiState new_state);
 
+  // Gets the next sequence number for a NetlinkMessage to be sent over
+  // Config80211's netlink socket.
+  uint32_t GetSequenceNumber();
+
  protected:
   friend struct base::DefaultLazyInstanceTraits<Config80211>;
 
diff --git a/config80211_unittest.cc b/config80211_unittest.cc
index e35761d..cc807e5 100644
--- a/config80211_unittest.cc
+++ b/config80211_unittest.cc
@@ -57,8 +57,20 @@
 
 const uint32_t kExpectedIfIndex = 4;
 const uint32_t kWiPhy = 0;
+const uint16_t kNl80211FamilyId = 0x13;
 const char kExpectedMacAddress[] = "c0:3f:0e:77:e8:7f";
 
+const uint8_t kMacAddressBytes[] = {
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f
+};
+
+const uint8_t kAssignedRespIeBytes[] = {
+  0x01, 0x08, 0x82, 0x84,
+  0x8b, 0x96, 0x0c, 0x12,
+  0x18, 0x24, 0x32, 0x04,
+  0x30, 0x48, 0x60, 0x6c
+};
+
 
 // wlan0 (phy #0): scan started
 
@@ -349,17 +361,12 @@
 
 }  // namespace
 
-bool MockNl80211Socket::SendMessage(Nl80211Message *message) {
-  if (!message) {
-    LOG(ERROR) << "Null |message|";
-    return false;
-  }
+bool MockNl80211Socket::SendMessage(const ByteString &out_string) {
   return true;
 }
 
 uint32_t MockNl80211Socket::GetSequenceNumber() {
-  // Sequence number 0 is reserved for broadcast messages from the kernel.
-  if (++sequence_number_ == 0)
+  if (++sequence_number_ == Nl80211Message::kBroadcastSequenceNumber)
     ++sequence_number_;
   return sequence_number_;
 }
@@ -572,7 +579,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_TRIGGER_SCAN)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_TRIGGER_SCAN, message->message_type());
+  EXPECT_EQ(NL80211_CMD_TRIGGER_SCAN, message->command());
 
   {
     uint32_t value;
@@ -621,7 +628,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_SCAN_RESULTS)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_NEW_SCAN_RESULTS, message->message_type());
+  EXPECT_EQ(NL80211_CMD_NEW_SCAN_RESULTS, message->command());
 
   {
     uint32_t value;
@@ -670,7 +677,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_STATION)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_NEW_STATION, message->message_type());
+  EXPECT_EQ(NL80211_CMD_NEW_STATION, message->command());
 
   {
     uint32_t value;
@@ -705,7 +712,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_AUTHENTICATE)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_AUTHENTICATE, message->message_type());
+  EXPECT_EQ(NL80211_CMD_AUTHENTICATE, message->command());
 
   {
     uint32_t value;
@@ -739,7 +746,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_ASSOCIATE)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_ASSOCIATE, message->message_type());
+  EXPECT_EQ(NL80211_CMD_ASSOCIATE, message->command());
 
   {
     uint32_t value;
@@ -773,7 +780,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_CONNECT)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_CONNECT, message->message_type());
+  EXPECT_EQ(NL80211_CMD_CONNECT, message->command());
 
   {
     uint32_t value;
@@ -810,13 +817,61 @@
   }
 }
 
+TEST_F(Config80211Test, Build_NL80211_CMD_CONNECT) {
+  SetupConfig80211Object();
+
+  // Build the message that is found in kNL80211_CMD_CONNECT.
+  ConnectMessage message;
+  EXPECT_TRUE(message.attributes()->CreateAttribute(NL80211_ATTR_WIPHY,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId)));
+  EXPECT_TRUE(message.attributes()->SetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                        kWiPhy));
+
+  EXPECT_TRUE(message.attributes()->CreateAttribute(NL80211_ATTR_IFINDEX,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId)));
+  EXPECT_TRUE(message.attributes()->SetU32AttributeValue(
+      NL80211_ATTR_IFINDEX, kExpectedIfIndex));
+
+  EXPECT_TRUE(message.attributes()->CreateAttribute(NL80211_ATTR_MAC,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId)));
+  EXPECT_TRUE(message.attributes()->SetRawAttributeValue(NL80211_ATTR_MAC,
+      ByteString(kMacAddressBytes, arraysize(kMacAddressBytes))));
+
+  EXPECT_TRUE(message.attributes()->CreateAttribute(NL80211_ATTR_STATUS_CODE,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId)));
+  EXPECT_TRUE(message.attributes()->SetU16AttributeValue(
+      NL80211_ATTR_STATUS_CODE, kExpectedConnectStatus));
+
+  EXPECT_TRUE(message.attributes()->CreateAttribute(NL80211_ATTR_RESP_IE,
+      Bind(&NetlinkAttribute::NewNl80211AttributeFromId)));
+  EXPECT_TRUE(message.attributes()->SetRawAttributeValue(NL80211_ATTR_RESP_IE,
+      ByteString(kAssignedRespIeBytes, arraysize(kAssignedRespIeBytes))));
+
+  // Encode the message to a ByteString and remove all the run-specific
+  // values.
+
+  // TODO(wdg): Get nl80211's family id from Config80211 after it starts
+  // keeping track of family id for each family name.
+  ByteString message_bytes = message.Encode(config80211_->GetSequenceNumber(),
+                                            kNl80211FamilyId);
+  nlmsghdr *header = reinterpret_cast<nlmsghdr *>(message_bytes.GetData());
+  header->nlmsg_flags = 0;  // Overwrite with known values.
+  header->nlmsg_seq = 0;
+  header->nlmsg_pid = 0;
+
+  // Verify that the messages are equal.
+  EXPECT_TRUE(message_bytes.Equals(
+      ByteString(kNL80211_CMD_CONNECT, arraysize(kNL80211_CMD_CONNECT))));
+}
+
+
 TEST_F(Config80211Test, Parse_NL80211_CMD_DEAUTHENTICATE) {
   Nl80211Message *message = Nl80211MessageFactory::CreateMessage(
       const_cast<nlmsghdr *>(
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DEAUTHENTICATE)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_DEAUTHENTICATE, message->message_type());
+  EXPECT_EQ(NL80211_CMD_DEAUTHENTICATE, message->command());
 
   {
     uint32_t value;
@@ -850,7 +905,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISCONNECT)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_DISCONNECT, message->message_type());
+  EXPECT_EQ(NL80211_CMD_DISCONNECT, message->command());
 
   {
     uint32_t value;
@@ -883,7 +938,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NOTIFY_CQM)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_NOTIFY_CQM, message->message_type());
+  EXPECT_EQ(NL80211_CMD_NOTIFY_CQM, message->command());
 
 
   {
@@ -926,7 +981,7 @@
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISASSOCIATE)));
 
   EXPECT_NE(reinterpret_cast<Nl80211Message *>(NULL), message);
-  EXPECT_EQ(NL80211_CMD_DISASSOCIATE, message->message_type());
+  EXPECT_EQ(NL80211_CMD_DISASSOCIATE, message->command());
 
 
   {
diff --git a/mock_nl80211_socket.h b/mock_nl80211_socket.h
index 93d067d..bde76ca 100644
--- a/mock_nl80211_socket.h
+++ b/mock_nl80211_socket.h
@@ -24,7 +24,7 @@
   MOCK_METHOD1(AddGroupMembership, bool(const std::string &group_name));
 
   virtual uint32_t GetSequenceNumber();
-  virtual bool SendMessage(Nl80211Message *message);
+  virtual bool SendMessage(const ByteString &message);
   uint32 GetLastSequenceNumber() const { return sequence_number_; }
 
  private:
diff --git a/netlink_socket.cc b/netlink_socket.cc
index aba280d..f45dbd6 100644
--- a/netlink_socket.cc
+++ b/netlink_socket.cc
@@ -73,10 +73,10 @@
   return true;
 }
 
-bool NetlinkSocket::SendMessage(Nl80211Message *message) {
-  if (!message) {
-    LOG(ERROR) << "NULL |message|.";
-    return false;
+bool NetlinkSocket::SendMessage(const ByteString &out_msg) {
+  if (out_msg.GetLength() == 0) {
+    SLOG(WiFi, 3) << "Not sending empty message.";
+    return true;
   }
 
   if (!nl_sock_) {
@@ -84,8 +84,6 @@
     return false;
   }
 
-  ByteString out_msg = message->Encode(family_id());
-
   int result = HANDLE_EINTR(send(GetFd(), out_msg.GetConstData(),
                                  out_msg.GetLength(), 0));
   if (!result) {
diff --git a/netlink_socket.h b/netlink_socket.h
index edb1ee4..30228a9 100644
--- a/netlink_socket.h
+++ b/netlink_socket.h
@@ -43,7 +43,7 @@
 
 namespace shill {
 
-class Nl80211Message;
+class ByteString;
 
 // Provides an abstraction to a netlink socket.  See
 // http://www.infradead.org/~tgr/libnl/ for documentation on how netlink
@@ -73,7 +73,7 @@
   virtual std::string GetSocketFamilyName() const = 0;
 
   // Sends a message, returns true if successful.
-  virtual bool SendMessage(Nl80211Message *message);
+  virtual bool SendMessage(const ByteString &message);
 
  protected:
   struct nl_sock *GetNlSock() { return nl_sock_; }
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;
   }
 }
 
diff --git a/nl80211_message.h b/nl80211_message.h
index dddc46d..10986fa 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -26,19 +26,28 @@
 
 namespace shill {
 
+class Config80211;
+
 // Class for messages received from libnl.
 class Nl80211Message {
  public:
+  static const uint32_t kBroadcastSequenceNumber;
+  static const uint16_t kIllegalMessageType;
   static const unsigned int kEthernetAddressBytes;
   static const char kBogusMacAddress[];
 
-  Nl80211Message(uint8 message_type, const char *message_type_string)
-      : message_type_(message_type),
-        message_type_string_(message_type_string),
-        sequence_number_(kIllegalMessage),
-        attributes_(new AttributeList) {}
+  Nl80211Message(uint8 command, const char *command_string)
+      : attributes_(new AttributeList),
+        command_(command),
+        command_string_(command_string),
+        flags_(0),
+        message_type_(nl80211_message_type_),
+        sequence_number_(kBroadcastSequenceNumber) {}
   virtual ~Nl80211Message() {}
 
+  uint8 command() const { return command_; }
+  const char *command_string() const { return command_string_; }
+
   // Initializes the message with bytes from the kernel.
   virtual bool InitFromNlmsg(const nlmsghdr *msg);
   static void PrintBytes(int log_level, const unsigned char *buf,
@@ -78,15 +87,22 @@
   static std::string StringFromReason(uint16_t reason);
   static std::string StringFromStatus(uint16_t status);
 
-  uint8 message_type() const { return message_type_; }
-  const char *message_type_string() const { return message_type_string_; }
-
   // Returns a netlink message suitable for Sockets::Send.  Return value is
   // empty on failure.  |nlmsg_type| needs to be the family id returned by
   // |genl_ctrl_resolve|.
-  ByteString Encode(uint16_t nlmsg_type) const;
+  ByteString Encode(uint32_t sequence_number, uint16_t nlmsg_type);
 
  protected:
+  // Reads the values from the |nlmsghdr| and |genlmsghdr| portions of the
+  // netlink message and removes those headers (and any padding that happens to
+  // be there) from |input| leaving, hopefully, the attributes.
+  bool InitAndStripHeader(ByteString *input);
+
+  // Returns a string of bytes representing an |nlmsghdr| with the members
+  // filled-in with values representing the current message, and any required
+  // padding.
+  ByteString EncodeHeader(uint32_t sequence_number, uint16_t nlmsg_type);
+
   // Returns a string that should precede all user-bound message strings.
   virtual std::string GetHeaderString() const;
 
@@ -109,15 +125,16 @@
   friend class Config80211Test;
   FRIEND_TEST(Config80211Test, NL80211_CMD_NOTIFY_CQM);
 
-  static const uint32_t kIllegalMessage;
-
-  const uint8 message_type_;
-  const char *message_type_string_;
+  static uint16_t nl80211_message_type_;
   static std::map<uint16_t, std::string> *reason_code_string_;
   static std::map<uint16_t, std::string> *status_code_string_;
-  uint32_t sequence_number_;
 
   AttributeListRefPtr attributes_;
+  const uint8 command_;
+  const char *command_string_;
+  uint16_t flags_;
+  uint16_t message_type_;
+  uint32_t sequence_number_;
 
   DISALLOW_COPY_AND_ASSIGN(Nl80211Message);
 };