shill: Breaks nl80211_attribute.* into files appropriate to classes.

This CL *ONLY* breaks nl80211_attribute.* into netlink_attribute.*
and nl80211_attribute.* and adds control_netlink_attribute.*.  None of
the netlink or nl80211 code should be changed, here.

BUG=None.
TEST=unittests.

Change-Id: Ia181642af052dceb280a23b5fac25a2400d93e1a
Reviewed-on: https://gerrit.chromium.org/gerrit/44255
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 64b549a..700f5c3 100644
--- a/Makefile
+++ b/Makefile
@@ -193,6 +193,7 @@
 	cellular_service.o \
 	config80211.o \
 	connection.o \
+	control_netlink_attribute.o \
 	crypto_des_cbc.o \
 	crypto_provider.o \
 	crypto_rot47.o \
@@ -262,6 +263,7 @@
 	modem_proxy.o \
 	modem_simple_proxy.o \
 	netlink_socket.o \
+	netlink_attribute.o \
 	nl80211_attribute.o \
 	nl80211_message.o \
 	nl80211_socket.o \
diff --git a/attribute_list.cc b/attribute_list.cc
index 757f150..a0d441f 100644
--- a/attribute_list.cc
+++ b/attribute_list.cc
@@ -17,7 +17,7 @@
 #include <base/stringprintf.h>
 
 #include "shill/logging.h"
-#include "shill/nl80211_attribute.h"
+#include "shill/netlink_attribute.h"
 #include "shill/scope_logger.h"
 
 using base::StringAppendF;
diff --git a/control_netlink_attribute.cc b/control_netlink_attribute.cc
new file mode 100644
index 0000000..2003880
--- /dev/null
+++ b/control_netlink_attribute.cc
@@ -0,0 +1,85 @@
+// 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.
+
+#include "shill/control_netlink_attribute.h"
+
+#include <netlink/attr.h>
+
+#include "shill/logging.h"
+#include "shill/scope_logger.h"
+
+namespace shill {
+
+const int ControlAttributeFamilyId::kName = CTRL_ATTR_FAMILY_ID;
+const char ControlAttributeFamilyId::kNameString[] = "CTRL_ATTR_FAMILY_ID";
+
+const int ControlAttributeFamilyName::kName = CTRL_ATTR_FAMILY_NAME;
+const char ControlAttributeFamilyName::kNameString[] = "CTRL_ATTR_FAMILY_NAME";
+
+const int ControlAttributeVersion::kName = CTRL_ATTR_VERSION;
+const char ControlAttributeVersion::kNameString[] = "CTRL_ATTR_VERSION";
+
+const int ControlAttributeHdrSize::kName = CTRL_ATTR_HDRSIZE;
+const char ControlAttributeHdrSize::kNameString[] = "CTRL_ATTR_HDRSIZE";
+
+const int ControlAttributeMaxAttr::kName = CTRL_ATTR_MAXATTR;
+const char ControlAttributeMaxAttr::kNameString[] = "CTRL_ATTR_MAXATTR";
+
+const int ControlAttributeAttrOps::kName = CTRL_ATTR_OPS;
+const char ControlAttributeAttrOps::kNameString[] = "CTRL_ATTR_OPS";
+
+ControlAttributeAttrOps::ControlAttributeAttrOps()
+      : NetlinkNestedAttribute(kName, kNameString) {}
+
+bool ControlAttributeAttrOps::InitFromNlAttr(const nlattr *const_data) {
+  static const NestedData kOps[CTRL_ATTR_OP_MAX  + 1] = {
+    {{NLA_U32, 0, 0}, "CTRL_ATTR_OP_UNSPEC", NULL, 0, false},
+    {{NLA_U32, 0, 0}, "CTRL_ATTR_OP_ID", NULL, 0, false},
+    {{NLA_U32, 0, 0}, "CTRL_ATTR_OP_FLAGS", NULL, 0, false},
+  };
+  static const NestedData kOpsList[] = {
+    {{NLA_NESTED, 0, 0 }, "FIRST", &kOps[0], arraysize(kOps), true},
+  };
+
+  if (!InitNestedFromNlAttr(value_.get(),
+                            kOpsList,
+                            arraysize(kOpsList),
+                            const_data)) {
+    LOG(ERROR) << "InitNestedFromNlAttr() failed";
+    return false;
+  }
+  has_a_value_ = true;
+  return true;
+}
+
+const int ControlAttributeMcastGroups::kName = CTRL_ATTR_MCAST_GROUPS;
+const char ControlAttributeMcastGroups::kNameString[] =
+    "CTRL_ATTR_MCAST_GROUPS";
+
+ControlAttributeMcastGroups::ControlAttributeMcastGroups()
+      : NetlinkNestedAttribute(kName, kNameString) {}
+
+bool ControlAttributeMcastGroups::InitFromNlAttr(const nlattr *const_data) {
+  static const NestedData kMulticast[CTRL_ATTR_MCAST_GRP_MAX  + 1] = {
+    {{NLA_U32, 0, 0}, "CTRL_ATTR_MCAST_GRP_UNSPEC", NULL, 0, false},
+    {{NLA_STRING, 0, 0}, "CTRL_ATTR_MCAST_GRP_NAME", NULL, 0, false},
+    {{NLA_U32, 0, 0}, "CTRL_ATTR_MCAST_GRP_ID", NULL, 0, false},
+  };
+  static const NestedData kMulticastList[] = {
+    {{NLA_NESTED, 0, 0}, "FIRST", &kMulticast[0], arraysize(kMulticast),
+      true},
+  };
+
+  if (!InitNestedFromNlAttr(value_.get(),
+                            kMulticastList,
+                            arraysize(kMulticastList),
+                            const_data)) {
+    LOG(ERROR) << "InitNestedFromNlAttr() failed";
+    return false;
+  }
+  has_a_value_ = true;
+  return true;
+}
+
+}  // namespace shill
diff --git a/control_netlink_attribute.h b/control_netlink_attribute.h
new file mode 100644
index 0000000..83cd7ee
--- /dev/null
+++ b/control_netlink_attribute.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef SHILL_CONTROL_NETLINK_ATTRIBUTE_H_
+#define SHILL_CONTROL_NETLINK_ATTRIBUTE_H_
+
+#include <netlink/attr.h>
+
+#include "shill/netlink_attribute.h"
+
+struct nlattr;
+
+namespace shill {
+
+// Control.
+
+class ControlAttributeFamilyId : public NetlinkU16Attribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeFamilyId() : NetlinkU16Attribute(kName, kNameString) {}
+};
+
+class ControlAttributeFamilyName : public NetlinkStringAttribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeFamilyName() : NetlinkStringAttribute(kName, kNameString) {}
+};
+
+class ControlAttributeVersion : public NetlinkU32Attribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeVersion() : NetlinkU32Attribute(kName, kNameString) {}
+};
+
+class ControlAttributeHdrSize : public NetlinkU32Attribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeHdrSize() : NetlinkU32Attribute(kName, kNameString) {}
+};
+
+class ControlAttributeMaxAttr : public NetlinkU32Attribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeMaxAttr() : NetlinkU32Attribute(kName, kNameString) {}
+};
+
+class ControlAttributeAttrOps : public NetlinkNestedAttribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeAttrOps();
+  virtual bool InitFromNlAttr(const nlattr *data);
+};
+
+class ControlAttributeMcastGroups : public NetlinkNestedAttribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  ControlAttributeMcastGroups();
+  virtual bool InitFromNlAttr(const nlattr *data);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_CONTROL_NETLINK_ATTRIBUTE_H_
diff --git a/netlink_attribute.cc b/netlink_attribute.cc
new file mode 100644
index 0000000..4235c5d
--- /dev/null
+++ b/netlink_attribute.cc
@@ -0,0 +1,978 @@
+// Copyright (c) 2012 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.
+
+#include "shill/netlink_attribute.h"
+
+#include <netlink/attr.h>
+
+#include <string>
+
+#include <base/format_macros.h>
+#include <base/stringprintf.h>
+
+#include "shill/attribute_list.h"
+#include "shill/control_netlink_attribute.h"
+#include "shill/logging.h"
+#include "shill/nl80211_attribute.h"
+#include "shill/scope_logger.h"
+
+using std::string;
+
+using base::StringAppendF;
+using base::StringPrintf;
+
+namespace shill {
+
+// This is a type-corrected copy of |nla_for_each_nested| from libnl.
+#define nla_for_each_nested_type_corrected(pos, nla, rem) \
+  for (pos = reinterpret_cast<struct nlattr *>(nla_data(nla)), \
+       rem = nla_len(nla); \
+    nla_ok(pos, rem); \
+    pos = nla_next(pos, &(rem)))
+
+NetlinkAttribute::NetlinkAttribute(int id,
+                                   const char *id_string,
+                                   Type datatype,
+                                   const char *datatype_string)
+    : has_a_value_(false), id_(id), id_string_(id_string), datatype_(datatype),
+      datatype_string_(datatype_string) {}
+
+// static
+NetlinkAttribute *NetlinkAttribute::NewNl80211AttributeFromId(int id) {
+  scoped_ptr<NetlinkAttribute> attr;
+  switch (id) {
+    case NL80211_ATTR_COOKIE:
+      attr.reset(new Nl80211AttributeCookie());
+      break;
+    case NL80211_ATTR_CQM:
+      attr.reset(new Nl80211AttributeCqm());
+      break;
+    case NL80211_ATTR_DISCONNECTED_BY_AP:
+      attr.reset(new Nl80211AttributeDisconnectedByAp());
+      break;
+    case NL80211_ATTR_DURATION:
+      attr.reset(new Nl80211AttributeDuration());
+      break;
+    case NL80211_ATTR_FRAME:
+      attr.reset(new Nl80211AttributeFrame());
+      break;
+    case NL80211_ATTR_GENERATION:
+      attr.reset(new Nl80211AttributeGeneration());
+      break;
+    case NL80211_ATTR_IFINDEX:
+      attr.reset(new Nl80211AttributeIfindex());
+      break;
+    case NL80211_ATTR_KEY_IDX:
+      attr.reset(new Nl80211AttributeKeyIdx());
+      break;
+    case NL80211_ATTR_KEY_SEQ:
+      attr.reset(new Nl80211AttributeKeySeq());
+      break;
+    case NL80211_ATTR_KEY_TYPE:
+      attr.reset(new Nl80211AttributeKeyType());
+      break;
+    case NL80211_ATTR_MAC:
+      attr.reset(new Nl80211AttributeMac());
+      break;
+    case NL80211_ATTR_REASON_CODE:
+      attr.reset(new Nl80211AttributeReasonCode());
+      break;
+    case NL80211_ATTR_REG_ALPHA2:
+      attr.reset(new Nl80211AttributeRegAlpha2());
+      break;
+    case NL80211_ATTR_REG_INITIATOR:
+      attr.reset(new Nl80211AttributeRegInitiator());
+      break;
+    case NL80211_ATTR_REG_TYPE:
+      attr.reset(new Nl80211AttributeRegType());
+      break;
+    case NL80211_ATTR_RESP_IE:
+      attr.reset(new Nl80211AttributeRespIe());
+      break;
+    case NL80211_ATTR_SCAN_FREQUENCIES:
+      attr.reset(new Nl80211AttributeScanFrequencies());
+      break;
+    case NL80211_ATTR_SCAN_SSIDS:
+      attr.reset(new Nl80211AttributeScanSsids());
+      break;
+    case NL80211_ATTR_STA_INFO:
+      attr.reset(new Nl80211AttributeStaInfo());
+      break;
+    case NL80211_ATTR_STATUS_CODE:
+      attr.reset(new Nl80211AttributeStatusCode());
+      break;
+    case NL80211_ATTR_SUPPORT_MESH_AUTH:
+      attr.reset(new Nl80211AttributeSupportMeshAuth());
+      break;
+    case NL80211_ATTR_TIMED_OUT:
+      attr.reset(new Nl80211AttributeTimedOut());
+      break;
+    case NL80211_ATTR_WIPHY_FREQ:
+      attr.reset(new Nl80211AttributeWiphyFreq());
+      break;
+    case NL80211_ATTR_WIPHY:
+      attr.reset(new Nl80211AttributeWiphy());
+      break;
+    case NL80211_ATTR_WIPHY_NAME:
+      attr.reset(new Nl80211AttributeWiphyName());
+      break;
+    default:
+      attr.reset(new NetlinkAttributeGeneric(id));
+      break;
+  }
+  return attr.release();
+}
+
+// static
+NetlinkAttribute *NetlinkAttribute::NewControlAttributeFromId(int id) {
+  scoped_ptr<NetlinkAttribute> attr;
+  switch (id) {
+    case CTRL_ATTR_FAMILY_ID:
+      attr.reset(new ControlAttributeFamilyId());
+      break;
+    case CTRL_ATTR_FAMILY_NAME:
+      attr.reset(new ControlAttributeFamilyName());
+      break;
+    case CTRL_ATTR_VERSION:
+      attr.reset(new ControlAttributeVersion());
+      break;
+    case CTRL_ATTR_HDRSIZE:
+      attr.reset(new ControlAttributeHdrSize());
+      break;
+    case CTRL_ATTR_MAXATTR:
+      attr.reset(new ControlAttributeMaxAttr());
+      break;
+    case CTRL_ATTR_OPS:
+      attr.reset(new ControlAttributeAttrOps());
+      break;
+    case CTRL_ATTR_MCAST_GROUPS:
+      attr.reset(new ControlAttributeMcastGroups());
+      break;
+    default:
+      attr.reset(new NetlinkAttributeGeneric(id));
+      break;
+  }
+  return attr.release();
+}
+
+// Duplicate attribute data, store in map indexed on |id|.
+bool NetlinkAttribute::InitFromNlAttr(const nlattr *other) {
+  if (!other) {
+    LOG(ERROR) << "NULL data";
+    return false;
+  }
+
+  data_ = ByteString(
+      reinterpret_cast<char *>(nla_data(const_cast<nlattr *>(other))),
+      nla_len(const_cast<nlattr *>(other)));
+  return true;
+}
+
+bool NetlinkAttribute::GetU8Value(uint8_t *value) const {
+  LOG(ERROR) << "Attribute is not of type 'U8'";
+  return false;
+}
+
+bool NetlinkAttribute::SetU8Value(uint8_t value) {
+  LOG(ERROR) << "Attribute is not of type 'U8'";
+  return false;
+}
+
+bool NetlinkAttribute::GetU16Value(uint16_t *value) const {
+  LOG(ERROR) << "Attribute is not of type 'U16'";
+  return false;
+}
+
+bool NetlinkAttribute::SetU16Value(uint16_t value) {
+  LOG(ERROR) << "Attribute is not of type 'U16'";
+  return false;
+}
+
+bool NetlinkAttribute::GetU32Value(uint32_t *value) const {
+  LOG(ERROR) << "Attribute is not of type 'U32'";
+  return false;
+}
+
+bool NetlinkAttribute::SetU32Value(uint32_t value) {
+  LOG(ERROR) << "Attribute is not of type 'U32'";
+  return false;
+}
+
+bool NetlinkAttribute::GetU64Value(uint64_t *value) const {
+  LOG(ERROR) << "Attribute is not of type 'U64'";
+  return false;
+}
+
+bool NetlinkAttribute::SetU64Value(uint64_t value) {
+  LOG(ERROR) << "Attribute is not of type 'U64'";
+  return false;
+}
+
+bool NetlinkAttribute::GetFlagValue(bool *value) const {
+  LOG(ERROR) << "Attribute is not of type 'Flag'";
+  return false;
+}
+
+bool NetlinkAttribute::SetFlagValue(bool value) {
+  LOG(ERROR) << "Attribute is not of type 'Flag'";
+  return false;
+}
+
+bool NetlinkAttribute::GetStringValue(string *value) const {
+  LOG(ERROR) << "Attribute is not of type 'String'";
+  return false;
+}
+
+bool NetlinkAttribute::SetStringValue(string value) {
+  LOG(ERROR) << "Attribute is not of type 'String'";
+  return false;
+}
+
+bool NetlinkAttribute::GetNestedAttributeList(AttributeListRefPtr *value) {
+  LOG(ERROR) << "Attribute is not of type 'Nested'";
+  return false;
+}
+
+bool NetlinkAttribute::ConstGetNestedAttributeList(
+    AttributeListConstRefPtr *value) const {
+  LOG(ERROR) << "Attribute is not of type 'Nested'";
+  return false;
+}
+
+bool NetlinkAttribute::SetNestedHasAValue() {
+  LOG(ERROR) << "Attribute is not of type 'Nested'";
+  return false;
+}
+
+bool NetlinkAttribute::GetRawValue(ByteString *value) const {
+  LOG(ERROR) << "Attribute is not of type 'Raw'";
+  return false;
+}
+
+bool NetlinkAttribute::SetRawValue(const ByteString new_value) {
+  LOG(ERROR) << "Attribute is not of type 'Raw'";
+  return false;
+}
+
+void NetlinkAttribute::Print(int log_level, int indent) const {
+  string attribute_value;
+  SLOG(WiFi, log_level) << HeaderToPrint(indent) << " "
+                        << (ToString(&attribute_value) ? attribute_value :
+                            "<DOES NOT EXIST>");
+}
+
+string NetlinkAttribute::RawToString() const {
+  string output = " === RAW: ";
+
+  if (!has_a_value_) {
+    StringAppendF(&output, "(empty)");
+    return output;
+  }
+
+  uint16_t length = data_.GetLength();
+  const uint8_t *const_data = data_.GetConstData();
+
+  StringAppendF(&output, "len=%u", length);
+  output.append(" DATA: ");
+  for (int i =0 ; i < length; ++i) {
+    StringAppendF(&output, "[%d]=%02x ", i, *(const_data)+i);
+  }
+  output.append(" ==== ");
+  return output;
+}
+
+string NetlinkAttribute::HeaderToPrint(int indent) const {
+  static const int kSpacesPerIndent = 2;
+  return StringPrintf("%*s%s(%d) %s %s=",
+            indent * kSpacesPerIndent, "",
+            id_string(),
+            id(),
+            datatype_string(),
+            ((has_a_value()) ?  "": "UNINITIALIZED "));
+}
+
+ByteString NetlinkAttribute::EncodeGeneric(const unsigned char *data,
+                                           size_t num_bytes) const {
+  nlattr header;
+  header.nla_type = id();
+  header.nla_len = nla_attr_size(num_bytes);
+  ByteString result(reinterpret_cast<unsigned char *>(&header), sizeof(header));
+  result.Resize(NLA_HDRLEN);  // Add padding after the header.
+  if (data && (num_bytes != 0)) {
+    result.Append(ByteString(data, num_bytes));
+  }
+  result.Resize(nla_total_size(num_bytes));  // Add padding.
+  return result;
+}
+
+// NetlinkU8Attribute
+
+const char NetlinkU8Attribute::kMyTypeString[] = "uint8_t";
+const NetlinkAttribute::Type NetlinkU8Attribute::kType =
+    NetlinkAttribute::kTypeU8;
+
+bool NetlinkU8Attribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  uint8_t data = NlaGetU8(input);
+  SetU8Value(data);
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+bool NetlinkU8Attribute::GetU8Value(uint8_t *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7) << "U8 attribute " << id_string()
+                  << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkU8Attribute::SetU8Value(uint8_t new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkU8Attribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  uint8_t value;
+  if (!GetU8Value(&value))
+    return false;
+  *output = StringPrintf("%u", value);
+  return true;
+}
+
+ByteString NetlinkU8Attribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(
+      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
+}
+
+
+// NetlinkU16Attribute
+
+const char NetlinkU16Attribute::kMyTypeString[] = "uint16_t";
+const NetlinkAttribute::Type NetlinkU16Attribute::kType =
+    NetlinkAttribute::kTypeU16;
+
+bool NetlinkU16Attribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  uint16_t data = NlaGetU16(input);
+  SetU16Value(data);
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+bool NetlinkU16Attribute::GetU16Value(uint16_t *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "U16 attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkU16Attribute::SetU16Value(uint16_t new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkU16Attribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  uint16_t value;
+  if (!GetU16Value(&value))
+    return false;
+  *output = StringPrintf("%u", value);
+  return true;
+}
+
+ByteString NetlinkU16Attribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(
+      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
+}
+
+// NetlinkU32Attribute::
+
+const char NetlinkU32Attribute::kMyTypeString[] = "uint32_t";
+const NetlinkAttribute::Type NetlinkU32Attribute::kType =
+    NetlinkAttribute::kTypeU32;
+
+bool NetlinkU32Attribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  uint32_t data = NlaGetU32(input);
+  SetU32Value(data);
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+bool NetlinkU32Attribute::GetU32Value(uint32_t *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "U32 attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkU32Attribute::SetU32Value(uint32_t new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkU32Attribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  uint32_t value;
+  if (!GetU32Value(&value))
+    return false;
+  *output = StringPrintf("%" PRIu32, value);
+  return true;
+}
+
+ByteString NetlinkU32Attribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(
+      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
+}
+
+// NetlinkU64Attribute
+
+const char NetlinkU64Attribute::kMyTypeString[] = "uint64_t";
+const NetlinkAttribute::Type NetlinkU64Attribute::kType =
+    NetlinkAttribute::kTypeU64;
+
+bool NetlinkU64Attribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  uint64_t data = NlaGetU64(input);
+  SetU64Value(data);
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+bool NetlinkU64Attribute::GetU64Value(uint64_t *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "U64 attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkU64Attribute::SetU64Value(uint64_t new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkU64Attribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  uint64_t value;
+  if (!GetU64Value(&value))
+    return false;
+  *output = StringPrintf("%" PRIu64, value);
+  return true;
+}
+
+ByteString NetlinkU64Attribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(
+      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
+}
+
+// NetlinkFlagAttribute
+
+const char NetlinkFlagAttribute::kMyTypeString[] = "flag";
+const NetlinkAttribute::Type NetlinkFlagAttribute::kType =
+    NetlinkAttribute::kTypeFlag;
+
+bool NetlinkFlagAttribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  // The existence of the parameter means it's true
+  SetFlagValue(true);
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+
+bool NetlinkFlagAttribute::GetFlagValue(bool *output) const {
+  if (output) {
+    // The lack of the existence of the attribute implies 'false'.
+    *output = (has_a_value_) ? value_ : false;
+  }
+  return true;
+}
+
+bool NetlinkFlagAttribute::SetFlagValue(bool new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkFlagAttribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  bool value;
+  if (!GetFlagValue(&value))
+    return false;
+  *output = StringPrintf("%s", value ? "true" : "false");
+  return true;
+}
+
+ByteString NetlinkFlagAttribute::Encode() const {
+  if (has_a_value_ && value_) {
+    return NetlinkAttribute::EncodeGeneric(NULL, 0);
+  }
+  return ByteString();  // Encoding of nothing implies 'false'.
+}
+
+// NetlinkStringAttribute
+
+const char NetlinkStringAttribute::kMyTypeString[] = "string";
+const NetlinkAttribute::Type NetlinkStringAttribute::kType =
+    NetlinkAttribute::kTypeString;
+
+bool NetlinkStringAttribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  SetStringValue(NlaGetString(input));
+  return NetlinkAttribute::InitFromNlAttr(input);
+}
+
+bool NetlinkStringAttribute::GetStringValue(string *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "String attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkStringAttribute::SetStringValue(const string new_value) {
+  value_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkStringAttribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  string value;
+  if (!GetStringValue(&value))
+    return false;
+
+  *output = StringPrintf("'%s'", value.c_str());
+  return true;
+}
+
+ByteString NetlinkStringAttribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(
+      reinterpret_cast<const unsigned char *>(value_.c_str()),
+      value_.size() + 1);
+}
+
+// NetlinkNestedAttribute
+
+const char NetlinkNestedAttribute::kMyTypeString[] = "nested";
+const NetlinkAttribute::Type NetlinkNestedAttribute::kType =
+    NetlinkAttribute::kTypeNested;
+
+NetlinkNestedAttribute::NetlinkNestedAttribute(int id,
+                                               const char *id_string) :
+    NetlinkAttribute(id, id_string, kType, kMyTypeString),
+    value_(new AttributeList) {}
+
+ByteString NetlinkNestedAttribute::Encode() const {
+  // Encode attribute header.
+  nlattr header;
+  header.nla_type = id();
+  header.nla_len = nla_attr_size(sizeof(header));
+  ByteString result(reinterpret_cast<unsigned char *>(&header), sizeof(header));
+  result.Resize(NLA_HDRLEN);  // Add padding after the header.
+
+  // Encode all nested attributes.
+  std::map<int, AttributeList::AttributePointer>::const_iterator attribute;
+  for (attribute = value_->attributes_.begin();
+       attribute != value_->attributes_.end();
+       ++attribute) {
+    // Each attribute appends appropriate padding so it's not necessary to
+    // re-add padding.
+    result.Append(attribute->second->Encode());
+  }
+
+  // Go back and fill-in the size.
+  nlattr *new_header = reinterpret_cast<nlattr *>(result.GetData());
+  new_header->nla_len = result.GetLength();
+
+  return result;
+}
+
+void NetlinkNestedAttribute::Print(int log_level, int indent) const {
+  SLOG(WiFi, log_level) << HeaderToPrint(indent);
+  value_->Print(log_level, indent + 1);
+}
+
+bool NetlinkNestedAttribute::ToString(std::string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+
+  // This should never be called (attribute->ToString is only called
+  // from attribute->Print but NetlinkNestedAttribute::Print doesn't call
+  // |ToString|.  Still, we should print something in case we got here
+  // accidentally.
+  LOG(WARNING) << "It is unexpected for this method to be called.";
+  output->append("<Nested Attribute>");
+  return true;
+}
+
+bool NetlinkNestedAttribute::GetNestedAttributeList(
+    AttributeListRefPtr *output) {
+  // Not checking |has_a_value| since GetNestedAttributeList is called to get
+  // a newly created AttributeList in order to have something to which to add
+  // attributes.
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkNestedAttribute::ConstGetNestedAttributeList(
+    AttributeListConstRefPtr *output) const {
+  if (!has_a_value_) {
+    LOG(ERROR) << "Attribute does not exist.";
+    return false;
+  }
+  if (output) {
+    *output = value_;
+  }
+  return true;
+}
+
+bool NetlinkNestedAttribute::SetNestedHasAValue() {
+  has_a_value_ = true;
+  return true;
+}
+
+// static
+bool NetlinkNestedAttribute::InitNestedFromNlAttr(
+    AttributeList *list,
+    const NestedData *nested_template,
+    size_t nested_template_size,
+    const nlattr *const_data) {
+  if (!nested_template) {
+    LOG(ERROR) << "Null |nested_template| parameter";
+    return false;
+  }
+  if ((nested_template_size == 1) && (nested_template[0].is_array)) {
+    return ParseNestedArray(list, *nested_template, const_data);
+  } else {
+    return ParseNestedStructure(list, nested_template, nested_template_size,
+                                const_data);
+  }
+  return true;
+}
+
+// A nested array provides an arbitrary number of children, all of the same
+// data type.  Each array element may be a simple type or may be a structure.
+//
+// static
+bool NetlinkNestedAttribute::ParseNestedArray(
+    AttributeList *list,
+    const NestedData &array_template,
+    const nlattr *const_data) {
+  if (!list) {
+    LOG(ERROR) << "NULL |list| parameter";
+    return false;
+  }
+  if (!const_data) {
+    LOG(ERROR) << "Null |const_data| parameter";
+    return false;
+  }
+  // Casting away constness since |nla_parse_nested| doesn't mark its input as
+  // const even though it doesn't modify this input parameter.
+  nlattr *attrs = const_cast<nlattr *>(const_data);
+
+  struct nlattr *attr;
+  int remaining;
+  // The |nlattr::nla_type| value for array elements in the provided data may
+  // start on any number and are allowed to be discontiguous.  In order to
+  // skirt writing an iterator, this code replaces the |nla_type| with a
+  // contiguous |id|, starting at 1 (note that, while nested structure
+  // attributes may not have an |nlattr::nla_type| valued at zero, no such
+  // restriction exists for nested array attributes -- this code starts the id
+  // at zero in order to be consistent with nested structures).
+  //
+  // TODO(wdg): Determine whether any code depends on the value of
+  // |nlattr::nla_type| for nested array attributes.
+  int id = 1;
+  nla_for_each_nested_type_corrected(attr, attrs, remaining) {
+    string attribute_name = StringPrintf("%s_%d",
+                                         array_template.attribute_name, id);
+    AddAttributeToNested(list, array_template.policy.type, id, attribute_name,
+                         *attr, array_template);
+    ++id;
+  }
+  return true;
+}
+
+// A nested structure provides a fixed set of child attributes (some of
+// which may be optional).  The caller provides the expectation of the members
+// and values of a nested structure in the supplied 'policy' template
+// (|struct_template|).
+//
+// static
+bool NetlinkNestedAttribute::ParseNestedStructure(
+    AttributeList *list,
+    const NestedData *struct_template,
+    size_t nested_template_size,
+    const nlattr *const_data) {
+  if (!list) {
+    LOG(ERROR) << "NULL |list| parameter";
+    return false;
+  }
+  if (!struct_template) {
+    LOG(ERROR) << "Null |struct_template| parameter";
+    return false;
+  }
+  if (nested_template_size == 0) {
+    LOG(ERROR) << "|nested_template_size| parameter is zero";
+    return false;
+  }
+  if (!const_data) {
+    LOG(ERROR) << "Null |const_data| parameter";
+    return false;
+  }
+  // Casting away constness since |nla_parse_nested| doesn't mark its input as
+  // const even though it doesn't modify this input parameter.
+  nlattr *attr_data = const_cast<nlattr *>(const_data);
+
+  // |nla_parse_nested| requires an array of |nla_policy|. While an attribute id
+  // of zero is illegal, we still need to fill that spot in the policy
+  // array so the loop will start at zero.
+  scoped_array<nla_policy> policy(new nla_policy[nested_template_size]);
+  for (size_t id = 0; id < nested_template_size ; ++id) {
+    memcpy(&policy[id], &struct_template[id].policy, sizeof(nla_policy));
+  }
+
+  // |nla_parse_nested| builds an array of |nlattr| from the input message.
+  scoped_array<nlattr *>attr(new nlattr *[nested_template_size]);
+  if (nla_parse_nested(attr.get(), nested_template_size-1, attr_data,
+                       policy.get())) {
+    LOG(ERROR) << "nla_parse_nested failed";
+    return false;
+  }
+
+  // Note that the attribute id of zero is illegal so we'll start with id=1.
+  for (size_t id = 1; id < nested_template_size; ++id) {
+    // Add |attr[id]| if it exists, otherwise, it's a legally omitted optional
+    // attribute.
+    if (attr[id]) {
+      AddAttributeToNested(list, policy[id].type, id,
+                           struct_template[id].attribute_name, *attr[id],
+                           struct_template[id]);
+    }
+  }
+  return true;
+}
+
+// static
+void NetlinkNestedAttribute::AddAttributeToNested(
+    AttributeList *list, uint16_t type, size_t id, const string &attribute_name,
+    const nlattr &attr, const NestedData &nested_template) {
+  switch (type) {
+    case NLA_UNSPEC:
+      list->CreateRawAttribute(id, attribute_name.c_str());
+      list->SetRawAttributeValue(id, ByteString(
+          reinterpret_cast<const char *>(nla_data(&attr)), nla_len(&attr)));
+      break;
+    case NLA_U8:
+      list->CreateU8Attribute(id, attribute_name.c_str());
+      list->SetU8AttributeValue(id, NlaGetU8(&attr));
+      break;
+    case NLA_U16:
+      list->CreateU16Attribute(id, attribute_name.c_str());
+      list->SetU16AttributeValue(id, NlaGetU16(&attr));
+      break;
+    case NLA_U32:
+      list->CreateU32Attribute(id, attribute_name.c_str());
+      list->SetU32AttributeValue(id, NlaGetU32(&attr));
+      break;
+    case NLA_U64:
+      list->CreateU64Attribute(id, attribute_name.c_str());
+      list->SetU64AttributeValue(id, NlaGetU64(&attr));
+      break;
+    case NLA_FLAG:
+      list->CreateFlagAttribute(id, attribute_name.c_str());
+      list->SetFlagAttributeValue(id, true);
+      break;
+    case NLA_STRING:
+      // Note that nested structure attributes are validated by |validate_nla|
+      // which requires a string attribute to have at least 1 character
+      // (presumably for the '\0') while the kernel can create an empty string
+      // for at least one nested string array attribute type
+      // (NL80211_ATTR_SCAN_SSIDS -- the emptyness of the string is exhibited
+      // by the value of |nlattr::nla_len|).  This code handles both cases.
+      list->CreateStringAttribute(id, attribute_name.c_str());
+      if (nla_len(&attr) <= 0) {
+        list->SetStringAttributeValue(id, "");
+      } else {
+        list->SetStringAttributeValue(id, NlaGetString(&attr));
+      }
+      break;
+    case NLA_NESTED:
+      {
+        if (!nested_template.deeper_nesting) {
+          LOG(ERROR) << "No rules for nesting " << attribute_name
+                     << ". Ignoring.";
+          break;
+        }
+        list->CreateNestedAttribute(id, attribute_name.c_str());
+
+        // Now, handle the nested data.
+        AttributeListRefPtr nested_attribute;
+        if (!list->GetNestedAttributeList(id, &nested_attribute) ||
+            !nested_attribute) {
+          LOG(FATAL) << "Couldn't get attribute " << attribute_name
+                     << " which we just created.";
+          break;
+        }
+
+        if (!InitNestedFromNlAttr(nested_attribute.get(),
+                                  nested_template.deeper_nesting,
+                                  nested_template.deeper_nesting_size,
+                                  &attr)) {
+          LOG(ERROR) << "Couldn't parse attribute " << attribute_name;
+          break;
+        }
+        list->SetNestedAttributeHasAValue(id);
+      }
+      break;
+    default:
+      LOG(ERROR) << "Discarding " << attribute_name
+                 << ".  Attribute has unhandled type " << type << ".";
+      break;
+  }
+}
+
+// NetlinkRawAttribute
+
+const char NetlinkRawAttribute::kMyTypeString[] = "<raw>";
+const NetlinkAttribute::Type NetlinkRawAttribute::kType =
+    NetlinkAttribute::kTypeRaw;
+
+bool NetlinkRawAttribute::InitFromNlAttr(const nlattr *input) {
+  if (!input) {
+    LOG(ERROR) << "Null |input| parameter";
+    return false;
+  }
+
+  if (!NetlinkAttribute::InitFromNlAttr(input)) {
+    return false;
+  }
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkRawAttribute::GetRawValue(ByteString *output) const {
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "Raw attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  if (output) {
+    *output = data_;
+  }
+  return true;
+}
+
+bool NetlinkRawAttribute::SetRawValue(const ByteString new_value) {
+  data_ = new_value;
+  has_a_value_ = true;
+  return true;
+}
+
+bool NetlinkRawAttribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  if (!has_a_value_) {
+    SLOG(WiFi, 7)  << "Raw attribute " << id_string()
+                   << " hasn't been set to any value.";
+    return false;
+  }
+  int total_bytes = data_.GetLength();
+  const uint8_t *const_data = data_.GetConstData();
+
+  *output = StringPrintf("%d bytes:", total_bytes);
+  for (int i = 0; i < total_bytes; ++i) {
+    StringAppendF(output, " 0x%02x", const_data[i]);
+  }
+  return true;
+}
+
+ByteString NetlinkRawAttribute::Encode() const {
+  return NetlinkAttribute::EncodeGeneric(data_.GetConstData(),
+                                         data_.GetLength());
+}
+
+NetlinkAttributeGeneric::NetlinkAttributeGeneric(int id)
+    : NetlinkRawAttribute(id, "unused-string") {
+  StringAppendF(&id_string_, "<UNKNOWN ATTRIBUTE %d>", id);
+}
+
+const char *NetlinkAttributeGeneric::id_string() const {
+  return id_string_.c_str();
+}
+
+}  // namespace shill
diff --git a/netlink_attribute.h b/netlink_attribute.h
new file mode 100644
index 0000000..3dbfa7e
--- /dev/null
+++ b/netlink_attribute.h
@@ -0,0 +1,342 @@
+// Copyright (c) 2012 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.
+
+#ifndef SHILL_NETLINK_ATTRIBUTE_H_
+#define SHILL_NETLINK_ATTRIBUTE_H_
+
+#include <netlink/attr.h>
+
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+
+#include "shill/attribute_list.h"
+#include "shill/byte_string.h"
+#include "shill/logging.h"
+#include "shill/refptr_types.h"
+
+struct nlattr;
+
+namespace shill {
+
+// NetlinkAttribute is an abstract base class that describes an attribute in a
+// netlink-80211 message.  Child classes are type-specific and will define
+// Get*Value and Set*Value methods (where * is the type).  A second-level of
+// child classes exist for each individual attribute type.
+//
+// An attribute has an id (which is really an enumerated value), a data type,
+// and a value.  In an nlattr (the underlying format for an attribute in a
+// message), the data is stored as a blob without type information; the writer
+// and reader of the attribute must agree on the data type.
+class NetlinkAttribute {
+ public:
+  enum Type {
+    kTypeU8,
+    kTypeU16,
+    kTypeU32,
+    kTypeU64,
+    kTypeFlag,
+    kTypeString,
+    kTypeNested,
+    kTypeRaw,
+    kTypeError
+  };
+
+  NetlinkAttribute(int id, const char *id_string,
+                   Type datatype, const char *datatype_string);
+  virtual ~NetlinkAttribute() {}
+
+  // Static factories generate the appropriate attribute object from the
+  // raw nlattr data.
+  static NetlinkAttribute *NewControlAttributeFromId(int id);
+  static NetlinkAttribute *NewNl80211AttributeFromId(int id);
+
+  virtual bool InitFromNlAttr(const nlattr *data);
+
+  // Accessors for the attribute's id and datatype information.
+  int id() const { return id_; }
+  virtual const char *id_string() const { return id_string_.c_str(); }
+  Type datatype() const { return datatype_; }
+  const char *datatype_string() const { return datatype_string_; }
+
+  // Accessors.  Return false if request is made on wrong type of attribute.
+  virtual bool GetU8Value(uint8_t *value) const;
+  virtual bool SetU8Value(uint8_t new_value);
+
+  virtual bool GetU16Value(uint16_t *value) const;
+  virtual bool SetU16Value(uint16_t value);
+
+  virtual bool GetU32Value(uint32_t *value) const;
+  virtual bool SetU32Value(uint32_t value);
+
+  virtual bool GetU64Value(uint64_t *value) const;
+  virtual bool SetU64Value(uint64_t value);
+
+  virtual bool GetFlagValue(bool *value) const;
+  virtual bool SetFlagValue(bool value);
+
+  virtual bool GetStringValue(std::string *value) const;
+  virtual bool SetStringValue(const std::string value);
+
+  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
+  virtual bool ConstGetNestedAttributeList(
+      AttributeListConstRefPtr *value) const;
+  virtual bool SetNestedHasAValue();
+
+  virtual bool GetRawValue(ByteString *value) const;
+  virtual bool SetRawValue(const ByteString value);
+
+  // Prints the attribute info -- for debugging.
+  virtual void Print(int log_level, int indent) const;
+
+  // Fill a string with characters that represents the value of the attribute.
+  // If no attribute is found or if the datatype isn't trivially stringizable,
+  // this method returns 'false' and |value| remains unchanged.
+  virtual bool ToString(std::string *value) const = 0;
+
+  // Writes the raw attribute data to a string.  For debug.
+  std::string RawToString() const;
+
+  // Encodes the attribute suitably for the attributes in the payload portion
+  // of a netlink message suitable for Sockets::Send.  Return value is empty on
+  // failure.
+  virtual ByteString Encode() const = 0;
+
+  bool has_a_value() const { return has_a_value_; }
+
+  // Wrappers for libnl parsers.  Needed because, while |nla_get_*| don't
+  // change their arguments, these methods don't declare themselves as 'const'.
+  static char *NlaGetString(const nlattr *input) {
+    return nla_get_string(const_cast<nlattr *>(input));
+  }
+  static uint8_t NlaGetU8(const nlattr *input) {
+    return nla_get_u8(const_cast<nlattr *>(input));
+  }
+  static uint16_t NlaGetU16(const nlattr *input) {
+    return nla_get_u16(const_cast<nlattr *>(input));
+  }
+  static uint32_t NlaGetU32(const nlattr *input) {
+    return nla_get_u32(const_cast<nlattr *>(input));
+  }
+  static uint64_t NlaGetU64(const nlattr *input) {
+    return nla_get_u64(const_cast<nlattr *>(input));
+  }
+
+ protected:
+  // Builds a string to precede a printout of this attribute.
+  std::string HeaderToPrint(int indent) const;
+
+  // Encodes the attribute suitably for the attributes in the payload portion
+  // of a netlink message suitable for Sockets::Send.  Return value is empty on
+  // failure.
+  ByteString EncodeGeneric(const unsigned char *data, size_t num_bytes) const;
+
+  // Attribute data (NOT including the nlattr header) corresponding to the
+  // value in any of the child classes.
+  ByteString data_;
+
+  // True if a value has been assigned to the attribute; false, otherwise.
+  bool has_a_value_;
+
+ private:
+  int id_;
+  std::string id_string_;
+  Type datatype_;
+  const char *datatype_string_;
+};
+
+class NetlinkU8Attribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkU8Attribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetU8Value(uint8_t *value) const;
+  virtual bool SetU8Value(uint8_t new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  uint8_t value_;
+};
+
+class NetlinkU16Attribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkU16Attribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetU16Value(uint16_t *value) const;
+  virtual bool SetU16Value(uint16_t new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  uint16_t value_;
+};
+
+class NetlinkU32Attribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkU32Attribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetU32Value(uint32_t *value) const;
+  virtual bool SetU32Value(uint32_t new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  uint32_t value_;
+};
+
+class NetlinkU64Attribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkU64Attribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetU64Value(uint64_t *value) const;
+  virtual bool SetU64Value(uint64_t new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  uint64_t value_;
+};
+
+class NetlinkFlagAttribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkFlagAttribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetFlagValue(bool *value) const;
+  virtual bool SetFlagValue(bool new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  bool value_;
+};
+
+class NetlinkStringAttribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkStringAttribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  virtual bool GetStringValue(std::string *value) const;
+  virtual bool SetStringValue(const std::string new_value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ private:
+  std::string value_;
+};
+
+class NetlinkNestedAttribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkNestedAttribute(int id, const char *id_string);
+  virtual bool InitFromNlAttr(const nlattr *data) {
+    LOG(FATAL) << "Try initializing a _specific_ nested type, instead.";
+    return false;
+  }
+  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
+  virtual bool ConstGetNestedAttributeList(
+      AttributeListConstRefPtr *value) const;
+  virtual bool SetNestedHasAValue();
+  virtual void Print(int log_level, int indent) const;
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+
+ protected:
+  // Describes a single nested attribute.  Provides the expected values and
+  // type (including further nesting).  Normally, an array of these, one for
+  // each attribute at one level of nesting is presented, along with the data
+  // to be parsed, to |InitNestedFromNlAttr|.  If the attributes on one level
+  // represent an array, a single |NestedData| is provided and |is_array| is
+  // set (note that one level of nesting either contains _only_ an array or
+  // _no_ array).
+  struct NestedData {
+    nla_policy policy;
+    const char *attribute_name;
+    const NestedData *deeper_nesting;
+    size_t deeper_nesting_size;
+    bool is_array;
+    // TODO(wdg): Add function pointer for a custom attribute parser.
+  };
+
+  // Builds an AttributeList (|list|) that contains all of the attriubtes in
+  // |const_data|.  |const_data| should point to the enclosing nested attribute
+  // header; for the example of the nested attribute NL80211_ATTR_CQM:
+  //    nlattr::nla_type: NL80211_ATTR_CQM <-- const_data points here
+  //    nlattr::nla_len: 12 bytes
+  //      nlattr::nla_type: PKT_LOSS_EVENT (1st and only nested attribute)
+  //      nlattr::nla_len: 8 bytes
+  //      <data>: 0x32
+  // One can assemble (hence, disassemble) a set of child attributes under a
+  // nested attribute parent as an array of elements or as a structure.
+  //
+  // The data is parsed using the expected configuration in |nested_template|.
+  // If the code expects an array, it will pass a single template element and
+  // mark that as an array.
+  static bool InitNestedFromNlAttr(AttributeList *list,
+                                   const NestedData *nested_template,
+                                   size_t nested_template_size,
+                                   const nlattr *const_data);
+
+  static bool ParseNestedArray(AttributeList *list,
+                               const NestedData &nested_template,
+                               const nlattr *const_data);
+
+  static bool ParseNestedStructure(AttributeList *list,
+                                   const NestedData *nested_template,
+                                   size_t nested_template_size,
+                                   const nlattr *const_data);
+
+  // Helper function used by InitNestedFromNlAttr to add a single child
+  // attribute to a nested attribute.
+  static void AddAttributeToNested(AttributeList *list, uint16_t type, size_t i,
+                                   const std::string &attribute_name,
+                                   const nlattr &attr,
+                                   const NestedData &nested_data);
+  AttributeListRefPtr value_;
+};
+
+class NetlinkRawAttribute : public NetlinkAttribute {
+ public:
+  static const char kMyTypeString[];
+  static const Type kType;
+  NetlinkRawAttribute(int id, const char *id_string)
+      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
+  virtual bool InitFromNlAttr(const nlattr *data);
+  // Gets the value of the data (the header is not stored).
+  virtual bool GetRawValue(ByteString *value) const;
+  // Should set the value of the data (not the attribute header).
+  virtual bool SetRawValue(const ByteString value);
+  virtual bool ToString(std::string *value) const;
+  virtual ByteString Encode() const;
+};
+
+class NetlinkAttributeGeneric : public NetlinkRawAttribute {
+ public:
+  explicit NetlinkAttributeGeneric(int id);
+  virtual const char *id_string() const;
+
+ private:
+  std::string id_string_;
+};
+
+}  // namespace shill
+
+#endif  // SHILL_NETLINK_ATTRIBUTE_H_
diff --git a/nl80211_attribute.cc b/nl80211_attribute.cc
index da12040..1216039 100644
--- a/nl80211_attribute.cc
+++ b/nl80211_attribute.cc
@@ -27,919 +27,6 @@
 
 namespace shill {
 
-// This is a type-corrected copy of |nla_for_each_nested| from libnl.
-#define nla_for_each_nested_type_corrected(pos, nla, rem) \
-  for (pos = reinterpret_cast<struct nlattr *>(nla_data(nla)), \
-       rem = nla_len(nla); \
-    nla_ok(pos, rem); \
-    pos = nla_next(pos, &(rem)))
-
-NetlinkAttribute::NetlinkAttribute(int id,
-                                   const char *id_string,
-                                   Type datatype,
-                                   const char *datatype_string)
-    : has_a_value_(false), id_(id), id_string_(id_string), datatype_(datatype),
-      datatype_string_(datatype_string) {}
-
-// static
-NetlinkAttribute *NetlinkAttribute::NewNl80211AttributeFromId(int id) {
-  scoped_ptr<NetlinkAttribute> attr;
-  switch (id) {
-    case NL80211_ATTR_COOKIE:
-      attr.reset(new Nl80211AttributeCookie());
-      break;
-    case NL80211_ATTR_CQM:
-      attr.reset(new Nl80211AttributeCqm());
-      break;
-    case NL80211_ATTR_DISCONNECTED_BY_AP:
-      attr.reset(new Nl80211AttributeDisconnectedByAp());
-      break;
-    case NL80211_ATTR_DURATION:
-      attr.reset(new Nl80211AttributeDuration());
-      break;
-    case NL80211_ATTR_FRAME:
-      attr.reset(new Nl80211AttributeFrame());
-      break;
-    case NL80211_ATTR_GENERATION:
-      attr.reset(new Nl80211AttributeGeneration());
-      break;
-    case NL80211_ATTR_IFINDEX:
-      attr.reset(new Nl80211AttributeIfindex());
-      break;
-    case NL80211_ATTR_KEY_IDX:
-      attr.reset(new Nl80211AttributeKeyIdx());
-      break;
-    case NL80211_ATTR_KEY_SEQ:
-      attr.reset(new Nl80211AttributeKeySeq());
-      break;
-    case NL80211_ATTR_KEY_TYPE:
-      attr.reset(new Nl80211AttributeKeyType());
-      break;
-    case NL80211_ATTR_MAC:
-      attr.reset(new Nl80211AttributeMac());
-      break;
-    case NL80211_ATTR_REASON_CODE:
-      attr.reset(new Nl80211AttributeReasonCode());
-      break;
-    case NL80211_ATTR_REG_ALPHA2:
-      attr.reset(new Nl80211AttributeRegAlpha2());
-      break;
-    case NL80211_ATTR_REG_INITIATOR:
-      attr.reset(new Nl80211AttributeRegInitiator());
-      break;
-    case NL80211_ATTR_REG_TYPE:
-      attr.reset(new Nl80211AttributeRegType());
-      break;
-    case NL80211_ATTR_RESP_IE:
-      attr.reset(new Nl80211AttributeRespIe());
-      break;
-    case NL80211_ATTR_SCAN_FREQUENCIES:
-      attr.reset(new Nl80211AttributeScanFrequencies());
-      break;
-    case NL80211_ATTR_SCAN_SSIDS:
-      attr.reset(new Nl80211AttributeScanSsids());
-      break;
-    case NL80211_ATTR_STA_INFO:
-      attr.reset(new Nl80211AttributeStaInfo());
-      break;
-    case NL80211_ATTR_STATUS_CODE:
-      attr.reset(new Nl80211AttributeStatusCode());
-      break;
-    case NL80211_ATTR_SUPPORT_MESH_AUTH:
-      attr.reset(new Nl80211AttributeSupportMeshAuth());
-      break;
-    case NL80211_ATTR_TIMED_OUT:
-      attr.reset(new Nl80211AttributeTimedOut());
-      break;
-    case NL80211_ATTR_WIPHY_FREQ:
-      attr.reset(new Nl80211AttributeWiphyFreq());
-      break;
-    case NL80211_ATTR_WIPHY:
-      attr.reset(new Nl80211AttributeWiphy());
-      break;
-    case NL80211_ATTR_WIPHY_NAME:
-      attr.reset(new Nl80211AttributeWiphyName());
-      break;
-    default:
-      attr.reset(new NetlinkAttributeGeneric(id));
-      break;
-  }
-  return attr.release();
-}
-
-// Duplicate attribute data, store in map indexed on |id|.
-bool NetlinkAttribute::InitFromNlAttr(const nlattr *other) {
-  if (!other) {
-    LOG(ERROR) << "NULL data";
-    return false;
-  }
-
-  data_ = ByteString(
-      reinterpret_cast<char *>(nla_data(const_cast<nlattr *>(other))),
-      nla_len(const_cast<nlattr *>(other)));
-  return true;
-}
-
-bool NetlinkAttribute::GetU8Value(uint8_t *value) const {
-  LOG(ERROR) << "Attribute is not of type 'U8'";
-  return false;
-}
-
-bool NetlinkAttribute::SetU8Value(uint8_t value) {
-  LOG(ERROR) << "Attribute is not of type 'U8'";
-  return false;
-}
-
-bool NetlinkAttribute::GetU16Value(uint16_t *value) const {
-  LOG(ERROR) << "Attribute is not of type 'U16'";
-  return false;
-}
-
-bool NetlinkAttribute::SetU16Value(uint16_t value) {
-  LOG(ERROR) << "Attribute is not of type 'U16'";
-  return false;
-}
-
-bool NetlinkAttribute::GetU32Value(uint32_t *value) const {
-  LOG(ERROR) << "Attribute is not of type 'U32'";
-  return false;
-}
-
-bool NetlinkAttribute::SetU32Value(uint32_t value) {
-  LOG(ERROR) << "Attribute is not of type 'U32'";
-  return false;
-}
-
-bool NetlinkAttribute::GetU64Value(uint64_t *value) const {
-  LOG(ERROR) << "Attribute is not of type 'U64'";
-  return false;
-}
-
-bool NetlinkAttribute::SetU64Value(uint64_t value) {
-  LOG(ERROR) << "Attribute is not of type 'U64'";
-  return false;
-}
-
-bool NetlinkAttribute::GetFlagValue(bool *value) const {
-  LOG(ERROR) << "Attribute is not of type 'Flag'";
-  return false;
-}
-
-bool NetlinkAttribute::SetFlagValue(bool value) {
-  LOG(ERROR) << "Attribute is not of type 'Flag'";
-  return false;
-}
-
-bool NetlinkAttribute::GetStringValue(string *value) const {
-  LOG(ERROR) << "Attribute is not of type 'String'";
-  return false;
-}
-
-bool NetlinkAttribute::SetStringValue(string value) {
-  LOG(ERROR) << "Attribute is not of type 'String'";
-  return false;
-}
-
-bool NetlinkAttribute::GetNestedAttributeList(AttributeListRefPtr *value) {
-  LOG(ERROR) << "Attribute is not of type 'Nested'";
-  return false;
-}
-
-bool NetlinkAttribute::ConstGetNestedAttributeList(
-    AttributeListConstRefPtr *value) const {
-  LOG(ERROR) << "Attribute is not of type 'Nested'";
-  return false;
-}
-
-bool NetlinkAttribute::SetNestedHasAValue() {
-  LOG(ERROR) << "Attribute is not of type 'Nested'";
-  return false;
-}
-
-bool NetlinkAttribute::GetRawValue(ByteString *value) const {
-  LOG(ERROR) << "Attribute is not of type 'Raw'";
-  return false;
-}
-
-bool NetlinkAttribute::SetRawValue(const ByteString new_value) {
-  LOG(ERROR) << "Attribute is not of type 'Raw'";
-  return false;
-}
-
-void NetlinkAttribute::Print(int log_level, int indent) const {
-  string attribute_value;
-  SLOG(WiFi, log_level) << HeaderToPrint(indent) << " "
-                        << (ToString(&attribute_value) ? attribute_value :
-                            "<DOES NOT EXIST>");
-}
-
-string NetlinkAttribute::RawToString() const {
-  string output = " === RAW: ";
-
-  if (!has_a_value_) {
-    StringAppendF(&output, "(empty)");
-    return output;
-  }
-
-  uint16_t length = data_.GetLength();
-  const uint8_t *const_data = data_.GetConstData();
-
-  StringAppendF(&output, "len=%u", length);
-  output.append(" DATA: ");
-  for (int i =0 ; i < length; ++i) {
-    StringAppendF(&output, "[%d]=%02x ", i, *(const_data)+i);
-  }
-  output.append(" ==== ");
-  return output;
-}
-
-string NetlinkAttribute::HeaderToPrint(int indent) const {
-  static const int kSpacesPerIndent = 2;
-  return StringPrintf("%*s%s(%d) %s %s=",
-            indent * kSpacesPerIndent, "",
-            id_string(),
-            id(),
-            datatype_string(),
-            ((has_a_value()) ?  "": "UNINITIALIZED "));
-}
-
-ByteString NetlinkAttribute::EncodeGeneric(const unsigned char *data,
-                                           size_t num_bytes) const {
-  nlattr header;
-  header.nla_type = id();
-  header.nla_len = nla_attr_size(num_bytes);
-  ByteString result(reinterpret_cast<unsigned char *>(&header), sizeof(header));
-  result.Resize(NLA_HDRLEN);  // Add padding after the header.
-  if (data && (num_bytes != 0)) {
-    result.Append(ByteString(data, num_bytes));
-  }
-  result.Resize(nla_total_size(num_bytes));  // Add padding.
-  return result;
-}
-
-// NetlinkU8Attribute
-
-const char NetlinkU8Attribute::kMyTypeString[] = "uint8_t";
-const NetlinkAttribute::Type NetlinkU8Attribute::kType =
-    NetlinkAttribute::kTypeU8;
-
-bool NetlinkU8Attribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  uint8_t data = NlaGetU8(input);
-  SetU8Value(data);
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-bool NetlinkU8Attribute::GetU8Value(uint8_t *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7) << "U8 attribute " << id_string()
-                  << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkU8Attribute::SetU8Value(uint8_t new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkU8Attribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  uint8_t value;
-  if (!GetU8Value(&value))
-    return false;
-  *output = StringPrintf("%u", value);
-  return true;
-}
-
-ByteString NetlinkU8Attribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(
-      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
-}
-
-
-// NetlinkU16Attribute
-
-const char NetlinkU16Attribute::kMyTypeString[] = "uint16_t";
-const NetlinkAttribute::Type NetlinkU16Attribute::kType =
-    NetlinkAttribute::kTypeU16;
-
-bool NetlinkU16Attribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  uint16_t data = NlaGetU16(input);
-  SetU16Value(data);
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-bool NetlinkU16Attribute::GetU16Value(uint16_t *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "U16 attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkU16Attribute::SetU16Value(uint16_t new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkU16Attribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  uint16_t value;
-  if (!GetU16Value(&value))
-    return false;
-  *output = StringPrintf("%u", value);
-  return true;
-}
-
-ByteString NetlinkU16Attribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(
-      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
-}
-
-// NetlinkU32Attribute::
-
-const char NetlinkU32Attribute::kMyTypeString[] = "uint32_t";
-const NetlinkAttribute::Type NetlinkU32Attribute::kType =
-    NetlinkAttribute::kTypeU32;
-
-bool NetlinkU32Attribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  uint32_t data = NlaGetU32(input);
-  SetU32Value(data);
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-bool NetlinkU32Attribute::GetU32Value(uint32_t *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "U32 attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkU32Attribute::SetU32Value(uint32_t new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkU32Attribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  uint32_t value;
-  if (!GetU32Value(&value))
-    return false;
-  *output = StringPrintf("%" PRIu32, value);
-  return true;
-}
-
-ByteString NetlinkU32Attribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(
-      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
-}
-
-// NetlinkU64Attribute
-
-const char NetlinkU64Attribute::kMyTypeString[] = "uint64_t";
-const NetlinkAttribute::Type NetlinkU64Attribute::kType =
-    NetlinkAttribute::kTypeU64;
-
-bool NetlinkU64Attribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  uint64_t data = NlaGetU64(input);
-  SetU64Value(data);
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-bool NetlinkU64Attribute::GetU64Value(uint64_t *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "U64 attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkU64Attribute::SetU64Value(uint64_t new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkU64Attribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  uint64_t value;
-  if (!GetU64Value(&value))
-    return false;
-  *output = StringPrintf("%" PRIu64, value);
-  return true;
-}
-
-ByteString NetlinkU64Attribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(
-      reinterpret_cast<const unsigned char *>(&value_), sizeof(value_));
-}
-
-// NetlinkFlagAttribute
-
-const char NetlinkFlagAttribute::kMyTypeString[] = "flag";
-const NetlinkAttribute::Type NetlinkFlagAttribute::kType =
-    NetlinkAttribute::kTypeFlag;
-
-bool NetlinkFlagAttribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  // The existence of the parameter means it's true
-  SetFlagValue(true);
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-
-bool NetlinkFlagAttribute::GetFlagValue(bool *output) const {
-  if (output) {
-    // The lack of the existence of the attribute implies 'false'.
-    *output = (has_a_value_) ? value_ : false;
-  }
-  return true;
-}
-
-bool NetlinkFlagAttribute::SetFlagValue(bool new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkFlagAttribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  bool value;
-  if (!GetFlagValue(&value))
-    return false;
-  *output = StringPrintf("%s", value ? "true" : "false");
-  return true;
-}
-
-ByteString NetlinkFlagAttribute::Encode() const {
-  if (has_a_value_ && value_) {
-    return NetlinkAttribute::EncodeGeneric(NULL, 0);
-  }
-  return ByteString();  // Encoding of nothing implies 'false'.
-}
-
-// NetlinkStringAttribute
-
-const char NetlinkStringAttribute::kMyTypeString[] = "string";
-const NetlinkAttribute::Type NetlinkStringAttribute::kType =
-    NetlinkAttribute::kTypeString;
-
-bool NetlinkStringAttribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  SetStringValue(NlaGetString(input));
-  return NetlinkAttribute::InitFromNlAttr(input);
-}
-
-bool NetlinkStringAttribute::GetStringValue(string *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "String attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkStringAttribute::SetStringValue(const string new_value) {
-  value_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkStringAttribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  string value;
-  if (!GetStringValue(&value))
-    return false;
-
-  *output = StringPrintf("'%s'", value.c_str());
-  return true;
-}
-
-ByteString NetlinkStringAttribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(
-      reinterpret_cast<const unsigned char *>(value_.c_str()),
-      value_.size() + 1);
-}
-
-// NetlinkNestedAttribute
-
-const char NetlinkNestedAttribute::kMyTypeString[] = "nested";
-const NetlinkAttribute::Type NetlinkNestedAttribute::kType =
-    NetlinkAttribute::kTypeNested;
-
-NetlinkNestedAttribute::NetlinkNestedAttribute(int id,
-                                               const char *id_string) :
-    NetlinkAttribute(id, id_string, kType, kMyTypeString),
-    value_(new AttributeList) {}
-
-ByteString NetlinkNestedAttribute::Encode() const {
-  // Encode attribute header.
-  nlattr header;
-  header.nla_type = id();
-  header.nla_len = nla_attr_size(sizeof(header));
-  ByteString result(reinterpret_cast<unsigned char *>(&header), sizeof(header));
-  result.Resize(NLA_HDRLEN);  // Add padding after the header.
-
-  // Encode all nested attributes.
-  std::map<int, AttributeList::AttributePointer>::const_iterator attribute;
-  for (attribute = value_->attributes_.begin();
-       attribute != value_->attributes_.end();
-       ++attribute) {
-    // Each attribute appends appropriate padding so it's not necessary to
-    // re-add padding.
-    result.Append(attribute->second->Encode());
-  }
-
-  // Go back and fill-in the size.
-  nlattr *new_header = reinterpret_cast<nlattr *>(result.GetData());
-  new_header->nla_len = result.GetLength();
-
-  return result;
-}
-
-void NetlinkNestedAttribute::Print(int log_level, int indent) const {
-  SLOG(WiFi, log_level) << HeaderToPrint(indent);
-  value_->Print(log_level, indent + 1);
-}
-
-bool NetlinkNestedAttribute::ToString(std::string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-
-  // This should never be called (attribute->ToString is only called
-  // from attribute->Print but NetlinkNestedAttribute::Print doesn't call
-  // |ToString|.  Still, we should print something in case we got here
-  // accidentally.
-  LOG(WARNING) << "It is unexpected for this method to be called.";
-  output->append("<Nested Attribute>");
-  return true;
-}
-
-bool NetlinkNestedAttribute::GetNestedAttributeList(
-    AttributeListRefPtr *output) {
-  // Not checking |has_a_value| since GetNestedAttributeList is called to get
-  // a newly created AttributeList in order to have something to which to add
-  // attributes.
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkNestedAttribute::ConstGetNestedAttributeList(
-    AttributeListConstRefPtr *output) const {
-  if (!has_a_value_) {
-    LOG(ERROR) << "Attribute does not exist.";
-    return false;
-  }
-  if (output) {
-    *output = value_;
-  }
-  return true;
-}
-
-bool NetlinkNestedAttribute::SetNestedHasAValue() {
-  has_a_value_ = true;
-  return true;
-}
-
-// static
-bool NetlinkNestedAttribute::InitNestedFromNlAttr(
-    AttributeList *list,
-    const NestedData *nested_template,
-    size_t nested_template_size,
-    const nlattr *const_data) {
-  if (!nested_template) {
-    LOG(ERROR) << "Null |nested_template| parameter";
-    return false;
-  }
-  if ((nested_template_size == 1) && (nested_template[0].is_array)) {
-    return ParseNestedArray(list, *nested_template, const_data);
-  } else {
-    return ParseNestedStructure(list, nested_template, nested_template_size,
-                                const_data);
-  }
-  return true;
-}
-
-// A nested array provides an arbitrary number of children, all of the same
-// data type.  Each array element may be a simple type or may be a structure.
-//
-// static
-bool NetlinkNestedAttribute::ParseNestedArray(
-    AttributeList *list,
-    const NestedData &array_template,
-    const nlattr *const_data) {
-  if (!list) {
-    LOG(ERROR) << "NULL |list| parameter";
-    return false;
-  }
-  if (!const_data) {
-    LOG(ERROR) << "Null |const_data| parameter";
-    return false;
-  }
-  // Casting away constness since |nla_parse_nested| doesn't mark its input as
-  // const even though it doesn't modify this input parameter.
-  nlattr *attrs = const_cast<nlattr *>(const_data);
-
-  struct nlattr *attr;
-  int remaining;
-  // The |nlattr::nla_type| value for array elements in the provided data may
-  // start on any number and are allowed to be discontiguous.  In order to
-  // skirt writing an iterator, this code replaces the |nla_type| with a
-  // contiguous |id|, starting at 1 (note that, while nested structure
-  // attributes may not have an |nlattr::nla_type| valued at zero, no such
-  // restriction exists for nested array attributes -- this code starts the id
-  // at zero in order to be consistent with nested structures).
-  //
-  // TODO(wdg): Determine whether any code depends on the value of
-  // |nlattr::nla_type| for nested array attributes.
-  int id = 1;
-  nla_for_each_nested_type_corrected(attr, attrs, remaining) {
-    string attribute_name = StringPrintf("%s_%d",
-                                         array_template.attribute_name, id);
-    AddAttributeToNested(list, array_template.policy.type, id, attribute_name,
-                         *attr, array_template);
-    ++id;
-  }
-  return true;
-}
-
-// A nested structure provides a fixed set of child attributes (some of
-// which may be optional).  The caller provides the expectation of the members
-// and values of a nested structure in the supplied 'policy' template
-// (|struct_template|).
-//
-// static
-bool NetlinkNestedAttribute::ParseNestedStructure(
-    AttributeList *list,
-    const NestedData *struct_template,
-    size_t nested_template_size,
-    const nlattr *const_data) {
-  if (!list) {
-    LOG(ERROR) << "NULL |list| parameter";
-    return false;
-  }
-  if (!struct_template) {
-    LOG(ERROR) << "Null |struct_template| parameter";
-    return false;
-  }
-  if (nested_template_size == 0) {
-    LOG(ERROR) << "|nested_template_size| parameter is zero";
-    return false;
-  }
-  if (!const_data) {
-    LOG(ERROR) << "Null |const_data| parameter";
-    return false;
-  }
-  // Casting away constness since |nla_parse_nested| doesn't mark its input as
-  // const even though it doesn't modify this input parameter.
-  nlattr *attr_data = const_cast<nlattr *>(const_data);
-
-  // |nla_parse_nested| requires an array of |nla_policy|. While an attribute id
-  // of zero is illegal, we still need to fill that spot in the policy
-  // array so the loop will start at zero.
-  scoped_array<nla_policy> policy(new nla_policy[nested_template_size]);
-  for (size_t id = 0; id < nested_template_size ; ++id) {
-    memcpy(&policy[id], &struct_template[id].policy, sizeof(nla_policy));
-  }
-
-  // |nla_parse_nested| builds an array of |nlattr| from the input message.
-  scoped_array<nlattr *>attr(new nlattr *[nested_template_size]);
-  if (nla_parse_nested(attr.get(), nested_template_size-1, attr_data,
-                       policy.get())) {
-    LOG(ERROR) << "nla_parse_nested failed";
-    return false;
-  }
-
-  // Note that the attribute id of zero is illegal so we'll start with id=1.
-  for (size_t id = 1; id < nested_template_size; ++id) {
-    // Add |attr[id]| if it exists, otherwise, it's a legally omitted optional
-    // attribute.
-    if (attr[id]) {
-      AddAttributeToNested(list, policy[id].type, id,
-                           struct_template[id].attribute_name, *attr[id],
-                           struct_template[id]);
-    }
-  }
-  return true;
-}
-
-// static
-void NetlinkNestedAttribute::AddAttributeToNested(
-    AttributeList *list, uint16_t type, size_t id, const string &attribute_name,
-    const nlattr &attr, const NestedData &nested_template) {
-  switch (type) {
-    case NLA_UNSPEC:
-      list->CreateRawAttribute(id, attribute_name.c_str());
-      list->SetRawAttributeValue(id, ByteString(
-          reinterpret_cast<const char *>(nla_data(&attr)), nla_len(&attr)));
-      break;
-    case NLA_U8:
-      list->CreateU8Attribute(id, attribute_name.c_str());
-      list->SetU8AttributeValue(id, NlaGetU8(&attr));
-      break;
-    case NLA_U16:
-      list->CreateU16Attribute(id, attribute_name.c_str());
-      list->SetU16AttributeValue(id, NlaGetU16(&attr));
-      break;
-    case NLA_U32:
-      list->CreateU32Attribute(id, attribute_name.c_str());
-      list->SetU32AttributeValue(id, NlaGetU32(&attr));
-      break;
-    case NLA_U64:
-      list->CreateU64Attribute(id, attribute_name.c_str());
-      list->SetU64AttributeValue(id, NlaGetU64(&attr));
-      break;
-    case NLA_FLAG:
-      list->CreateFlagAttribute(id, attribute_name.c_str());
-      list->SetFlagAttributeValue(id, true);
-      break;
-    case NLA_STRING:
-      // Note that nested structure attributes are validated by |validate_nla|
-      // which requires a string attribute to have at least 1 character
-      // (presumably for the '\0') while the kernel can create an empty string
-      // for at least one nested string array attribute type
-      // (NL80211_ATTR_SCAN_SSIDS -- the emptyness of the string is exhibited
-      // by the value of |nlattr::nla_len|).  This code handles both cases.
-      list->CreateStringAttribute(id, attribute_name.c_str());
-      if (nla_len(&attr) <= 0) {
-        list->SetStringAttributeValue(id, "");
-      } else {
-        list->SetStringAttributeValue(id, NlaGetString(&attr));
-      }
-      break;
-    case NLA_NESTED:
-      {
-        if (!nested_template.deeper_nesting) {
-          LOG(ERROR) << "No rules for nesting " << attribute_name
-                     << ". Ignoring.";
-          break;
-        }
-        list->CreateNestedAttribute(id, attribute_name.c_str());
-
-        // Now, handle the nested data.
-        AttributeListRefPtr nested_attribute;
-        if (!list->GetNestedAttributeList(id, &nested_attribute) ||
-            !nested_attribute) {
-          LOG(FATAL) << "Couldn't get attribute " << attribute_name
-                     << " which we just created.";
-          break;
-        }
-
-        if (!InitNestedFromNlAttr(nested_attribute.get(),
-                                  nested_template.deeper_nesting,
-                                  nested_template.deeper_nesting_size,
-                                  &attr)) {
-          LOG(ERROR) << "Couldn't parse attribute " << attribute_name;
-          break;
-        }
-        list->SetNestedAttributeHasAValue(id);
-      }
-      break;
-    default:
-      LOG(ERROR) << "Discarding " << attribute_name
-                 << ".  Attribute has unhandled type " << type << ".";
-      break;
-  }
-}
-
-// NetlinkRawAttribute
-
-const char NetlinkRawAttribute::kMyTypeString[] = "<raw>";
-const NetlinkAttribute::Type NetlinkRawAttribute::kType =
-    NetlinkAttribute::kTypeRaw;
-
-bool NetlinkRawAttribute::InitFromNlAttr(const nlattr *input) {
-  if (!input) {
-    LOG(ERROR) << "Null |input| parameter";
-    return false;
-  }
-
-  if (!NetlinkAttribute::InitFromNlAttr(input)) {
-    return false;
-  }
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkRawAttribute::GetRawValue(ByteString *output) const {
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "Raw attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  if (output) {
-    *output = data_;
-  }
-  return true;
-}
-
-bool NetlinkRawAttribute::SetRawValue(const ByteString new_value) {
-  data_ = new_value;
-  has_a_value_ = true;
-  return true;
-}
-
-bool NetlinkRawAttribute::ToString(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-  if (!has_a_value_) {
-    SLOG(WiFi, 7)  << "Raw attribute " << id_string()
-                   << " hasn't been set to any value.";
-    return false;
-  }
-  int total_bytes = data_.GetLength();
-  const uint8_t *const_data = data_.GetConstData();
-
-  *output = StringPrintf("%d bytes:", total_bytes);
-  for (int i = 0; i < total_bytes; ++i) {
-    StringAppendF(output, " 0x%02x", const_data[i]);
-  }
-  return true;
-}
-
-ByteString NetlinkRawAttribute::Encode() const {
-  return NetlinkAttribute::EncodeGeneric(data_.GetConstData(),
-                                         data_.GetLength());
-}
-
-// Specific Attributes.
-
-
 const int Nl80211AttributeCookie::kName = NL80211_ATTR_COOKIE;
 const char Nl80211AttributeCookie::kNameString[] = "NL80211_ATTR_COOKIE";
 
@@ -1149,13 +236,4 @@
 const int Nl80211AttributeWiphyName::kName = NL80211_ATTR_WIPHY_NAME;
 const char Nl80211AttributeWiphyName::kNameString[] = "NL80211_ATTR_WIPHY_NAME";
 
-NetlinkAttributeGeneric::NetlinkAttributeGeneric(int id)
-    : NetlinkRawAttribute(id, "unused-string") {
-  StringAppendF(&id_string_, "<UNKNOWN ATTRIBUTE %d>", id);
-}
-
-const char *NetlinkAttributeGeneric::id_string() const {
-  return id_string_.c_str();
-}
-
 }  // namespace shill
diff --git a/nl80211_attribute.h b/nl80211_attribute.h
index b1ace45..9cc197e 100644
--- a/nl80211_attribute.h
+++ b/nl80211_attribute.h
@@ -2,169 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SHILL_NLATTRIBUTE_H_
-#define SHILL_NLATTRIBUTE_H_
+#ifndef SHILL_NL80211_ATTRIBUTE_H_
+#define SHILL_NL80211_ATTRIBUTE_H_
 
-#include <linux/nl80211.h>
 #include <netlink/attr.h>
-#include <netlink/netlink.h>
 
-#include <map>
-#include <string>
-
-#include "shill/attribute_list.h"
-#include "shill/byte_string.h"
-#include "shill/logging.h"
+#include "shill/netlink_attribute.h"
 #include "shill/refptr_types.h"
 
 struct nlattr;
 
 namespace shill {
 
-// NetlinkAttribute is an abstract base class that describes an attribute in a
-// netlink-80211 message.  Child classes are type-specific and will define
-// Get*Value and Set*Value methods (where * is the type).  A second-level of
-// child classes exist for each individual attribute type.
-//
-// An attribute has an id (which is really an enumerated value), a data type,
-// and a value.  In an nlattr (the underlying format for an attribute in a
-// message), the data is stored as a blob without type information; the writer
-// and reader of the attribute must agree on the data type.
-class NetlinkAttribute {
- public:
-  enum Type {
-    kTypeU8,
-    kTypeU16,
-    kTypeU32,
-    kTypeU64,
-    kTypeFlag,
-    kTypeString,
-    kTypeNested,
-    kTypeRaw,
-    kTypeError
-  };
-
-  NetlinkAttribute(int id, const char *id_string,
-                   Type datatype, const char *datatype_string);
-  virtual ~NetlinkAttribute() {}
-
-  virtual bool InitFromNlAttr(const nlattr *data);
-
-  // Accessors for the attribute's id and datatype information.
-  int id() const { return id_; }
-  virtual const char *id_string() const { return id_string_.c_str(); }
-  Type datatype() const { return datatype_; }
-  const char *datatype_string() const { return datatype_string_; }
-
-  // Accessors.  Return false if request is made on wrong type of attribute.
-  virtual bool GetU8Value(uint8_t *value) const;
-  virtual bool SetU8Value(uint8_t new_value);
-
-  virtual bool GetU16Value(uint16_t *value) const;
-  virtual bool SetU16Value(uint16_t value);
-
-  virtual bool GetU32Value(uint32_t *value) const;
-  virtual bool SetU32Value(uint32_t value);
-
-  virtual bool GetU64Value(uint64_t *value) const;
-  virtual bool SetU64Value(uint64_t value);
-
-  virtual bool GetFlagValue(bool *value) const;
-  virtual bool SetFlagValue(bool value);
-
-  virtual bool GetStringValue(std::string *value) const;
-  virtual bool SetStringValue(const std::string value);
-
-  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
-  virtual bool ConstGetNestedAttributeList(
-      AttributeListConstRefPtr *value) const;
-  virtual bool SetNestedHasAValue();
-
-  virtual bool GetRawValue(ByteString *value) const;
-  virtual bool SetRawValue(const ByteString value);
-
-  // Prints the attribute info -- for debugging.
-  virtual void Print(int log_level, int indent) const;
-
-  // Fill a string with characters that represents the value of the attribute.
-  // If no attribute is found or if the datatype isn't trivially stringizable,
-  // this method returns 'false' and |value| remains unchanged.
-  virtual bool ToString(std::string *value) const = 0;
-
-  // Writes the raw attribute data to a string.  For debug.
-  std::string RawToString() const;
-
-  // Encodes the attribute suitably for the attributes in the payload portion
-  // of a netlink message suitable for Sockets::Send.  Return value is empty on
-  // failure.
-  virtual ByteString Encode() const = 0;
-
-  bool has_a_value() const { return has_a_value_; }
-
-  // Wrappers for libnl parsers.  Needed because, while |nla_get_*| don't
-  // change their arguments, these methods don't declare themselves as 'const'.
-  static char *NlaGetString(const nlattr *input) {
-    return nla_get_string(const_cast<nlattr *>(input));
-  }
-  static uint8_t NlaGetU8(const nlattr *input) {
-    return nla_get_u8(const_cast<nlattr *>(input));
-  }
-  static uint16_t NlaGetU16(const nlattr *input) {
-    return nla_get_u16(const_cast<nlattr *>(input));
-  }
-  static uint32_t NlaGetU32(const nlattr *input) {
-    return nla_get_u32(const_cast<nlattr *>(input));
-  }
-  static uint64_t NlaGetU64(const nlattr *input) {
-    return nla_get_u64(const_cast<nlattr *>(input));
-  }
-
-  // Static factories generate the appropriate attribute object from the
-  // raw nlattr data.
-  static NetlinkAttribute *NewControlAttributeFromId(int id);
-  static NetlinkAttribute *NewNl80211AttributeFromId(int id);
-
- protected:
-  // Builds a string to precede a printout of this attribute.
-  std::string HeaderToPrint(int indent) const;
-
-  // Encodes the attribute suitably for the attributes in the payload portion
-  // of a netlink message suitable for Sockets::Send.  Return value is empty on
-  // failure.
-  ByteString EncodeGeneric(const unsigned char *data, size_t num_bytes) const;
-
-  // Attribute data (NOT including the nlattr header) corresponding to the
-  // value in any of the child classes.
-  ByteString data_;
-
-  // True if a value has been assigned to the attribute; false, otherwise.
-  bool has_a_value_;
-
- private:
-  int id_;
-  std::string id_string_;
-  Type datatype_;
-  const char *datatype_string_;
-};
-
 // U8.
 
-class NetlinkU8Attribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkU8Attribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetU8Value(uint8_t *value) const;
-  virtual bool SetU8Value(uint8_t new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  uint8_t value_;
-};
-
 class Nl80211AttributeKeyIdx : public NetlinkU8Attribute {
  public:
   static const int kName;
@@ -181,22 +32,6 @@
 
 // U16.
 
-class NetlinkU16Attribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkU16Attribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetU16Value(uint16_t *value) const;
-  virtual bool SetU16Value(uint16_t new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  uint16_t value_;
-};
-
 class Nl80211AttributeReasonCode : public NetlinkU16Attribute {
  public:
   static const int kName;
@@ -213,22 +48,6 @@
 
 // U32.
 
-class NetlinkU32Attribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkU32Attribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetU32Value(uint32_t *value) const;
-  virtual bool SetU32Value(uint32_t new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  uint32_t value_;
-};
-
 class Nl80211AttributeDuration : public NetlinkU32Attribute {
  public:
   static const int kName;
@@ -280,22 +99,6 @@
 
 // U64.
 
-class NetlinkU64Attribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkU64Attribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetU64Value(uint64_t *value) const;
-  virtual bool SetU64Value(uint64_t new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  uint64_t value_;
-};
-
 class Nl80211AttributeCookie : public NetlinkU64Attribute {
  public:
   static const int kName;
@@ -305,22 +108,6 @@
 
 // Flag.
 
-class NetlinkFlagAttribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkFlagAttribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetFlagValue(bool *value) const;
-  virtual bool SetFlagValue(bool new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  bool value_;
-};
-
 class Nl80211AttributeDisconnectedByAp : public NetlinkFlagAttribute {
  public:
   static const int kName;
@@ -346,22 +133,6 @@
 
 // String.
 
-class NetlinkStringAttribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkStringAttribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  virtual bool GetStringValue(std::string *value) const;
-  virtual bool SetStringValue(const std::string new_value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- private:
-  std::string value_;
-};
-
 class Nl80211AttributeRegAlpha2 : public NetlinkStringAttribute {
  public:
   static const int kName;
@@ -378,77 +149,6 @@
 
 // Nested.
 
-class NetlinkNestedAttribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkNestedAttribute(int id, const char *id_string);
-  virtual bool InitFromNlAttr(const nlattr *data) {
-    LOG(FATAL) << "Try initializing a _specific_ nested type, instead.";
-    return false;
-  }
-  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
-  virtual bool ConstGetNestedAttributeList(
-      AttributeListConstRefPtr *value) const;
-  virtual bool SetNestedHasAValue();
-  virtual void Print(int log_level, int indent) const;
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-
- protected:
-  // Describes a single nested attribute.  Provides the expected values and
-  // type (including further nesting).  Normally, an array of these, one for
-  // each attribute at one level of nesting is presented, along with the data
-  // to be parsed, to |InitNestedFromNlAttr|.  If the attributes on one level
-  // represent an array, a single |NestedData| is provided and |is_array| is
-  // set (note that one level of nesting either contains _only_ an array or
-  // _no_ array).
-  struct NestedData {
-    nla_policy policy;
-    const char *attribute_name;
-    const NestedData *deeper_nesting;
-    size_t deeper_nesting_size;
-    bool is_array;
-    // TODO(wdg): Add function pointer for a custom attribute parser.
-  };
-
-  // Builds an AttributeList (|list|) that contains all of the attriubtes in
-  // |const_data|.  |const_data| should point to the enclosing nested attribute
-  // header; for the example of the nested attribute NL80211_ATTR_CQM:
-  //    nlattr::nla_type: NL80211_ATTR_CQM <-- const_data points here
-  //    nlattr::nla_len: 12 bytes
-  //      nlattr::nla_type: PKT_LOSS_EVENT (1st and only nested attribute)
-  //      nlattr::nla_len: 8 bytes
-  //      <data>: 0x32
-  // One can assemble (hence, disassemble) a set of child attributes under a
-  // nested attribute parent as an array of elements or as a structure.
-  //
-  // The data is parsed using the expected configuration in |nested_template|.
-  // If the code expects an array, it will pass a single template element and
-  // mark that as an array.
-  static bool InitNestedFromNlAttr(AttributeList *list,
-                                   const NestedData *nested_template,
-                                   size_t nested_template_size,
-                                   const nlattr *const_data);
-
-  static bool ParseNestedArray(AttributeList *list,
-                               const NestedData &nested_template,
-                               const nlattr *const_data);
-
-  static bool ParseNestedStructure(AttributeList *list,
-                                   const NestedData *nested_template,
-                                   size_t nested_template_size,
-                                   const nlattr *const_data);
-
-  // Helper function used by InitNestedFromNlAttr to add a single child
-  // attribute to a nested attribute.
-  static void AddAttributeToNested(AttributeList *list, uint16_t type, size_t i,
-                                   const std::string &attribute_name,
-                                   const nlattr &attr,
-                                   const NestedData &nested_data);
-  AttributeListRefPtr value_;
-};
-
 class Nl80211AttributeCqm : public NetlinkNestedAttribute {
  public:
   static const int kName;
@@ -483,21 +183,6 @@
 
 // Raw.
 
-class NetlinkRawAttribute : public NetlinkAttribute {
- public:
-  static const char kMyTypeString[];
-  static const Type kType;
-  NetlinkRawAttribute(int id, const char *id_string)
-      : NetlinkAttribute(id, id_string, kType, kMyTypeString) {}
-  virtual bool InitFromNlAttr(const nlattr *data);
-  // Gets the value of the data (the header is not stored).
-  virtual bool GetRawValue(ByteString *value) const;
-  // Should set the value of the data (not the attribute header).
-  virtual bool SetRawValue(const ByteString value);
-  virtual bool ToString(std::string *value) const;
-  virtual ByteString Encode() const;
-};
-
 class Nl80211AttributeFrame : public NetlinkRawAttribute {
  public:
   static const int kName;
@@ -505,15 +190,6 @@
   Nl80211AttributeFrame() : NetlinkRawAttribute(kName, kNameString) {}
 };
 
-class NetlinkAttributeGeneric : public NetlinkRawAttribute {
- public:
-  explicit NetlinkAttributeGeneric(int id);
-  virtual const char *id_string() const;
-
- private:
-  std::string id_string_;
-};
-
 class Nl80211AttributeKeySeq : public NetlinkRawAttribute {
  public:
   static const int kName;
diff --git a/nl80211_message.cc b/nl80211_message.cc
index 34381e0..fe60011 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -49,8 +49,8 @@
 #include "shill/attribute_list.h"
 #include "shill/ieee80211.h"
 #include "shill/logging.h"
+#include "shill/netlink_attribute.h"
 #include "shill/netlink_socket.h"
-#include "shill/nl80211_attribute.h"
 #include "shill/scope_logger.h"
 #include "shill/wifi.h"