net: NetworkMonitor: improve handling of NLMSG_DONE

It can also include extended ACK TLVs after the payload, similar to
NLMSG_ERROR. Therefore, parse the attributes and log NLMSGERR_ATTR_MSG,
if present, which can be quite helpful for debugging.

Change-Id: Ic3bf95d76523a198fd4b57efe6d4ea74e512073f
diff --git a/ndn-cxx/net/impl/netlink-socket.cpp b/ndn-cxx/net/impl/netlink-socket.cpp
index cfc9e08..2fe007d 100644
--- a/ndn-cxx/net/impl/netlink-socket.cpp
+++ b/ndn-cxx/net/impl/netlink-socket.cpp
@@ -252,12 +252,12 @@
       cbIt = m_pendingRequests.find(nlmsg->nlmsg_seq);
     }
     else {
-      NDN_LOG_TRACE("pid mismatch, ignoring");
+      NDN_LOG_TRACE("  pid mismatch, ignoring");
       continue;
     }
 
     if (cbIt == m_pendingRequests.end()) {
-      NDN_LOG_TRACE("no handler registered, ignoring");
+      NDN_LOG_TRACE("  no handler registered, ignoring");
       continue;
     }
     else if (nlmsg->nlmsg_flags & NLM_F_DUMP_INTR) {
@@ -485,8 +485,8 @@
         NDN_LOG_WARN("malformed nlmsgerr");
       }
       else if (err->error != 0) {
-        NDN_LOG_DEBUG("failed to resolve netlink family " << m_family << ": "
-                      << std::strerror(std::abs(err->error)));
+        NDN_LOG_DEBUG("  failed to resolve netlink family " << m_family
+                      << ": " << std::strerror(std::abs(err->error)));
       }
       onError();
       break;
@@ -519,7 +519,7 @@
         return onError();
       }
 
-      NDN_LOG_TRACE("resolved netlink family name=" << m_family << " id=" << *familyId);
+      NDN_LOG_TRACE("  resolved netlink family name=" << m_family << " id=" << *familyId);
       onResolved(*familyId);
       break;
     }
diff --git a/ndn-cxx/net/impl/network-monitor-impl-netlink.cpp b/ndn-cxx/net/impl/network-monitor-impl-netlink.cpp
index f340964..40dcccd 100644
--- a/ndn-cxx/net/impl/network-monitor-impl-netlink.cpp
+++ b/ndn-cxx/net/impl/network-monitor-impl-netlink.cpp
@@ -112,21 +112,7 @@
     break;
 
   case NLMSG_DONE:
-    if (m_isEnumeratingLinks) {
-      // links enumeration complete, now request all the addresses
-      m_isEnumeratingLinks = false;
-      NDN_LOG_TRACE("enumerating addresses");
-      m_rtnlSocket.sendDumpRequest(RTM_GETADDR,
-                                   [this] (const auto& msg) { this->parseRtnlMessage(msg); });
-      m_isEnumeratingAddresses = true;
-    }
-    else if (m_isEnumeratingAddresses) {
-      // links and addresses enumeration complete
-      m_isEnumeratingAddresses = false;
-      // TODO: enumerate routes
-      NDN_LOG_DEBUG("enumeration complete");
-      this->emitSignal(onEnumerationCompleted);
-    }
+    parseDoneMessage(nlmsg);
     break;
 
   case NLMSG_ERROR:
@@ -197,6 +183,22 @@
   }
 }
 
+#ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
+static void
+parseExtAckAttributes(const NetlinkMessageAttributes<nlattr>& attrs, bool isError)
+{
+  NDN_LOG_TRACE("  message contains " << attrs.size() << " attributes");
+
+  auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG);
+  if (msg && !msg->empty()) {
+    if (isError)
+      NDN_LOG_ERROR("  extended err: " << *msg);
+    else
+      NDN_LOG_DEBUG("  extended msg: " << *msg);
+  }
+}
+#endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
+
 void
 NetworkMonitorImplNetlink::parseLinkMessage(const NetlinkMessage& nlmsg)
 {
@@ -207,7 +209,7 @@
   }
 
   if (ifiTypeToInterfaceType(ifi->ifi_type) == InterfaceType::UNKNOWN) {
-    NDN_LOG_DEBUG("unhandled interface type " << ifi->ifi_type);
+    NDN_LOG_DEBUG("  unhandled interface type " << ifi->ifi_type);
     return;
   }
 
@@ -221,7 +223,7 @@
 
   if (nlmsg->nlmsg_type == RTM_DELLINK) {
     if (interface != nullptr) {
-      NDN_LOG_DEBUG("removing interface " << interface->getName());
+      NDN_LOG_DEBUG("  removing interface " << interface->getName());
       m_interfaces.erase(it);
       this->emitSignal(onInterfaceRemoved, interface);
     }
@@ -236,7 +238,7 @@
   interface->setFlags(ifi->ifi_flags);
 
   auto attrs = nlmsg.getAttributes<rtattr>(ifi);
-  NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
+  NDN_LOG_TRACE("  message contains " << attrs.size() << " attributes");
 
   auto address = attrs.getAttributeByType<ethernet::Address>(IFLA_ADDRESS);
   if (address)
@@ -258,7 +260,7 @@
   updateInterfaceState(*interface, state ? *state : linux_if::OPER_STATE_UNKNOWN);
 
   if (it == m_interfaces.end()) {
-    NDN_LOG_DEBUG("adding interface " << interface->getName());
+    NDN_LOG_DEBUG("  adding interface " << interface->getName());
     m_interfaces[interface->getIndex()] = interface;
     this->emitSignal(onInterfaceAdded, interface);
   }
@@ -276,14 +278,14 @@
   auto it = m_interfaces.find(ifa->ifa_index);
   if (it == m_interfaces.end()) {
     // unknown interface, ignore message
-    NDN_LOG_TRACE("unknown interface index " << ifa->ifa_index);
+    NDN_LOG_TRACE("  unknown interface index " << ifa->ifa_index);
     return;
   }
   auto interface = it->second;
   BOOST_ASSERT(interface != nullptr);
 
   auto attrs = nlmsg.getAttributes<rtattr>(ifa);
-  NDN_LOG_TRACE("message contains " << attrs.size() << " attributes");
+  NDN_LOG_TRACE("  message contains " << attrs.size() << " attributes");
 
   namespace ip = boost::asio::ip;
   ip::address ipAddr, broadcastAddr;
@@ -334,21 +336,52 @@
 }
 
 void
+NetworkMonitorImplNetlink::parseDoneMessage(const NetlinkMessage& nlmsg)
+{
+  const int* errcode = nlmsg.getPayload<int>();
+  if (errcode == nullptr) {
+    NDN_LOG_WARN("malformed NLMSG_DONE");
+  }
+  else {
+    if (*errcode != 0) {
+      NDN_LOG_ERROR("NLMSG_DONE err=" << *errcode << " " << std::strerror(std::abs(*errcode)));
+    }
+#ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
+    if (nlmsg->nlmsg_flags & NLM_F_ACK_TLVS) {
+      parseExtAckAttributes(nlmsg.getAttributes<nlattr>(errcode), *errcode != 0);
+    }
+#endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
+  }
+
+  if (m_isEnumeratingLinks) {
+    // links enumeration complete, now request all the addresses
+    m_isEnumeratingLinks = false;
+    NDN_LOG_TRACE("enumerating addresses");
+    m_rtnlSocket.sendDumpRequest(RTM_GETADDR,
+                                 [this] (const auto& msg) { this->parseRtnlMessage(msg); });
+    m_isEnumeratingAddresses = true;
+  }
+  else if (m_isEnumeratingAddresses) {
+    // links and addresses enumeration complete
+    m_isEnumeratingAddresses = false;
+    // TODO: enumerate routes
+    NDN_LOG_DEBUG("enumeration complete");
+    this->emitSignal(onEnumerationCompleted);
+  }
+}
+
+void
 NetworkMonitorImplNetlink::parseErrorMessage(const NetlinkMessage& nlmsg)
 {
   const nlmsgerr* err = nlmsg.getPayload<nlmsgerr>();
   if (err == nullptr) {
-    NDN_LOG_WARN("malformed nlmsgerr");
+    NDN_LOG_WARN("malformed NLMSG_ERROR");
     return;
   }
 
-  if (err->error == 0) {
-    // an error code of zero indicates an ACK message, not an error
-    NDN_LOG_TRACE("ACK");
-    return;
-  }
-
-  NDN_LOG_ERROR("NLMSG_ERROR: " << std::strerror(std::abs(err->error)));
+  if (err->error != 0)
+    NDN_LOG_ERROR("NLMSG_ERROR for seq=" << err->msg.nlmsg_seq
+                  << " err=" << err->error << " " << std::strerror(std::abs(err->error)));
 
 #ifdef NDN_CXX_HAVE_NETLINK_EXT_ACK
   if (!(nlmsg->nlmsg_flags & NLM_F_ACK_TLVS))
@@ -363,9 +396,7 @@
 
   auto nla = reinterpret_cast<const nlattr*>(reinterpret_cast<const uint8_t*>(&*nlmsg) + errLen);
   auto attrs = NetlinkMessageAttributes<nlattr>(nla, nlmsg->nlmsg_len - errLen);
-  auto msg = attrs.getAttributeByType<std::string>(NLMSGERR_ATTR_MSG);
-  if (msg && !msg->empty())
-    NDN_LOG_ERROR("kernel message: " << *msg);
+  parseExtAckAttributes(attrs, err->error != 0);
 #endif // NDN_CXX_HAVE_NETLINK_EXT_ACK
 }
 
diff --git a/ndn-cxx/net/impl/network-monitor-impl-netlink.hpp b/ndn-cxx/net/impl/network-monitor-impl-netlink.hpp
index eba485d..559271f 100644
--- a/ndn-cxx/net/impl/network-monitor-impl-netlink.hpp
+++ b/ndn-cxx/net/impl/network-monitor-impl-netlink.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -79,6 +79,9 @@
   parseRouteMessage(const NetlinkMessage& nlmsg);
 
   void
+  parseDoneMessage(const NetlinkMessage& nlmsg);
+
+  void
   parseErrorMessage(const NetlinkMessage& nlmsg);
 
 private: