shill: Added AttributeList.

Moved Nl80211Attribute types to a new AttributeList type.  This enables
handling nested attributes (which are shivering with the anticipation of
being added in the next checkin).  Lots of attribute-specific accessors
were moved from UserBoundNlMessage to AttributeList.

As part of this checkin, AttributeIterators were removed (direct
Attribute access was removed from the public interface and better ways to
do everything the iterator did were found).  A moment of silence, please.

The requirement of a non-NULL pointer parameter was lifted in the
Get*AttributeValue methods.  This allows code to check for the existence
of valid data for an attribute without having to supply a dummy
parameter.

BUG=chromium-os:36637
TEST=unittests.

Change-Id: I5f9212189508c1f95e74b8882956034a8642cdbb
Reviewed-on: https://gerrit.chromium.org/gerrit/39868
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/Makefile b/Makefile
index b903cd4..e5f4973 100644
--- a/Makefile
+++ b/Makefile
@@ -155,6 +155,7 @@
 	arp_client.o \
 	arp_packet.o \
 	async_connection.o \
+	attribute_list.o \
 	byte_string.o \
 	callback80211_metrics.o \
 	callback80211_object.o \
diff --git a/attribute_list.cc b/attribute_list.cc
new file mode 100644
index 0000000..59e226e
--- /dev/null
+++ b/attribute_list.cc
@@ -0,0 +1,181 @@
+// 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/attribute_list.h"
+
+#include <ctype.h>
+#include <linux/nl80211.h>
+#include <netlink/attr.h>
+#include <netlink/netlink.h>
+
+#include <iomanip>
+#include <map>
+#include <string>
+
+#include <base/stl_util.h>
+
+#include "shill/logging.h"
+#include "shill/nl80211_attribute.h"
+#include "shill/scope_logger.h"
+
+using std::string;
+using std::map;
+
+namespace shill {
+
+AttributeList::~AttributeList() {
+  map<int, Nl80211Attribute *>::iterator i;
+  for (i = attributes_.begin(); i != attributes_.end(); ++i) {
+    delete i->second;
+  }
+}
+
+bool AttributeList::CreateAttribute(nl80211_attrs name) {
+  if (ContainsKey(attributes_, name)) {
+    LOG(ERROR) << "Trying to re-add attribute: " << name;
+    return false;
+  }
+  attributes_[name] = Nl80211Attribute::NewFromName(name);
+  return true;
+}
+
+bool AttributeList::CreateAndInitFromNlAttr(nl80211_attrs name,
+                                            const nlattr *data) {
+  if (!CreateAttribute(name)) {
+    return false;
+  }
+  return attributes_[name]->InitFromNlAttr(data);
+}
+
+// U8 Attribute.
+
+bool AttributeList::GetU8AttributeValue(nl80211_attrs name,
+                                        uint8_t *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeU8));
+  const Nl80211U8Attribute *attr =
+      reinterpret_cast<const Nl80211U8Attribute *>(GetAttribute(name));
+  return attr->GetU8Value(value);
+}
+
+// U16 Attribute.
+
+bool AttributeList::GetU16AttributeValue(nl80211_attrs name,
+                                         uint16_t *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeU16));
+  const Nl80211U16Attribute *attr =
+      reinterpret_cast<const Nl80211U16Attribute *>(GetAttribute(name));
+  return attr->GetU16Value(value);
+}
+
+// U32 Attribute.
+
+bool AttributeList::GetU32AttributeValue(nl80211_attrs name,
+                                         uint32_t *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeU32));
+  const Nl80211U32Attribute *attr =
+      reinterpret_cast<const Nl80211U32Attribute *>(GetAttribute(name));
+  return attr->GetU32Value(value);
+}
+
+// U64 Attribute.
+
+bool AttributeList::GetU64AttributeValue(nl80211_attrs name,
+                                         uint64_t *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeU64));
+  const Nl80211U64Attribute *attr =
+      reinterpret_cast<const Nl80211U64Attribute *>(GetAttribute(name));
+  return attr->GetU64Value(value);
+}
+
+// Flag Attribute.
+
+bool AttributeList::GetFlagAttributeValue(nl80211_attrs name,
+                                          bool *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeFlag));
+  const Nl80211FlagAttribute *attr =
+      reinterpret_cast<const Nl80211FlagAttribute *>(GetAttribute(name));
+  return attr->GetFlagValue(value);
+}
+
+bool AttributeList::IsFlagAttributeTrue(nl80211_attrs name) const {
+  bool flag;
+
+  // TODO(wdg): After message constructors add attributes, remove the following
+  // lines.
+  if (!HasAttribute(name, Nl80211Attribute::kTypeFlag)) {
+    return false;
+  }
+
+  if (!GetFlagAttributeValue(name, &flag)) {
+    return false;
+  }
+  return flag;
+}
+
+// String Attribute.
+
+bool AttributeList::GetStringAttributeValue(nl80211_attrs name,
+                                            string *value) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeString));
+  const Nl80211StringAttribute *attr =
+      reinterpret_cast<const Nl80211StringAttribute *>(GetAttribute(name));
+  return attr->GetStringValue(value);
+}
+
+// Raw Attribute.
+
+bool AttributeList::GetRawAttributeValue(nl80211_attrs name,
+                                         ByteString *output) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeRaw));
+  const Nl80211RawAttribute *attr =
+      reinterpret_cast<const Nl80211RawAttribute *>(GetAttribute(name));
+
+  ByteString raw_value;
+
+  if (!attr->GetRawValue(&raw_value))
+    return false;
+
+  if (output) {
+    const nlattr *const_data =
+        reinterpret_cast<const nlattr *>(raw_value.GetConstData());
+    // nla_data and nla_len don't change their parameters but don't declare
+    // them to be const.  Hence the cast.
+    nlattr *data_nlattr = const_cast<nlattr *>(const_data);
+    *output = ByteString(
+        reinterpret_cast<unsigned char *>(nla_data(data_nlattr)),
+        nla_len(data_nlattr));
+  }
+  return true;
+}
+
+const Nl80211RawAttribute *AttributeList::GetRawAttribute(
+    nl80211_attrs name) const {
+  CHECK(HasAttribute(name, Nl80211Attribute::kTypeRaw));
+  const Nl80211RawAttribute *attr =
+      reinterpret_cast<const Nl80211RawAttribute *>(GetAttribute(name));
+  return attr;
+}
+
+Nl80211Attribute *AttributeList::GetAttribute(nl80211_attrs name) const {
+  map<int, Nl80211Attribute *>::const_iterator i;
+  i = attributes_.find(name);
+  if (i == attributes_.end()) {
+    return NULL;
+  }
+  return i->second;
+}
+
+bool AttributeList::HasAttribute(nl80211_attrs name,
+                                 Nl80211Attribute::Type type) const {
+  map<int, Nl80211Attribute *>::const_iterator i;
+  i = attributes_.find(name);
+  if (i == attributes_.end()) {
+    LOG(ERROR) << "FALSE - Didn't find name " << name;
+    return false;
+  }
+  return (i->second->type() == type) ? true : false;
+}
+
+
+}  // namespace shill
diff --git a/attribute_list.h b/attribute_list.h
new file mode 100644
index 0000000..3ca29ed
--- /dev/null
+++ b/attribute_list.h
@@ -0,0 +1,67 @@
+// 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_ATTRIBUTE_LIST_H_
+#define SHILL_ATTRIBUTE_LIST_H_
+
+#include <linux/nl80211.h>
+#include <netlink/netlink.h>
+
+#include <map>
+#include <string>
+
+#include "shill/nl80211_attribute.h"
+
+struct nlattr;
+namespace shill {
+
+class Nl80211RawAttribute;
+
+class AttributeList {
+ public:
+  ~AttributeList();
+
+  // Instantiates an Nl80211Attribute of the appropriate type from |name|,
+  // and adds it to |attributes_|.
+  bool CreateAttribute(nl80211_attrs name);
+
+  // Instantiates an Nl80211Attribute of the appropriate type from |name|,
+  // initializes it from |data|, and adds it to |attributes_|.
+  // TODO(wdg): This is a stop-gap for use before message constructors add
+  // their attributes as message templates.
+  bool CreateAndInitFromNlAttr(nl80211_attrs name, const nlattr *data);
+
+  bool GetU8AttributeValue(nl80211_attrs name, uint8_t *value) const;
+  bool GetU16AttributeValue(nl80211_attrs name, uint16_t *value) const;
+  bool GetU32AttributeValue(nl80211_attrs name, uint32_t *value) const;
+  bool GetU64AttributeValue(nl80211_attrs name, uint64_t *value) const;
+  bool GetFlagAttributeValue(nl80211_attrs name, bool *value) const;
+  // |IsFlagAttributeTrue| returns true if the flag attribute named |name| is
+  // true.  It retruns false if the attribute does not exist, is not of type
+  // kTypeFlag, or is not true.
+  bool IsFlagAttributeTrue(nl80211_attrs name) const;
+  bool GetStringAttributeValue(nl80211_attrs name, std::string *value) const;
+
+  // A raw attribute is a place to store unrecognized attributes when they
+  // from the kernel.  For this reason, only limited support is provided for
+  // them.
+  bool GetRawAttributeValue(nl80211_attrs name, ByteString *output) const;
+  // TODO(wdg): |GetRawAttribute| is a stopgap to support various
+  // UserBoundNlMessage::ToString methods and must, once those are re-written,
+  // be destroyed.
+  const Nl80211RawAttribute *GetRawAttribute(nl80211_attrs name) const;
+
+ private:
+  // Using this to get around issues with const and operator[].
+  Nl80211Attribute *GetAttribute(nl80211_attrs name) const;
+
+  bool HasAttribute(nl80211_attrs name, Nl80211Attribute::Type type) const;
+
+  std::map<int, Nl80211Attribute *> attributes_;
+};
+
+}  // namespace shill
+
+#endif  // SHILL_ATTRIBUTE_LIST_H_
+
diff --git a/callback80211_metrics.cc b/callback80211_metrics.cc
index fb5d0f1..41cf24d 100644
--- a/callback80211_metrics.cc
+++ b/callback80211_metrics.cc
@@ -25,12 +25,14 @@
   if (metrics_ &&
       message.message_type() == DeauthenticateMessage::kCommand) {
     Metrics::WiFiDisconnectByWhom by_whom =
-        message.AttributeExists(NL80211_ATTR_DISCONNECTED_BY_AP) ?
+        message.attributes().IsFlagAttributeTrue(
+            NL80211_ATTR_DISCONNECTED_BY_AP) ?
                     Metrics::kDisconnectedByAp : Metrics::kDisconnectedNotByAp;
     uint16_t reason = static_cast<uint16_t>(
         IEEE_80211::kReasonCodeInvalid);
     ByteString rawdata;
-    if (message.GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata)) {
+    if (message.attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                  &rawdata)) {
       Nl80211Frame frame(rawdata);
       reason = frame.reason();
     }
diff --git a/callback80211_object.cc b/callback80211_object.cc
index 45b229b..6d9c9d7 100644
--- a/callback80211_object.cc
+++ b/callback80211_object.cc
@@ -44,15 +44,7 @@
   SLOG(WiFi, 3) << "Received " << message.message_type_string()
                 << " (" << + message.message_type() << ")";
 
-  for (UserBoundNlMessage::AttributeIterator i(message.GetAttributeIterator());
-       !i.AtEnd(); i.Advance()) {
-    const Nl80211Attribute *attribute = i.GetAttribute();
-    string attribute_string;
-    attribute->AsString(&attribute_string);
-    SLOG(WiFi, 3) << "   Attr:" << attribute->name_string()
-                  << "=" << attribute_string
-                  << " Type:" << attribute->type_string();
-  }
+  // TODO(wdg): Make a message->AsString() method.
 }
 
 bool Callback80211Object::InstallAsBroadcastCallback() {
diff --git a/config80211_unittest.cc b/config80211_unittest.cc
index 5743ac6..1a2df4b 100644
--- a/config80211_unittest.cc
+++ b/config80211_unittest.cc
@@ -577,13 +577,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -610,8 +612,8 @@
     EXPECT_EQ(ssids[0].compare(""), 0);  // Expect a single, empty SSID.
   }
 
-  // Significant only in its existence.
-  EXPECT_TRUE(message->AttributeExists(NL80211_ATTR_SUPPORT_MESH_AUTH));
+  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+      NL80211_ATTR_SUPPORT_MESH_AUTH));
 }
 
 TEST_F(Config80211Test, NL80211_CMD_NEW_SCAN_RESULTS) {
@@ -624,13 +626,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -657,8 +661,8 @@
     EXPECT_EQ(ssids[0].compare(""), 0);  // Expect a single, empty SSID.
   }
 
-  // Significant only in its existence.
-  EXPECT_TRUE(message->AttributeExists(NL80211_ATTR_SUPPORT_MESH_AUTH));
+  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+      NL80211_ATTR_SUPPORT_MESH_AUTH));
 }
 
 TEST_F(Config80211Test, NL80211_CMD_NEW_STATION) {
@@ -671,7 +675,8 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -681,14 +686,17 @@
     EXPECT_EQ(strncmp(value.c_str(), kExpectedMacAddress, value.length()), 0);
   }
 
-  // TODO(wdg): Make config80211 handle nested attributes so it can deal
-  // with things like NL80211_ATTR_STA_INFO (without just calling
-  // nla_parse_nested).
-  EXPECT_TRUE(message->AttributeExists(NL80211_ATTR_STA_INFO));
+  // TODO(wdg): Look at nested values of NL80211_ATTR_STA_INFO.
+  {
+    ByteString nested;
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(
+        NL80211_ATTR_STA_INFO, &nested));
+  }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_GENERATION, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(
+        NL80211_ATTR_GENERATION, &value));
     EXPECT_EQ(value, kNewStationExpectedGeneration);
   }
 }
@@ -703,19 +711,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                           &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kAuthenticateFrame,
@@ -734,19 +745,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                           &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kAssociateFrame,
@@ -765,13 +779,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -783,12 +799,17 @@
 
   {
     uint16_t value;
-    EXPECT_TRUE(message->GetU16Attribute(NL80211_ATTR_STATUS_CODE, &value));
+    EXPECT_TRUE(message->attributes().GetU16AttributeValue(
+        NL80211_ATTR_STATUS_CODE, &value));
     EXPECT_EQ(value, kExpectedConnectStatus);
   }
 
   // TODO(wdg): Need to check the value of this attribute.
-  EXPECT_TRUE(message->AttributeExists(NL80211_ATTR_RESP_IE));
+  {
+    ByteString rawdata;
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_RESP_IE,
+                                                           &rawdata));
+  }
 }
 
 TEST_F(Config80211Test, NL80211_CMD_DEAUTHENTICATE) {
@@ -801,19 +822,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                           &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kDeauthenticateFrame,
@@ -832,24 +856,27 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     uint16_t value;
-    EXPECT_TRUE(message->GetU16Attribute(NL80211_ATTR_REASON_CODE, &value));
+    EXPECT_TRUE(message->attributes().GetU16AttributeValue(
+        NL80211_ATTR_REASON_CODE, &value));
     EXPECT_EQ(value, kExpectedDisconnectReason);
   }
 
-  // Significant only in its existence.
-  EXPECT_TRUE(message->AttributeExists(NL80211_ATTR_DISCONNECTED_BY_AP));
+  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+      NL80211_ATTR_DISCONNECTED_BY_AP));
 }
 
 TEST_F(Config80211Test, NL80211_CMD_NOTIFY_CQM) {
@@ -863,13 +890,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                          &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -879,30 +908,13 @@
     EXPECT_EQ(strncmp(value.c_str(), kExpectedMacAddress, value.length()), 0);
   }
 
-  // TODO(wdg): Make config80211 handle nested attributes so it can deal
-  // with things like NL80211_ATTR_CQM (without just calling nla_parse_nested).
+  // TODO(wdg): The next check-in contains a perfectly fine test for
+  // NL80211_ATTR_CQM so it seems silly to pound one out, using completely
+  // different semantics, for this check-in.
   {
-    static const nla_policy kCqmPolicy[NL80211_ATTR_CQM_MAX + 1] = {
-      { NLA_U32, 0, 0 },  // Who Knows?
-      { NLA_U32, 0, 0 },  // [NL80211_ATTR_CQM_RSSI_THOLD]
-      { NLA_U32, 0, 0 },  // [NL80211_ATTR_CQM_RSSI_HYST]
-      { NLA_U32, 0, 0 },  // [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]
-    };
-
-    const Nl80211Attribute *attribute = message->GetAttribute(NL80211_ATTR_CQM);
-    EXPECT_NE(attribute, reinterpret_cast<const Nl80211Attribute *>(NULL));
-    const nlattr *const_data = attribute->data();
-    nlattr *cqm_attr = const_cast<nlattr *>(const_data);
-    EXPECT_NE(cqm_attr, reinterpret_cast<nlattr *>(NULL));
-
-    nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
-    EXPECT_EQ(nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, cqm_attr,
-                     const_cast<nla_policy *>(kCqmPolicy)), 0);
-
-    EXPECT_FALSE(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
-    EXPECT_TRUE(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
-    EXPECT_EQ(Nl80211Attribute::NlaGetU32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]),
-              kExpectedCqmNotAcked);
+    ByteString nested;
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(
+        NL80211_ATTR_CQM, &nested));
   }
 }
 
@@ -917,19 +929,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_WIPHY, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                           &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->GetU32Attribute(NL80211_ATTR_IFINDEX, &value));
+    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                           &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->GetRawAttributeData(NL80211_ATTR_FRAME, &rawdata));
+    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                           &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kDisassociateFrame,
diff --git a/nl80211_attribute.cc b/nl80211_attribute.cc
index bb65fcc..a2d6b4a 100644
--- a/nl80211_attribute.cc
+++ b/nl80211_attribute.cc
@@ -16,6 +16,7 @@
 #include <base/stl_util.h>
 #include <base/stringprintf.h>
 
+#include "shill/attribute_list.h"
 #include "shill/logging.h"
 #include "shill/scope_logger.h"
 
@@ -34,8 +35,7 @@
       type_string_(type_string) {}
 
 // static
-Nl80211Attribute *Nl80211Attribute::NewFromNlAttr(nl80211_attrs name,
-                                                  const nlattr *data) {
+Nl80211Attribute *Nl80211Attribute::NewFromName(nl80211_attrs name) {
   scoped_ptr<Nl80211Attribute> attr;
   switch (name) {
     case NL80211_ATTR_COOKIE:
@@ -117,7 +117,6 @@
       attr.reset(new Nl80211AttributeGeneric(name));
       break;
   }
-  attr->InitFromNlAttr(data);
   return attr.release();
 }
 
@@ -133,17 +132,6 @@
   return true;
 }
 
-// Copies raw attribute data but not the header.
-bool Nl80211Attribute::GetRawData(ByteString *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
-  }
-
-  *output = data_;
-  return true;
-}
-
 string Nl80211Attribute::RawToString() const {
   string output = " === RAW: ";
 
@@ -179,11 +167,9 @@
 }
 
 bool Nl80211U8Attribute::GetU8Value(uint8_t *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -223,11 +209,9 @@
 }
 
 bool Nl80211U16Attribute::GetU16Value(uint16_t *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -266,11 +250,9 @@
 }
 
 bool Nl80211U32Attribute::GetU32Value(uint32_t *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -309,11 +291,9 @@
 }
 
 bool Nl80211U64Attribute::GetU64Value(uint64_t *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -353,11 +333,9 @@
 
 
 bool Nl80211FlagAttribute::GetFlagValue(bool *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -395,11 +373,9 @@
 }
 
 bool Nl80211StringAttribute::GetStringValue(string *output) const {
-  if (!output) {
-    LOG(ERROR) << "Null |output| parameter";
-    return false;
+  if (output) {
+    *output = value_;
   }
-  *output = value_;
   return true;
 }
 
@@ -433,11 +409,9 @@
 }
 
 bool Nl80211RawAttribute::GetRawValue(ByteString *output) const {
-  if (!output) {
-    LOG(ERROR) << "NULL |output|";
-    return false;
+  if (output) {
+    *output = data_;
   }
-  *output = data_;
   return true;
 }
 
diff --git a/nl80211_attribute.h b/nl80211_attribute.h
index 89079b3..f12028c 100644
--- a/nl80211_attribute.h
+++ b/nl80211_attribute.h
@@ -20,6 +20,8 @@
 
 namespace shill {
 
+class AttributeList;
+
 // Nl80211Attribute 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
@@ -36,9 +38,8 @@
     kTypeU16,
     kTypeU32,
     kTypeU64,
-    kTypeString,
     kTypeFlag,
-    kTypeMsecs,
+    kTypeString,
     kTypeNested,
     kTypeRaw,
     kTypeError
@@ -52,8 +53,7 @@
 
   // Static factory generates the appropriate Nl80211Attribute object from the
   // raw nlattr data.
-  static Nl80211Attribute *NewFromNlAttr(nl80211_attrs name,
-                                         const nlattr *data);
+  static Nl80211Attribute *NewFromName(nl80211_attrs name);
 
   // Accessors for the attribute's name and type information.
   nl80211_attrs name() const { return name_; }
@@ -71,16 +71,6 @@
     return reinterpret_cast<const nlattr *>(data_.GetConstData());
   }
 
-  // TODO(wdg): GetRawData is used to support
-  // UserBoundNlMessage::GetRawAttributeData which, in turn, is used to support
-  // NL80211_ATTR_FRAME and NL80211_ATTR_KEY_SEQ.  Remove this method (and that
-  // one) once those classes are fleshed-out.
-  //
-  // If successful, returns 'true' and sets *|value| to the raw attribute data
-  // (after the header) for this attribute.  Otherwise, returns 'false' and
-  // leaves |value| unchanged.
-  bool GetRawData(ByteString *output) const;
-
   // Fill a string with characters that represents the value of the attribute.
   // If no attribute is found or if the type isn't trivially stringizable,
   // this method returns 'false' and |value| remains unchanged.
diff --git a/user_bound_nlmessage.cc b/user_bound_nlmessage.cc
index 8c5af67..d5a2ef5 100644
--- a/user_bound_nlmessage.cc
+++ b/user_bound_nlmessage.cc
@@ -44,9 +44,9 @@
 #include <base/stl_util.h>
 #include <base/stringprintf.h>
 
+#include "shill/attribute_list.h"
 #include "shill/ieee80211.h"
 #include "shill/logging.h"
-#include "shill/nl80211_attribute.h"
 #include "shill/scope_logger.h"
 
 using base::LazyInstance;
@@ -101,13 +101,6 @@
 // UserBoundNlMessage
 //
 
-UserBoundNlMessage::~UserBoundNlMessage() {
-  map<nl80211_attrs, Nl80211Attribute *>::iterator i;
-  for (i = attributes_.begin(); i != attributes_.end(); ++i) {
-    delete i->second;
-  }
-}
-
 bool UserBoundNlMessage::Init(nlattr *tb[NL80211_ATTR_MAX + 1],
                               nlmsghdr *msg) {
   if (!tb) {
@@ -121,10 +114,8 @@
 
   for (int i = 0; i < NL80211_ATTR_MAX + 1; ++i) {
     if (tb[i]) {
-      Nl80211Attribute *attribute =
-          Nl80211Attribute::NewFromNlAttr(static_cast<enum nl80211_attrs>(i),
+      attributes_.CreateAndInitFromNlAttr(static_cast<enum nl80211_attrs>(i),
                                           tb[i]);
-      AddAttribute(attribute);
     }
   }
 
@@ -326,17 +317,6 @@
   return true;
 }
 
-UserBoundNlMessage::AttributeIterator
-    UserBoundNlMessage::GetAttributeIterator() const {
-  return UserBoundNlMessage::AttributeIterator(attributes_);
-}
-
-// Return true if the attribute is in our map, regardless of the value of
-// the attribute, itself.
-bool UserBoundNlMessage::AttributeExists(enum nl80211_attrs name) const {
-  return ContainsKey(attributes_, name);
-}
-
 uint32_t UserBoundNlMessage::GetId() const {
   if (!message_) {
     return kIllegalMessage;
@@ -344,155 +324,6 @@
   return message_->nlmsg_seq;
 }
 
-// Returns the raw attribute data but not the header.
-bool UserBoundNlMessage::GetRawAttributeData(enum nl80211_attrs name,
-                                             ByteString *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    LOG(ERROR) << "No attribute - returning FALSE";
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeRaw) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than RAW";
-    return false;
-  }
-  const Nl80211RawAttribute *attr
-      = reinterpret_cast<const Nl80211RawAttribute *>(raw_attr);
-
-  ByteString raw_data;
-  if (!attr->GetRawValue(&raw_data))
-    return false;
-
-  const nlattr *const_data =
-      reinterpret_cast<const nlattr *>(raw_data.GetConstData());
-  // nla_data and nla_len don't change their parameters but don't declare
-  // them to be const.  Hence the cast.
-  nlattr *data_nlattr = const_cast<nlattr *>(const_data);
-  *value = ByteString(
-      reinterpret_cast<unsigned char *>(nla_data(data_nlattr)),
-      nla_len(data_nlattr));
-  return true;
-}
-
-bool UserBoundNlMessage::GetStringAttribute(enum nl80211_attrs name,
-                                            string *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeString) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than STRING";
-    return false;
-  }
-  const Nl80211StringAttribute *attr
-      = reinterpret_cast<const Nl80211StringAttribute *>(raw_attr);
-  return attr->GetStringValue(value);
-}
-
-bool UserBoundNlMessage::GetU8Attribute(enum nl80211_attrs name,
-                                        uint8_t *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    LOG(ERROR) << "No attribute - returning FALSE";
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeU8) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than U8";
-    return false;
-  }
-  const Nl80211U8Attribute *attr
-      = reinterpret_cast<const Nl80211U8Attribute *>(raw_attr);
-  return attr->GetU8Value(value);
-}
-
-bool UserBoundNlMessage::GetU16Attribute(enum nl80211_attrs name,
-                                         uint16_t *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    LOG(ERROR) << "No attribute - returning FALSE";
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeU16) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than U16";
-    return false;
-  }
-  const Nl80211U16Attribute *attr
-      = reinterpret_cast<const Nl80211U16Attribute *>(raw_attr);
-  return attr->GetU16Value(value);
-}
-
-bool UserBoundNlMessage::GetU32Attribute(enum nl80211_attrs name,
-                                         uint32_t *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    LOG(ERROR) << "No attribute - returning FALSE";
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeU32) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than U32";
-    return false;
-  }
-  const Nl80211U32Attribute *attr
-      = reinterpret_cast<const Nl80211U32Attribute *>(raw_attr);
-  return attr->GetU32Value(value);
-}
-
-bool UserBoundNlMessage::GetU64Attribute(enum nl80211_attrs name,
-                                         uint64_t *value) const {
-  if (!value) {
-    LOG(ERROR) << "Null |value| parameter";
-    return false;
-  }
-
-  const Nl80211Attribute *raw_attr = GetAttribute(name);
-  if (!raw_attr) {
-    LOG(ERROR) << "No attribute - returning FALSE";
-    return false;
-  }
-  if (raw_attr->type() != Nl80211Attribute::kTypeU64) {
-    LOG(ERROR) << "Attribute with name " << raw_attr->name_string()
-               << " has type " << raw_attr->type_string()
-               << " rather than U64";
-    return false;
-  }
-  const Nl80211U64Attribute *attr
-      = reinterpret_cast<const Nl80211U64Attribute *>(raw_attr);
-  return attr->GetU64Value(value);
-}
 
 // Helper function to provide a string for a MAC address.
 bool UserBoundNlMessage::GetMacAttributeString(enum nl80211_attrs name,
@@ -503,7 +334,7 @@
   }
 
   ByteString data;
-  if (!GetRawAttributeData(name, &data)) {
+  if (!attributes().GetRawAttributeValue(name, &data)) {
     value->assign(kBogusMacAddress);
     return false;
   }
@@ -522,7 +353,7 @@
 
   value->clear();
   ByteString rawdata;
-  if (!GetRawAttributeData(name, &rawdata) && !rawdata.IsEmpty())
+  if (!attributes().GetRawAttributeValue(name, &rawdata) && !rawdata.IsEmpty())
     return false;
 
   nlattr *nst = NULL;
@@ -546,61 +377,36 @@
     return false;
   }
 
-  if (AttributeExists(name)) {
-    ByteString rawdata;
-    if (GetRawAttributeData(name, &rawdata) && !rawdata.IsEmpty()) {
-      nlattr *nst = NULL;
-      // |nla_for_each_attr| requires a non-const parameter even though it
-      // doesn't change the data.
-      nlattr *data = reinterpret_cast<nlattr *>(rawdata.GetData());
-      int rem_nst;
-      int len = rawdata.GetLength();
+  ByteString rawdata;
+  if (!attributes().GetRawAttributeValue(name, &rawdata) || rawdata.IsEmpty())
+    return false;
 
-      nla_for_each_attr(nst, data, len, rem_nst) {
-        value->push_back(StringFromSsid(nla_len(nst),
-                                        reinterpret_cast<const uint8_t *>(
-                                          nla_data(nst))).c_str());
-      }
-    }
-    return true;
+  nlattr *nst = NULL;
+  // |nla_for_each_attr| requires a non-const parameter even though it
+  // doesn't change the data.
+  nlattr *data = reinterpret_cast<nlattr *>(rawdata.GetData());
+  int rem_nst;
+  int len = rawdata.GetLength();
+
+  nla_for_each_attr(nst, data, len, rem_nst) {
+    value->push_back(StringFromSsid(nla_len(nst),
+                                    reinterpret_cast<const uint8_t *>(
+                                      nla_data(nst))).c_str());
   }
-  return false;
+  return true;
 }
 
 // Protected members.
 
-bool UserBoundNlMessage::AddAttribute(Nl80211Attribute *attr) {
-  if (!attr) {
-    LOG(ERROR) << "Not adding NULL attribute";
-    return false;
-  }
-  if (ContainsKey(attributes_, attr->name())) {
-    LOG(ERROR) << "Already have attribute name " << attr->name_string();
-    return false;
-  }
-  attributes_[attr->name()] = attr;
-  return true;
-}
-
-const Nl80211Attribute *UserBoundNlMessage::GetAttribute(nl80211_attrs name)
-    const {
-  map<nl80211_attrs, Nl80211Attribute *>::const_iterator match;
-  match = attributes_.find(name);
-  // This method may be called to explore the existence of the attribute so
-  // we'll not emit an error if it's not found.
-  if (match == attributes_.end()) {
-    return NULL;
-  }
-  return match->second;
-}
-
 string UserBoundNlMessage::GetHeaderString() const {
   char ifname[IF_NAMESIZE] = "";
   uint32_t ifindex = UINT32_MAX;
-  bool ifindex_exists = GetU32Attribute(NL80211_ATTR_IFINDEX, &ifindex);
+  bool ifindex_exists = attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
+                                                          &ifindex);
 
   uint32_t wifi = UINT32_MAX;
-  bool wifi_exists = GetU32Attribute(NL80211_ATTR_WIPHY, &wifi);
+  bool wifi_exists = attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                       &wifi);
 
   string output;
   if (ifindex_exists && wifi_exists) {
@@ -620,7 +426,8 @@
 string UserBoundNlMessage::StringFromFrame(enum nl80211_attrs attr_name) const {
   string output;
   ByteString frame_data;
-  if (GetRawAttributeData(attr_name, &frame_data) && !frame_data.IsEmpty()) {
+  if (attributes().GetRawAttributeValue(attr_name,
+                                        &frame_data) && !frame_data.IsEmpty()) {
     Nl80211Frame frame(frame_data);
     frame.ToString(&output);
   } else {
@@ -840,9 +647,9 @@
 string AssociateMessage::ToString() const {
   string output(GetHeaderString());
   output.append("assoc");
-  if (AttributeExists(NL80211_ATTR_FRAME))
+  if (attributes().GetRawAttributeValue(NL80211_ATTR_FRAME, NULL))
     output.append(StringFromFrame(NL80211_ATTR_FRAME));
-  else if (AttributeExists(NL80211_ATTR_TIMED_OUT))
+  else if (attributes().IsFlagAttributeTrue(NL80211_ATTR_TIMED_OUT))
     output.append(": timed out");
   else
     output.append(": unknown event");
@@ -855,10 +662,10 @@
 string AuthenticateMessage::ToString() const {
   string output(GetHeaderString());
   output.append("auth");
-  if (AttributeExists(NL80211_ATTR_FRAME)) {
+  if (attributes().GetRawAttributeValue(NL80211_ATTR_FRAME, NULL)) {
     output.append(StringFromFrame(NL80211_ATTR_FRAME));
   } else {
-    output.append(AttributeExists(NL80211_ATTR_TIMED_OUT) ?
+    output.append(attributes().IsFlagAttributeTrue(NL80211_ATTR_TIMED_OUT) ?
                   ": timed out" : ": unknown event");
   }
   return output;
@@ -875,8 +682,10 @@
   uint64_t cookie;
   StringAppendF(&output,
                 "done with remain on freq %" PRIu32 " (cookie %" PRIx64 ")",
-                (GetU32Attribute(NL80211_ATTR_WIPHY_FREQ, &freq) ? 0 : freq),
-                (GetU64Attribute(NL80211_ATTR_COOKIE, &cookie) ? 0 : cookie));
+                (attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY_FREQ,
+                                                   &freq) ?  0 : freq),
+                (attributes().GetU64AttributeValue(NL80211_ATTR_COOKIE,
+                                                   &cookie) ?  0 : cookie));
   return output;
 }
 
@@ -888,7 +697,7 @@
 
   uint16_t status = UINT16_MAX;
 
-  if (!GetU16Attribute(NL80211_ATTR_STATUS_CODE, &status)) {
+  if (!attributes().GetU16AttributeValue(NL80211_ATTR_STATUS_CODE, &status)) {
     output.append("unknown connect status");
   } else if (status == 0) {
     output.append("connected");
@@ -896,7 +705,7 @@
     output.append("failed to connect");
   }
 
-  if (AttributeExists(NL80211_ATTR_MAC)) {
+  if (attributes().GetRawAttributeValue(NL80211_ATTR_MAC, NULL)) {
     string mac;
     GetMacAttributeString(NL80211_ATTR_MAC, &mac);
     StringAppendF(&output, " to %s", mac.c_str());
@@ -945,11 +754,12 @@
 string DisconnectMessage::ToString() const {
   string output(GetHeaderString());
   StringAppendF(&output, "disconnected %s",
-                ((AttributeExists(NL80211_ATTR_DISCONNECTED_BY_AP)) ?
-                  "(by AP)" : "(local request)"));
+                ((attributes().IsFlagAttributeTrue(
+                    NL80211_ATTR_DISCONNECTED_BY_AP)) ?
+                 "(by AP)" : "(local request)"));
 
   uint16_t reason = UINT16_MAX;
-  if (GetU16Attribute(NL80211_ATTR_REASON_CODE, &reason)) {
+  if (attributes().GetU16AttributeValue(NL80211_ATTR_REASON_CODE, &reason)) {
     StringAppendF(&output, " reason: %u: %s",
                   reason, StringFromReason(reason).c_str());
   }
@@ -963,11 +773,11 @@
 string FrameTxStatusMessage::ToString() const {
   string output(GetHeaderString());
   uint64_t cookie = UINT64_MAX;
-  GetU64Attribute(NL80211_ATTR_COOKIE, &cookie);
+  attributes().GetU64AttributeValue(NL80211_ATTR_COOKIE, &cookie);
 
-  StringAppendF(&output, "mgmt TX status (cookie %" PRIx64 "): %s",
-                cookie,
-                (AttributeExists(NL80211_ATTR_ACK) ? "acked" : "no ack"));
+  StringAppendF(&output, "mgmt TX status (cookie %" PRIx64 "): %s", cookie,
+                (attributes().IsFlagAttributeTrue(NL80211_ATTR_ACK) ?
+                 "acked" : "no ack"));
   return output;
 }
 
@@ -992,15 +802,16 @@
 
   output.append("Michael MIC failure event:");
 
-  if (AttributeExists(NL80211_ATTR_MAC)) {
+  if (attributes().GetRawAttributeValue(NL80211_ATTR_MAC, NULL)) {
     string mac;
     GetMacAttributeString(NL80211_ATTR_MAC, &mac);
     StringAppendF(&output, " source MAC address %s", mac.c_str());
   }
 
-  if (AttributeExists(NL80211_ATTR_KEY_SEQ)) {
+  {
     ByteString rawdata;
-    if (GetRawAttributeData(NL80211_ATTR_KEY_SEQ, &rawdata) &&
+    if (attributes().GetRawAttributeValue(NL80211_ATTR_KEY_SEQ,
+                                          &rawdata) &&
         rawdata.GetLength() == UserBoundNlMessage::kEthernetAddressBytes) {
       const unsigned char *seq = rawdata.GetConstData();
       StringAppendF(&output, " seq=%02x%02x%02x%02x%02x%02x",
@@ -1008,14 +819,14 @@
     }
   }
   uint32_t key_type_val = UINT32_MAX;
-  if (GetU32Attribute(NL80211_ATTR_KEY_TYPE, &key_type_val)) {
+  if (attributes().GetU32AttributeValue(NL80211_ATTR_KEY_TYPE, &key_type_val)) {
     enum nl80211_key_type key_type =
         static_cast<enum nl80211_key_type >(key_type_val);
     StringAppendF(&output, " Key Type %s", StringFromKeyType(key_type).c_str());
   }
 
   uint8_t key_index = UINT8_MAX;
-  if (GetU8Attribute(NL80211_ATTR_KEY_IDX, &key_index)) {
+  if (attributes().GetU8AttributeValue(NL80211_ATTR_KEY_IDX, &key_index)) {
     StringAppendF(&output, " Key Id %u", key_index);
   }
 
@@ -1077,7 +888,7 @@
 string NewWifiMessage::ToString() const {
   string output(GetHeaderString());
   string wifi_name = "None";
-  GetStringAttribute(NL80211_ATTR_WIPHY_NAME, &wifi_name);
+  attributes().GetStringAttributeValue(NL80211_ATTR_WIPHY_NAME, &wifi_name);
   StringAppendF(&output, "renamed to %s", wifi_name.c_str());
   return output;
 }
@@ -1086,6 +897,7 @@
 const char NotifyCqmMessage::kCommandString[] = "NL80211_CMD_NOTIFY_CQM";
 
 string NotifyCqmMessage::ToString() const {
+  // TODO(wdg): use attributes().GetNestedAttributeValue()...
   static const nla_policy kCqmPolicy[NL80211_ATTR_CQM_MAX + 1] = {
     { NLA_U32, 0, 0 },  // Who Knows?
     { NLA_U32, 0, 0 },  // [NL80211_ATTR_CQM_RSSI_THOLD]
@@ -1096,7 +908,8 @@
   string output(GetHeaderString());
   output.append("connection quality monitor event: ");
 
-  const Nl80211Attribute *attribute = GetAttribute(NL80211_ATTR_CQM);
+  const Nl80211RawAttribute *attribute =
+      attributes().GetRawAttribute(NL80211_ATTR_CQM);
   if (!attribute) {
     output.append("missing data!");
     return output;
@@ -1123,7 +936,7 @@
     else
       output.append("RSSI went below threshold");
   } else if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT] &&
-       AttributeExists(NL80211_ATTR_MAC)) {
+       attributes().GetRawAttributeValue(NL80211_ATTR_MAC, NULL)) {
     string mac;
     GetMacAttributeString(NL80211_ATTR_MAC, &mac);
     StringAppendF(&output, "peer %s didn't ACK %" PRIu32 " packets",
@@ -1153,9 +966,10 @@
 string RegBeaconHintMessage::ToString() const {
   string output(GetHeaderString());
   uint32_t wiphy_idx = UINT32_MAX;
-  GetU32Attribute(NL80211_ATTR_WIPHY, &wiphy_idx);
+  attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY, &wiphy_idx);
 
-  const Nl80211Attribute *freq_before = GetAttribute(NL80211_ATTR_FREQ_BEFORE);
+  const Nl80211RawAttribute *freq_before =
+      attributes().GetRawAttribute(NL80211_ATTR_FREQ_BEFORE);
   if (!freq_before)
     return "";
   const nlattr *const_before = freq_before->data();
@@ -1164,7 +978,8 @@
   if (ParseBeaconHintChan(const_before, &chan_before_beacon))
     return "";
 
-  const Nl80211Attribute *freq_after = GetAttribute(NL80211_ATTR_FREQ_AFTER);
+  const Nl80211RawAttribute *freq_after =
+      attributes().GetRawAttribute(NL80211_ATTR_FREQ_AFTER);
   if (!freq_after)
     return "";
 
@@ -1253,16 +1068,17 @@
   output.append("regulatory domain change: ");
 
   uint8_t reg_type = UINT8_MAX;
-  GetU8Attribute(NL80211_ATTR_REG_TYPE, &reg_type);
+  attributes().GetU8AttributeValue(NL80211_ATTR_REG_TYPE, &reg_type);
 
   uint32_t initiator = UINT32_MAX;
-  GetU32Attribute(NL80211_ATTR_REG_INITIATOR, &initiator);
+  attributes().GetU32AttributeValue(NL80211_ATTR_REG_INITIATOR, &initiator);
 
   uint32_t wifi = UINT32_MAX;
-  bool wifi_exists = GetU32Attribute(NL80211_ATTR_WIPHY, &wifi);
+  bool wifi_exists = attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
+                                                       &wifi);
 
   string alpha2 = "<None>";
-  GetStringAttribute(NL80211_ATTR_REG_ALPHA2, &alpha2);
+  attributes().GetStringAttributeValue(NL80211_ATTR_REG_ALPHA2, &alpha2);
 
   switch (reg_type) {
   case NL80211_REGDOM_TYPE_COUNTRY:
@@ -1306,13 +1122,13 @@
   string output(GetHeaderString());
 
   uint32_t wifi_freq = UINT32_MAX;
-  GetU32Attribute(NL80211_ATTR_WIPHY_FREQ, &wifi_freq);
+  attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY_FREQ, &wifi_freq);
 
   uint32_t duration = UINT32_MAX;
-  GetU32Attribute(NL80211_ATTR_DURATION, &duration);
+  attributes().GetU32AttributeValue(NL80211_ATTR_DURATION, &duration);
 
   uint64_t cookie = UINT64_MAX;
-  GetU64Attribute(NL80211_ATTR_COOKIE, &cookie);
+  attributes().GetU64AttributeValue(NL80211_ATTR_COOKIE, &cookie);
 
   StringAppendF(&output, "remain on freq %" PRIu32 " (%" PRIu32 "ms, cookie %"
                 PRIx64 ")",
@@ -1327,7 +1143,7 @@
   string output(GetHeaderString());
   output.append("roamed");
 
-  if (AttributeExists(NL80211_ATTR_MAC)) {
+  if (attributes().GetRawAttributeValue(NL80211_ATTR_MAC, NULL)) {
     string mac;
     GetMacAttributeString(NL80211_ATTR_MAC, &mac);
     StringAppendF(&output, " to %s", mac.c_str());
diff --git a/user_bound_nlmessage.h b/user_bound_nlmessage.h
index 5bc4089..6a5da4a 100644
--- a/user_bound_nlmessage.h
+++ b/user_bound_nlmessage.h
@@ -17,7 +17,7 @@
 #include <base/lazy_instance.h>
 #include <gtest/gtest.h>
 
-#include "shill/nl80211_attribute.h"
+#include "shill/attribute_list.h"
 
 struct nlattr;
 struct nlmsghdr;
@@ -28,100 +28,41 @@
 class UserBoundNlMessage {
  public:
   static const unsigned int kEthernetAddressBytes;
-
-  // A const iterator to the attribute names in the attributes_ map of a
-  // UserBoundNlMessage.  The purpose, here, is to hide the way that the
-  // attribute is stored.
-  class AttributeIterator {
-   public:
-    explicit AttributeIterator(const std::map<nl80211_attrs,
-                                              Nl80211Attribute *> &map_param)
-        : map_(map_param), iter_(map_.begin()) {}
-    AttributeIterator(const AttributeIterator &other)
-        : map_(other.map_), iter_(other.iter_) {}
-
-    // Causes the iterator to point to the next attribute in the list.
-    void Advance() { ++iter_; }
-
-    // Returns 'true' if the iterator points beyond the last attribute in the
-    // list; returns 'false' otherwise.
-    bool AtEnd() const { return iter_ == map_.end(); }
-
-    const Nl80211Attribute *GetAttribute() const { return iter_->second; }
-
-   private:
-    // DISALLOW_ASSIGN_BUT_ALLOW_COPY:
-    AttributeIterator &operator=(const AttributeIterator &other);
-
-    const std::map<nl80211_attrs, Nl80211Attribute *> &map_;
-    std::map<nl80211_attrs, Nl80211Attribute *>::const_iterator iter_;
-  };
-
   static const char kBogusMacAddress[];
 
   UserBoundNlMessage(uint8 message_type, const char *message_type_string)
       : message_(NULL),
         message_type_(message_type),
-        message_type_string_(message_type_string) { }
-  virtual ~UserBoundNlMessage();
+        message_type_string_(message_type_string) {}
+  virtual ~UserBoundNlMessage() {}
 
   // Non-trivial initialization.
   virtual bool Init(nlattr *tb[NL80211_ATTR_MAX + 1], nlmsghdr *msg);
 
-  // Allow (const) iteration over the attributes inside a message object.
-  AttributeIterator GetAttributeIterator() const;
-
-  // Other ways to see the internals of the object.
-
-  // Return true if the attribute is in our map, regardless of the value of
-  // the attribute, itself.
-  bool AttributeExists(nl80211_attrs name) const;
-
   // Message ID is equivalent to the message's sequence number.
   uint32_t GetId() const;
 
-  const Nl80211Attribute *GetAttribute(nl80211_attrs name) const;
+  const AttributeList &attributes() const { return attributes_; }
 
-  // TODO(wdg): GetRawAttributeData is only used for NL80211_ATTR_FRAME and
-  // NL80211_ATTR_KEY_SEQ.  Remove this method once those classes are
-  // fleshed-out.
-  //
-  // Returns a string describing the data type of a given attribute.
-  std::string GetAttributeTypeString(nl80211_attrs name) const;
-
-  // If successful, returns 'true' and the raw attribute data (after the
-  // header) into |value| for this attribute.  If no attribute by this name
-  // exists in this message, clears |value| and returns 'false'.  If otherwise
-  // unsuccessful, returns 'false' and leaves |value| unchanged.
-  bool GetRawAttributeData(nl80211_attrs name, ByteString *value) const;
-
-  // TODO(wdg): Replace all message->GetXxxAttribute with
-  // attribute->GetXxxAttribute; remove message methods.
-
-  // Each of these methods set |value| with the value of the specified
-  // attribute (if the attribute is not found, |value| remains unchanged).
-
-  bool GetStringAttribute(nl80211_attrs name, std::string *value) const;
-  bool GetU8Attribute(nl80211_attrs name, uint8_t *value) const;
-  bool GetU16Attribute(nl80211_attrs name, uint16_t *value) const;
-  bool GetU32Attribute(nl80211_attrs name, uint32_t *value) const;
-  bool GetU64Attribute(nl80211_attrs name, uint64_t *value) const;
-
+  // TODO(wdg): This needs to be moved to AttributeMac.
   // Helper function to provide a string for a MAC address.  If no attribute
   // is found, this method returns 'false'.  On any error with a non-NULL
   // |value|, this method sets |value| to a bogus MAC address.
   bool GetMacAttributeString(nl80211_attrs name, std::string *value) const;
 
+  // TODO(wdg): This needs to be moved to AttributeScanFrequencies.
   // Helper function to provide a vector of scan frequencies for attributes
   // that contain them (such as NL80211_ATTR_SCAN_FREQUENCIES).
   bool GetScanFrequenciesAttribute(enum nl80211_attrs name,
                                    std::vector<uint32_t> *value) const;
 
+  // TODO(wdg): This needs to be moved to AttributeScanSSids.
   // Helper function to provide a vector of SSIDs for attributes that contain
   // them (such as NL80211_ATTR_SCAN_SSIDS).
   bool GetScanSsidsAttribute(enum nl80211_attrs name,
                              std::vector<std::string> *value) const;
 
+  // TODO(wdg): This needs to be moved to AttributeMac.
   // Stringizes the MAC address found in 'arg'.  If there are problems (such
   // as a NULL |arg|), |value| is set to a bogus MAC address.
   static std::string StringFromMacAddress(const uint8_t *arg);
@@ -139,9 +80,6 @@
   const char *message_type_string() const { return message_type_string_; }
 
  protected:
-  // Take ownership of attribute, store in map indexed on |attr->name()|.
-  bool AddAttribute(Nl80211Attribute *attr);
-
   // Returns a string that should precede all user-bound message strings.
   virtual std::string GetHeaderString() const;
 
@@ -161,7 +99,6 @@
   static std::string StringFromSsid(const uint8_t len, const uint8_t *data);
 
  private:
-  friend class AttributeIterator;
   friend class Config80211Test;
   FRIEND_TEST(Config80211Test, NL80211_CMD_NOTIFY_CQM);
 
@@ -172,7 +109,8 @@
   const char *message_type_string_;
   static std::map<uint16_t, std::string> *reason_code_string_;
   static std::map<uint16_t, std::string> *status_code_string_;
-  std::map<nl80211_attrs, Nl80211Attribute *> attributes_;
+
+  AttributeList attributes_;
 
   DISALLOW_COPY_AND_ASSIGN(UserBoundNlMessage);
 };