shill: Times-out old netlink response handlers.

Adds a default timeout to all message response handlers.  Adds a 'type'
to auxilliary message handlers.  When/if a netlink message goes
unanswered (we don't receive a unicast response with the same serial
number) after the timeout (and there's no intervening sent message -- we
only timeout old handlers on message send), the axilliary message
handler is called with a 'type' of 'timeout' and the handler is deleted.

BUG=chromium:249028
TEST=unittest

Change-Id: I85e84a27acfaa6d9b6af6f25288056bda40a649b
Reviewed-on: https://gerrit.chromium.org/gerrit/59306
Commit-Queue: Wade Guthrie <wdg@chromium.org>
Reviewed-by: Wade Guthrie <wdg@chromium.org>
Tested-by: Wade Guthrie <wdg@chromium.org>
diff --git a/netlink_manager.cc b/netlink_manager.cc
index 9df5cad..9459c2b 100644
--- a/netlink_manager.cc
+++ b/netlink_manager.cc
@@ -8,6 +8,7 @@
 #include <sys/select.h>
 #include <sys/time.h>
 
+#include <list>
 #include <map>
 
 #include <base/memory/weak_ptr.h>
@@ -42,8 +43,10 @@
 const char NetlinkManager::kEventTypeScan[] = "scan";
 const char NetlinkManager::kEventTypeRegulatory[] = "regulatory";
 const char NetlinkManager::kEventTypeMlme[] = "mlme";
-const long NetlinkManager::kMaximumNewFamilyWaitSeconds = 1;
-const long NetlinkManager::kMaximumNewFamilyWaitMicroSeconds = 0;
+const long NetlinkManager::kMaximumNewFamilyWaitSeconds = 1;  // NOLINT
+const long NetlinkManager::kMaximumNewFamilyWaitMicroSeconds = 0;  // NOLINT
+const long NetlinkManager::kResponseTimeoutSeconds = 5;  // NOLINT
+const long NetlinkManager::kResponseTimeoutMicroSeconds = 0;  // NOLINT
 
 NetlinkManager::NetlinkResponseHandler::NetlinkResponseHandler(
     const NetlinkManager::NetlinkAuxilliaryMessageHandler &error_handler)
@@ -52,9 +55,9 @@
 NetlinkManager::NetlinkResponseHandler::~NetlinkResponseHandler() {}
 
 void NetlinkManager::NetlinkResponseHandler::HandleError(
-    const NetlinkMessage *netlink_message) const {
+    AuxilliaryMessageType type, const NetlinkMessage *netlink_message) const {
   if (!error_handler_.is_null())
-    error_handler_.Run(netlink_message);
+    error_handler_.Run(type, netlink_message);
 }
 
 class ControlResponseHandler : public NetlinkManager::NetlinkResponseHandler {
@@ -193,26 +196,43 @@
 }
 
 // static
-void NetlinkManager::OnNetlinkMessageError(const NetlinkMessage *raw_message) {
-  if (!raw_message) {
-    LOG(ERROR) << "NetlinkManager error.";
-    return;
+void NetlinkManager::OnNetlinkMessageError(AuxilliaryMessageType type,
+                                           const NetlinkMessage *raw_message) {
+  switch (type) {
+    case kErrorFromKernel:
+      if (!raw_message) {
+        LOG(ERROR) << "Unknown error from kernel.";
+        break;
+      }
+      if (raw_message->message_type() == ErrorAckMessage::GetMessageType()) {
+        const ErrorAckMessage *error_ack_message =
+            dynamic_cast<const ErrorAckMessage *>(raw_message);
+        if (error_ack_message->error()) {
+          LOG(ERROR) << __func__ << ": Message (seq: "
+                     << error_ack_message->sequence_number() << ") failed: "
+                     << error_ack_message->ToString();
+        } else {
+          SLOG(WiFi, 6) << __func__ << ": Message (seq: "
+                     << error_ack_message->sequence_number() << ") ACKed";
+        }
+      }
+      break;
+
+    case kUnexpectedResponseType:
+      LOG(ERROR) << "Message not handled by regular message handler:";
+      if (raw_message) {
+        raw_message->Print(0, 0);
+      }
+      break;
+
+    case kTimeoutWaitingForResponse:
+      LOG(WARNING) << "Timeout waiting for response";
+      break;
+
+    default:
+      LOG(ERROR) << "Unexpected auxilliary message type: " << type;
+      break;
   }
-  if (raw_message->message_type() == ErrorAckMessage::GetMessageType()) {
-    const ErrorAckMessage *error_ack_message =
-        dynamic_cast<const ErrorAckMessage *>(raw_message);
-    if (error_ack_message->error()) {
-      LOG(ERROR) << __func__ << ": Message (seq: "
-                 << error_ack_message->sequence_number() << ") failed: "
-                 << error_ack_message->ToString();
-    } else {
-      SLOG(WiFi, 6) << __func__ << ": Message (seq: "
-                 << error_ack_message->sequence_number() << ") ACKed";
-    }
-    return;
-  }
-  LOG(ERROR) << "Not expecting this message:";
-  raw_message->Print(0, 0);
 }
 
 bool NetlinkManager::Init() {
@@ -328,7 +348,6 @@
 }
 
 bool NetlinkManager::AddBroadcastHandler(const NetlinkMessageHandler &handler) {
-  list<NetlinkMessageHandler>::iterator i;
   if (FindBroadcastHandler(handler)) {
     LOG(WARNING) << "Trying to re-add a handler";
     return false;  // Should only be one copy in the list.
@@ -399,6 +418,26 @@
     return false;
   }
 
+  // Clean out timed-out message handlers.  The list of outstanding messages
+  // should be small so the time wasted by looking through all of them should
+  // be small.
+  struct timeval now;
+  time_->GetTimeMonotonic(&now);
+  map<uint32_t, NetlinkResponseHandlerRefPtr>::iterator handler_it =
+      message_handlers_.begin();
+  while (handler_it != message_handlers_.end()) {
+    if (timercmp(&now, &handler_it->second->delete_after(), >)) {
+      // A timeout isn't always unexpected so this is not a warning.
+      SLOG(WiFi, 3) << "Removing timed-out handler for sequence number "
+                    << handler_it->first;
+      handler_it->second->HandleError(kTimeoutWaitingForResponse, NULL);
+      handler_it = message_handlers_.erase(handler_it);
+    } else {
+      ++handler_it;
+    }
+  }
+
+  // On to the business at hand...
   ByteString message_string = message->Encode(this->GetSequenceNumber());
 
   if (!response_handler) {
@@ -408,6 +447,12 @@
                << message->sequence_number();
     return false;
   } else {
+    struct timeval response_timeout = {kResponseTimeoutSeconds,
+                                       kResponseTimeoutMicroSeconds};
+    struct timeval delete_after;
+    timeradd(&now, &response_timeout, &delete_after);
+    response_handler->set_delete_after(delete_after);
+
     message_handlers_[message->sequence_number()] =
         NetlinkResponseHandlerRefPtr(response_handler);
   }
@@ -491,6 +536,7 @@
     return;
   }
   const uint32 sequence_number = msg->nlmsg_seq;
+
   scoped_ptr<NetlinkMessage> message(message_factory_.CreateMessage(msg));
   if (message == NULL) {
     SLOG(WiFi, 3) << "NL Message " << sequence_number << " <===";
@@ -510,7 +556,8 @@
     if (error_ack_message->error()) {
       if (ContainsKey(message_handlers_, sequence_number)) {
         SLOG(WiFi, 6) << "Found message-specific error handler";
-        message_handlers_[sequence_number]->HandleError(message.get());
+        message_handlers_[sequence_number]->HandleError(kErrorFromKernel,
+                                                        message.get());
         message_handlers_.erase(sequence_number);
       }
     } else {
@@ -525,7 +572,8 @@
       LOG(ERROR) << "Couldn't call message handler for " << sequence_number;
       // Call the error handler but, since we don't have an |ErrorAckMessage|,
       // we'll have to pass a NULL pointer.
-      message_handlers_[sequence_number]->HandleError(NULL);
+      message_handlers_[sequence_number]->HandleError(kUnexpectedResponseType,
+                                                      NULL);
     }
     if ((message->flags() & NLM_F_MULTI) &&
         (message->message_type() != NLMSG_DONE)) {
diff --git a/netlink_manager.h b/netlink_manager.h
index 7209ae1..1d714e2 100644
--- a/netlink_manager.h
+++ b/netlink_manager.h
@@ -96,6 +96,11 @@
 //  netlink_manager_->Start(&dispatcher_);
 class NetlinkManager {
  public:
+  enum AuxilliaryMessageType {
+    kErrorFromKernel,
+    kUnexpectedResponseType,
+    kTimeoutWaitingForResponse
+  };
   typedef base::Callback<void(const NetlinkMessage &)> NetlinkMessageHandler;
   typedef base::Callback<void(const ControlNetlinkMessage &)>
       ControlNetlinkMessageHandler;
@@ -105,7 +110,8 @@
   // discovered by |NetlinkManager| (which are passed as NULL pointers because
   // there is no way to reserve a part of the ErrorAckMessage space for
   // non-netlink errors).
-  typedef base::Callback<void(const NetlinkMessage *)>
+  typedef base::Callback<void(AuxilliaryMessageType type,
+                              const NetlinkMessage *)>
       NetlinkAuxilliaryMessageHandler;
 
   // ResponseHandlers provide a polymorphic context for the base::Callback
@@ -122,13 +128,17 @@
     // (which is declared in the private area of derived classes) with
     // properly cast version of |netlink_message|.
     virtual bool HandleMessage(const NetlinkMessage &netlink_message) const = 0;
-    void HandleError(const NetlinkMessage *netlink_message) const;
+    void HandleError(AuxilliaryMessageType type,
+                     const NetlinkMessage *netlink_message) const;
+    void set_delete_after(const timeval &time) { delete_after_ = time; }
+    const struct timeval &delete_after() const { return delete_after_; }
 
    protected:
     NetlinkResponseHandler();
 
    private:
     NetlinkAuxilliaryMessageHandler error_handler_;
+    struct timeval delete_after_;
 
     DISALLOW_COPY_AND_ASSIGN(NetlinkResponseHandler);
   };
@@ -208,7 +218,8 @@
       const NetlinkAuxilliaryMessageHandler &error_handler);
 
   // Generic erroneous message handler everyone can use.
-  static void OnNetlinkMessageError(const NetlinkMessage *raw_message);
+  static void OnNetlinkMessageError(AuxilliaryMessageType type,
+                                    const NetlinkMessage *raw_message);
 
   // Uninstall the handler for a specific netlink message.
   bool RemoveMessageHandler(const NetlinkMessage &message);
@@ -237,21 +248,26 @@
   FRIEND_TEST(NetlinkManagerTest, GetFamilyTimeout);
   FRIEND_TEST(NetlinkManagerTest, MessageHandler);
   FRIEND_TEST(NetlinkManagerTest, MultipartMessageHandler);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_TRIGGER_SCAN);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_SCAN_RESULTS);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_STATION);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_AUTHENTICATE);
+  FRIEND_TEST(NetlinkManagerTest, TimeoutResponseHandlers);
   FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_ASSOCIATE);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_AUTHENTICATE);
   FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_CONNECT);
   FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DEAUTHENTICATE);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISCONNECT);
-  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NOTIFY_CQM);
   FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISASSOCIATE);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_DISCONNECT);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_SCAN_RESULTS);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NEW_STATION);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_NOTIFY_CQM);
+  FRIEND_TEST(NetlinkMessageTest, Parse_NL80211_CMD_TRIGGER_SCAN);
 
   typedef scoped_refptr<NetlinkResponseHandler> NetlinkResponseHandlerRefPtr;
 
-  static const long kMaximumNewFamilyWaitSeconds;
-  static const long kMaximumNewFamilyWaitMicroSeconds;
+  // These need to be member variables, even though they're only used once in
+  // the code, since they're needed for unittests.
+  static const long kMaximumNewFamilyWaitSeconds;  // NOLINT
+  static const long kMaximumNewFamilyWaitMicroSeconds;  // NOLINT
+  static const long kResponseTimeoutSeconds;  // NOLINT
+  static const long kResponseTimeoutMicroSeconds;  // NOLINT
 
   // Returns the file descriptor of socket used to read wifi data.
   int file_descriptor() const;
diff --git a/netlink_manager_unittest.cc b/netlink_manager_unittest.cc
index a1f929b..d5bf2e9 100644
--- a/netlink_manager_unittest.cc
+++ b/netlink_manager_unittest.cc
@@ -159,7 +159,7 @@
     GetFamilyMessage get_family_message;
     // Any number that's not 0 or 1 is acceptable, here.  Zero is bad because
     // we want to make sure that this message is different than the main
-    // send/receive pair.  One is bad becasue the default for
+    // send/receive pair.  One is bad because the default for
     // |saved_sequence_number_| is zero and the likely default value for the
     // first sequence number generated from the code is 1.
     const uint32_t kRandomOffset = 1003;
@@ -190,9 +190,11 @@
    public:
     MockHandlerNetlinkAuxilliary() :
       on_netlink_message_(
-          base::Bind(&MockHandlerNetlinkAuxilliary::OnNetlinkMessage,
+          base::Bind(&MockHandlerNetlinkAuxilliary::OnErrorHandler,
                      base::Unretained(this))) {}
-    MOCK_METHOD1(OnNetlinkMessage, void(const NetlinkMessage *msg));
+    MOCK_METHOD2(OnErrorHandler,
+                 void(NetlinkManager::AuxilliaryMessageType type,
+                      const NetlinkMessage *msg));
     const NetlinkManager::NetlinkAuxilliaryMessageHandler &on_netlink_message()
         const {
       return on_netlink_message_;
@@ -367,6 +369,7 @@
 TEST_F(NetlinkManagerTest, GetFamilyTimeout) {
   Reset();
   MockTime time;
+  Time *old_time = netlink_manager_->time_;
   netlink_manager_->time_ = &time;
 
   EXPECT_CALL(netlink_socket_, SendMessage(_)).WillOnce(Return(true));
@@ -388,9 +391,11 @@
   const string kSampleMessageName("SampleMessageName");
   EXPECT_EQ(NetlinkMessage::kIllegalMessageType,
             netlink_manager_->GetFamily(kSampleMessageName, null_factory));
+  netlink_manager_->time_ = old_time;
 }
 
 TEST_F(NetlinkManagerTest, BroadcastHandler) {
+  Reset();
   nlmsghdr *message = const_cast<nlmsghdr *>(
         reinterpret_cast<const nlmsghdr *>(kNL80211_CMD_DISCONNECT));
 
@@ -548,16 +553,93 @@
       done_message.Encode(netlink_socket_.GetLastSequenceNumber()));
 
   // Verify that the message-specific handler is called for the done message.
-  EXPECT_CALL(auxilliary_handler, OnNetlinkMessage(_));
+  EXPECT_CALL(auxilliary_handler, OnErrorHandler(_, _));
   netlink_manager_->OnNlMessageReceived(
       reinterpret_cast<nlmsghdr *>(done_message_bytes.GetData()));
 
   // Verify that broadcast handler is called now that the done message has
   // been seen.
   EXPECT_CALL(response_handler, OnNetlinkMessage(_)).Times(0);
-  EXPECT_CALL(auxilliary_handler, OnNetlinkMessage(_)).Times(0);
+  EXPECT_CALL(auxilliary_handler, OnErrorHandler(_, _)).Times(0);
   EXPECT_CALL(broadcast_handler, OnNetlinkMessage(_)).Times(1);
   netlink_manager_->OnNlMessageReceived(received_message);
 }
 
+TEST_F(NetlinkManagerTest, TimeoutResponseHandlers) {
+  Reset();
+  MockHandlerNetlink broadcast_handler;
+  EXPECT_TRUE(netlink_manager_->AddBroadcastHandler(
+      broadcast_handler.on_netlink_message()));
+
+  // Set up the received message as a response to the get_wiphi_message
+  // we're going to send.
+  NewWiphyMessage new_wiphy_message;
+  const uint32_t kRandomSequenceNumber = 3;
+  ByteString new_wiphy_message_bytes =
+      new_wiphy_message.Encode(kRandomSequenceNumber);
+  nlmsghdr *received_message =
+      reinterpret_cast<nlmsghdr *>(new_wiphy_message_bytes.GetData());
+
+  // Setup a random received message to trigger wiping out old messages.
+  NewScanResultsMessage new_scan_results;
+  ByteString new_scan_results_bytes =
+      new_scan_results.Encode(kRandomSequenceNumber);
+
+  // Setup the timestamps of various messages
+  MockTime time;
+  Time *old_time = netlink_manager_->time_;
+  netlink_manager_->time_ = &time;
+
+  time_t kStartSeconds = 1234;  // Arbitrary.
+  suseconds_t kSmallUsec = 100;
+  EXPECT_CALL(time, GetTimeMonotonic(_)).
+      WillOnce(Invoke(TimeFunctor(kStartSeconds, 0))).  // Initial time.
+      WillOnce(Invoke(TimeFunctor(kStartSeconds, kSmallUsec))).
+
+      WillOnce(Invoke(TimeFunctor(kStartSeconds, 0))).  // Initial time.
+      WillOnce(Invoke(TimeFunctor(
+          kStartSeconds + NetlinkManager::kResponseTimeoutSeconds + 1,
+          NetlinkManager::kResponseTimeoutMicroSeconds)));
+  EXPECT_CALL(netlink_socket_, SendMessage(_)).WillRepeatedly(Return(true));
+
+  GetWiphyMessage get_wiphi_message;
+  MockHandler80211 response_handler;
+  MockHandlerNetlinkAuxilliary auxilliary_handler;
+
+  GetRegMessage get_reg_message;  // Just a message to trigger timeout.
+  NetlinkManager::Nl80211MessageHandler null_message_handler;
+  NetlinkManager::NetlinkAuxilliaryMessageHandler null_error_handler;
+
+  // Send two messages within the message handler timeout; verify that we
+  // get called back (i.e., that the first handler isn't discarded).
+  EXPECT_TRUE(netlink_manager_->SendNl80211Message(
+      &get_wiphi_message, response_handler.on_netlink_message(),
+      auxilliary_handler.on_netlink_message()));
+  received_message->nlmsg_seq = netlink_socket_.GetLastSequenceNumber();
+  EXPECT_TRUE(netlink_manager_->SendNl80211Message(
+      &get_reg_message, null_message_handler, null_error_handler));
+  EXPECT_CALL(response_handler, OnNetlinkMessage(_));
+  netlink_manager_->OnNlMessageReceived(received_message);
+
+  // Send two messages at an interval greater than the message handler timeout
+  // before the response to the first arrives.  Verify that the error handler
+  // for the first message is called (with a timeout flag) and that the
+  // broadcast handler gets called, instead of the message's handler.
+  EXPECT_TRUE(netlink_manager_->SendNl80211Message(
+      &get_wiphi_message, response_handler.on_netlink_message(),
+      auxilliary_handler.on_netlink_message()));
+  received_message->nlmsg_seq = netlink_socket_.GetLastSequenceNumber();
+  EXPECT_CALL(auxilliary_handler,
+              OnErrorHandler(NetlinkManager::kTimeoutWaitingForResponse, NULL));
+  EXPECT_TRUE(netlink_manager_->SendNl80211Message(&get_reg_message,
+                                                   null_message_handler,
+                                                   null_error_handler));
+  EXPECT_CALL(response_handler, OnNetlinkMessage(_)).Times(0);
+  EXPECT_CALL(broadcast_handler, OnNetlinkMessage(_));
+  netlink_manager_->OnNlMessageReceived(received_message);
+
+  // Put the state of the singleton back where it was.
+  netlink_manager_->time_ = old_time;
+}
+
 }  // namespace shill
diff --git a/scan_session.cc b/scan_session.cc
index 254ce97..f1db223 100644
--- a/scan_session.cc
+++ b/scan_session.cc
@@ -205,45 +205,73 @@
 }
 
 void ScanSession::OnTriggerScanErrorResponse(
+    NetlinkManager::AuxilliaryMessageType type,
     const NetlinkMessage *netlink_message) {
-  if (!netlink_message) {
-    LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
-    found_error_ = true;
-    on_scan_failed_.Run();
-    return;
-  }
-  if (netlink_message->message_type() != ErrorAckMessage::GetMessageType()) {
-    LOG(ERROR) << __func__ << ": Message failed: Not an error.";
-    on_scan_failed_.Run();
-    return;
-  }
-  const ErrorAckMessage *error_ack_message =
-      dynamic_cast<const ErrorAckMessage *>(netlink_message);
-  if (error_ack_message->error()) {
-    LOG(ERROR) << __func__ << ": Message failed: "
-               << error_ack_message->ToString();
-    if (error_ack_message->error() == EBUSY) {
-      if (scan_tries_left_ == 0) {
-        LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
-                   << " times and failed each time.  Giving up.";
-        found_error_ = true;
-        on_scan_failed_.Run();
-        scan_tries_left_ = kScanRetryCount;
-        return;
+  switch (type) {
+    case NetlinkManager::kErrorFromKernel: {
+        if (!netlink_message) {
+          LOG(ERROR) << __func__ << ": Message failed: NetlinkManager Error.";
+          found_error_ = true;
+          on_scan_failed_.Run();
+          break;
+        }
+        if (netlink_message->message_type() !=
+            ErrorAckMessage::GetMessageType()) {
+          LOG(ERROR) << __func__ << ": Message failed: Not an error.";
+          found_error_ = true;
+          on_scan_failed_.Run();
+          break;
+        }
+        const ErrorAckMessage *error_ack_message =
+            dynamic_cast<const ErrorAckMessage *>(netlink_message);
+        if (error_ack_message->error()) {
+          LOG(ERROR) << __func__ << ": Message failed: "
+                     << error_ack_message->ToString();
+          if (error_ack_message->error() == EBUSY) {
+            if (scan_tries_left_ == 0) {
+              LOG(ERROR) << "Retried progressive scan " << kScanRetryCount
+                         << " times and failed each time.  Giving up.";
+              found_error_ = true;
+              on_scan_failed_.Run();
+              scan_tries_left_ = kScanRetryCount;
+              return;
+            }
+            --scan_tries_left_;
+            SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_
+                          << " remaining after this)";
+            ebusy_timer_.Resume();
+            dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
+                                              weak_ptr_factory_.GetWeakPtr()),
+                                         kScanRetryDelayMilliseconds);
+            break;
+          }
+          found_error_ = true;
+          on_scan_failed_.Run();
+        } else {
+          SLOG(WiFi, 6) << __func__ << ": Message ACKed";
+        }
       }
-      --scan_tries_left_;
-      SLOG(WiFi, 3) << __func__ << " - trying again (" << scan_tries_left_
-                    << " remaining after this)";
-      ebusy_timer_.Resume();
-      dispatcher_->PostDelayedTask(Bind(&ScanSession::ReInitiateScan,
-                                        weak_ptr_factory_.GetWeakPtr()),
-                                   kScanRetryDelayMilliseconds);
-      return;
-    }
-    found_error_ = true;
-    on_scan_failed_.Run();
-  } else {
-    SLOG(WiFi, 6) << __func__ << ": Message ACKed";
+      break;
+
+    case NetlinkManager::kUnexpectedResponseType:
+      LOG(ERROR) << "Message not handled by regular message handler:";
+      if (netlink_message) {
+        netlink_message->Print(0, 0);
+      }
+      found_error_ = true;
+      on_scan_failed_.Run();
+      break;
+
+    case NetlinkManager::kTimeoutWaitingForResponse:
+      // This is actually expected since, in the working case, a trigger scan
+      // message gets its responses broadcast rather than unicast.
+      break;
+
+    default:
+      LOG(ERROR) << "Unexpected auxilliary message type: " << type;
+      found_error_ = true;
+      on_scan_failed_.Run();
+      break;
   }
 }
 
diff --git a/scan_session.h b/scan_session.h
index de02911..3c3606f 100644
--- a/scan_session.h
+++ b/scan_session.h
@@ -16,6 +16,7 @@
 #include <metrics/timer.h>
 
 #include "shill/byte_string.h"
+#include "shill/netlink_manager.h"
 #include "shill/wifi_provider.h"
 
 namespace shill {
@@ -179,8 +180,8 @@
   // likely, an error -- when things work, we get an
   // NL80211_CMD_NEW_SCAN_RESULTS broadcast message).
   void OnTriggerScanResponse(const Nl80211Message &message);
-  void OnTriggerScanErrorResponse(const NetlinkMessage *netlink_message);
-
+  void OnTriggerScanErrorResponse(NetlinkManager::AuxilliaryMessageType type,
+                                  const NetlinkMessage *netlink_message);
   void ReportEbusyTime(int log_level);
 
   // Logs the results of the scan.
diff --git a/scan_session_unittest.cc b/scan_session_unittest.cc
index d16c5c6..dbf9089 100644
--- a/scan_session_unittest.cc
+++ b/scan_session_unittest.cc
@@ -475,7 +475,8 @@
 
   EXPECT_CALL(*this, OnScanError());
   ErrorAckMessage error_message(-EINTR);
-  scan_session()->OnTriggerScanErrorResponse(&error_message);
+  scan_session()->OnTriggerScanErrorResponse(NetlinkManager::kErrorFromKernel,
+                                             &error_message);
 }
 
 TEST_F(ScanSessionTest, EBusy) {
@@ -491,11 +492,13 @@
   for (size_t i = 0; i < kSmallRetryNumber; ++i) {
     EXPECT_CALL(*this, OnScanError()).Times(0);
     EXPECT_CALL(*dispatcher(), PostDelayedTask(_, _));
-    scan_session()->OnTriggerScanErrorResponse(&error_message);
+    scan_session()->OnTriggerScanErrorResponse(NetlinkManager::kErrorFromKernel,
+                                               &error_message);
   }
 
   EXPECT_CALL(*this, OnScanError());
-  scan_session()->OnTriggerScanErrorResponse(&error_message);
+  scan_session()->OnTriggerScanErrorResponse(NetlinkManager::kErrorFromKernel,
+                                             &error_message);
 }
 
 TEST_F(ScanSessionTest, ScanHidden) {