routing_table: Use common netlink parser

This reduces the LOC and redundant netlink parsing logic that is
untested.

It also happens to fix an issue with the current parsing breaking with
some of the routes returned by the kernel
```
[19259.086333] phosphor-network-manager[400]: Refreshing the objects.
[19259.094507] phosphor-network-manager[400]: Error validating header
[19259.095419] phosphor-network-manager[400]: The operation failed internally.
[19259.124862] phosphor-network-manager[400]: Error validating header
[19259.125593] phosphor-network-manager[400]: The operation failed internally.
[19259.143722] phosphor-network-manager[400]: Error validating header
[19259.144569] phosphor-network-manager[400]: The operation failed internally.
[19259.165901] phosphor-network-manager[400]: Error validating header
[19259.169497] phosphor-network-manager[400]: The operation failed internally.
[19259.204178] phosphor-network-manager[400]: Error validating header
[19259.204838] phosphor-network-manager[400]: The operation failed internally.
[19259.223547] phosphor-network-manager[400]: Error validating header
[19259.224302] phosphor-network-manager[400]: The operation failed internally.
[19259.604162] phosphor-network-manager[400]: Error validating header
[19259.605017] phosphor-network-manager[400]: The operation failed internally.
[19259.623701] phosphor-network-manager[400]: Refreshing complete.
```

Change-Id: I00772361090430ff79a5dd1dcf6cc6077f3f55f1
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/routing_table.cpp b/src/routing_table.cpp
index ab495eb..356338e 100644
--- a/src/routing_table.cpp
+++ b/src/routing_table.cpp
@@ -1,20 +1,15 @@
 #include "routing_table.hpp"
 
+#include "netlink.hpp"
 #include "util.hpp"
 
-#include <arpa/inet.h>
-#include <linux/rtnetlink.h>
 #include <net/if.h>
-#include <netinet/in.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
 
 #include <optional>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <stdexcept>
+#include <stdplus/raw.hpp>
 #include <string_view>
 #include <xyz/openbmc_project/Common/error.hpp>
 
@@ -32,109 +27,50 @@
 {
     try
     {
-        getRoutes();
+        rtmsg msg{};
+        netlink::performRequest(NETLINK_ROUTE, RTM_GETROUTE, NLM_F_DUMP, msg,
+                                [&](const nlmsghdr& hdr, std::string_view msg) {
+                                    this->parseRoutes(hdr, msg);
+                                });
     }
-    catch (const InternalFailure& e)
+    catch (const std::exception& e)
     {
+        log<level::ERR>("Reading routes failed", entry("ERROR=%s", e.what()));
         commit<InternalFailure>();
     }
 }
 
-int Table::readNetLinkSock(int sockFd, std::array<char, BUFSIZE>& buf)
+void Table::parseRoutes(const nlmsghdr& hdr, std::string_view msg)
 {
-    struct nlmsghdr* nlHdr = nullptr;
-    int readLen{};
-    int msgLen{};
-    uint8_t seqNum = 1;
-    uint8_t pID = getpid();
-    char* bufPtr = buf.data();
-
-    do
-    {
-        // Receive response from the kernel
-        if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0)
-        {
-            auto error = errno;
-            log<level::ERR>("Socket recv failed:",
-                            entry("ERROR=%s", strerror(error)));
-            elog<InternalFailure>();
-        }
-
-        nlHdr = reinterpret_cast<nlmsghdr*>(bufPtr);
-
-        // Check if the header is valid
-
-        if ((NLMSG_OK(nlHdr, readLen) == 0) ||
-            (nlHdr->nlmsg_type == NLMSG_ERROR))
-        {
-
-            auto error = errno;
-            log<level::ERR>("Error validating header",
-                            entry("NLMSGTYPE=%d", nlHdr->nlmsg_type),
-                            entry("ERROR=%s", strerror(error)));
-            elog<InternalFailure>();
-        }
-
-        // Check if the its the last message
-        if (nlHdr->nlmsg_type == NLMSG_DONE)
-        {
-            break;
-        }
-        else
-        {
-            // Else move the pointer to buffer appropriately
-            bufPtr += readLen;
-            msgLen += readLen;
-        }
-
-        // Check if its a multi part message
-        if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0)
-        {
-            break;
-        }
-    } while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pID));
-    return msgLen;
-}
-
-void Table::parseRoutes(const nlmsghdr* nlHdr)
-{
-    rtmsg* rtMsg = nullptr;
-    rtattr* rtAttr = nullptr;
-    int rtLen{};
     std::optional<InAddrAny> dstAddr;
     std::optional<InAddrAny> gateWayAddr;
     char ifName[IF_NAMESIZE] = {};
 
-    rtMsg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nlHdr));
+    if (hdr.nlmsg_type != RTM_NEWROUTE)
+    {
+        throw std::runtime_error("Not a route msg");
+    }
+    auto rtm = stdplus::raw::extract<rtmsg>(msg);
 
-    // If the route is not for AF_INET{,6} or does not belong to main routing
-    // table then return.
-    if ((rtMsg->rtm_family != AF_INET && rtMsg->rtm_family != AF_INET6) ||
-        rtMsg->rtm_table != RT_TABLE_MAIN)
+    if ((rtm.rtm_family != AF_INET && rtm.rtm_family != AF_INET6) ||
+        rtm.rtm_table != RT_TABLE_MAIN)
     {
         return;
     }
 
-    // get the rtattr field
-    rtAttr = reinterpret_cast<rtattr*>(RTM_RTA(rtMsg));
-
-    rtLen = RTM_PAYLOAD(nlHdr);
-
-    for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen))
+    while (!msg.empty())
     {
-        std::string_view attrData(reinterpret_cast<char*>(RTA_DATA(rtAttr)),
-                                  RTA_PAYLOAD(rtAttr));
-        switch (rtAttr->rta_type)
+        auto [hdr, data] = netlink::extractRtAttr(msg);
+        switch (hdr.rta_type)
         {
             case RTA_OIF:
-                if_indextoname(*reinterpret_cast<int*>(RTA_DATA(rtAttr)),
-                               ifName);
+                if_indextoname(stdplus::raw::copyFrom<int>(data), ifName);
                 break;
             case RTA_GATEWAY:
-                gateWayAddr = addrFromBuf(rtMsg->rtm_family, attrData);
+                gateWayAddr = addrFromBuf(rtm.rtm_family, data);
                 break;
             case RTA_DST:
-                dstAddr = addrFromBuf(rtMsg->rtm_family, attrData);
+                dstAddr = addrFromBuf(rtm.rtm_family, data);
                 break;
         }
     }
@@ -149,14 +85,14 @@
     {
         gatewayStr = toString(*gateWayAddr);
     }
-    if (!dstAddr && gateWayAddr)
+    if (rtm.rtm_dst_len == 0 && gateWayAddr)
     {
         std::string ifNameStr(ifName);
-        if (rtMsg->rtm_family == AF_INET)
+        if (rtm.rtm_family == AF_INET)
         {
             defaultGateway[ifNameStr] = gatewayStr;
         }
-        else if (rtMsg->rtm_family == AF_INET6)
+        else if (rtm.rtm_family == AF_INET6)
         {
             defaultGateway6[ifNameStr] = gatewayStr;
         }
@@ -173,60 +109,6 @@
     }
 }
 
-Map Table::getRoutes()
-{
-    nlmsghdr* nlMsg = nullptr;
-    std::array<char, BUFSIZE> msgBuf = {0};
-
-    int sock = -1;
-    int len{0};
-
-    uint8_t msgSeq{0};
-
-    // Create Socket
-    if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
-    {
-        auto error = errno;
-        log<level::ERR>("Error occurred during socket creation",
-                        entry("ERRNO=%s", strerror(error)));
-        elog<InternalFailure>();
-    }
-
-    phosphor::Descriptor smartSock(sock);
-    sock = -1;
-
-    // point the header and the msg structure pointers into the buffer.
-    nlMsg = reinterpret_cast<nlmsghdr*>(msgBuf.data());
-    // Length of message
-    nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
-    // Get the routes from kernel routing table
-    nlMsg->nlmsg_type = RTM_GETROUTE;
-    // The message is a request for dump
-    nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
-
-    nlMsg->nlmsg_seq = msgSeq;
-    nlMsg->nlmsg_pid = getpid();
-
-    // Send the request
-    if (send(smartSock(), nlMsg, nlMsg->nlmsg_len, 0) < 0)
-    {
-        auto error = errno;
-        log<level::ERR>("Error occurred during send on netlink socket",
-                        entry("ERRNO=%s", strerror(error)));
-        elog<InternalFailure>();
-    }
-
-    // Read the response
-    len = readNetLinkSock(smartSock(), msgBuf);
-
-    // Parse and print the response
-    for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len))
-    {
-        parseRoutes(nlMsg);
-    }
-    return routeList;
-}
-
 } // namespace route
 } // namespace network
 } // namespace phosphor
diff --git a/src/routing_table.hpp b/src/routing_table.hpp
index 8b634c6..9b3b77e 100644
--- a/src/routing_table.hpp
+++ b/src/routing_table.hpp
@@ -1,14 +1,9 @@
 #pragma once
-
-#include <asm/types.h>
 #include <linux/netlink.h>
-#include <sys/socket.h>
 
-#include <iostream>
-#include <list>
 #include <map>
 #include <string>
-#include <vector>
+#include <string_view>
 
 namespace phosphor
 {
@@ -54,13 +49,6 @@
     Table& operator=(Table&&) = default;
 
     /**
-     * @brief gets the list of routes.
-     *
-     * @returns list of routes.
-     */
-    Map getRoutes();
-
-    /**
      * @brief gets the default v4 gateway.
      *
      * @returns the default v4 gateway list.
@@ -82,18 +70,11 @@
 
   private:
     /**
-     * @brief read the routing data from the socket and fill the buffer.
-     *
-     * @param[in] bufPtr - unique pointer to confidentiality algorithm
-     *                     instance
-     */
-    int readNetLinkSock(int sockFd, std::array<char, BUFSIZE>& buff);
-    /**
      * @brief Parse the route and add it to the route list.
      *
      * @param[in] nlHdr - net link message header.
      */
-    void parseRoutes(const struct nlmsghdr* nlHdr);
+    void parseRoutes(const struct nlmsghdr& nlHdr, std::string_view msg);
 
     std::map<std::string, std::string> defaultGateway;  // default gateway list
     std::map<std::string, std::string> defaultGateway6; // default gateway list