shill: Adds BSS nl80211 attribute.

The BSS attribute, which is contained in the
NL80211_CMD_NEW_SCAN_RESULTS netlink message, contains SSID information
received from a scan.

BUG=chromium:221118
TEST=unittest.

Change-Id: I20766ae6d73ae5729e4ae8de47cac8bcbf5b3fe9
Reviewed-on: https://gerrit.chromium.org/gerrit/48013
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
Commit-Queue: Wade Guthrie <wdg@chromium.org>
diff --git a/attribute_list.cc b/attribute_list.cc
index a0d441f..33e1b95 100644
--- a/attribute_list.cc
+++ b/attribute_list.cc
@@ -221,6 +221,16 @@
   return true;
 }
 
+bool AttributeList::CreateSsidAttribute(int id, const char *id_string) {
+  if (ContainsKey(attributes_, id)) {
+    LOG(ERROR) << "Trying to re-add attribute: " << id;
+    return false;
+  }
+  attributes_[id] = AttributePointer(
+      new NetlinkSsidAttribute(id, id_string));
+  return true;
+}
+
 bool AttributeList::SetStringAttributeValue(int id, string value) {
   NetlinkAttribute *attribute = GetAttribute(id);
   if (!attribute)
diff --git a/attribute_list.h b/attribute_list.h
index 1e6503d..58ad37b 100644
--- a/attribute_list.h
+++ b/attribute_list.h
@@ -49,6 +49,9 @@
   // attributes exist).
   ByteString Encode() const;
 
+  // Create, get, and set attributes of the given types.  Attributes are
+  // accessed via an integer |id|.  |id_string| is a string used to describe
+  // the attribute in debug output.
   bool CreateU8Attribute(int id, const char *id_string);
   bool SetU8AttributeValue(int id, uint8_t value);
   bool GetU8AttributeValue(int id, uint8_t *value) const;
@@ -74,6 +77,8 @@
   bool IsFlagAttributeTrue(int id) const;
 
   bool CreateStringAttribute(int id, const char *id_string);
+  // SSID attributes are derived from string attributes.
+  bool CreateSsidAttribute(int id, const char *id_string);
   bool SetStringAttributeValue(int id, std::string value);
   bool GetStringAttributeValue(int id, std::string *value) const;
 
diff --git a/netlink_attribute.cc b/netlink_attribute.cc
index af1c842..a934074 100644
--- a/netlink_attribute.cc
+++ b/netlink_attribute.cc
@@ -15,6 +15,7 @@
 #include "shill/control_netlink_attribute.h"
 #include "shill/logging.h"
 #include "shill/nl80211_attribute.h"
+#include "shill/wifi.h"
 
 using std::map;
 using std::string;
@@ -42,6 +43,9 @@
 NetlinkAttribute *NetlinkAttribute::NewNl80211AttributeFromId(int id) {
   scoped_ptr<NetlinkAttribute> attr;
   switch (id) {
+    case NL80211_ATTR_BSS:
+      attr.reset(new Nl80211AttributeBss());
+      break;
     case NL80211_ATTR_COOKIE:
       attr.reset(new Nl80211AttributeCookie());
       break;
@@ -626,6 +630,21 @@
       value_.size() + 1);
 }
 
+// SSID attribute.
+
+bool NetlinkSsidAttribute::ToString(string *output) const {
+  if (!output) {
+    LOG(ERROR) << "Null |output| parameter";
+    return false;
+  }
+  string value;
+  if (!GetStringValue(&value))
+    return false;
+
+  *output = WiFi::LogSSID(value);
+  return true;
+}
+
 // NetlinkNestedAttribute
 
 const char NetlinkNestedAttribute::kMyTypeString[] = "nested";
diff --git a/netlink_attribute.h b/netlink_attribute.h
index 33cffd6..43e4575 100644
--- a/netlink_attribute.h
+++ b/netlink_attribute.h
@@ -249,13 +249,27 @@
   virtual bool SetStringValue(const std::string new_value);
   virtual bool ToString(std::string *value) const;
   virtual ByteString Encode() const;
+  std::string value() const { return value_; }
+  void set_value(const std::string &value) { value_ = value; }
 
  private:
   std::string value_;
-
   DISALLOW_COPY_AND_ASSIGN(NetlinkStringAttribute);
 };
 
+// SSID attributes are just string attributes with different output semantics.
+class NetlinkSsidAttribute : public NetlinkStringAttribute {
+ public:
+  NetlinkSsidAttribute(int id, const char *id_string)
+      : NetlinkStringAttribute(id, id_string) {}
+
+  // NOTE: |ToString| or |Print| must be used for logging to allow scrubbing.
+  virtual bool ToString(std::string *output) const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NetlinkSsidAttribute);
+};
+
 class NetlinkNestedAttribute : public NetlinkAttribute {
  public:
   static const char kMyTypeString[];
diff --git a/nl80211_attribute.cc b/nl80211_attribute.cc
index a921fc7..92dac5c 100644
--- a/nl80211_attribute.cc
+++ b/nl80211_attribute.cc
@@ -6,13 +6,150 @@
 
 #include <netlink/attr.h>
 
+#include <string>
+
+#include <base/bind.h>
+#include <base/format_macros.h>
+#include <base/stringprintf.h>
+
 #include "shill/logging.h"
 
+using base::Bind;
+using base::StringPrintf;
+using std::string;
+
 namespace shill {
 
 const int Nl80211AttributeCookie::kName = NL80211_ATTR_COOKIE;
 const char Nl80211AttributeCookie::kNameString[] = "NL80211_ATTR_COOKIE";
 
+const int Nl80211AttributeBss::kName = NL80211_ATTR_BSS;
+const char Nl80211AttributeBss::kNameString[] = "NL80211_ATTR_BSS";
+const int Nl80211AttributeBss::kChannelsAttributeId = 0x24;
+const int Nl80211AttributeBss::kChallengeTextAttributeId = 0x10;
+const int Nl80211AttributeBss::kCountryInfoAttributeId = 0x07;
+const int Nl80211AttributeBss::kDSParameterSetAttributeId = 0x03;
+const int Nl80211AttributeBss::kErpAttributeId = 0x2a;
+const int Nl80211AttributeBss::kExtendedRatesAttributeId = 0x32;
+const int Nl80211AttributeBss::kHtCapAttributeId = 0x2d;
+const int Nl80211AttributeBss::kHtInfoAttributeId = 0x3d;
+const int Nl80211AttributeBss::kPowerCapabilityAttributeId = 0x21;
+const int Nl80211AttributeBss::kPowerConstraintAttributeId = 0x20;
+const int Nl80211AttributeBss::kRequestAttributeId = 0x0a;
+const int Nl80211AttributeBss::kRsnAttributeId = 0x30;
+const int Nl80211AttributeBss::kSsidAttributeId = 0x00;
+const int Nl80211AttributeBss::kSupportedRatesAttributeId = 0x01;
+const int Nl80211AttributeBss::kTcpReportAttributeId = 0x23;
+const int Nl80211AttributeBss::kVendorSpecificAttributeId = 0xdd;
+
+static const char kSsidString[] = "SSID";
+static const char kRatesString[] = "Rates";
+
+Nl80211AttributeBss::Nl80211AttributeBss()
+      : NetlinkNestedAttribute(kName, kNameString) {
+  nested_template_.push_back(NestedData(NLA_U32, "__NL80211_BSS_INVALID",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_UNSPEC, "NL80211_BSS_BSSID",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U32, "NL80211_BSS_FREQUENCY",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U64, "NL80211_BSS_TSF", false));
+  nested_template_.push_back(NestedData(NLA_U16, "NL80211_BSS_BEACON_INTERVAL",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U16, "NL80211_BSS_CAPABILITY",
+                                        false));
+  nested_template_.push_back(NestedData(
+      NLA_UNSPEC, "NL80211_BSS_INFORMATION_ELEMENTS", false,
+      Bind(&Nl80211AttributeBss::ParseInformationElements)));
+  nested_template_.push_back(NestedData(NLA_U32, "NL80211_BSS_SIGNAL_MBM",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U8, "NL80211_BSS_SIGNAL_UNSPEC",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U32, "NL80211_BSS_STATUS",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_U32, "NL80211_BSS_SEEN_MS_AGO",
+                                        false));
+  nested_template_.push_back(NestedData(NLA_UNSPEC, "NL80211_BSS_BEACON_IES",
+                                        false));
+}
+
+bool Nl80211AttributeBss::ParseInformationElements(
+    AttributeList *attribute_list, size_t id, const string &attribute_name,
+    ByteString data) {
+  if (!attribute_list) {
+    LOG(ERROR) << "NULL |attribute_list| parameter";
+    return false;
+  }
+  attribute_list->CreateNestedAttribute(id, attribute_name.c_str());
+
+  // Now, handle the nested data.
+  AttributeListRefPtr ie_attribute;
+  if (!attribute_list->GetNestedAttributeList(id, &ie_attribute) ||
+      !ie_attribute) {
+    LOG(ERROR) << "Couldn't get attribute " << attribute_name
+               << " which we just created.";
+    return false;
+  }
+  while (data.GetLength()) {
+    const uint8_t *sub_attribute = data.GetConstData();
+    const size_t kHeaderBytes = 2;
+    uint8_t type = sub_attribute[0];
+    uint8_t payload_bytes = sub_attribute[1];
+    const uint8_t *payload = &sub_attribute[kHeaderBytes];
+    // See http://dox.ipxe.org/ieee80211_8h_source.html for more info on types
+    // and data inside information elements.
+    switch (type) {
+      case kSsidAttributeId: {
+        ie_attribute->CreateSsidAttribute(type, kSsidString);
+        if (payload_bytes == 0) {
+          ie_attribute->SetStringAttributeValue(type, "");
+        } else {
+          ie_attribute->SetStringAttributeValue(type, string(
+              reinterpret_cast<const char *>(payload), payload_bytes));
+        }
+        break;
+      }
+      case kSupportedRatesAttributeId:
+      case kExtendedRatesAttributeId: {
+        ie_attribute->CreateNestedAttribute(type, kRatesString);
+        AttributeListRefPtr rates_attribute;
+        if (!ie_attribute->GetNestedAttributeList(type, &rates_attribute) ||
+            !rates_attribute) {
+          LOG(ERROR) << "Couldn't get attribute " << attribute_name
+                     << " which we just created.";
+          break;
+        }
+        // Extract each rate, add it to the list.
+        for (size_t i = 0; i < payload_bytes; ++i) {
+          string rate_name = StringPrintf("Rate-%zu", i);
+          rates_attribute->CreateU8Attribute(i, rate_name.c_str());
+          rates_attribute->SetU8AttributeValue(i, payload[i]);
+        }
+        ie_attribute->SetNestedAttributeHasAValue(type);
+        break;
+      }
+      case kDSParameterSetAttributeId:
+      case kCountryInfoAttributeId:
+      case kRequestAttributeId:
+      case kChallengeTextAttributeId:
+      case kPowerConstraintAttributeId:
+      case kPowerCapabilityAttributeId:
+      case kTcpReportAttributeId:
+      case kChannelsAttributeId:
+      case kErpAttributeId:
+      case kHtCapAttributeId:
+      case kRsnAttributeId:
+      case kHtInfoAttributeId:
+      case kVendorSpecificAttributeId:
+      default:
+        break;
+    }
+    data.RemovePrefix(kHeaderBytes + payload_bytes);
+  }
+  attribute_list->SetNestedAttributeHasAValue(id);
+  return true;
+}
+
 const int Nl80211AttributeCqm::kName = NL80211_ATTR_CQM;
 const char Nl80211AttributeCqm::kNameString[] = "NL80211_ATTR_CQM";
 
@@ -42,8 +179,8 @@
 const char Nl80211AttributeFrame::kNameString[] = "NL80211_ATTR_FRAME";
 
 const int Nl80211AttributeGeneration::kName = NL80211_ATTR_GENERATION;
-const char Nl80211AttributeGeneration::kNameString[]
-    = "NL80211_ATTR_GENERATION";
+const char Nl80211AttributeGeneration::kNameString[] =
+    "NL80211_ATTR_GENERATION";
 
 const int Nl80211AttributeIfindex::kName = NL80211_ATTR_IFINDEX;
 const char Nl80211AttributeIfindex::kNameString[] = "NL80211_ATTR_IFINDEX";
diff --git a/nl80211_attribute.h b/nl80211_attribute.h
index 463ac70..99974cc 100644
--- a/nl80211_attribute.h
+++ b/nl80211_attribute.h
@@ -211,6 +211,39 @@
 
 // Nested.
 
+class Nl80211AttributeBss : public NetlinkNestedAttribute {
+ public:
+  static const int kName;
+  static const char kNameString[];
+  // These are sorted alphabetically.
+  static const int kChallengeTextAttributeId;
+  static const int kChannelsAttributeId;
+  static const int kCountryInfoAttributeId;
+  static const int kDSParameterSetAttributeId;
+  static const int kErpAttributeId;
+  static const int kExtendedRatesAttributeId;
+  static const int kHtCapAttributeId;
+  static const int kHtInfoAttributeId;
+  static const int kPowerCapabilityAttributeId;
+  static const int kPowerConstraintAttributeId;
+  static const int kRequestAttributeId;
+  static const int kRsnAttributeId;
+  static const int kSsidAttributeId;
+  static const int kSupportedRatesAttributeId;
+  static const int kTcpReportAttributeId;
+  static const int kVendorSpecificAttributeId;
+
+  Nl80211AttributeBss();
+
+ private:
+  static bool ParseInformationElements(AttributeList *attribute_list,
+                                       size_t id,
+                                       const std::string &attribute_name,
+                                       ByteString data);
+
+  DISALLOW_COPY_AND_ASSIGN(Nl80211AttributeBss);
+};
+
 class Nl80211AttributeCqm : public NetlinkNestedAttribute {
  public:
   static const int kName;
diff --git a/wifi_service_unittest.cc b/wifi_service_unittest.cc
index bd89b81..3078eee 100644
--- a/wifi_service_unittest.cc
+++ b/wifi_service_unittest.cc
@@ -360,7 +360,7 @@
   WiFiServiceRefPtr wifi_service = MakeSimpleService(flimflam::kSecurityWpa);
   ReadablePropertyConstIterator<string> it =
       (wifi_service->store()).GetStringPropertiesIter();
-  for( ; !it.AtEnd(); it.Advance())
+  for ( ; !it.AtEnd(); it.Advance())
     EXPECT_NE(it.Key(), flimflam::kPassphraseProperty);
 }