shill: add get wake-on-packet settings functionality

Add functionality to send nl80211 requests to the kernel for
the NIC's wake-on-packet settings and to parse response
messages from the kernel. Change add and remove wake-on-packet
function logic to make use of this new functionality to
asychronously verify the programming of the NIC.

BUG=chromium:399137
TEST='P2_TEST_FILTER="shill::*" FEATURES="test" emerge-squawks
platform2' succeeds; manual testing on squawks
CQ-DEPEND=CL:214100

Change-Id: I2133af7818f5655d62c371d928f0b62eca99e196
Reviewed-on: https://chromium-review.googlesource.com/214153
Tested-by: Samuel Tan <samueltan@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
Commit-Queue: Samuel Tan <samueltan@chromium.org>
diff --git a/device.h b/device.h
index 8ca41b4..ca7e9eb 100644
--- a/device.h
+++ b/device.h
@@ -279,7 +279,7 @@
   virtual void RemoveAllWakeOnPacketConnections(Error *error);
 
   // Get all registered wake-on-packet connections
-  const IPAddressStore &GetAllWakeOnPacketConnections() const {
+  const IPAddressStore &GetWakeOnPacketConnections() const {
     return wake_on_packet_connections_;
   }
 
diff --git a/manager.cc b/manager.cc
index 0641065..2445ff9 100644
--- a/manager.cc
+++ b/manager.cc
@@ -2127,7 +2127,7 @@
     return;  // No transfer required
   }
   new_device->AddWakeOnPacketConnections(
-      old_device->GetAllWakeOnPacketConnections());
+      old_device->GetWakeOnPacketConnections());
   Error e;
   old_device->RemoveAllWakeOnPacketConnections(&e);
   if (e.IsFailure()) {
diff --git a/nl80211_message.cc b/nl80211_message.cc
index 077f666..b2d0fe0 100644
--- a/nl80211_message.cc
+++ b/nl80211_message.cc
@@ -488,6 +488,10 @@
 const char SetWakeOnPacketConnMessage::kCommandString[] =
     "NL80211_CMD_SET_WOWLAN";
 
+const uint8_t GetWakeOnPacketConnMessage::kCommand = NL80211_CMD_GET_WOWLAN;
+const char GetWakeOnPacketConnMessage::kCommandString[] =
+    "NL80211_CMD_GET_WOWLAN";
+
 const uint8_t GetWiphyMessage::kCommand = NL80211_CMD_GET_WIPHY;
 const char GetWiphyMessage::kCommandString[] = "NL80211_CMD_GET_WIPHY";
 
diff --git a/nl80211_message.h b/nl80211_message.h
index e5f952e..baf51c3 100644
--- a/nl80211_message.h
+++ b/nl80211_message.h
@@ -237,6 +237,17 @@
   DISALLOW_COPY_AND_ASSIGN(SetWakeOnPacketConnMessage);
 };
 
+class GetWakeOnPacketConnMessage : public Nl80211Message {
+ public:
+  static const uint8_t kCommand;
+  static const char kCommandString[];
+
+  GetWakeOnPacketConnMessage() : Nl80211Message(kCommand, kCommandString) {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GetWakeOnPacketConnMessage);
+};
+
 class GetWiphyMessage : public Nl80211Message {
  public:
   static const uint8_t kCommand;
diff --git a/wake_on_wifi.cc b/wake_on_wifi.cc
index ca20587..ec876ef 100644
--- a/wake_on_wifi.cc
+++ b/wake_on_wifi.cc
@@ -6,23 +6,30 @@
 
 #include <stdio.h>
 
-#include <linux/if_ether.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-
+#include <set>
+#include <utility>
 #include <vector>
 
 #include "shill/byte_string.h"
 #include "shill/error.h"
 #include "shill/ip_address.h"
+#include "shill/ip_address_store.h"
 #include "shill/nl80211_message.h"
 
+using std::pair;
+using std::set;
 using std::vector;
 
 namespace shill {
 
 namespace WakeOnWifi {
 
+bool ByteStringPairIsLessThan(const std::pair<ByteString, ByteString> &lhs,
+                    const std::pair<ByteString, ByteString> &rhs) {
+  // Treat the first value of the pair as the key.
+  return ByteString::IsLessThan(lhs.first, rhs.first);
+}
+
 void SetMask(ByteString *mask, uint32_t pattern_len, uint32_t offset) {
   // Round up number of bytes required for the mask.
   int result_mask_len = (pattern_len + 8 - 1) / 8;
@@ -58,7 +65,6 @@
     struct ethhdr eth_hdr;
     struct iphdr ipv4_hdr;
   } __attribute__((__packed__)) pattern_bytes;
-
   memset(&pattern_bytes, 0, sizeof(pattern_bytes));
   CHECK_EQ(sizeof(pattern_bytes.ipv4_hdr.saddr), ip_addr.GetLength());
   memcpy(&pattern_bytes.ipv4_hdr.saddr, ip_addr.GetConstData(),
@@ -79,7 +85,6 @@
     struct ethhdr eth_hdr;
     struct ip6_hdr ipv6_hdr;
   } __attribute__((__packed__)) pattern_bytes;
-
   memset(&pattern_bytes, 0, sizeof(pattern_bytes));
   CHECK_EQ(sizeof(pattern_bytes.ipv6_hdr.ip6_src), ip_addr.GetLength());
   memcpy(&pattern_bytes.ipv6_hdr.ip6_src, ip_addr.GetConstData(),
@@ -94,7 +99,7 @@
   WakeOnWifi::SetMask(mask, pattern_len, src_ip_offset);
 }
 
-bool ConfigureWiphyIndex(SetWakeOnPacketConnMessage *msg, int32_t index) {
+bool ConfigureWiphyIndex(Nl80211Message *msg, int32_t index) {
   if (!msg->attributes()->CreateU32Attribute(NL80211_ATTR_WIPHY,
                                              "WIPHY index")) {
     return false;
@@ -108,7 +113,7 @@
 bool ConfigureRemoveAllWakeOnPacketMsg(SetWakeOnPacketConnMessage *msg,
                                        uint32_t wiphy_index, Error *error) {
   if (!WakeOnWifi::ConfigureWiphyIndex(msg, wiphy_index)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Failed to configure Wiphy index.");
     return false;
   }
@@ -116,27 +121,24 @@
 }
 
 bool ConfigureAddWakeOnPacketMsg(SetWakeOnPacketConnMessage *msg,
-                                 const IPAddress &ip_addr, uint32_t wiphy_index,
-                                 Error *error) {
-  ByteString pattern;
-  ByteString mask;
-  WakeOnWifi::CreateIPAddressPatternAndMask(ip_addr, &pattern, &mask);
+                                 const IPAddressStore &addrs,
+                                 uint32_t wiphy_index, Error *error) {
   if (!WakeOnWifi::ConfigureWiphyIndex(msg, wiphy_index)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Failed to configure Wiphy index.");
     return false;
   }
-  if (!(*msg).attributes()->CreateNestedAttribute(NL80211_ATTR_WOWLAN_TRIGGERS,
+  if (!msg->attributes()->CreateNestedAttribute(NL80211_ATTR_WOWLAN_TRIGGERS,
                                                   "WoWLAN Triggers")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not create nested attribute "
                           "NL80211_ATTR_WOWLAN_TRIGGERS for "
                           "SetWakeOnPacketConnMessage.");
     return false;
   }
-  if (!(*msg).attributes()->SetNestedAttributeHasAValue(
+  if (!msg->attributes()->SetNestedAttributeHasAValue(
           NL80211_ATTR_WOWLAN_TRIGGERS)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set nested attribute "
                           "NL80211_ATTR_WOWLAN_TRIGGERS for "
                           "SetWakeOnPacketConnMessage.");
@@ -144,9 +146,9 @@
   }
 
   AttributeListRefPtr triggers;
-  if (!(*msg).attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
+  if (!msg->attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
                                                    &triggers)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not get nested attribute list "
                           "NL80211_ATTR_WOWLAN_TRIGGERS for "
                           "SetWakeOnPacketConnMessage.");
@@ -154,14 +156,14 @@
   }
   if (!triggers->CreateNestedAttribute(NL80211_WOWLAN_TRIG_PKT_PATTERN,
                                        "Pattern trigger")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not create nested attribute "
                           "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
                           "SetWakeOnPacketConnMessage.");
     return false;
   }
   if (!triggers->SetNestedAttributeHasAValue(NL80211_WOWLAN_TRIG_PKT_PATTERN)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set nested attribute "
                           "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
                           "SetWakeOnPacketConnMessage.");
@@ -171,25 +173,36 @@
   AttributeListRefPtr patterns;
   if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
                                         &patterns)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not get nested attribute list "
                           "NL80211_WOWLAN_TRIG_PKT_PATTERN for "
                           "SetWakeOnPacketConnMessage.");
     return false;
   }
-  // This additional level of attribute nesting on an attribute indexed by
-  // 1 is necessary to get the packet accepted. It is unclear what this
-  // attribute is (wowlan.c in the iw source code references it as "patnum")
-  // TODO(samueltan): identify this nested attribute
+
   uint8_t patnum = 1;
+  for (const IPAddress &addr : addrs.GetIPAddresses()) {
+    if (!CreateSinglePattern(addr, patterns, patnum++, error)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool CreateSinglePattern(const IPAddress &ip_addr, AttributeListRefPtr patterns,
+                         uint8_t patnum, Error *error) {
+  ByteString pattern;
+  ByteString mask;
+  WakeOnWifi::CreateIPAddressPatternAndMask(ip_addr, &pattern, &mask);
   if (!patterns->CreateNestedAttribute(patnum, "Pattern info")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not create nested attribute "
                           "patnum for SetWakeOnPacketConnMessage.");
     return false;
   }
   if (!patterns->SetNestedAttributeHasAValue(patnum)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set nested attribute "
                           "patnum for SetWakeOnPacketConnMessage.");
     return false;
@@ -197,48 +210,48 @@
 
   AttributeListRefPtr pattern_info;
   if (!patterns->GetNestedAttributeList(patnum, &pattern_info)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not get nested attribute list "
                           "patnum for SetWakeOnPacketConnMessage.");
     return false;
   }
-  // Add mask
+  // Add mask.
   if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_MASK, "Mask")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not add attribute NL80211_PKTPAT_MASK to "
                           "pattern_info.");
     return false;
   }
   if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_MASK, mask)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set attribute NL80211_PKTPAT_MASK in "
                           "pattern_info.");
     return false;
   }
 
-  // Add pattern
+  // Add pattern.
   if (!pattern_info->CreateRawAttribute(NL80211_PKTPAT_PATTERN, "Pattern")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not add attribute NL80211_PKTPAT_PATTERN to "
                           "pattern_info.");
     return false;
   }
   if (!pattern_info->SetRawAttributeValue(NL80211_PKTPAT_PATTERN, pattern)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set attribute NL80211_PKTPAT_PATTERN in "
                           "pattern_info.");
     return false;
   }
 
-  // Add offset
+  // Add offset.
   if (!pattern_info->CreateU32Attribute(NL80211_PKTPAT_OFFSET, "Offset")) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not add attribute NL80211_PKTPAT_OFFSET to "
                           "pattern_info.");
     return false;
   }
   if (!pattern_info->SetU32AttributeValue(NL80211_PKTPAT_OFFSET, 0)) {
-    error->PopulateAndLog(error, Error::kOperationFailed,
+    Error::PopulateAndLog(error, Error::kOperationFailed,
                           "Could not set attribute NL80211_PKTPAT_OFFSET in "
                           "pattern_info.");
     return false;
@@ -246,6 +259,90 @@
   return true;
 }
 
+bool ConfigureGetWakeOnPacketMsg(GetWakeOnPacketConnMessage *msg,
+                                 uint32_t wiphy_index, Error *error) {
+  if (!WakeOnWifi::ConfigureWiphyIndex(msg, wiphy_index)) {
+    Error::PopulateAndLog(error, Error::kOperationFailed,
+                          "Failed to configure Wiphy index.");
+    return false;
+  }
+  return true;
+}
+
+bool WakeOnPacketSettingsMatch(const Nl80211Message &msg,
+                               const IPAddressStore &addrs) {
+  if (msg.command() != NL80211_CMD_GET_WOWLAN &&
+      msg.command() != NL80211_CMD_SET_WOWLAN) {
+    LOG(ERROR) << "Invalid message command";
+    return false;
+  }
+  set<pair<ByteString, ByteString>,
+      bool (*)(const pair<ByteString, ByteString> &,
+               const pair<ByteString, ByteString> &)>
+      expected_patt_mask_pairs(ByteStringPairIsLessThan);
+  ByteString temp_pattern;
+  ByteString temp_mask;
+  for (const IPAddress &addr : addrs.GetIPAddresses()) {
+    temp_pattern.Clear();
+    temp_mask.Clear();
+    WakeOnWifi::CreateIPAddressPatternAndMask(addr, &temp_pattern, &temp_mask);
+    expected_patt_mask_pairs.emplace(temp_pattern, temp_mask);
+  }
+  AttributeListConstRefPtr triggers;
+  if (!msg.const_attributes()->ConstGetNestedAttributeList(
+          NL80211_ATTR_WOWLAN_TRIGGERS, &triggers)) {
+    // No triggers in the returned message, which is valid iff the NIC has no
+    // wake-on-packet settings currently programmed into it.
+    return (expected_patt_mask_pairs.size() == 0);
+  }
+  AttributeListConstRefPtr patterns;
+  if (!triggers->ConstGetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                                             &patterns)) {
+    LOG(ERROR) << "Could not get nested attribute list "
+                  "NL80211_WOWLAN_TRIG_PKT_PATTERN.";
+    return false;
+  }
+  bool mismatch_found = false;
+  size_t num_mismatch = expected_patt_mask_pairs.size();
+  int pattern_index;
+  AttributeIdIterator pattern_iter(*patterns);
+  AttributeListConstRefPtr pattern_info;
+  ByteString returned_mask;
+  ByteString returned_pattern;
+  while (!pattern_iter.AtEnd()) {
+    returned_mask.Clear();
+    returned_pattern.Clear();
+    pattern_index = pattern_iter.GetId();
+    if (!patterns->ConstGetNestedAttributeList(pattern_index, &pattern_info)) {
+      LOG(ERROR) << "Could not get nested attribute list index "
+                 << pattern_index << " in patterns.";
+      return false;
+    }
+    if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_MASK,
+                                            &returned_mask)) {
+      LOG(ERROR) << "Could not get attribute NL80211_PKTPAT_MASK in "
+                    "pattern_info.";
+      return false;
+    }
+    if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_PATTERN,
+                                            &returned_pattern)) {
+      LOG(ERROR) << "Could not get attribute NL80211_PKTPAT_PATTERN in "
+                    "pattern_info.";
+      return false;
+    }
+    if (expected_patt_mask_pairs.find(
+            pair<ByteString, ByteString>(returned_pattern, returned_mask)) ==
+        expected_patt_mask_pairs.end()) {
+      mismatch_found = true;
+      break;
+    } else {
+      --num_mismatch;
+    }
+    pattern_iter.Advance();
+  }
+  return (!mismatch_found && !num_mismatch);
+}
+
 }  // namespace WakeOnWifi
 
 }  // namespace shill
diff --git a/wake_on_wifi.h b/wake_on_wifi.h
index 6777e8a..d68cb36 100644
--- a/wake_on_wifi.h
+++ b/wake_on_wifi.h
@@ -5,22 +5,37 @@
 #ifndef SHILL_WAKE_ON_WIFI_H_
 #define SHILL_WAKE_ON_WIFI_H_
 
+#include <linux/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <utility>
+#include <vector>
+
 #include <base/basictypes.h>
 
+#include "shill/refptr_types.h"
+
 namespace shill {
 
 class ByteString;
 class Error;
 class IPAddress;
+class IPAddressStore;
+class GetWakeOnPacketConnMessage;
+class Nl80211Message;
 class SetWakeOnPacketConnMessage;
 
 namespace WakeOnWifi {
 
+// Used for comparison of ByteString pairs in a set.
+bool ByteStringPairIsLessThan(const std::pair<ByteString, ByteString> &lhs,
+                    const std::pair<ByteString, ByteString> &rhs);
 // Creates a mask which specifies which bytes in pattern of length |pattern_len|
 // to match against. Bits |offset| to |pattern_len| - 1 are set, which bits 0 to
 // bits 0 to |offset| - 1 are unset. This mask is saved in |mask|.
 void SetMask(ByteString *mask, uint32_t pattern_len, uint32_t offset);
-// Creates a pattern and mask for an NL80211 message that programs the NIC to
+// Creates a pattern and mask for a NL80211 message that programs the NIC to
 // wake on packets originating from IP address |ip_addr|. The pattern and mask
 // are saved in |pattern| and |mask| respectively.
 bool CreateIPAddressPatternAndMask(const IPAddress &ip_addr,
@@ -29,22 +44,43 @@
                               ByteString *mask);
 void CreateIPV6PatternAndMask(const IPAddress &ip_addr, ByteString *pattern,
                               ByteString *mask);
-// Creates and sets an attribute in an NL80211 message |msg| which indicates the
+// Creates and sets an attribute in a NL80211 message |msg| which indicates the
 // index of the wiphy device to program.
-bool ConfigureWiphyIndex(SetWakeOnPacketConnMessage *msg, int32_t index);
-// Creates and sets attributes in an NL80211 message |msg| so that the message
-// will deprogram all wake-on-packet rules from the NIC with wiphy index
-// |wiphy_index|.
+bool ConfigureWiphyIndex(Nl80211Message *msg, int32_t index);
+// Creates and sets attributes in an SetWakeOnPacketConnMessage |msg| so that
+// the message will deprogram all wake-on-packet rules from the NIC with wiphy
+// index |wiphy_index|.
 // NOTE: Assumes that |msg| has not been altered since construction.
 bool ConfigureRemoveAllWakeOnPacketMsg(SetWakeOnPacketConnMessage *msg,
                                        uint32_t wiphy_index, Error *error);
-// Creates and sets attributes in an NL80211 message |msg| so that the message
-// will program the NIC with wiphy index |wiphy_index| to wake on packets
-// originating from IP address |ip_addr|.
+// Creates and sets attributes in a SetWakeOnPacketConnMessage |msg|
+// so that the message will program the NIC with wiphy index |wiphy_index| to
+// wake on packets originating from IP addresses in |addrs|.
 // NOTE: Assumes that |msg| has not been altered since construction.
 bool ConfigureAddWakeOnPacketMsg(SetWakeOnPacketConnMessage *msg,
-                                 const IPAddress &ip_addr, uint32_t wiphy_index,
-                                 Error *error);
+                                 const IPAddressStore &addrs,
+                                 uint32_t wiphy_index, Error *error);
+// Helper function to ConfigureAddWakeOnPacketMsg that creates a single nested
+// attribute inside the attribute list referenced by |patterns| representing
+// a single wake-on-packet pattern matching rule with index |patnum|.
+// NOTE: |patterns| is assumed to reference the nested attribute list
+// NL80211_WOWLAN_TRIG_PKT_PATTERN.
+// NOTE: |patnum| should be unique across multiple calls to this function to
+// prevent the formation of a erroneous nl80211 message or the overwriting of
+// pattern matching rules.
+bool CreateSinglePattern(const IPAddress &ip_addr, AttributeListRefPtr patterns,
+                         uint8_t patnum, Error *error);
+// Creates and sets attributes in an GetWakeOnPacketConnMessage msg| so that
+// the message will request for wake-on-packet settings information from the NIC
+// with wiphy index |wiphy_index|.
+// NOTE: Assumes that |msg| has not been altered since construction.
+bool ConfigureGetWakeOnPacketMsg(GetWakeOnPacketConnMessage *msg,
+                                 uint32_t wiphy_index, Error *error);
+// Given a NL80211_CMD_GET_WOWLAN response or NL80211_CMD_SET_WOWLAN request
+// |msg|, returns true if the source IP addresses in |msg| match those in
+// |addrs|. Returns false otherwise.
+bool WakeOnPacketSettingsMatch(const Nl80211Message &msg,
+                               const IPAddressStore &addrs);
 
 }  // namespace WakeOnWifi
 
diff --git a/wake_on_wifi_unittest.cc b/wake_on_wifi_unittest.cc
index 2899c23..d1ed951 100644
--- a/wake_on_wifi_unittest.cc
+++ b/wake_on_wifi_unittest.cc
@@ -2,25 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <set>
+#include <string>
+#include <utility>
+
 #include <gtest/gtest.h>
 
 #include "shill/byte_string.h"
 #include "shill/error.h"
 #include "shill/ip_address.h"
+#include "shill/ip_address_store.h"
 #include "shill/nl80211_message.h"
 #include "shill/testing.h"
 #include "shill/wake_on_wifi.h"
 
+using std::string;
+
 namespace shill {
 
 namespace {
 
-// Zero-byte pattern prefixes to match the offsetting bytes in the ethernet
-// frame that lie before the source ip address field
-const uint8_t kIPV4PatternPrefix[] = {
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+// Zero-byte pattern prefixes to match the offsetting bytes in the Ethernet
+// frame that lie before the source IP address field.
+const uint8_t kIPV4PatternPrefix[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                      0x00, 0x00, 0x00, 0x00, 0x00};
 const uint8_t kIPV6PatternPrefix[] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@@ -28,7 +35,7 @@
 // These masks have bits set to 1 to match bytes in an IP address pattern that
 // represent the source IP address of the frame. They are padded with zero
 // bits in front to ignore the frame offset and at the end to byte-align the
-// mask itself
+// mask itself.
 const uint8_t kIPV4MaskBytes[] = {0x00, 0x00, 0x00, 0x3c};
 const uint8_t kIPV6MaskBytes[] = {0x00, 0x00, 0xc0, 0xff, 0x3f};
 
@@ -70,6 +77,71 @@
                                       0x00, 0x00, 0x00, 0x00, 0xde, 0xde,
                                       0xbe, 0x90, 0x34, 0x26};
 
+// These blobs represent NL80211 messages from the kernel reporting the NIC's
+// wake-on-packet settings, sent in response to NL80211_CMD_GET_WOWLAN requests.
+const uint8_t kResponseNoIPAddresses[] = {
+    0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x57, 0x40, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00};
+const uint8_t kResponseIPV40[] = {
+    0x4C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+    0x00, 0x57, 0x40, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x38, 0x00,
+    0x75, 0x00, 0x34, 0x00, 0x04, 0x00, 0x30, 0x00, 0x01, 0x00, 0x08,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8, 0x0A, 0x14, 0x00, 0x00};
+const uint8_t kResponseIPV401[] = {
+    0x7C, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x57, 0x40, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x68, 0x00, 0x75, 0x00,
+    0x64, 0x00, 0x04, 0x00, 0x30, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+    0x03, 0x04, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8,
+    0x0A, 0x14, 0x00, 0x00};
+const uint8_t kResponseIPV401IPV60[] = {
+    0xB8, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x57, 0x40, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0xA4, 0x00, 0x75, 0x00,
+    0xA0, 0x00, 0x04, 0x00, 0x30, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+    0x03, 0x04, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8,
+    0x0A, 0x14, 0x00, 0x00, 0x3C, 0x00, 0x03, 0x00, 0x09, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xDC,
+    0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
+    0x32, 0x10, 0x00, 0x00};
+const uint8_t kResponseIPV401IPV601[] = {
+    0xF4, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x57, 0x40, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0xE0, 0x00, 0x75, 0x00,
+    0xDC, 0x00, 0x04, 0x00, 0x30, 0x00, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+    0x03, 0x04, 0x00, 0x00, 0x3C, 0x00, 0x02, 0x00, 0x09, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x20, 0x0C,
+    0x41, 0x7A, 0x00, 0x00, 0x30, 0x00, 0x03, 0x00, 0x08, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x3C, 0x22, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA8,
+    0x0A, 0x14, 0x00, 0x00, 0x3C, 0x00, 0x04, 0x00, 0x09, 0x00, 0x01, 0x00,
+    0x00, 0x00, 0xC0, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x02, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xDC,
+    0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54,
+    0x32, 0x10, 0x00, 0x00};
+
 }  // namespace
 
 ByteString CreatePattern(const unsigned char *prefix, size_t prefix_len,
@@ -79,7 +151,29 @@
   return result;
 }
 
-TEST(WifiUtilTest, CreateIPAddressPatternAndMask) {
+bool IPAddressesMatch(const IPAddressStore &addrs0,
+                      const std::vector<IPAddress> &addrs1) {
+  if (addrs0.Count() != addrs1.size()) {
+    return false;
+  }
+  bool mismatch_found = false;
+  int num_mismatch = addrs0.Count();
+  for (const IPAddress &addr : addrs1) {
+    if (addrs0.Contains(addr)) {
+      --num_mismatch;
+    } else {
+      mismatch_found = true;
+      break;
+    }
+  }
+  if (mismatch_found || num_mismatch != 0) {
+    return false;
+  } else {
+    return true;
+  }
+}
+
+TEST(WakeOnWifiTest, CreateIPAddressPatternAndMask) {
   ByteString pattern;
   ByteString mask;
   ByteString expected_pattern;
@@ -192,7 +286,7 @@
   EXPECT_TRUE(mask.Equals(ByteString(kIPV6MaskBytes, sizeof(kIPV6MaskBytes))));
 }
 
-TEST(WifiUtilTest, ConfigureWiphyIndex) {
+TEST(WakeOnWifiTest, ConfigureWiphyIndex) {
   SetWakeOnPacketConnMessage msg;
   uint32_t value;
   EXPECT_FALSE(
@@ -204,7 +298,7 @@
   EXPECT_EQ(value, 137);
 }
 
-TEST(WifiUtilTest, ConfigureRemoveAllWakeOnPacketMsg) {
+TEST(WakeOnWifiTest, ConfigureRemoveAllWakeOnPacketMsg) {
   SetWakeOnPacketConnMessage msg;
   Error e;
   uint32_t value;
@@ -218,190 +312,163 @@
   EXPECT_EQ(value, 57);
 }
 
-bool AddWakeOnPacketMsgAttributesSet(SetWakeOnPacketConnMessage *msg) {
-  AttributeListRefPtr triggers;
-  AttributeListRefPtr patterns;
-  AttributeListRefPtr pattern_info;
-  ByteString mask;
-  ByteString pattern;
-  uint32_t value;
-  uint32_t offset;
-  uint8_t patnum = 1;
-  if (!msg->attributes()->GetU32AttributeValue(NL80211_ATTR_WIPHY, &value)) {
-    return false;
-  }
-  if (!msg->attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
-                                                 &triggers)) {
-    return false;
-  }
-  if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
-                                        &patterns)) {
-    return false;
-  }
-  if (!patterns->GetNestedAttributeList(patnum, &pattern_info)) {
-    return false;
-  }
+TEST(WakeOnWifiTest, WakeOnPacketSettingsMatch) {
+  IPAddressStore all_addresses;
+  scoped_ptr<uint8_t[]> message_memory_0(
+      new uint8_t[sizeof(kResponseNoIPAddresses)]);
+  memcpy(message_memory_0.get(), kResponseNoIPAddresses,
+         sizeof(kResponseNoIPAddresses));
+  nlmsghdr *hdr0 = reinterpret_cast<nlmsghdr *>(message_memory_0.get());
+  GetWakeOnPacketConnMessage msg0;
+  msg0.InitFromNlmsg(hdr0);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
 
-  if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_MASK, &mask)) {
-    return false;
-  }
-  if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_PATTERN, &pattern)) {
-    return false;
-  }
-  if (!pattern_info->GetU32AttributeValue(NL80211_PKTPAT_OFFSET, &offset)) {
-    return false;
-  }
-  if (offset != 0) {
-    return false;
-  }
-  return true;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV4Address0, sizeof(kIPV4Address0))));
+  scoped_ptr<uint8_t[]> message_memory_1(new uint8_t[sizeof(kResponseIPV40)]);
+  memcpy(message_memory_1.get(), kResponseIPV40, sizeof(kResponseIPV40));
+  nlmsghdr *hdr1 = reinterpret_cast<nlmsghdr *>(message_memory_1.get());
+  GetWakeOnPacketConnMessage msg1;
+  msg1.InitFromNlmsg(hdr1);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg1, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
+
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV4Address1, sizeof(kIPV4Address1))));
+  scoped_ptr<uint8_t[]> message_memory_2(new uint8_t[sizeof(kResponseIPV401)]);
+  memcpy(message_memory_2.get(), kResponseIPV401, sizeof(kResponseIPV401));
+  nlmsghdr *hdr2 = reinterpret_cast<nlmsghdr *>(message_memory_2.get());
+  GetWakeOnPacketConnMessage msg2;
+  msg2.InitFromNlmsg(hdr2);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg2, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg1, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
+
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address0, sizeof(kIPV6Address0))));
+  scoped_ptr<uint8_t[]> message_memory_3(
+      new uint8_t[sizeof(kResponseIPV401IPV60)]);
+  memcpy(message_memory_3.get(), kResponseIPV401IPV60,
+         sizeof(kResponseIPV401IPV60));
+  nlmsghdr *hdr3 = reinterpret_cast<nlmsghdr *>(message_memory_3.get());
+  GetWakeOnPacketConnMessage msg3;
+  msg3.InitFromNlmsg(hdr3);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg3, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg2, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg1, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
+
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address1, sizeof(kIPV6Address1))));
+  scoped_ptr<uint8_t[]> message_memory_4(
+      new uint8_t[sizeof(kResponseIPV401IPV601)]);
+  memcpy(message_memory_4.get(), kResponseIPV401IPV601,
+         sizeof(kResponseIPV401IPV601));
+  nlmsghdr *hdr4 = reinterpret_cast<nlmsghdr *>(message_memory_4.get());
+  GetWakeOnPacketConnMessage msg4;
+  msg4.InitFromNlmsg(hdr4);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg4, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg3, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg2, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg1, all_addresses));
+  EXPECT_FALSE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
 }
 
-bool AddWakeOnPacketMsgPatternAndMaskMatch(SetWakeOnPacketConnMessage *msg,
-                                           ByteString expected_pattern,
-                                           ByteString expected_mask) {
-  AttributeListRefPtr triggers;
-  AttributeListRefPtr patterns;
-  AttributeListRefPtr pattern_info;
-  ByteString msg_mask;
-  ByteString msg_pattern;
-
-  uint8_t patnum = 1;
-  if (!msg->attributes()->GetNestedAttributeList(NL80211_ATTR_WOWLAN_TRIGGERS,
-                                                 &triggers)) {
-    return false;
-  }
-  if (!triggers->GetNestedAttributeList(NL80211_WOWLAN_TRIG_PKT_PATTERN,
-                                        &patterns)) {
-    return false;
-  }
-  if (!patterns->GetNestedAttributeList(patnum, &pattern_info)) {
-    return false;
-  }
-  if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_PATTERN,
-                                          &msg_pattern)) {
-    return false;
-  }
-  if (!pattern_info->GetRawAttributeValue(NL80211_PKTPAT_MASK, &msg_mask)) {
-    return false;
-  }
-  if (!expected_pattern.Equals(msg_pattern)) {
-    LOG(INFO) << "Expected pattern: " << expected_pattern.HexEncode();
-    LOG(INFO) << "Actual pattern: " << msg_pattern.HexEncode();
-    return false;
-  }
-  if (!expected_mask.Equals(msg_mask)) {
-    LOG(INFO) << "Expected mask: " << expected_mask.HexEncode();
-    LOG(INFO) << "Actual mask: " << msg_mask.HexEncode();
-    return false;
-  }
-  return true;
-}
-
-TEST(WifiUtilTest, ConfigureAddWakeOnPacketMsg) {
+TEST(WakeOnWifiTest, ConfigureAddWakeOnPacketMsg) {
+  IPAddressStore all_addresses;
+  int index = 1;  // wiphy device number
   SetWakeOnPacketConnMessage msg0;
-  int index = 1;
   Error e;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV4Address0, sizeof(kIPV4Address0))));
   ByteString expected_mask = ByteString(kIPV4MaskBytes, sizeof(kIPV4MaskBytes));
   ByteString expected_pattern =
       CreatePattern(kIPV4PatternPrefix, sizeof(kIPV4PatternPrefix),
                     kIPV4Address0Bytes, sizeof(kIPV4Address0Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg0, IPAddress(kIPV4Address0),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg0));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg0, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg0, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg0, all_addresses));
 
   SetWakeOnPacketConnMessage msg1;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV4Address1, sizeof(kIPV4Address1))));
   expected_pattern =
       CreatePattern(kIPV4PatternPrefix, sizeof(kIPV4PatternPrefix),
                     kIPV4Address1Bytes, sizeof(kIPV4Address1Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg1, IPAddress(kIPV4Address1),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg1));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg1, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg1, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg1, all_addresses));
 
   SetWakeOnPacketConnMessage msg2;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address0, sizeof(kIPV6Address0))));
   expected_mask = ByteString(kIPV6MaskBytes, sizeof(kIPV6MaskBytes));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address0Bytes, sizeof(kIPV6Address0Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg2, IPAddress(kIPV6Address0),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg2));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg2, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg2, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg2, all_addresses));
 
   SetWakeOnPacketConnMessage msg3;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address1, sizeof(kIPV6Address1))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address1Bytes, sizeof(kIPV6Address1Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg3, IPAddress(kIPV6Address1),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg3));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg3, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg3, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg3, all_addresses));
 
   SetWakeOnPacketConnMessage msg4;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address2, sizeof(kIPV6Address2))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address2Bytes, sizeof(kIPV6Address2Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg4, IPAddress(kIPV6Address2),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg4));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg4, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg4, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg4, all_addresses));
 
   SetWakeOnPacketConnMessage msg5;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address3, sizeof(kIPV6Address3))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address3Bytes, sizeof(kIPV6Address3Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg5, IPAddress(kIPV6Address3),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg5));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg5, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg5, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg5, all_addresses));
 
   SetWakeOnPacketConnMessage msg6;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address4, sizeof(kIPV6Address4))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address4Bytes, sizeof(kIPV6Address4Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg6, IPAddress(kIPV6Address4),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg6));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg6, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg6, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg6, all_addresses));
 
   SetWakeOnPacketConnMessage msg7;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address5, sizeof(kIPV6Address5))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address5Bytes, sizeof(kIPV6Address5Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg7, IPAddress(kIPV6Address5),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg7));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg7, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg7, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg7, all_addresses));
 
   SetWakeOnPacketConnMessage msg8;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address6, sizeof(kIPV6Address6))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address6Bytes, sizeof(kIPV6Address6Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg8, IPAddress(kIPV6Address6),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg8));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg8, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg8, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg8, all_addresses));
 
   SetWakeOnPacketConnMessage msg9;
+  all_addresses.AddUnique(
+      IPAddress(string(kIPV6Address7, sizeof(kIPV6Address7))));
   expected_pattern =
       CreatePattern(kIPV6PatternPrefix, sizeof(kIPV6PatternPrefix),
                     kIPV6Address7Bytes, sizeof(kIPV6Address7Bytes));
-  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg9, IPAddress(kIPV6Address7),
-                                          index, &e);
-  EXPECT_TRUE(AddWakeOnPacketMsgAttributesSet(&msg9));
-  EXPECT_TRUE(AddWakeOnPacketMsgPatternAndMaskMatch(&msg9, expected_pattern,
-                                                    expected_mask));
+  WakeOnWifi::ConfigureAddWakeOnPacketMsg(&msg9, all_addresses, index, &e);
+  EXPECT_TRUE(WakeOnWifi::WakeOnPacketSettingsMatch(msg9, all_addresses));
 }
 
 }  // namespace shill
diff --git a/wifi.cc b/wifi.cc
index cd234a9..cf9553e 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -95,6 +95,8 @@
 const char WiFi::kProgressiveScanFieldTrialFlagFile[] =
     "/home/chronos/.progressive_scan_variation";
 const size_t WiFi::kStuckQueueLengthThreshold = 40;  // ~1 full-channel scan
+const int WiFi::kVerifyWakeOnPacketSettingsDelaySeconds = 1;
+const int WiFi::kMaxSetWakeOnPacketRetries = 2;
 
 WiFi::WiFi(ControlInterface *control_interface,
            EventDispatcher *dispatcher,
@@ -146,7 +148,8 @@
       fraction_per_scan_(kDefaultFractionPerScan),
       scan_state_(kScanIdle),
       scan_method_(kScanMethodNone),
-      receive_byte_count_at_connect_(0) {
+      receive_byte_count_at_connect_(0),
+      num_set_wake_on_packet_retries_(0) {
   PropertyStore *store = this->mutable_store();
   store->RegisterDerivedString(
       kBgscanMethodProperty,
@@ -1767,47 +1770,24 @@
 
 void WiFi::AddWakeOnPacketConnection(const IPAddress &ip_endpoint,
                                      Error *error) {
-  SetWakeOnPacketConnMessage add_wowlan_msg;
-  if (!WakeOnWifi::ConfigureAddWakeOnPacketMsg(&add_wowlan_msg, ip_endpoint,
-                                               wiphy_index_, error)) {
-    LOG(ERROR) << "Failed to configure nl80211 message.";
-    return;
-  }
-  add_wowlan_msg.AddAckFlag();
-  netlink_manager_->SendNl80211Message(
-      &add_wowlan_msg, Bind(&WiFi::OnSetWakeOnPacketConnectionResponse,
-                            weak_ptr_factory_.GetWeakPtr()),
-      Bind(&WiFi::OnAddWakeOnPacketConnectionAck,
-           weak_ptr_factory_.GetWeakPtr(), ip_endpoint),
-      Bind(&NetlinkManager::OnNetlinkMessageError));
+  wake_on_packet_connections_.AddUnique(ip_endpoint);
+  SendSetWakeOnPacketMessage(error);
 }
 
 void WiFi::RemoveWakeOnPacketConnection(const IPAddress &ip_endpoint,
                                         Error *error) {
   if (!wake_on_packet_connections_.Contains(ip_endpoint)) {
     Error::PopulateAndLog(error, Error::kNotFound,
-                    "No such wake-on-packet connection registered");
+                          "No such wake-on-packet connection registered");
     return;
   }
   wake_on_packet_connections_.Remove(ip_endpoint);
-  RemoveAllWakeOnPacketConnections(error);
-  for (const IPAddress &addr : wake_on_packet_connections_.GetIPAddresses()) {
-    AddWakeOnPacketConnection(addr, error);
-    if (error->IsFailure()) {
-      Error::PopulateAndLog(error, Error::kOperationFailed,
-                            "Call to AddWakeOnPacketConnection failed; "
-                            "removing all wake-on-packet rules");
-      RemoveAllWakeOnPacketConnections(error);
-      return;
-    }
-  }
-  // TODO(samueltan): Add a PostDelayedTask with callback that checks NIC state
-  // against wake_on_packet_connections_ once such a function has been
-  // implemented.
+  SendSetWakeOnPacketMessage(error);
 }
 
 void WiFi::RemoveAllWakeOnPacketConnections(Error *error) {
-  // Send an empty NL80211_CMD_SET_WOWLAN message.
+  // Send an empty NL80211_CMD_SET_WOWLAN message to disable wowlan.
+  wake_on_packet_connections_.Clear();
   SetWakeOnPacketConnMessage disable_wowlan_msg;
   if (!WakeOnWifi::ConfigureRemoveAllWakeOnPacketMsg(&disable_wowlan_msg,
                                                      wiphy_index_, error)) {
@@ -1815,13 +1795,17 @@
                           "Failed to configure nl80211 message");
     return;
   }
-  disable_wowlan_msg.AddAckFlag();
   netlink_manager_->SendNl80211Message(
       &disable_wowlan_msg, Bind(&WiFi::OnSetWakeOnPacketConnectionResponse,
                                 weak_ptr_factory_.GetWeakPtr()),
-      Bind(&WiFi::OnRemoveAllWakeOnPacketConnectionAck,
-           weak_ptr_factory_.GetWeakPtr()),
+      Bind(&NetlinkManager::OnAckDoNothing),
       Bind(&NetlinkManager::OnNetlinkMessageError));
+
+  verify_wake_on_packet_settings_callback_.Reset(Bind(
+      &WiFi::RequestWakeOnPacketSettings, weak_ptr_factory_.GetWeakPtr()));
+  dispatcher()->PostDelayedTask(
+      verify_wake_on_packet_settings_callback_.callback(),
+      kVerifyWakeOnPacketSettingsDelaySeconds * 1000);
 }
 
 void WiFi::OnSetWakeOnPacketConnectionResponse(
@@ -1830,19 +1814,70 @@
   // requests.
 }
 
-void WiFi::OnAddWakeOnPacketConnectionAck(IPAddress ip_endpoint,
-                                          bool *remove_callbacks) {
-  wake_on_packet_connections_.AddUnique(ip_endpoint);
-  // No other responses expected for original message, so remove callbacks.
-  *remove_callbacks = true;
+void WiFi::RequestWakeOnPacketSettings() {
+  Error e;
+  GetWakeOnPacketConnMessage get_wowlan_msg;
+  if (!WakeOnWifi::ConfigureGetWakeOnPacketMsg(&get_wowlan_msg, wiphy_index_,
+                                               &e)) {
+    LOG(ERROR) << e.message();
+    return;
+  }
+  netlink_manager_->SendNl80211Message(
+      &get_wowlan_msg, Bind(&WiFi::VerifyWakeOnPacketConnections,
+                            weak_ptr_factory_.GetWeakPtr()),
+      Bind(&NetlinkManager::OnAckDoNothing),
+      Bind(&NetlinkManager::OnNetlinkMessageError));
 }
 
-void WiFi::OnRemoveAllWakeOnPacketConnectionAck(bool *remove_callbacks) {
-  wake_on_packet_connections_.Clear();
-  // No other responses expected for original message, so remove callbacks.
-  *remove_callbacks = true;
+void WiFi::VerifyWakeOnPacketConnections(
+    const Nl80211Message &nl80211_message) {
+  if (WakeOnWifi::WakeOnPacketSettingsMatch(nl80211_message,
+                                            wake_on_packet_connections_)) {
+    SLOG(WiFi, 2) << __func__ << ": "
+                  << "Wake-on-packet settings successfully verified";
+  } else {
+    LOG(ERROR) << __func__ << " failed: discrepancy between wake-on-packet "
+                              "settings on NIC and those in local data "
+                              "structure detected";
+    RetrySetWakeOnPacketConnections();
+  }
 }
 
+void WiFi::SendSetWakeOnPacketMessage(Error *error) {
+  if (wake_on_packet_connections_.Empty()) {
+    RemoveAllWakeOnPacketConnections(error);
+    return;
+  }
+  SetWakeOnPacketConnMessage set_wowlan_msg;
+  if (!WakeOnWifi::ConfigureAddWakeOnPacketMsg(
+          &set_wowlan_msg, wake_on_packet_connections_, wiphy_index_, error)) {
+    LOG(ERROR) << "Failed to configure nl80211 message.";
+    return;
+  }
+  netlink_manager_->SendNl80211Message(
+      &set_wowlan_msg, Bind(&WiFi::OnSetWakeOnPacketConnectionResponse,
+                            weak_ptr_factory_.GetWeakPtr()),
+      Bind(&NetlinkManager::OnAckDoNothing),
+      Bind(&NetlinkManager::OnNetlinkMessageError));
+
+  verify_wake_on_packet_settings_callback_.Reset(
+      Bind(&WiFi::RequestWakeOnPacketSettings, weak_ptr_factory_.GetWeakPtr()));
+  dispatcher()->PostDelayedTask(
+      verify_wake_on_packet_settings_callback_.callback(),
+      kVerifyWakeOnPacketSettingsDelaySeconds * 1000);
+}
+
+void WiFi::RetrySetWakeOnPacketConnections() {
+  if (num_set_wake_on_packet_retries_ < kMaxSetWakeOnPacketRetries) {
+    Error e;
+    SLOG(WiFi, 2) << __func__;
+    SendSetWakeOnPacketMessage(&e);
+    ++num_set_wake_on_packet_retries_;
+  } else {
+    SLOG(WiFi, 2) << __func__ << ": max retry attempts reached";
+    num_set_wake_on_packet_retries_ = 0;
+  }
+}
 
 void WiFi::RestartFastScanAttempts() {
   fast_scans_remaining_ = kNumFastScanAttempts;
diff --git a/wifi.h b/wifi.h
index 907346c..e9b5e8b 100644
--- a/wifi.h
+++ b/wifi.h
@@ -141,17 +141,29 @@
   // Callback for when a service fails to configure with an IP.
   void OnIPConfigFailure() override;
 
-  // Implementation of Wake-on-WLAN interface that programs the NIC.
+  // Program the NIC to wake on packets received from |ip_endpoint|.
   void AddWakeOnPacketConnection(const IPAddress &ip_endpoint,
                                  Error *error) override;
+  // Remove rule to wake on packets received from |ip_endpoint| from the NIC.
   void RemoveWakeOnPacketConnection(const IPAddress &ip_endpoint,
                                     Error *error) override;
+  // Remove all rules to wake on incoming packets from the NIC.
   void RemoveAllWakeOnPacketConnections(Error *error) override;
+  // Message handler for NL80211_CMD_SET_WOWLAN responses.
   void OnSetWakeOnPacketConnectionResponse(
       const Nl80211Message &nl80211_message);
-  void OnAddWakeOnPacketConnectionAck(IPAddress ip_endpoint,
-                                      bool *remove_callbacks);
-  void OnRemoveAllWakeOnPacketConnectionAck(bool *remove_callbacks);
+  // Request wake-on-packet settings for this wifi device.
+  void RequestWakeOnPacketSettings();
+  // Verify that the wake-on-packet connections programmed into the NIC match
+  // those recorded locally for this device.
+  void VerifyWakeOnPacketConnections(const Nl80211Message &nl80211_message);
+  // Sends an NL80211 message to program the NIC to wake on packets from
+  // the IP addresses stored locally in wake_on_packet_connections_.
+  void SendSetWakeOnPacketMessage(Error *error);
+  // Calls |SendSetWakeOnPacketMessage| and tracks this call as a retry. If
+  // |kMaxSetWakeOnPacketRetries| retries have already been performed, resets
+  // counter and returns.
+  void RetrySetWakeOnPacketConnections();
 
   // Implementation of SupplicantEventDelegateInterface.  These methods
   // are called by SupplicantInterfaceProxy, in response to events from
@@ -317,6 +329,8 @@
   // TODO(wdg): Remove after progressive scan field trial is over.
   static const char kProgressiveScanFieldTrialFlagFile[];
   static const size_t kStuckQueueLengthThreshold;
+  static const int kVerifyWakeOnPacketSettingsDelaySeconds;
+  static const int kMaxSetWakeOnPacketRetries;
 
   // TODO(wdg): Remove after progressive scan field trial is over.
   void ParseFieldTrialFile(const base::FilePath &field_trial_file_path);
@@ -552,6 +566,10 @@
   // Executes periodically while a service is connected, to update the
   // signal strength from the currently connected AP.
   base::CancelableClosure request_station_info_callback_;
+  // Executes after the NIC's wake-on-packet settings are configured via
+  // NL80211 messages to verify that the new configuration has taken effect.
+  // Calls RequestWakeOnPacketSettings.
+  base::CancelableClosure verify_wake_on_packet_settings_callback_;
   // Number of remaining fast scans to be done during startup and disconnect.
   int fast_scans_remaining_;
   // Indicates that the current BSS has reached the completed state according
@@ -601,6 +619,9 @@
   // Used to report the current state of our wireless link.
   KeyValueStore link_statistics_;
 
+  // Number of retries to program the NIC's wake-on-packet settings.
+  int num_set_wake_on_packet_retries_;
+
   DISALLOW_COPY_AND_ASSIGN(WiFi);
 };