blob: 83d2d246a4d55ce33c94d4313a7a9c4184d7102f [file] [log] [blame]
#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
{
// 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 {};
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 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;
}
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