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/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