blob: 83d2d246a4d55ce33c94d4313a7a9c4184d7102f [file] [log] [blame]
Ratan Gupta233524c2017-05-27 11:47:31 +05301#include "routing_table.hpp"
2#include "util.hpp"
3#include "xyz/openbmc_project/Common/error.hpp"
4
5#include <phosphor-logging/log.hpp>
6#include <phosphor-logging/elog-errors.hpp>
7
8#include <netinet/in.h>
9#include <net/if.h>
10#include <stdio.h>
11#include <string.h>
12#include <stdlib.h>
13#include <unistd.h>
14#include <sys/ioctl.h>
15#include <linux/rtnetlink.h>
16#include <sys/types.h>
17#include <sys/socket.h>
18#include <arpa/inet.h>
19
20#include <stdexcept>
21
22namespace phosphor
23{
24namespace network
25{
26namespace route
27{
28
29using namespace phosphor::logging;
30using namespace sdbusplus::xyz::openbmc_project::Common::Error;
31
32Table::Table()
33{
34 try
35 {
36 getRoutes();
37 }
38 catch (InternalFailure& e)
39 {
40 commit<InternalFailure>();
41 }
42
43}
44
45int Table::readNetLinkSock(int sockFd, std::array<char,BUFSIZE>& buf)
46{
47 struct nlmsghdr* nlHdr = nullptr;
48 int readLen {};
49 int msgLen {};
50 uint8_t seqNum = 1;
51 uint8_t pID = getpid();
52 char* bufPtr = buf.data();
53
54 do
55 {
Gunnar Millsd75f0492017-10-25 20:33:32 -050056 // Receive response from the kernel
Ratan Gupta233524c2017-05-27 11:47:31 +053057 if ((readLen = recv(sockFd, bufPtr, BUFSIZE - msgLen, 0)) < 0)
58 {
59 auto error = errno;
60 log<level::ERR>("Socket recv failed:",
61 entry("ERROR=%s", strerror(error)));
62 elog<InternalFailure>();
63
64 }
65
66 nlHdr = reinterpret_cast<nlmsghdr*>(bufPtr);
67
68 // Check if the header is valid
69
70 if ((NLMSG_OK(nlHdr, readLen) == 0)
71 || (nlHdr->nlmsg_type == NLMSG_ERROR))
72 {
73
74 auto error = errno;
75 log<level::ERR>("Error validating header",
76 entry("NLMSGTYPE=%d", nlHdr->nlmsg_type),
77 entry("ERROR=%s", strerror(error)));
78 elog<InternalFailure>();
79 }
80
81 // Check if the its the last message
82 if (nlHdr->nlmsg_type == NLMSG_DONE)
83 {
84 break;
85 }
86 else
87 {
88 // Else move the pointer to buffer appropriately
89 bufPtr += readLen;
90 msgLen += readLen;
91 }
92
93 // Check if its a multi part message
94 if ((nlHdr->nlmsg_flags & NLM_F_MULTI) == 0)
95 {
96 break;
97 }
98 }
99 while ((nlHdr->nlmsg_seq != seqNum) || (nlHdr->nlmsg_pid != pID));
100 return msgLen;
101}
102
103void Table::parseRoutes(const nlmsghdr* nlHdr)
104{
105 rtmsg* rtMsg = nullptr;
106 rtattr* rtAttr = nullptr;
107 int rtLen {};
108 in_addr dstAddr {};
109 in_addr gateWayAddr {};
110 char ifName[IF_NAMESIZE] = {};
111
112 rtMsg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nlHdr));
113
114 // If the route is not for AF_INET or does not belong to main routing table
115 // then return.
116 if ((rtMsg->rtm_family != AF_INET) || (rtMsg->rtm_table != RT_TABLE_MAIN))
117 {
118 return;
119 }
120
121 // get the rtattr field
122 rtAttr = reinterpret_cast<rtattr*>(RTM_RTA(rtMsg));
123
124 rtLen = RTM_PAYLOAD(nlHdr);
125
126 for (; RTA_OK(rtAttr, rtLen); rtAttr = RTA_NEXT(rtAttr, rtLen))
127 {
128 switch (rtAttr->rta_type)
129 {
130 case RTA_OIF:
131 if_indextoname(*reinterpret_cast<int*>(RTA_DATA(rtAttr)), ifName);
132 break;
133 case RTA_GATEWAY:
134 gateWayAddr.s_addr = *reinterpret_cast<u_int*>(RTA_DATA(rtAttr));
135 break;
136 case RTA_DST:
137 dstAddr.s_addr = *reinterpret_cast<u_int*>(RTA_DATA(rtAttr));
138 break;
139 }
140 }
141
142 std::string dstStr;
143 std::string gatewayStr;
144
145 if (dstAddr.s_addr == 0)
146 {
147 defaultGateway = reinterpret_cast<char*>(inet_ntoa(gateWayAddr));
148 }
149
150 dstStr = inet_ntoa(dstAddr);
151 gatewayStr = inet_ntoa(gateWayAddr);
152
153 Entry route(dstStr, gatewayStr, ifName);
154 routeList.emplace(std::make_pair(dstStr, std::move(route)));
155}
156
157Map Table::getRoutes()
158{
159 nlmsghdr* nlMsg = nullptr;
160 std::array<char, BUFSIZE> msgBuf = {0};
161
162 int sock = -1;
163 int len {0};
164
165 uint8_t msgSeq {0};
166
167 // Create Socket
168 if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0)
169 {
170 auto error = errno;
Gunnar Millsd75f0492017-10-25 20:33:32 -0500171 log<level::ERR>("Error occurred during socket creation",
Ratan Gupta233524c2017-05-27 11:47:31 +0530172 entry("ERRNO=%s", strerror(error)));
173 elog<InternalFailure>();
174 }
175
176 phosphor::Descriptor smartSock(sock);
177 sock = -1;
178
179 // point the header and the msg structure pointers into the buffer.
180 nlMsg = reinterpret_cast<nlmsghdr*>(msgBuf.data());
181 // Length of message
182 nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg));
183 // Get the routes from kernel routing table
184 nlMsg->nlmsg_type = RTM_GETROUTE;
185 // The message is a request for dump
186 nlMsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
187
188 nlMsg->nlmsg_seq = msgSeq;
189 nlMsg->nlmsg_pid = getpid();
190
191 // Send the request
192 if (send(smartSock(), nlMsg, nlMsg->nlmsg_len, 0) < 0)
193 {
194 auto error = errno;
195 log<level::ERR>("Error occurred during send on netlink socket",
196 entry("ERRNO=%s", strerror(error)));
197 elog<InternalFailure>();
198 }
199
200 // Read the response
201 len = readNetLinkSock(smartSock(), msgBuf);
202
203 // Parse and print the response
204 for (; NLMSG_OK(nlMsg, len); nlMsg = NLMSG_NEXT(nlMsg, len))
205 {
206 parseRoutes(nlMsg);
207 }
208 return routeList;
209}
210
211std::string Table::getGateway(int addressFamily,
212 const std::string& ipaddress,
213 uint8_t prefix) const
214{
215 std::string gateway;
216 std::string network = getNetworkID(addressFamily, ipaddress, prefix);
217 auto it = routeList.find(network);
218 if (it != routeList.end())
219 {
220 gateway = it->second.gateway;
221 }
222
223 return gateway;
224}
225
226}// namespace route
227}// namespace network
228}// namespace phosphor