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