shill: Splits the netlink message factory between message types.

Provides a mechanism to register message factories with
|NetlinkMessageFactory|.  Splits |NetlinkMessageFactory| into a generic
factory and one for |ControlNetlinkMessage| and |Nl80211Message|.  Uses
the registration mechanism to install the control and nl80211 message
factories.

This CL also moves |UnknownMessage| from being a subtype of
|Nl80211Message| to being a subtype of |NetlinkMessage|.

Lucy has some 'splaining to do: At startup time, the message type isn't
known for some types of netlink message (notably, the nl80211 class of
messages).  This message type is discovered, in the code, through the
|Config80211::AddFamilyByString| call.  This CL adds a message factory
closure to that call so that Config80211 can install the factory when
the code knows the message type against which to register the factory.

BUG=chromium:220372
TEST=unittest and manual (restarted shill with log=-10,wifi and observed
messages of all types being created for netlink messages coming from the
kernel).

Change-Id: I496b405c20ef98b6e23e96e8088fc542ff29264c
Reviewed-on: https://gerrit.chromium.org/gerrit/45314
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/Makefile b/Makefile
index 42a57ce..3643a5d 100644
--- a/Makefile
+++ b/Makefile
@@ -424,6 +424,7 @@
 	mock_wimax_provider.o \
 	mock_wimax_service.o \
 	modem_info_unittest.o \
+	netlink_message_unittest.o \
 	netlink_socket_unittest.o \
 	nice_mock_control.o \
 	nss_unittest.o \
diff --git a/config80211.cc b/config80211.cc
index 57daffc..abea104 100644
--- a/config80211.cc
+++ b/config80211.cc
@@ -136,6 +136,11 @@
 }
 
 bool Config80211::Init() {
+  // Install message factory for control class of messages, which has
+  // statically-known message type.
+  message_factory_.AddFactoryMethod(
+      ControlNetlinkMessage::kMessageType,
+      Bind(&ControlNetlinkMessage::CreateMessage));
   if (!sock_) {
     sock_ = new NetlinkSocket;
     if (!sock_) {
@@ -165,7 +170,8 @@
   return (sock_ ? sock_->file_descriptor() : -1);
 }
 
-uint16_t Config80211::GetFamily(string name) {
+uint16_t Config80211::GetFamily(string name,
+    const NetlinkMessageFactory::FactoryMethod &message_factory) {
   MessageType &message_type = message_types_[name];
   if (message_type.family_id != NetlinkMessage::kIllegalMessageType) {
     return message_type.family_id;
@@ -230,6 +236,10 @@
     InputData input_data(received.GetData(), received.GetLength());
     OnRawNlMessageReceived(&input_data);
     if (message_type.family_id != NetlinkMessage::kIllegalMessageType) {
+      uint16_t family_id = message_type.family_id;
+      if (family_id != NetlinkMessage::kIllegalMessageType) {
+        message_factory_.AddFactoryMethod(family_id, message_factory);
+      }
       time->GetTimeMonotonic(&now);
       timersub(&now, &start_time, &wait_duration);
       SLOG(WiFi, 5) << "Found id " << message_type.family_id
@@ -255,7 +265,6 @@
   return family->second.family_id;
 }
 
-
 bool Config80211::AddBroadcastHandler(const NetlinkMessageHandler &handler) {
   list<NetlinkMessageHandler>::iterator i;
   if (FindBroadcastHandler(handler)) {
@@ -400,7 +409,7 @@
     return;
   }
   const uint32 sequence_number = msg->nlmsg_seq;
-  scoped_ptr<NetlinkMessage> message(NetlinkMessageFactory::CreateMessage(msg));
+  scoped_ptr<NetlinkMessage> message(message_factory_.CreateMessage(msg));
   if (message == NULL) {
     SLOG(WiFi, 3) << "NL Message " << sequence_number << " <===";
     SLOG(WiFi, 3) << __func__ << "(msg:NULL)";
diff --git a/config80211.h b/config80211.h
index cb2e9fa..9582f11 100644
--- a/config80211.h
+++ b/config80211.h
@@ -70,6 +70,8 @@
 #include <base/bind.h>
 #include <base/lazy_instance.h>
 
+#include "shill/nl80211_message.h"
+
 struct nlmsghdr;
 
 namespace shill {
@@ -117,12 +119,6 @@
   static const char kEventTypeRegulatory[];
   static const char kEventTypeMlme[];
 
-  // This represents whether the cfg80211/mac80211 are installed in the kernel.
-  enum WifiState {
-    kWifiUp,
-    kWifiDown
-  };
-
   // Config80211 is a singleton and this is the way to access it.
   static Config80211 *GetInstance();
 
@@ -144,7 +140,8 @@
   // |NetlinkMessage::kIllegalMessageType| if the message type could not be
   // determined.  May block so |GetFamily| should be called before entering the
   // event loop.
-  uint16_t GetFamily(std::string family_name);
+  uint16_t GetFamily(std::string family_name,
+      const NetlinkMessageFactory::FactoryMethod &message_factory);
 
   // Retrieves a family id (message type) given the |name| string describing
   // the message family.
@@ -191,10 +188,22 @@
 
  private:
   friend class Config80211Test;
+  friend class NetlinkMessageTest;
   friend class ShillDaemonTest;
   FRIEND_TEST(Config80211Test, AddLinkTest);
   FRIEND_TEST(Config80211Test, BroadcastHandlerTest);
   FRIEND_TEST(Config80211Test, MessageHandlerTest);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_TRIGGER_SCAN);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_SCAN_RESULTS);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_STATION);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_AUTHENTICATE);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_ASSOCIATE);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_CONNECT);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DEAUTHENTICATE);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISCONNECT);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NOTIFY_CQM);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISASSOCIATE);
+
   typedef std::map<uint32_t, NetlinkMessageHandler> MessageHandlers;
 
   static const long kMaximumNewFamilyWaitSeconds;
@@ -240,6 +249,7 @@
 
   NetlinkSocket *sock_;
   std::map<const std::string, MessageType> message_types_;
+  NetlinkMessageFactory message_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Config80211);
 };
diff --git a/config80211_unittest.cc b/config80211_unittest.cc
index 0c8a4ec..d22a735 100644
--- a/config80211_unittest.cc
+++ b/config80211_unittest.cc
@@ -11,25 +11,14 @@
 
 #include "shill/config80211.h"
 
-#include <netlink/netlink.h>
-
-#include <string>
-#include <vector>
-
-#include <base/stl_util.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include "shill/mock_netlink_socket.h"
-#include "shill/netlink_attribute.h"
 #include "shill/nl80211_message.h"
-#include "shill/refptr_types.h"
 
 using base::Bind;
 using base::Unretained;
-using base::WeakPtr;
-using std::string;
-using std::vector;
 using testing::_;
 using testing::Invoke;
 using testing::Return;
@@ -39,13 +28,6 @@
 
 namespace {
 
-// The group_id of the scan and mlme groups are, for the purposes of this
-// test, arbitrary as long as they're different.  The values, here, have been
-// extracted from a specific run on hardware but that was, in reality, just
-// being pedantic.
-const uint32_t kEventTypeScanId = 4;
-const uint32_t kEventTypeMlmeId = 6;
-
 // These data blocks have been collected by shill using Config80211 while,
 // simultaneously (and manually) comparing shill output with that of the 'iw'
 // code from which it was derived.  The test strings represent the raw packet
@@ -54,258 +36,11 @@
 
 // These constants are consistent throughout the packets, below.
 
-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
-
-const uint32_t kScanFrequencyTrigger[] = {
-  2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
-  2452, 2457, 2462, 2467, 2472, 2484, 5180, 5200,
-  5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520,
-  5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680,
-  5700, 5745, 5765, 5785, 5805, 5825
-};
-
-const unsigned char kNL80211_CMD_TRIGGER_SCAN[] = {
-  0x68, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x21, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2d, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x34, 0x01, 0x2c, 0x00,
-  0x08, 0x00, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x01, 0x00, 0x71, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x02, 0x00, 0x76, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x03, 0x00, 0x7b, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x04, 0x00, 0x80, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x05, 0x00, 0x85, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x06, 0x00, 0x8a, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x07, 0x00, 0x8f, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x08, 0x00, 0x94, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x09, 0x00, 0x99, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0a, 0x00, 0x9e, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0b, 0x00, 0xa3, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0c, 0x00, 0xa8, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0d, 0x00, 0xb4, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0e, 0x00, 0x3c, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x0f, 0x00, 0x50, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x10, 0x00, 0x64, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x11, 0x00, 0x78, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x12, 0x00, 0x8c, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x13, 0x00, 0xa0, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x14, 0x00, 0xb4, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x15, 0x00, 0xc8, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x16, 0x00, 0x7c, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x17, 0x00, 0x90, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x18, 0x00, 0xa4, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x19, 0x00, 0xb8, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1a, 0x00, 0xcc, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1b, 0x00, 0xe0, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1c, 0x00, 0xf4, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1d, 0x00, 0x08, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x1e, 0x00, 0x1c, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x1f, 0x00, 0x30, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x20, 0x00, 0x44, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x21, 0x00, 0x71, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x22, 0x00, 0x85, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x23, 0x00, 0x99, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x24, 0x00, 0xad, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x25, 0x00, 0xc1, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-
-// wlan0 (phy #0): scan finished: 2412 2417 2422 2427 2432 2437 2442 2447 2452
-// 2457 2462 2467 2472 2484 5180 5200 5220 5240 5260 5280 5300 5320 5500 5520
-// 5540 5560 5580 5600 5620 5640 5660 5680 5700 5745 5765 5785 5805 5825, ""
-
-const uint32_t kScanFrequencyResults[] = {
-  2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
-  2452, 2457, 2462, 2467, 2472, 2484, 5180, 5200,
-  5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520,
-  5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680,
-  5700, 5745, 5765, 5785, 5805, 5825
-};
-
-const unsigned char kNL80211_CMD_NEW_SCAN_RESULTS[] = {
-  0x68, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x22, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2d, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x34, 0x01, 0x2c, 0x00,
-  0x08, 0x00, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x01, 0x00, 0x71, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x02, 0x00, 0x76, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x03, 0x00, 0x7b, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x04, 0x00, 0x80, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x05, 0x00, 0x85, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x06, 0x00, 0x8a, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x07, 0x00, 0x8f, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x08, 0x00, 0x94, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x09, 0x00, 0x99, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0a, 0x00, 0x9e, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0b, 0x00, 0xa3, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0c, 0x00, 0xa8, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0d, 0x00, 0xb4, 0x09, 0x00, 0x00,
-  0x08, 0x00, 0x0e, 0x00, 0x3c, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x0f, 0x00, 0x50, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x10, 0x00, 0x64, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x11, 0x00, 0x78, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x12, 0x00, 0x8c, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x13, 0x00, 0xa0, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x14, 0x00, 0xb4, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x15, 0x00, 0xc8, 0x14, 0x00, 0x00,
-  0x08, 0x00, 0x16, 0x00, 0x7c, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x17, 0x00, 0x90, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x18, 0x00, 0xa4, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x19, 0x00, 0xb8, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1a, 0x00, 0xcc, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1b, 0x00, 0xe0, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1c, 0x00, 0xf4, 0x15, 0x00, 0x00,
-  0x08, 0x00, 0x1d, 0x00, 0x08, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x1e, 0x00, 0x1c, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x1f, 0x00, 0x30, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x20, 0x00, 0x44, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x21, 0x00, 0x71, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x22, 0x00, 0x85, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x23, 0x00, 0x99, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x24, 0x00, 0xad, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x25, 0x00, 0xc1, 0x16, 0x00, 0x00,
-  0x08, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-
-// wlan0: new station c0:3f:0e:77:e8:7f
-
-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,
-};
-
-
-// wlan0 (phy #0): auth c0:3f:0e:77:e8:7f -> 48:5d:60:77:2d:cf status: 0:
-// Successful [frame: b0 00 3a 01 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f c0
-// 3f 0e 77 e8 7f 30 07 00 00 02 00 00 00]
-
-const unsigned char kAuthenticateFrame[] = {
-  0xb0, 0x00, 0x3a, 0x01, 0x48, 0x5d, 0x60, 0x77,
-  0x2d, 0xcf, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x30, 0x07,
-  0x00, 0x00, 0x02, 0x00, 0x00, 0x00
-};
-
-const unsigned char kNL80211_CMD_AUTHENTICATE[] = {
-  0x48, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x25, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x22, 0x00, 0x33, 0x00,
-  0xb0, 0x00, 0x3a, 0x01, 0x48, 0x5d, 0x60, 0x77,
-  0x2d, 0xcf, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x30, 0x07,
-  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-
-// wlan0 (phy #0): assoc c0:3f:0e:77:e8:7f -> 48:5d:60:77:2d:cf status: 0:
-// Successful [frame: 10 00 3a 01 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f c0 3f 0e
-// 77 e8 7f 40 07 01 04 00 00 01 c0 01 08 82 84 8b 96 0c 12 18 24 32 04 30 48
-// 60 6c]
-
-const unsigned char kAssociateFrame[] = {
-  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
-};
-
-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,
-};
-
-
-// wlan0 (phy #0): connected to c0:3f:0e:77:e8:7f
-
-const uint16_t kExpectedConnectStatus = 0;
-
-const unsigned char kNL80211_CMD_CONNECT[] = {
-  0x4c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x2e, 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,
-  0x06, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x14, 0x00, 0x4e, 0x00, 0x01, 0x08, 0x82, 0x84,
-  0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x32, 0x04,
-  0x30, 0x48, 0x60, 0x6c,
-};
-
-
-// wlan0 (phy #0): deauth c0:3f:0e:77:e8:7f -> ff:ff:ff:ff:ff:ff reason 2:
-// Previous authentication no longer valid [frame: c0 00 00 00 ff ff ff ff
-// ff ff c0 3f 0e 77 e8 7f c0 3f 0e 77 e8 7f c0 0e 02 00]
-
-const unsigned char kDeauthenticateFrame[] = {
-  0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0xc0, 0x0e,
-  0x02, 0x00
-};
-
-const unsigned char kNL80211_CMD_DEAUTHENTICATE[] = {
-  0x44, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x27, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x33, 0x00,
-  0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
-  0xff, 0xff, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0xc0, 0x0e,
-  0x02, 0x00, 0x00, 0x00,
-};
-
 
 // wlan0 (phy #0): disconnected (by AP) reason: 2: Previous authentication no
 // longer valid
 
-const uint16_t kExpectedDisconnectReason = 2;
-
 const unsigned char kNL80211_CMD_DISCONNECT[] = {
   0x30, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -315,47 +50,6 @@
   0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x47, 0x00,
 };
 
-
-// wlan0 (phy #0): connection quality monitor event: peer c0:3f:0e:77:e8:7f
-// didn't ACK 50 packets
-
-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,
-};
-
-
-// wlan0 (phy #0): disassoc 48:5d:60:77:2d:cf -> c0:3f:0e:77:e8:7f reason 3:
-// Deauthenticated because sending station is  [frame: a0 00 00 00 c0 3f 0e
-// 77 e8 7f 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f 00 00 03 00]
-
-const unsigned char kDisassociateFrame[] = {
-  0xa0, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x0e, 0x77,
-  0xe8, 0x7f, 0x48, 0x5d, 0x60, 0x77, 0x2d, 0xcf,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00,
-  0x03, 0x00
-};
-
-const unsigned char kNL80211_CMD_DISASSOCIATE[] = {
-  0x44, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-  0x28, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
-  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
-  0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x33, 0x00,
-  0xa0, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x0e, 0x77,
-  0xe8, 0x7f, 0x48, 0x5d, 0x60, 0x77, 0x2d, 0xcf,
-  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00,
-  0x03, 0x00, 0x00, 0x00,
-};
-
 const char kGetFamilyCommandString[] = "CTRL_CMD_GETFAMILY";
 
 }  // namespace
@@ -369,8 +63,10 @@
   Config80211Test() : config80211_(Config80211::GetInstance()) {
     config80211_->message_types_[Nl80211Message::kMessageTypeString].family_id =
         kNl80211FamilyId;
+    config80211_->message_factory_.AddFactoryMethod(
+        kNl80211FamilyId, Bind(&Nl80211Message::CreateMessage));
     Nl80211Message::SetMessageType(
-        config80211_->GetFamily(Nl80211Message::kMessageTypeString));
+        config80211_->GetMessageType(Nl80211Message::kMessageTypeString));
   }
 
   ~Config80211Test() {
@@ -408,39 +104,11 @@
 // TODO(wdg): Add a test for multi-part messages.  crbug.com/224652
 // TODO(wdg): Add a test for GetFaimily.  crbug.com/224649
 // TODO(wdg): Add a test for OnNewFamilyMessage.  crbug.com/222486
+// TODO(wdg): Add a test for GetMessageType
 // TODO(wdg): Add a test for SubscribeToEvents (verify that it handles bad input
 // appropriately, and that it calls NetlinkSocket::SubscribeToEvents if input
 // is good.)
 
-class TestHandlerObject {
- public:
-  TestHandlerObject() :
-    message_handler_(Bind(&TestHandlerObject::MessageHandler,
-                          Unretained(this))) { }
-  void MessageHandler(const NetlinkMessage &msg) {
-  }
-  const Config80211::NetlinkMessageHandler &message_handler() const {
-    return message_handler_;
-  }
-
- private:
-  Config80211::NetlinkMessageHandler message_handler_;
-};
-
-// Checks a config80211 parameter to make sure it contains |handler_arg|
-// in its list of broadcast handlers.
-MATCHER_P(ContainsHandler, handler_arg, "") {
-  if (arg == reinterpret_cast<void *>(NULL)) {
-    LOG(WARNING) << "NULL parameter";
-    return false;
-  }
-  const Config80211 *config80211 = static_cast<Config80211 *>(arg);
-  const Config80211::NetlinkMessageHandler message_handler =
-      static_cast<const Config80211::NetlinkMessageHandler>(handler_arg);
-
-  return config80211->FindBroadcastHandler(message_handler);
-}
-
 TEST_F(Config80211Test, BroadcastHandlerTest) {
   SetupConfig80211Object();
 
@@ -452,7 +120,11 @@
 
   // Simple, 1 handler, case.
   EXPECT_CALL(handler1, OnNetlinkMessage(_)).Times(1);
+  EXPECT_FALSE(
+      config80211_->FindBroadcastHandler(handler1.on_netlink_message()));
   config80211_->AddBroadcastHandler(handler1.on_netlink_message());
+  EXPECT_TRUE(
+      config80211_->FindBroadcastHandler(handler1.on_netlink_message()));
   config80211_->OnNlMessageReceived(message);
 
   // Add a second handler.
@@ -552,469 +224,4 @@
   config80211_->OnNlMessageReceived(received_message);
 }
 
-TEST_F(Config80211Test, Parse_NL80211_CMD_TRIGGER_SCAN) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_TRIGGER_SCAN)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-
-  EXPECT_EQ(NL80211_CMD_TRIGGER_SCAN, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  // Make sure the scan frequencies in the attribute are the ones we expect.
-  {
-    vector<uint32_t>list;
-    EXPECT_TRUE(message->GetScanFrequenciesAttribute(
-        NL80211_ATTR_SCAN_FREQUENCIES, &list));
-    EXPECT_EQ(list.size(), arraysize(kScanFrequencyTrigger));
-    int i = 0;
-    vector<uint32_t>::const_iterator j = list.begin();
-    while (j != list.end()) {
-      EXPECT_EQ(kScanFrequencyTrigger[i], *j);
-      ++i;
-      ++j;
-    }
-  }
-
-  {
-    vector<string> ssids;
-    EXPECT_TRUE(message->GetScanSsidsAttribute(NL80211_ATTR_SCAN_SSIDS,
-                                               &ssids));
-    EXPECT_EQ(1, ssids.size());
-    EXPECT_EQ(0, ssids[0].compare(""));  // Expect a single, empty SSID.
-  }
-
-  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
-      NL80211_ATTR_SUPPORT_MESH_AUTH));
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_NEW_SCAN_RESULTS) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_SCAN_RESULTS)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-
-  EXPECT_EQ(NL80211_CMD_NEW_SCAN_RESULTS, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  // Make sure the scan frequencies in the attribute are the ones we expect.
-  {
-    vector<uint32_t>list;
-    EXPECT_TRUE(message->GetScanFrequenciesAttribute(
-        NL80211_ATTR_SCAN_FREQUENCIES, &list));
-    EXPECT_EQ(arraysize(kScanFrequencyResults), list.size());
-    int i = 0;
-    vector<uint32_t>::const_iterator j = list.begin();
-    while (j != list.end()) {
-      EXPECT_EQ(kScanFrequencyResults[i], *j);
-      ++i;
-      ++j;
-    }
-  }
-
-  {
-    vector<string> ssids;
-    EXPECT_TRUE(message->GetScanSsidsAttribute(NL80211_ATTR_SCAN_SSIDS,
-                                               &ssids));
-    EXPECT_EQ(1, ssids.size());
-    EXPECT_EQ(0, ssids[0].compare(""));  // Expect a single, empty SSID.
-  }
-
-  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
-      NL80211_ATTR_SUPPORT_MESH_AUTH));
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_NEW_STATION) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_STATION)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_NEW_STATION, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    string value;
-    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
-    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
-  }
-
-  {
-    AttributeListConstRefPtr nested;
-    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
-        NL80211_ATTR_STA_INFO, &nested));
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_GENERATION, &value));
-    EXPECT_EQ(kNewStationExpectedGeneration, value);
-  }
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_AUTHENTICATE) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_AUTHENTICATE)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_AUTHENTICATE, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    ByteString rawdata;
-    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
-        NL80211_ATTR_FRAME, &rawdata));
-    EXPECT_FALSE(rawdata.IsEmpty());
-    Nl80211Frame frame(rawdata);
-    Nl80211Frame expected_frame(ByteString(kAuthenticateFrame,
-                                           sizeof(kAuthenticateFrame)));
-    EXPECT_TRUE(frame.IsEqual(expected_frame));
-  }
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_ASSOCIATE) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_ASSOCIATE)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_ASSOCIATE, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    ByteString rawdata;
-    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
-        NL80211_ATTR_FRAME, &rawdata));
-    EXPECT_FALSE(rawdata.IsEmpty());
-    Nl80211Frame frame(rawdata);
-    Nl80211Frame expected_frame(ByteString(kAssociateFrame,
-                                           sizeof(kAssociateFrame)));
-    EXPECT_TRUE(frame.IsEqual(expected_frame));
-  }
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_CONNECT) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_CONNECT)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_CONNECT, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    string value;
-    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
-    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
-  }
-
-  {
-    uint16_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
-        NL80211_ATTR_STATUS_CODE, &value));
-    EXPECT_EQ(kExpectedConnectStatus, value);
-  }
-
-  // TODO(wdg): Need to check the value of this attribute.
-  {
-    ByteString rawdata;
-    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
-        NL80211_ATTR_RESP_IE, &rawdata));
-  }
-}
-
-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.
-
-  ByteString message_bytes = message.Encode(config80211_->GetSequenceNumber());
-  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) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DEAUTHENTICATE)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_DEAUTHENTICATE, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    ByteString rawdata;
-    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
-        NL80211_ATTR_FRAME, &rawdata));
-    EXPECT_FALSE(rawdata.IsEmpty());
-    Nl80211Frame frame(rawdata);
-    Nl80211Frame expected_frame(ByteString(kDeauthenticateFrame,
-                                           sizeof(kDeauthenticateFrame)));
-    EXPECT_TRUE(frame.IsEqual(expected_frame));
-  }
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_DISCONNECT) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISCONNECT)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_DISCONNECT, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    uint16_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
-        NL80211_ATTR_REASON_CODE, &value));
-    EXPECT_EQ(kExpectedDisconnectReason, value);
-  }
-
-  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
-      NL80211_ATTR_DISCONNECTED_BY_AP));
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_NOTIFY_CQM) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NOTIFY_CQM)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_NOTIFY_CQM, message->command());
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    string value;
-    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
-    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
-  }
-
-  {
-    AttributeListConstRefPtr nested;
-    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
-        NL80211_ATTR_CQM, &nested));
-    uint32_t threshold_event;
-    EXPECT_FALSE(nested->GetU32AttributeValue(
-        NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, &threshold_event));
-    uint32_t pkt_loss_event;
-    EXPECT_TRUE(nested->GetU32AttributeValue(
-        NL80211_ATTR_CQM_PKT_LOSS_EVENT, &pkt_loss_event));
-    EXPECT_EQ(kExpectedCqmNotAcked, pkt_loss_event);
-  }
-}
-
-TEST_F(Config80211Test, Parse_NL80211_CMD_DISASSOCIATE) {
-  NetlinkMessage *netlink_message = NetlinkMessageFactory::CreateMessage(
-      const_cast<nlmsghdr *>(
-        reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISASSOCIATE)));
-
-  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
-  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
-  // The follwing is legal if the message_type is kNl80211FamilyId.
-  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
-  EXPECT_EQ(NL80211_CMD_DISASSOCIATE, message->command());
-
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_WIPHY, &value));
-    EXPECT_EQ(kWiPhy, value);
-  }
-
-  {
-    uint32_t value;
-    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
-        NL80211_ATTR_IFINDEX, &value));
-    EXPECT_EQ(kExpectedIfIndex, value);
-  }
-
-  {
-    ByteString rawdata;
-    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
-        NL80211_ATTR_FRAME, &rawdata));
-    EXPECT_FALSE(rawdata.IsEmpty());
-    Nl80211Frame frame(rawdata);
-    Nl80211Frame expected_frame(ByteString(kDisassociateFrame,
-                                           sizeof(kDisassociateFrame)));
-    EXPECT_TRUE(frame.IsEqual(expected_frame));
-  }
-}
-
 }  // namespace shill
diff --git a/netlink_message_unittest.cc b/netlink_message_unittest.cc
new file mode 100644
index 0000000..5eebade
--- /dev/null
+++ b/netlink_message_unittest.cc
@@ -0,0 +1,826 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides tests for individual messages.  It tests
+// NetlinkMessageFactory's ability to create specific message types and it
+// tests the various NetlinkMessage types' ability to parse those
+// messages.
+
+// This file tests the public interface to NetlinkMessage.
+
+#include "shill/nl80211_message.h"
+
+#include <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "shill/mock_netlink_socket.h"
+#include "shill/netlink_attribute.h"
+#include "shill/refptr_types.h"
+
+using base::Bind;
+using base::Unretained;
+using std::string;
+using std::vector;
+using testing::_;
+using testing::Invoke;
+using testing::Return;
+using testing::Test;
+
+namespace shill {
+
+namespace {
+
+// These data blocks have been collected by shill using Config80211 while,
+// simultaneously (and manually) comparing shill output with that of the 'iw'
+// code from which it was derived.  The test strings represent the raw packet
+// data coming from the kernel.  The comments above each of these strings is
+// the markup that "iw" outputs for ech of these packets.
+
+// These constants are consistent throughout the packets, below.
+
+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
+
+const uint32_t kScanFrequencyTrigger[] = {
+  2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
+  2452, 2457, 2462, 2467, 2472, 2484, 5180, 5200,
+  5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520,
+  5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680,
+  5700, 5745, 5765, 5785, 5805, 5825
+};
+
+const unsigned char kNL80211_CMD_TRIGGER_SCAN[] = {
+  0x68, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x21, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2d, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x34, 0x01, 0x2c, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x01, 0x00, 0x71, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x02, 0x00, 0x76, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x03, 0x00, 0x7b, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x04, 0x00, 0x80, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x05, 0x00, 0x85, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x06, 0x00, 0x8a, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x07, 0x00, 0x8f, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x08, 0x00, 0x94, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x09, 0x00, 0x99, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0a, 0x00, 0x9e, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0b, 0x00, 0xa3, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0c, 0x00, 0xa8, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0d, 0x00, 0xb4, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0e, 0x00, 0x3c, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x0f, 0x00, 0x50, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x10, 0x00, 0x64, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x11, 0x00, 0x78, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x12, 0x00, 0x8c, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x13, 0x00, 0xa0, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x14, 0x00, 0xb4, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x15, 0x00, 0xc8, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x16, 0x00, 0x7c, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x17, 0x00, 0x90, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x18, 0x00, 0xa4, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x19, 0x00, 0xb8, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1a, 0x00, 0xcc, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1b, 0x00, 0xe0, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1c, 0x00, 0xf4, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1d, 0x00, 0x08, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x1e, 0x00, 0x1c, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x1f, 0x00, 0x30, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x20, 0x00, 0x44, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x21, 0x00, 0x71, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x22, 0x00, 0x85, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x23, 0x00, 0x99, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x24, 0x00, 0xad, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x25, 0x00, 0xc1, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+// wlan0 (phy #0): scan finished: 2412 2417 2422 2427 2432 2437 2442 2447 2452
+// 2457 2462 2467 2472 2484 5180 5200 5220 5240 5260 5280 5300 5320 5500 5520
+// 5540 5560 5580 5600 5620 5640 5660 5680 5700 5745 5765 5785 5805 5825, ""
+
+const uint32_t kScanFrequencyResults[] = {
+  2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447,
+  2452, 2457, 2462, 2467, 2472, 2484, 5180, 5200,
+  5220, 5240, 5260, 5280, 5300, 5320, 5500, 5520,
+  5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680,
+  5700, 5745, 5765, 5785, 5805, 5825
+};
+
+const unsigned char kNL80211_CMD_NEW_SCAN_RESULTS[] = {
+  0x68, 0x01, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x22, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x2d, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x34, 0x01, 0x2c, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x6c, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x01, 0x00, 0x71, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x02, 0x00, 0x76, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x03, 0x00, 0x7b, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x04, 0x00, 0x80, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x05, 0x00, 0x85, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x06, 0x00, 0x8a, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x07, 0x00, 0x8f, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x08, 0x00, 0x94, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x09, 0x00, 0x99, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0a, 0x00, 0x9e, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0b, 0x00, 0xa3, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0c, 0x00, 0xa8, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0d, 0x00, 0xb4, 0x09, 0x00, 0x00,
+  0x08, 0x00, 0x0e, 0x00, 0x3c, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x0f, 0x00, 0x50, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x10, 0x00, 0x64, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x11, 0x00, 0x78, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x12, 0x00, 0x8c, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x13, 0x00, 0xa0, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x14, 0x00, 0xb4, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x15, 0x00, 0xc8, 0x14, 0x00, 0x00,
+  0x08, 0x00, 0x16, 0x00, 0x7c, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x17, 0x00, 0x90, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x18, 0x00, 0xa4, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x19, 0x00, 0xb8, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1a, 0x00, 0xcc, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1b, 0x00, 0xe0, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1c, 0x00, 0xf4, 0x15, 0x00, 0x00,
+  0x08, 0x00, 0x1d, 0x00, 0x08, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x1e, 0x00, 0x1c, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x1f, 0x00, 0x30, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x20, 0x00, 0x44, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x21, 0x00, 0x71, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x22, 0x00, 0x85, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x23, 0x00, 0x99, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x24, 0x00, 0xad, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x25, 0x00, 0xc1, 0x16, 0x00, 0x00,
+  0x08, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+// wlan0: new station c0:3f:0e:77:e8:7f
+
+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,
+};
+
+
+// wlan0 (phy #0): auth c0:3f:0e:77:e8:7f -> 48:5d:60:77:2d:cf status: 0:
+// Successful [frame: b0 00 3a 01 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f c0
+// 3f 0e 77 e8 7f 30 07 00 00 02 00 00 00]
+
+const unsigned char kAuthenticateFrame[] = {
+  0xb0, 0x00, 0x3a, 0x01, 0x48, 0x5d, 0x60, 0x77,
+  0x2d, 0xcf, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x30, 0x07,
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00
+};
+
+const unsigned char kNL80211_CMD_AUTHENTICATE[] = {
+  0x48, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x25, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x22, 0x00, 0x33, 0x00,
+  0xb0, 0x00, 0x3a, 0x01, 0x48, 0x5d, 0x60, 0x77,
+  0x2d, 0xcf, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x30, 0x07,
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+// wlan0 (phy #0): assoc c0:3f:0e:77:e8:7f -> 48:5d:60:77:2d:cf status: 0:
+// Successful [frame: 10 00 3a 01 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f c0 3f 0e
+// 77 e8 7f 40 07 01 04 00 00 01 c0 01 08 82 84 8b 96 0c 12 18 24 32 04 30 48
+// 60 6c]
+
+const unsigned char kAssociateFrame[] = {
+  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
+};
+
+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,
+};
+
+
+// wlan0 (phy #0): connected to c0:3f:0e:77:e8:7f
+
+const uint16_t kExpectedConnectStatus = 0;
+
+const unsigned char kNL80211_CMD_CONNECT[] = {
+  0x4c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2e, 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,
+  0x06, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x14, 0x00, 0x4e, 0x00, 0x01, 0x08, 0x82, 0x84,
+  0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x32, 0x04,
+  0x30, 0x48, 0x60, 0x6c,
+};
+
+
+// wlan0 (phy #0): deauth c0:3f:0e:77:e8:7f -> ff:ff:ff:ff:ff:ff reason 2:
+// Previous authentication no longer valid [frame: c0 00 00 00 ff ff ff ff
+// ff ff c0 3f 0e 77 e8 7f c0 3f 0e 77 e8 7f c0 0e 02 00]
+
+const unsigned char kDeauthenticateFrame[] = {
+  0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0xc0, 0x0e,
+  0x02, 0x00
+};
+
+const unsigned char kNL80211_CMD_DEAUTHENTICATE[] = {
+  0x44, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x27, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x33, 0x00,
+  0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0xc0, 0x0e,
+  0x02, 0x00, 0x00, 0x00,
+};
+
+
+// wlan0 (phy #0): disconnected (by AP) reason: 2: Previous authentication no
+// longer valid
+
+const uint16_t kExpectedDisconnectReason = 2;
+
+const unsigned char kNL80211_CMD_DISCONNECT[] = {
+  0x30, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x30, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x36, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x47, 0x00,
+};
+
+
+// wlan0 (phy #0): connection quality monitor event: peer c0:3f:0e:77:e8:7f
+// didn't ACK 50 packets
+
+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,
+};
+
+
+// wlan0 (phy #0): disassoc 48:5d:60:77:2d:cf -> c0:3f:0e:77:e8:7f reason 3:
+// Deauthenticated because sending station is  [frame: a0 00 00 00 c0 3f 0e
+// 77 e8 7f 48 5d 60 77 2d cf c0 3f 0e 77 e8 7f 00 00 03 00]
+
+const unsigned char kDisassociateFrame[] = {
+  0xa0, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x0e, 0x77,
+  0xe8, 0x7f, 0x48, 0x5d, 0x60, 0x77, 0x2d, 0xcf,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00,
+  0x03, 0x00
+};
+
+const unsigned char kNL80211_CMD_DISASSOCIATE[] = {
+  0x44, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x28, 0x01, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x33, 0x00,
+  0xa0, 0x00, 0x00, 0x00, 0xc0, 0x3f, 0x0e, 0x77,
+  0xe8, 0x7f, 0x48, 0x5d, 0x60, 0x77, 0x2d, 0xcf,
+  0xc0, 0x3f, 0x0e, 0x77, 0xe8, 0x7f, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00,
+};
+
+const char kGetFamilyCommandString[] = "CTRL_CMD_GETFAMILY";
+
+}  // namespace
+
+class NetlinkMessageTest : public Test {
+ public:
+  NetlinkMessageTest() {
+    message_factory_.AddFactoryMethod(
+        kNl80211FamilyId, Bind(&Nl80211Message::CreateMessage));
+    Nl80211Message::SetMessageType(kNl80211FamilyId);
+  }
+
+ protected:
+  NetlinkMessageFactory message_factory_;
+};
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_TRIGGER_SCAN) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_TRIGGER_SCAN)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+
+  EXPECT_EQ(NL80211_CMD_TRIGGER_SCAN, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  // Make sure the scan frequencies in the attribute are the ones we expect.
+  {
+    vector<uint32_t>list;
+    EXPECT_TRUE(message->GetScanFrequenciesAttribute(
+        NL80211_ATTR_SCAN_FREQUENCIES, &list));
+    EXPECT_EQ(list.size(), arraysize(kScanFrequencyTrigger));
+    int i = 0;
+    vector<uint32_t>::const_iterator j = list.begin();
+    while (j != list.end()) {
+      EXPECT_EQ(kScanFrequencyTrigger[i], *j);
+      ++i;
+      ++j;
+    }
+  }
+
+  {
+    vector<string> ssids;
+    EXPECT_TRUE(message->GetScanSsidsAttribute(NL80211_ATTR_SCAN_SSIDS,
+                                               &ssids));
+    EXPECT_EQ(1, ssids.size());
+    EXPECT_EQ(0, ssids[0].compare(""));  // Expect a single, empty SSID.
+  }
+
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
+      NL80211_ATTR_SUPPORT_MESH_AUTH));
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_NEW_SCAN_RESULTS) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_SCAN_RESULTS)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+
+  EXPECT_EQ(NL80211_CMD_NEW_SCAN_RESULTS, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  // Make sure the scan frequencies in the attribute are the ones we expect.
+  {
+    vector<uint32_t>list;
+    EXPECT_TRUE(message->GetScanFrequenciesAttribute(
+        NL80211_ATTR_SCAN_FREQUENCIES, &list));
+    EXPECT_EQ(arraysize(kScanFrequencyResults), list.size());
+    int i = 0;
+    vector<uint32_t>::const_iterator j = list.begin();
+    while (j != list.end()) {
+      EXPECT_EQ(kScanFrequencyResults[i], *j);
+      ++i;
+      ++j;
+    }
+  }
+
+  {
+    vector<string> ssids;
+    EXPECT_TRUE(message->GetScanSsidsAttribute(NL80211_ATTR_SCAN_SSIDS,
+                                               &ssids));
+    EXPECT_EQ(1, ssids.size());
+    EXPECT_EQ(0, ssids[0].compare(""));  // Expect a single, empty SSID.
+  }
+
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
+      NL80211_ATTR_SUPPORT_MESH_AUTH));
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_NEW_STATION) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NEW_STATION)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_NEW_STATION, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    string value;
+    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
+    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
+  }
+
+  {
+    AttributeListConstRefPtr nested;
+    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
+        NL80211_ATTR_STA_INFO, &nested));
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_GENERATION, &value));
+    EXPECT_EQ(kNewStationExpectedGeneration, value);
+  }
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_AUTHENTICATE) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_AUTHENTICATE)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_AUTHENTICATE, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_FALSE(rawdata.IsEmpty());
+    Nl80211Frame frame(rawdata);
+    Nl80211Frame expected_frame(ByteString(kAuthenticateFrame,
+                                           sizeof(kAuthenticateFrame)));
+    EXPECT_TRUE(frame.IsEqual(expected_frame));
+  }
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_ASSOCIATE) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_ASSOCIATE)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_ASSOCIATE, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_FALSE(rawdata.IsEmpty());
+    Nl80211Frame frame(rawdata);
+    Nl80211Frame expected_frame(ByteString(kAssociateFrame,
+                                           sizeof(kAssociateFrame)));
+    EXPECT_TRUE(frame.IsEqual(expected_frame));
+  }
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_CONNECT) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_CONNECT)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_CONNECT, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    string value;
+    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
+    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
+  }
+
+  {
+    uint16_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
+        NL80211_ATTR_STATUS_CODE, &value));
+    EXPECT_EQ(kExpectedConnectStatus, value);
+  }
+
+  // TODO(wdg): Need to check the value of this attribute.
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_RESP_IE, &rawdata));
+  }
+}
+
+TEST_F(NetlinkMessageTest, Build_NL80211_CMD_CONNECT) {
+  // 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.
+  static const uint32_t kArbitrarySequenceNumber = 42;
+  ByteString message_bytes = message.Encode(kArbitrarySequenceNumber);
+  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(NetlinkMessageTest, Parse_NL80211_CMD_DEAUTHENTICATE) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DEAUTHENTICATE)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_DEAUTHENTICATE, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_FALSE(rawdata.IsEmpty());
+    Nl80211Frame frame(rawdata);
+    Nl80211Frame expected_frame(ByteString(kDeauthenticateFrame,
+                                           sizeof(kDeauthenticateFrame)));
+    EXPECT_TRUE(frame.IsEqual(expected_frame));
+  }
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_DISCONNECT) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISCONNECT)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_DISCONNECT, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    uint16_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
+        NL80211_ATTR_REASON_CODE, &value));
+    EXPECT_EQ(kExpectedDisconnectReason, value);
+  }
+
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
+      NL80211_ATTR_DISCONNECTED_BY_AP));
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_NOTIFY_CQM) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_NOTIFY_CQM)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_NOTIFY_CQM, message->command());
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    string value;
+    EXPECT_TRUE(message->GetMacAttributeString(NL80211_ATTR_MAC, &value));
+    EXPECT_EQ(0, strncmp(value.c_str(), kExpectedMacAddress, value.length()));
+  }
+
+  {
+    AttributeListConstRefPtr nested;
+    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
+        NL80211_ATTR_CQM, &nested));
+    uint32_t threshold_event;
+    EXPECT_FALSE(nested->GetU32AttributeValue(
+        NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, &threshold_event));
+    uint32_t pkt_loss_event;
+    EXPECT_TRUE(nested->GetU32AttributeValue(
+        NL80211_ATTR_CQM_PKT_LOSS_EVENT, &pkt_loss_event));
+    EXPECT_EQ(kExpectedCqmNotAcked, pkt_loss_event);
+  }
+}
+
+TEST_F(NetlinkMessageTest, Parse_NL80211_CMD_DISASSOCIATE) {
+  NetlinkMessage *netlink_message =
+      message_factory_.CreateMessage(const_cast<nlmsghdr *>(
+          reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISASSOCIATE)));
+
+  EXPECT_NE(reinterpret_cast<NetlinkMessage *>(NULL), netlink_message);
+  EXPECT_EQ(kNl80211FamilyId, netlink_message->message_type());
+  // The follwing is legal if the message_type is kNl80211FamilyId.
+  Nl80211Message *message = reinterpret_cast<Nl80211Message *>(netlink_message);
+  EXPECT_EQ(NL80211_CMD_DISASSOCIATE, message->command());
+
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
+    EXPECT_EQ(kWiPhy, value);
+  }
+
+  {
+    uint32_t value;
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
+    EXPECT_EQ(kExpectedIfIndex, value);
+  }
+
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_FALSE(rawdata.IsEmpty());
+    Nl80211Frame frame(rawdata);
+    Nl80211Frame expected_frame(ByteString(kDisassociateFrame,
+                                           sizeof(kDisassociateFrame)));
+    EXPECT_TRUE(frame.IsEqual(expected_frame));
+  }
+}
+
+}  // namespace shill
diff --git a/nl80211_message.cc b/nl80211_message.cc
index d990a76..c0c2400 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -36,6 +36,7 @@
 
 #include <base/bind.h>
 #include <base/format_macros.h>
+#include <base/stl_util.h>
 #include <base/stringprintf.h>
 
 #include "shill/attribute_list.h"
@@ -186,6 +187,8 @@
   }
 }
 
+// ErrorAckMessage.
+
 const uint16_t ErrorAckMessage::kMessageType = NLMSG_ERROR;
 
 bool ErrorAckMessage::InitFromNlmsg(const nlmsghdr *const_msg) {
@@ -224,6 +227,8 @@
   SLOG(WiFi, log_level) << ToString();
 }
 
+// NoopMessage.
+
 const uint16_t NoopMessage::kMessageType = NLMSG_NOOP;
 
 ByteString NoopMessage::Encode(uint32_t sequence_number) {
@@ -235,6 +240,8 @@
   SLOG(WiFi, log_level) << ToString();
 }
 
+// DoneMessage.
+
 const uint16_t DoneMessage::kMessageType = NLMSG_DONE;
 
 ByteString DoneMessage::Encode(uint32_t sequence_number) {
@@ -247,6 +254,8 @@
   SLOG(WiFi, log_level) << ToString();
 }
 
+// OverrunMessage.
+
 const uint16_t OverrunMessage::kMessageType = NLMSG_OVERRUN;
 
 ByteString OverrunMessage::Encode(uint32_t sequence_number) {
@@ -258,6 +267,24 @@
   SLOG(WiFi, log_level) << ToString();
 }
 
+// UnknownMessage.
+
+ByteString UnknownMessage::Encode(uint32_t sequence_number) {
+  LOG(ERROR) << "We're not supposed to send UNKNOWN messages to the kernel";
+  return ByteString();
+}
+
+void UnknownMessage::Print(int log_level) const {
+  int total_bytes = message_body_.GetLength();
+  const uint8_t *const_data = message_body_.GetConstData();
+
+  string output = StringPrintf("%d bytes:", total_bytes);
+  for (int i = 0; i < total_bytes; ++i) {
+    StringAppendF(&output, " 0x%02x", const_data[i]);
+  }
+  SLOG(WiFi, log_level) << output;
+}
+
 // GenericNetlinkMessage
 
 ByteString GenericNetlinkMessage::EncodeHeader(uint32_t sequence_number) {
@@ -731,6 +758,31 @@
 const uint8_t GetFamilyMessage::kCommand = CTRL_CMD_GETFAMILY;
 const char GetFamilyMessage::kCommandString[] = "CTRL_CMD_GETFAMILY";
 
+// static
+NetlinkMessage *ControlNetlinkMessage::CreateMessage(
+    const nlmsghdr *const_msg) {
+  if (!const_msg) {
+    LOG(ERROR) << "NULL |const_msg| parameter";
+    return NULL;
+  }
+  // Casting away constness since, while nlmsg_data doesn't change its
+  // parameter, it also doesn't declare its paramenter as const.
+  nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
+  void *payload = nlmsg_data(msg);
+  genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
+
+  switch (gnlh->cmd) {
+    case NewFamilyMessage::kCommand:
+      return new NewFamilyMessage();
+    case GetFamilyMessage::kCommand:
+      return new GetFamilyMessage();
+    default:
+      LOG(WARNING) << "Unknown/unhandled netlink control message " << gnlh->cmd;
+      break;
+  }
+  return NULL;
+}
+
 // Nl80211Frame
 
 Nl80211Frame::Nl80211Frame(const ByteString &raw_frame)
@@ -831,7 +883,6 @@
   return frame_.Equals(other.frame_);
 }
 
-
 //
 // Specific Nl80211Message types.
 //
@@ -918,9 +969,6 @@
 const uint8_t TriggerScanMessage::kCommand = NL80211_CMD_TRIGGER_SCAN;
 const char TriggerScanMessage::kCommandString[] = "NL80211_CMD_TRIGGER_SCAN";
 
-const uint8_t UnknownMessage::kCommand = 0xff;
-const char UnknownMessage::kCommandString[] = "<Unknown Message Type>";
-
 const uint8_t UnprotDeauthenticateMessage::kCommand =
     NL80211_CMD_UNPROT_DEAUTHENTICATE;
 const char UnprotDeauthenticateMessage::kCommandString[] =
@@ -931,102 +979,131 @@
 const char UnprotDisassociateMessage::kCommandString[] =
     "NL80211_CMD_UNPROT_DISASSOCIATE";
 
+// static
+NetlinkMessage *Nl80211Message::CreateMessage(const nlmsghdr *const_msg) {
+  if (!const_msg) {
+    LOG(ERROR) << "NULL |const_msg| parameter";
+    return NULL;
+  }
+  // Casting away constness since, while nlmsg_data doesn't change its
+  // parameter, it also doesn't declare its paramenter as const.
+  nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
+  void *payload = nlmsg_data(msg);
+  genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
+  scoped_ptr<NetlinkMessage> message;
+
+  switch (gnlh->cmd) {
+    case AssociateMessage::kCommand:
+      return new AssociateMessage();
+    case AuthenticateMessage::kCommand:
+      return new AuthenticateMessage();
+    case CancelRemainOnChannelMessage::kCommand:
+      return new CancelRemainOnChannelMessage();
+    case ConnectMessage::kCommand:
+      return new ConnectMessage();
+    case DeauthenticateMessage::kCommand:
+      return new DeauthenticateMessage();
+    case DeleteStationMessage::kCommand:
+      return new DeleteStationMessage();
+    case DisassociateMessage::kCommand:
+      return new DisassociateMessage();
+    case DisconnectMessage::kCommand:
+      return new DisconnectMessage();
+    case FrameTxStatusMessage::kCommand:
+      return new FrameTxStatusMessage();
+    case GetRegMessage::kCommand:
+      return new GetRegMessage();
+    case JoinIbssMessage::kCommand:
+      return new JoinIbssMessage();
+    case MichaelMicFailureMessage::kCommand:
+      return new MichaelMicFailureMessage();
+    case NewScanResultsMessage::kCommand:
+      return new NewScanResultsMessage();
+    case NewStationMessage::kCommand:
+      return new NewStationMessage();
+    case NewWifiMessage::kCommand:
+      return new NewWifiMessage();
+    case NotifyCqmMessage::kCommand:
+      return new NotifyCqmMessage();
+    case PmksaCandidateMessage::kCommand:
+      return new PmksaCandidateMessage();
+    case RegBeaconHintMessage::kCommand:
+      return new RegBeaconHintMessage();
+    case RegChangeMessage::kCommand:
+      return new RegChangeMessage();
+    case RemainOnChannelMessage::kCommand:
+      return new RemainOnChannelMessage();
+    case RoamMessage::kCommand:
+      return new RoamMessage();
+    case ScanAbortedMessage::kCommand:
+      return new ScanAbortedMessage();
+    case TriggerScanMessage::kCommand:
+      return new TriggerScanMessage();
+    case UnprotDeauthenticateMessage::kCommand:
+      return new UnprotDeauthenticateMessage();
+    case UnprotDisassociateMessage::kCommand:
+      return new UnprotDisassociateMessage();
+    default:
+      LOG(WARNING) << "Unknown/unhandled netlink nl80211 message " << gnlh->cmd;
+      break;
+  }
+  return NULL;
+}
+
 //
 // Factory class.
 //
 
-// TODO(wdg): later, each message_type should register its own callback for
-// creating messages.  For now, however, this is much easier.
-NetlinkMessage *NetlinkMessageFactory::CreateMessage(nlmsghdr *msg) {
-  if (!msg) {
-    LOG(ERROR) << "NULL |msg| parameter";
+bool NetlinkMessageFactory::AddFactoryMethod(uint16_t message_type,
+                                             FactoryMethod factory) {
+  if (ContainsKey(factories_, message_type)) {
+    LOG(WARNING) << "Message type " << message_type << " already exists.";
+    return false;
+  }
+  if (message_type == NetlinkMessage::kIllegalMessageType) {
+    LOG(ERROR) << "Not installing factory for illegal message type.";
+    return false;
+  }
+  factories_[message_type] = factory;
+  return true;
+}
+
+NetlinkMessage *NetlinkMessageFactory::CreateMessage(
+    const nlmsghdr *const_msg) const {
+  if (!const_msg) {
+    LOG(ERROR) << "NULL |const_msg| parameter";
     return NULL;
   }
 
   scoped_ptr<NetlinkMessage> message;
-  void *payload = nlmsg_data(msg);
 
-  if (msg->nlmsg_type == NoopMessage::kMessageType) {
+  if (const_msg->nlmsg_type == NoopMessage::kMessageType) {
     message.reset(new NoopMessage());
-  } else if (msg->nlmsg_type == DoneMessage::kMessageType) {
+  } else if (const_msg->nlmsg_type == DoneMessage::kMessageType) {
     message.reset(new DoneMessage());
-  } else if (msg->nlmsg_type == OverrunMessage::kMessageType) {
+  } else if (const_msg->nlmsg_type == OverrunMessage::kMessageType) {
     message.reset(new OverrunMessage());
-  } else if (msg->nlmsg_type == ErrorAckMessage::kMessageType) {
+  } else if (const_msg->nlmsg_type == ErrorAckMessage::kMessageType) {
     message.reset(new ErrorAckMessage());
-  } else if (msg->nlmsg_type == ControlNetlinkMessage::kMessageType) {
-    genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
-
-    switch (gnlh->cmd) {
-      case NewFamilyMessage::kCommand:
-        message.reset(new NewFamilyMessage()); break;
-      case GetFamilyMessage::kCommand:
-        message.reset(new GetFamilyMessage()); break;
-
-      default:
-        message.reset(new UnknownMessage(gnlh->cmd)); break;
-    }
-  } else {
-    genlmsghdr *gnlh = reinterpret_cast<genlmsghdr *>(payload);
-
-    switch (gnlh->cmd) {
-      case AssociateMessage::kCommand:
-        message.reset(new AssociateMessage()); break;
-      case AuthenticateMessage::kCommand:
-        message.reset(new AuthenticateMessage()); break;
-      case CancelRemainOnChannelMessage::kCommand:
-        message.reset(new CancelRemainOnChannelMessage()); break;
-      case ConnectMessage::kCommand:
-        message.reset(new ConnectMessage()); break;
-      case DeauthenticateMessage::kCommand:
-        message.reset(new DeauthenticateMessage()); break;
-      case DeleteStationMessage::kCommand:
-        message.reset(new DeleteStationMessage()); break;
-      case DisassociateMessage::kCommand:
-        message.reset(new DisassociateMessage()); break;
-      case DisconnectMessage::kCommand:
-        message.reset(new DisconnectMessage()); break;
-      case FrameTxStatusMessage::kCommand:
-        message.reset(new FrameTxStatusMessage()); break;
-      case GetRegMessage::kCommand:
-        message.reset(new GetRegMessage()); break;
-      case JoinIbssMessage::kCommand:
-        message.reset(new JoinIbssMessage()); break;
-      case MichaelMicFailureMessage::kCommand:
-        message.reset(new MichaelMicFailureMessage()); break;
-      case NewScanResultsMessage::kCommand:
-        message.reset(new NewScanResultsMessage()); break;
-      case NewStationMessage::kCommand:
-        message.reset(new NewStationMessage()); break;
-      case NewWifiMessage::kCommand:
-        message.reset(new NewWifiMessage()); break;
-      case NotifyCqmMessage::kCommand:
-        message.reset(new NotifyCqmMessage()); break;
-      case PmksaCandidateMessage::kCommand:
-        message.reset(new PmksaCandidateMessage()); break;
-      case RegBeaconHintMessage::kCommand:
-        message.reset(new RegBeaconHintMessage()); break;
-      case RegChangeMessage::kCommand:
-        message.reset(new RegChangeMessage()); break;
-      case RemainOnChannelMessage::kCommand:
-        message.reset(new RemainOnChannelMessage()); break;
-      case RoamMessage::kCommand:
-        message.reset(new RoamMessage()); break;
-      case ScanAbortedMessage::kCommand:
-        message.reset(new ScanAbortedMessage()); break;
-      case TriggerScanMessage::kCommand:
-        message.reset(new TriggerScanMessage()); break;
-      case UnprotDeauthenticateMessage::kCommand:
-        message.reset(new UnprotDeauthenticateMessage()); break;
-      case UnprotDisassociateMessage::kCommand:
-        message.reset(new UnprotDisassociateMessage()); break;
-
-      default:
-        message.reset(new UnknownMessage(gnlh->cmd)); break;
-    }
+  } else if (ContainsKey(factories_, const_msg->nlmsg_type)) {
+    map<uint16_t, FactoryMethod>::const_iterator factory;
+    factory = factories_.find(const_msg->nlmsg_type);
+    message.reset(factory->second.Run(const_msg));
   }
 
-  if (!message->InitFromNlmsg(msg)) {
+  // If no factory exists for this message _or_ if a factory exists but it
+  // failed, there'll be no message.  Handle either of those cases, by
+  // creating an |UnknownMessage|.
+  if (!message) {
+    // Casting away constness since, while nlmsg_data doesn't change its
+    // parameter, it also doesn't declare its paramenter as const.
+    nlmsghdr *msg = const_cast<nlmsghdr *>(const_msg);
+    ByteString payload(reinterpret_cast<char *>(nlmsg_data(msg)),
+                       nlmsg_datalen(msg));
+    message.reset(new UnknownMessage(msg->nlmsg_type, payload));
+  }
+
+  if (!message->InitFromNlmsg(const_msg)) {
     LOG(ERROR) << "Message did not initialize properly";
     return NULL;
   }
@@ -1034,6 +1111,10 @@
   return message.release();
 }
 
+//
+// Data Collector
+//
+
 Nl80211MessageDataCollector *
     Nl80211MessageDataCollector::GetInstance() {
   return g_datacollector.Pointer();
diff --git a/nl80211_message.h b/nl80211_message.h
index 198b467..33b3dbc 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -181,6 +181,20 @@
   DISALLOW_COPY_AND_ASSIGN(OverrunMessage);
 };
 
+
+class UnknownMessage : public NetlinkMessage {
+ public:
+  UnknownMessage(uint16_t message_type, ByteString message_body) :
+      NetlinkMessage(message_type), message_body_(message_body) {}
+  virtual ByteString Encode(uint32_t sequence_number);
+  virtual void Print(int log_level) const;
+
+ private:
+  ByteString message_body_;
+
+  DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
+};
+
 // Objects of the |GenericNetlinkMessage| type represent messages that contain
 // a |genlmsghdr| after a |nlmsghdr|.  These messages seem to all contain a
 // payload that consists of a list of structured attributes (it's possible that
@@ -265,6 +279,9 @@
       : GenericNetlinkMessage(kMessageType, command, command_string) {}
 
   virtual bool InitFromNlmsg(const nlmsghdr *msg);
+
+  // Message factory for all types of Control netlink message.
+  static NetlinkMessage *CreateMessage(const nlmsghdr *const_msg);
 };
 
 class NewFamilyMessage : public ControlNetlinkMessage {
@@ -340,6 +357,9 @@
   static std::string StringFromReason(uint16_t reason);
   static std::string StringFromStatus(uint16_t status);
 
+  // Message factory for all types of Nl80211 message.
+  static NetlinkMessage *CreateMessage(const nlmsghdr *const_msg);
+
  private:
   static std::map<uint16_t, std::string> *reason_code_string_;
   static std::map<uint16_t, std::string> *status_code_string_;
@@ -673,22 +693,6 @@
 };
 
 
-class UnknownMessage : public Nl80211Message {
- public:
-  explicit UnknownMessage(uint8_t command)
-      : Nl80211Message(command, kCommandString),
-        command_(command) {}
-
-  static const uint8_t kCommand;
-  static const char kCommandString[];
-
- private:
-  uint8_t command_;
-
-  DISALLOW_COPY_AND_ASSIGN(UnknownMessage);
-};
-
-
 class UnprotDeauthenticateMessage : public Nl80211Message {
  public:
   static const uint8_t kCommand;
@@ -720,18 +724,24 @@
 
 class NetlinkMessageFactory {
  public:
+  typedef base::Callback<NetlinkMessage *(const nlmsghdr *msg)> FactoryMethod;
+
+  NetlinkMessageFactory() {}
+
+  // Adds a message factory for a specific message_type.  Intended to be used
+  // at initialization.
+  bool AddFactoryMethod(uint16_t message_type, FactoryMethod factory);
+
   // Ownership of the message is passed to the caller and, as such, he should
   // delete it.
-  static NetlinkMessage *CreateMessage(nlmsghdr *msg);
-  // TODO(wdg): Need a way for a class to register a callback and a message
-  // type.  That way, CreateMessage can call the appropriate factory based on
-  // the incoming message type.
+  NetlinkMessage *CreateMessage(const nlmsghdr *msg) const;
 
  private:
+  std::map<uint16_t, FactoryMethod> factories_;
+
   DISALLOW_COPY_AND_ASSIGN(NetlinkMessageFactory);
 };
 
-
 // Nl80211MessageDataCollector - this class is used to collect data to be
 // used for unit tests.  It is only invoked in this case.
 
diff --git a/shill_daemon.cc b/shill_daemon.cc
index 38866c8..55de12f 100644
--- a/shill_daemon.cc
+++ b/shill_daemon.cc
@@ -105,7 +105,8 @@
   if (config80211_) {
     config80211_->Init();
     uint16_t nl80211_family_id = config80211_->GetFamily(
-        Nl80211Message::kMessageTypeString);
+        Nl80211Message::kMessageTypeString,
+        Bind(&Nl80211Message::CreateMessage));
     if (nl80211_family_id == NetlinkMessage::kIllegalMessageType) {
       LOG(FATAL) << "Didn't get a legal message type for 'nl80211' messages.";
     }