shill: Parses nl80211 nested attributes _way_ better than before.

Adds code to parse nested attributes using tables.  Makes the whole
nested attribute parsing thing much more readable.  Simplifies setting
up future nested attributes, reduces duplication and, potentially,
lots of bugs.  As part of this CL, the |AttributeList::Set*| and
|AttributeList::Create*| methods were made public.

There's at least one more CL coming to add custom parsing to
individual nested attributes.

BUG=chromium-os:37742
TEST=unittests.  Other unittests will be modified to utilize these
  changes in the next CL.

Change-Id: Icf2d234c189652dec56891c7e688ee4fef41191e
Reviewed-on: https://gerrit.chromium.org/gerrit/44132
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/attribute_list.cc b/attribute_list.cc
index 03f22b3..6cea9f7 100644
--- a/attribute_list.cc
+++ b/attribute_list.cc
@@ -82,7 +82,7 @@
   return true;
 }
 
-bool AttributeList::SetU8AttributeValue(int id, uint8_t value) const {
+bool AttributeList::SetU8AttributeValue(int id, uint8_t value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -109,7 +109,7 @@
   return true;
 }
 
-bool AttributeList::SetU16AttributeValue(int id, uint16_t value) const {
+bool AttributeList::SetU16AttributeValue(int id, uint16_t value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -135,7 +135,7 @@
   return true;
 }
 
-bool AttributeList::SetU32AttributeValue(int id, uint32_t value) const {
+bool AttributeList::SetU32AttributeValue(int id, uint32_t value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -161,7 +161,7 @@
   return true;
 }
 
-bool AttributeList::SetU64AttributeValue(int id, uint64_t value) const {
+bool AttributeList::SetU64AttributeValue(int id, uint64_t value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -187,7 +187,7 @@
   return true;
 }
 
-bool AttributeList::SetFlagAttributeValue(int id, bool value) const {
+bool AttributeList::SetFlagAttributeValue(int id, bool value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -221,7 +221,7 @@
   return true;
 }
 
-bool AttributeList::SetStringAttributeValue(int id, string value) const {
+bool AttributeList::SetStringAttributeValue(int id, string value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
@@ -230,12 +230,27 @@
 
 // Nested Attribute.
 
-bool AttributeList::GetNestedAttributeValue(
-    int id, WeakPtr<AttributeList> *value) const {
+bool AttributeList::GetNestedAttributeList(int id,
+                                           AttributeListRefPtr *value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
     return false;
-  return attribute->GetNestedValue(value);
+  return attribute->GetNestedAttributeList(value);
+}
+
+bool AttributeList::ConstGetNestedAttributeList(
+    int id, AttributeListConstRefPtr *value) const {
+  NetlinkAttribute *attribute = GetAttribute(id);
+  if (!attribute)
+    return false;
+  return attribute->ConstGetNestedAttributeList(value);
+}
+
+bool AttributeList::SetNestedAttributeHasAValue(int id) {
+  NetlinkAttribute *attribute = GetAttribute(id);
+  if (!attribute)
+    return false;
+  return attribute->SetNestedHasAValue();
 }
 
 bool AttributeList::CreateNestedAttribute(int id, const char *id_string) {
diff --git a/attribute_list.h b/attribute_list.h
index 8c1b5d3..b165d8c 100644
--- a/attribute_list.h
+++ b/attribute_list.h
@@ -13,7 +13,8 @@
 #include <tr1/memory>
 
 #include <base/bind.h>
-#include <base/memory/weak_ptr.h>
+
+#include "shill/refptr_types.h"
 
 struct nlattr;
 namespace shill {
@@ -22,7 +23,7 @@
 class NetlinkAttribute;
 class NetlinkRawAttribute;
 
-class AttributeList : public base::SupportsWeakPtr<AttributeList> {
+class AttributeList : public base::RefCounted<AttributeList> {
  public:
   typedef std::tr1::shared_ptr<NetlinkAttribute> AttributePointer;
   typedef base::Callback<NetlinkAttribute *(int id)> NewFromIdMethod;
@@ -48,18 +49,39 @@
   // attributes exist).
   ByteString Encode() const;
 
+  bool CreateU8Attribute(int id, const char *id_string);
+  bool SetU8AttributeValue(int id, uint8_t value);
   bool GetU8AttributeValue(int id, uint8_t *value) const;
+
+  bool CreateU16Attribute(int id, const char *id_string);
+  bool SetU16AttributeValue(int id, uint16_t value);
   bool GetU16AttributeValue(int id, uint16_t *value) const;
+
+  bool CreateU32Attribute(int id, const char *id_string);
+  bool SetU32AttributeValue(int id, uint32_t value);
   bool GetU32AttributeValue(int id, uint32_t *value) const;
+
+  bool CreateU64Attribute(int id, const char *id_string);
+  bool SetU64AttributeValue(int id, uint64_t value);
   bool GetU64AttributeValue(int id, uint64_t *value) const;
+
+  bool CreateFlagAttribute(int id, const char *id_string);
+  bool SetFlagAttributeValue(int id, bool value);
   bool GetFlagAttributeValue(int id, bool *value) const;
   // |IsFlagAttributeTrue| returns true if the flag attribute |id| is true.  It
   // retruns false if the attribute does not exist, is not of type kTypeFlag,
   // or is not true.
   bool IsFlagAttributeTrue(int id) const;
+
+  bool CreateStringAttribute(int id, const char *id_string);
+  bool SetStringAttributeValue(int id, std::string value);
   bool GetStringAttributeValue(int id, std::string *value) const;
-  bool GetNestedAttributeValue(int id,
-                               base::WeakPtr<AttributeList> *value) const;
+
+  bool CreateNestedAttribute(int id, const char *id_string);
+  bool SetNestedAttributeHasAValue(int id);
+  bool GetNestedAttributeList(int id, AttributeListRefPtr *value);
+  bool ConstGetNestedAttributeList(int id,
+                                   AttributeListConstRefPtr *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
@@ -71,33 +93,6 @@
   const NetlinkRawAttribute *GetRawAttribute(int id) const;
 
  private:
-  // The Create*Attribute and Set*Attribute methods are specifically for use
-  // by nested attributes to add their sub-attributes.  Classes derived from
-  // NetlinkNestedAttribute should be added, here.
-  friend class Nl80211AttributeCqm;
-  friend class Nl80211AttributeStaInfo;
-
-  bool CreateU8Attribute(int id, const char *id_string);
-  bool SetU8AttributeValue(int id, uint8_t value) const;
-
-  bool CreateU16Attribute(int id, const char *id_string);
-  bool SetU16AttributeValue(int id, uint16_t value) const;
-
-  bool CreateU32Attribute(int id, const char *id_string);
-  bool SetU32AttributeValue(int id, uint32_t value) const;
-
-  bool CreateU64Attribute(int id, const char *id_string);
-  bool SetU64AttributeValue(int id, uint64_t value) const;
-
-  bool CreateFlagAttribute(int id, const char *id_string);
-  bool SetFlagAttributeValue(int id, bool value) const;
-
-  bool CreateStringAttribute(int id, const char *id_string);
-  bool SetStringAttributeValue(int id, std::string value) const;
-
-  bool CreateNestedAttribute(int id, const char *id_string);
-  // No |SetNestedAttributeValue| method as it would make no sense.
-
   // Using this to get around issues with const and operator[].
   NetlinkAttribute *GetAttribute(int id) const;
 
diff --git a/callback80211_metrics.cc b/callback80211_metrics.cc
index a309aa8..0583165 100644
--- a/callback80211_metrics.cc
+++ b/callback80211_metrics.cc
@@ -25,14 +25,14 @@
   if (metrics_ &&
       message.message_type() == DeauthenticateMessage::kCommand) {
     Metrics::WiFiDisconnectByWhom by_whom =
-        message.attributes().IsFlagAttributeTrue(
+        message.const_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.attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
-                                                  &rawdata)) {
+    if (message.const_attributes()->GetRawAttributeValue(NL80211_ATTR_FRAME,
+                                                         &rawdata)) {
       Nl80211Frame frame(rawdata);
       reason = frame.reason();
     }
diff --git a/config80211_unittest.cc b/config80211_unittest.cc
index 7a7d7b6..c336f91 100644
--- a/config80211_unittest.cc
+++ b/config80211_unittest.cc
@@ -30,6 +30,7 @@
 #include "shill/nl80211_attribute.h"
 #include "shill/nl80211_message.h"
 #include "shill/nl80211_socket.h"
+#include "shill/refptr_types.h"
 
 using base::Bind;
 using base::Unretained;
@@ -572,15 +573,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -607,7 +608,7 @@
     EXPECT_EQ(ssids[0].compare(""), 0);  // Expect a single, empty SSID.
   }
 
-  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
       NL80211_ATTR_SUPPORT_MESH_AUTH));
 }
 
@@ -621,15 +622,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -656,7 +657,7 @@
     EXPECT_EQ(ssids[0].compare(""), 0);  // Expect a single, empty SSID.
   }
 
-  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
       NL80211_ATTR_SUPPORT_MESH_AUTH));
 }
 
@@ -670,8 +671,8 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -682,14 +683,14 @@
   }
 
   {
-    WeakPtr<AttributeList> nested;
-    EXPECT_TRUE(message->attributes().GetNestedAttributeValue(
+    AttributeListConstRefPtr nested;
+    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
         NL80211_ATTR_STA_INFO, &nested));
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
         NL80211_ATTR_GENERATION, &value));
     EXPECT_EQ(value, kNewStationExpectedGeneration);
   }
@@ -705,22 +706,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
-                                                           &rawdata));
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kAuthenticateFrame,
@@ -739,22 +740,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
-                                                           &rawdata));
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kAssociateFrame,
@@ -773,15 +774,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -793,7 +794,7 @@
 
   {
     uint16_t value;
-    EXPECT_TRUE(message->attributes().GetU16AttributeValue(
+    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
         NL80211_ATTR_STATUS_CODE, &value));
     EXPECT_EQ(value, kExpectedConnectStatus);
   }
@@ -801,8 +802,8 @@
   // TODO(wdg): Need to check the value of this attribute.
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_RESP_IE,
-                                                           &rawdata));
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_RESP_IE, &rawdata));
   }
 }
 
@@ -816,22 +817,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
-                                                           &rawdata));
+    EXPECT_TRUE(message->const_attributes()->GetRawAttributeValue(
+        NL80211_ATTR_FRAME, &rawdata));
     EXPECT_FALSE(rawdata.IsEmpty());
     Nl80211Frame frame(rawdata);
     Nl80211Frame expected_frame(ByteString(kDeauthenticateFrame,
@@ -850,26 +851,26 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     uint16_t value;
-    EXPECT_TRUE(message->attributes().GetU16AttributeValue(
+    EXPECT_TRUE(message->const_attributes()->GetU16AttributeValue(
         NL80211_ATTR_REASON_CODE, &value));
     EXPECT_EQ(value, kExpectedDisconnectReason);
   }
 
-  EXPECT_TRUE(message->attributes().IsFlagAttributeTrue(
+  EXPECT_TRUE(message->const_attributes()->IsFlagAttributeTrue(
       NL80211_ATTR_DISCONNECTED_BY_AP));
 }
 
@@ -884,15 +885,15 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                          &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
@@ -903,9 +904,9 @@
   }
 
   {
-    WeakPtr<AttributeList> nested;
-    EXPECT_TRUE(message->attributes().GetNestedAttributeValue(NL80211_ATTR_CQM,
-                                                              &nested));
+    AttributeListConstRefPtr nested;
+    EXPECT_TRUE(message->const_attributes()->ConstGetNestedAttributeList(
+        NL80211_ATTR_CQM, &nested));
     uint32_t threshold_event;
     EXPECT_FALSE(nested->GetU32AttributeValue(
         NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, &threshold_event));
@@ -927,22 +928,22 @@
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_WIPHY, &value));
     EXPECT_EQ(value, kExpectedWifi);
   }
 
   {
     uint32_t value;
-    EXPECT_TRUE(message->attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                           &value));
+    EXPECT_TRUE(message->const_attributes()->GetU32AttributeValue(
+        NL80211_ATTR_IFINDEX, &value));
     EXPECT_EQ(value, kExpectedIfIndex);
   }
 
   {
     ByteString rawdata;
-    EXPECT_TRUE(message->attributes().GetRawAttributeValue(NL80211_ATTR_FRAME,
-                                                           &rawdata));
+    EXPECT_TRUE(message->const_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 1dc5c4d..6204979 100644
--- a/nl80211_attribute.cc
+++ b/nl80211_attribute.cc
@@ -24,10 +24,16 @@
 
 using base::StringAppendF;
 using base::StringPrintf;
-using base::WeakPtr;
 
 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,
@@ -193,7 +199,18 @@
   return false;
 }
 
-bool NetlinkAttribute::GetNestedValue(WeakPtr<AttributeList> *value) {
+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;
 }
@@ -569,26 +586,246 @@
 }
 
 // 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) {}
+    NetlinkAttribute(id, id_string, kType, kMyTypeString),
+    value_(new AttributeList) {}
 
-bool NetlinkNestedAttribute::GetNestedValue(WeakPtr<AttributeList> *output) {
+bool NetlinkNestedAttribute::GetNestedAttributeList(
+    AttributeListRefPtr *output) {
+  // Not checking |has_a_value| since GetNestedValue 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_) {
-    SLOG(WiFi, 7)  << "Nested attribute " << id_string()
-                   << " hasn't been set to any value.";
+    LOG(ERROR) << "Attribute does not exist.";
     return false;
   }
   if (output) {
-    *output = value_.AsWeakPtr();
+    *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:
+      NOTIMPLEMENTED() << "NLA_UNSPEC parsing is in an upcoming CL.";
+      // TODO(wdg): list->CreateRawAttribute(id, attribute_name.c_str());
+      // TODO(wdg): 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>";
@@ -650,14 +887,14 @@
 
 Nl80211AttributeCqm::Nl80211AttributeCqm()
       : NetlinkNestedAttribute(kName, kNameString) {
-  value_.CreateU32Attribute(NL80211_ATTR_CQM_RSSI_THOLD,
-                            "NL80211_ATTR_CQM_RSSI_THOLD");
-  value_.CreateU32Attribute(NL80211_ATTR_CQM_RSSI_HYST,
-                            "NL80211_ATTR_CQM_RSSI_HYST");
-  value_.CreateU32Attribute(NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
-                            "NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT");
-  value_.CreateU32Attribute(NL80211_ATTR_CQM_PKT_LOSS_EVENT,
-                            "NL80211_ATTR_CQM_PKT_LOSS_EVENT");
+  value_->CreateU32Attribute(NL80211_ATTR_CQM_RSSI_THOLD,
+                             "NL80211_ATTR_CQM_RSSI_THOLD");
+  value_->CreateU32Attribute(NL80211_ATTR_CQM_RSSI_HYST,
+                             "NL80211_ATTR_CQM_RSSI_HYST");
+  value_->CreateU32Attribute(NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+                             "NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT");
+  value_->CreateU32Attribute(NL80211_ATTR_CQM_PKT_LOSS_EVENT,
+                             "NL80211_ATTR_CQM_PKT_LOSS_EVENT");
 }
 
 bool Nl80211AttributeCqm::InitFromNlAttr(const nlattr *const_data) {
@@ -683,22 +920,22 @@
   }
 
   if (cqm[NL80211_ATTR_CQM_RSSI_THOLD]) {
-    value_.SetU32AttributeValue(
+    value_->SetU32AttributeValue(
         NL80211_ATTR_CQM_RSSI_THOLD,
         nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THOLD]));
   }
   if (cqm[NL80211_ATTR_CQM_RSSI_HYST]) {
-    value_.SetU32AttributeValue(
+    value_->SetU32AttributeValue(
         NL80211_ATTR_CQM_RSSI_HYST,
         nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_HYST]));
   }
   if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]) {
-    value_.SetU32AttributeValue(
+    value_->SetU32AttributeValue(
         NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
         nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]));
   }
   if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
-    value_.SetU32AttributeValue(
+    value_->SetU32AttributeValue(
         NL80211_ATTR_CQM_PKT_LOSS_EVENT,
         nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]));
   }
diff --git a/nl80211_attribute.h b/nl80211_attribute.h
index 1c555a7..241ec92 100644
--- a/nl80211_attribute.h
+++ b/nl80211_attribute.h
@@ -12,11 +12,10 @@
 #include <map>
 #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;
 
@@ -51,10 +50,6 @@
 
   virtual bool InitFromNlAttr(const nlattr *data);
 
-  // Static factory generates the appropriate NetlinkAttribute object from the
-  // raw nlattr data.
-  static NetlinkAttribute *NewFromId(int id);
-
   // Accessors for the attribute's id and datatype information.
   int id() const { return id_; }
   virtual const char *id_string() const { return id_string_; }
@@ -89,7 +84,10 @@
   virtual bool GetStringValue(std::string *value) const;
   virtual bool SetStringValue(const std::string value);
 
-  virtual bool GetNestedValue(base::WeakPtr<AttributeList> *value);
+  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
+  virtual bool ConstGetNestedAttributeList(
+      AttributeListConstRefPtr *value) const;
+  virtual bool SetNestedHasAValue();
 
   virtual bool GetRawValue(ByteString *value) const;
 
@@ -398,8 +396,11 @@
     LOG(FATAL) << "Try initializing a _specific_ nested type, instead.";
     return false;
   }
-  bool GetNestedValue(base::WeakPtr<AttributeList> *value);
-  bool ToString(std::string *value) const {
+  virtual bool GetNestedAttributeList(AttributeListRefPtr *value);
+  virtual bool ConstGetNestedAttributeList(
+      AttributeListConstRefPtr *value) const;
+  virtual bool SetNestedHasAValue();
+  virtual bool ToString(std::string *value) const {
     return false;  // TODO(wdg): Actually generate a string, here.
   }
   virtual ByteString Encode() const {
@@ -407,7 +408,57 @@
   }
 
  protected:
-  AttributeList value_;
+  // 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 {
diff --git a/nl80211_message.cc b/nl80211_message.cc
index 4b5ef2c..1b2583f 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -112,7 +112,7 @@
   SLOG(WiFi, log_level) << StringPrintf("Message %s (%d)",
                                         message_type_string(),
                                         message_type());
-  attributes_.Print(log_level, 1);
+  attributes_->Print(log_level, 1);
 }
 
 bool Nl80211Message::InitFromNlmsg(const nlmsghdr *const_msg) {
@@ -142,7 +142,7 @@
     if (tb[i]) {
       // TODO(wdg): When Nl80211Messages instantiate their own attributes,
       // this call should, instead, call |SetAttributeFromNlAttr|.
-      attributes_.CreateAndInitAttribute(
+      attributes_->CreateAndInitAttribute(
           i, tb[i], Bind(&NetlinkAttribute::NewNl80211AttributeFromId));
     }
   }
@@ -402,7 +402,7 @@
   }
 
   ByteString data;
-  if (!attributes().GetRawAttributeValue(id, &data)) {
+  if (!const_attributes()->GetRawAttributeValue(id, &data)) {
     value->assign(kBogusMacAddress);
     return false;
   }
@@ -421,7 +421,8 @@
 
   value->clear();
   ByteString rawdata;
-  if (!attributes().GetRawAttributeValue(id, &rawdata) && !rawdata.IsEmpty())
+  if (!const_attributes()->GetRawAttributeValue(id, &rawdata) &&
+      !rawdata.IsEmpty())
     return false;
 
   nlattr *nst = NULL;
@@ -446,7 +447,8 @@
   }
 
   ByteString rawdata;
-  if (!attributes().GetRawAttributeValue(id, &rawdata) || rawdata.IsEmpty())
+  if (!const_attributes()->GetRawAttributeValue(id, &rawdata) ||
+      rawdata.IsEmpty())
     return false;
 
   nlattr *nst = NULL;
@@ -469,12 +471,12 @@
 string Nl80211Message::GetHeaderString() const {
   char ifname[IF_NAMESIZE] = "";
   uint32_t ifindex = UINT32_MAX;
-  bool ifindex_exists = attributes().GetU32AttributeValue(NL80211_ATTR_IFINDEX,
-                                                          &ifindex);
+  bool ifindex_exists = const_attributes()->GetU32AttributeValue(
+      NL80211_ATTR_IFINDEX, &ifindex);
 
   uint32_t wifi = UINT32_MAX;
-  bool wifi_exists = attributes().GetU32AttributeValue(NL80211_ATTR_WIPHY,
-                                                       &wifi);
+  bool wifi_exists = const_attributes()->GetU32AttributeValue(
+      NL80211_ATTR_WIPHY, &wifi);
 
   string output;
   if (ifindex_exists && wifi_exists) {
@@ -494,8 +496,8 @@
 string Nl80211Message::StringFromFrame(int attr_name) const {
   string output;
   ByteString frame_data;
-  if (attributes().GetRawAttributeValue(attr_name,
-                                        &frame_data) && !frame_data.IsEmpty()) {
+  if (const_attributes()->GetRawAttributeValue(attr_name, &frame_data) &&
+      !frame_data.IsEmpty()) {
     Nl80211Frame frame(frame_data);
     frame.ToString(&output);
   } else {
@@ -625,8 +627,8 @@
   genl_header.reserved = 0;
 
   // Assemble attributes (padding is included by AttributeList::Encode).
-  ByteString attributes = attributes_.Encode();
-  header.nlmsg_len += attributes.GetLength();
+  ByteString attribute_bytes = attributes_->Encode();
+  header.nlmsg_len += attribute_bytes.GetLength();
 
   // Now that we know the total message size, build the output ByteString.
   ByteString result;
@@ -642,7 +644,7 @@
   result.Resize(nlmsghdr_with_pad + genlmsghdr_with_pad);  // Zero-fill.
 
   // Attributes including pad.
-  result.Append(attributes);
+  result.Append(attribute_bytes);
 
   return result;
 }
diff --git a/nl80211_message.h b/nl80211_message.h
index ca2f772..dddc46d 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -19,6 +19,7 @@
 
 #include "shill/attribute_list.h"
 #include "shill/byte_string.h"
+#include "shill/refptr_types.h"
 
 struct nlattr;
 struct nlmsghdr;
@@ -34,7 +35,8 @@
   Nl80211Message(uint8 message_type, const char *message_type_string)
       : message_type_(message_type),
         message_type_string_(message_type_string),
-        sequence_number_(kIllegalMessage) {}
+        sequence_number_(kIllegalMessage),
+        attributes_(new AttributeList) {}
   virtual ~Nl80211Message() {}
 
   // Initializes the message with bytes from the kernel.
@@ -46,7 +48,8 @@
 
   void set_sequence_number(uint32_t seq) { sequence_number_ = seq; }
 
-  const AttributeList &attributes() const { return attributes_; }
+  AttributeListConstRefPtr const_attributes() const { return attributes_; }
+  AttributeListRefPtr attributes() { return attributes_; }
 
   // TODO(wdg): This needs to be moved to AttributeMac.
   // Helper function to provide a string for a MAC address.  If no attribute
@@ -114,7 +117,7 @@
   static std::map<uint16_t, std::string> *status_code_string_;
   uint32_t sequence_number_;
 
-  AttributeList attributes_;
+  AttributeListRefPtr attributes_;
 
   DISALLOW_COPY_AND_ASSIGN(Nl80211Message);
 };
diff --git a/refptr_types.h b/refptr_types.h
index eedcd47..5478c1a 100644
--- a/refptr_types.h
+++ b/refptr_types.h
@@ -9,6 +9,10 @@
 
 namespace shill {
 
+class AttributeList;
+typedef scoped_refptr<const AttributeList> AttributeListConstRefPtr;
+typedef scoped_refptr<AttributeList> AttributeListRefPtr;
+
 class Device;
 typedef scoped_refptr<const Device> DeviceConstRefPtr;
 typedef scoped_refptr<Device> DeviceRefPtr;