Fetch the kernel routing table

It would be used to get the gateway for
a specific network.
It would also be used to get the default
gateway for the system.

Change-Id: I1f7f81e1d6ea6c3f4385f454ad229d4074341c44
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 3ccff0d..370709f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,7 +26,8 @@
 		network_manager_main.cpp \
 		xyz/openbmc_project/Network/VLAN/Create/server.cpp \
 		xyz/openbmc_project/Network/IP/Create/server.cpp \
-		util.cpp
+		util.cpp \
+		routing_table.cpp
 
 CLEANFILES = \
 		xyz/openbmc_project/Network/VLAN/Create/server.cpp \
@@ -50,8 +51,8 @@
 phosphor_network_manager_CXXFLAGS = \
 		$(SYSTEMD_CFLAGS) \
 		$(SDBUSPLUS_CFLAGS) \
-		$(PHOSPHOR_LOGGING_CFLAGS) \
-		$(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
+		$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
+		$(PHOSPHOR_LOGGING_CFLAGS)
 
 xyz/openbmc_project/Network/VLAN/Create/server.cpp: xyz/openbmc_project/Network/VLAN/Create.interface.yaml xyz/openbmc_project/Network/VLAN/Create/server.hpp
 	@mkdir -p `dirname $@`
diff --git a/routing_table.cpp b/routing_table.cpp
new file mode 100644
index 0000000..bd8f9e0
--- /dev/null
+++ b/routing_table.cpp
@@ -0,0 +1,228 @@
+#include "routing_table.hpp"
+#include "util.hpp"
+#include "xyz/openbmc_project/Common/error.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+
+#include <netinet/in.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <stdexcept>
+
+namespace phosphor
+{
+namespace network
+{
+namespace route
+{
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+
+Table::Table()
+{
+    try
+    {
+        getRoutes();
+    }
+    catch (InternalFailure& e)
+    {
+        commit<InternalFailure>();
+    }
+
+}
+
+int Table::readNetLinkSock(int sockFd, std::array<char,BUFSIZE>& buf)
+{
+    struct nlmsghdr* nlHdr = nullptr;
+    int readLen {};
+    int msgLen {};
+    uint8_t seqNum = 1;
+    uint8_t pID = getpid();
+    char* bufPtr = buf.data();
+
+    do
+    {
+        // Recieve 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 {};
+    in_addr dstAddr {};
+    in_addr gateWayAddr {};
+    char ifName[IF_NAMESIZE] = {};
+
+    rtMsg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nlHdr));
+
+    // If the route is not for AF_INET or does not belong to main routing table
+    // then return.
+    if ((rtMsg->rtm_family != AF_INET) || (rtMsg->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))
+    {
+        switch (rtAttr->rta_type)
+        {
+            case RTA_OIF:
+                if_indextoname(*reinterpret_cast<int*>(RTA_DATA(rtAttr)), ifName);
+                break;
+            case RTA_GATEWAY:
+                gateWayAddr.s_addr = *reinterpret_cast<u_int*>(RTA_DATA(rtAttr));
+                break;
+            case RTA_DST:
+                dstAddr.s_addr = *reinterpret_cast<u_int*>(RTA_DATA(rtAttr));
+                break;
+        }
+    }
+
+    std::string dstStr;
+    std::string gatewayStr;
+
+    if (dstAddr.s_addr == 0)
+    {
+        defaultGateway = reinterpret_cast<char*>(inet_ntoa(gateWayAddr));
+    }
+
+    dstStr = inet_ntoa(dstAddr);
+    gatewayStr = inet_ntoa(gateWayAddr);
+
+    Entry route(dstStr, gatewayStr, ifName);
+    routeList.emplace(std::make_pair(dstStr, std::move(route)));
+}
+
+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 occured 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;
+}
+
+std::string Table::getGateway(int addressFamily,
+                              const std::string& ipaddress,
+                              uint8_t prefix) const
+{
+    std::string gateway;
+    std::string network = getNetworkID(addressFamily, ipaddress, prefix);
+    auto it = routeList.find(network);
+    if (it != routeList.end())
+    {
+        gateway = it->second.gateway;
+    }
+
+    return gateway;
+}
+
+}// namespace route
+}// namespace network
+}// namespace phosphor
diff --git a/routing_table.hpp b/routing_table.hpp
new file mode 100644
index 0000000..e137e23
--- /dev/null
+++ b/routing_table.hpp
@@ -0,0 +1,107 @@
+#pragma once
+
+#include <asm/types.h>
+#include <sys/user.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#include <iostream>
+#include <list>
+#include <string>
+#include <map>
+
+namespace phosphor
+{
+namespace network
+{
+namespace route
+{
+constexpr auto BUFSIZE = PAGE_SIZE;
+
+struct Entry
+{
+    // destination network
+    std::string destination;
+    // gateway for this network.
+    std::string gateway;
+    // interface for this route
+    std::string interface;
+    Entry(std::string dest,
+          std::string gtw,
+          std::string intf) :
+          destination(dest),
+          gateway(gtw),
+          interface(intf){}
+
+    bool operator==(const Entry& rhs)
+    {
+        return this->destination == rhs.destination &&
+               this->gateway == rhs.gateway &&
+               this->interface == rhs.interface;
+    }
+};
+
+// Map of network address and the route entry
+using Map = std::map<std::string, struct Entry>;
+
+class Table
+{
+    public:
+        Table();
+        ~Table() = default;
+        Table(const Table&) = default;
+        Table& operator=(const Table&) = default;
+        Table(Table&&) = default;
+        Table& operator=(Table &&) = default;
+
+        /**
+         * @brief gets the list of routes.
+         *
+         * @returns list of routes.
+         */
+        Map getRoutes();
+
+        /**
+         * @brief gets the default gateway.
+         *
+         * @returns the default gateway.
+         */
+        std::string getDefaultGateway() const
+        {
+            return defaultGateway;
+        };
+
+        /**
+         * @brief get the gateway for the network.
+         * @param[in] addressFamily - ip address family(AF_INET/AF_INET6)
+         * @param[in] ipaddress - ip address.
+         * @param[in] prefix - prefix length.
+         * @returns the gatway for the given network.
+         */
+        std::string getGateway(int addressFamily,
+                               const std::string& ipaddress,
+                               uint8_t prefix) const;
+    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);
+
+        std::string defaultGateway; // default gateway
+        Map routeList; //List of routes
+
+};
+
+}// namespace route
+}// namespace network
+}// namespace phosphor