blob: 421dbfed8aabdc30c30ed719a43c1be5ea382180 [file] [log] [blame]
William A. Kennington III5bb7da92019-04-22 21:30:23 -07001#include "util.hpp"
2
Gunnar Mills57d9c502018-09-14 14:42:34 -05003#include <arpa/inet.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07004#include <dlfcn.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -05005#include <ifaddrs.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -07006#include <linux/netlink.h>
7#include <linux/rtnetlink.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07008#include <net/ethernet.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +05309#include <net/if.h>
10#include <netinet/in.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070011#include <sys/ioctl.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -050012#include <sys/socket.h>
13#include <sys/types.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070014#include <unistd.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +053015
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070016#include <cstdarg>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070017#include <cstdio>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070018#include <cstring>
19#include <map>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070020#include <queue>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070021#include <stdexcept>
22#include <string>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070023#include <string_view>
24#include <vector>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070025
Ratan Gupta8ab17922017-05-25 13:07:05 +053026#define MAX_IFADDRS 5
27
28int debugging = false;
29
30/* Data for mocking getifaddrs */
Gunnar Mills57d9c502018-09-14 14:42:34 -050031struct ifaddr_storage
32{
Ratan Gupta8ab17922017-05-25 13:07:05 +053033 struct ifaddrs ifaddr;
34 struct sockaddr_storage addr;
35 struct sockaddr_storage mask;
36 struct sockaddr_storage bcast;
37} mock_ifaddr_storage[MAX_IFADDRS];
38
Gunnar Mills57d9c502018-09-14 14:42:34 -050039struct ifaddrs* mock_ifaddrs = nullptr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053040
41int ifaddr_count = 0;
42
43/* Stub library functions */
Gunnar Mills57d9c502018-09-14 14:42:34 -050044void freeifaddrs(ifaddrs* ifp)
Ratan Gupta8ab17922017-05-25 13:07:05 +053045{
Gunnar Mills57d9c502018-09-14 14:42:34 -050046 return;
Ratan Gupta8ab17922017-05-25 13:07:05 +053047}
48
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070049std::map<int, std::queue<std::string>> mock_rtnetlinks;
50
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070051std::map<std::string, int> mock_if_nametoindex;
52std::map<int, std::string> mock_if_indextoname;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070053std::map<std::string, ether_addr> mock_macs;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070054
William A. Kennington III862275a2019-04-22 20:37:08 -070055void mock_clear()
56{
57 mock_ifaddrs = nullptr;
58 ifaddr_count = 0;
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070059 mock_rtnetlinks.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070060 mock_if_nametoindex.clear();
61 mock_if_indextoname.clear();
62 mock_macs.clear();
63}
64
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070065void mock_addIF(const std::string& name, int idx, const ether_addr& mac)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070066{
67 if (idx == 0)
68 {
69 throw std::invalid_argument("Bad interface index");
70 }
71
72 mock_if_nametoindex[name] = idx;
73 mock_if_indextoname[idx] = name;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070074 mock_macs[name] = mac;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070075}
76
Ratan Gupta8ab17922017-05-25 13:07:05 +053077void mock_addIP(const char* name, const char* addr, const char* mask,
78 unsigned int flags)
79{
Gunnar Mills57d9c502018-09-14 14:42:34 -050080 struct ifaddrs* ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053081
Gunnar Mills57d9c502018-09-14 14:42:34 -050082 struct sockaddr_in* in =
83 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].addr);
84 struct sockaddr_in* mask_in =
85 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].mask);
Ratan Gupta8ab17922017-05-25 13:07:05 +053086
87 in->sin_family = AF_INET;
88 in->sin_port = 0;
89 in->sin_addr.s_addr = inet_addr(addr);
90
91 mask_in->sin_family = AF_INET;
92 mask_in->sin_port = 0;
93 mask_in->sin_addr.s_addr = inet_addr(mask);
94
95 ifaddr->ifa_next = nullptr;
96 ifaddr->ifa_name = const_cast<char*>(name);
97 ifaddr->ifa_flags = flags;
98 ifaddr->ifa_addr = reinterpret_cast<struct sockaddr*>(in);
99 ifaddr->ifa_netmask = reinterpret_cast<struct sockaddr*>(mask_in);
100 ifaddr->ifa_data = nullptr;
101
102 if (ifaddr_count > 0)
103 mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr;
104 ifaddr_count++;
105 mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +0530106}
107
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700108void validateMsgHdr(const struct msghdr* msg)
109{
110 if (msg->msg_namelen != sizeof(sockaddr_nl))
111 {
112 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
113 abort();
114 }
115 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
116 if (from.nl_family != AF_NETLINK)
117 {
118 fprintf(stderr, "recvmsg bad family data\n");
119 abort();
120 }
121 if (msg->msg_iovlen != 1)
122 {
123 fprintf(stderr, "recvmsg unsupported iov configuration\n");
124 abort();
125 }
126}
127
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700128ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
129{
130 const ssize_t ret = in.size();
131 const auto& hdrin = phosphor::copyFrom<nlmsghdr>(in);
132 if (hdrin.nlmsg_type != RTM_GETLINK)
133 {
134 return 0;
135 }
136
137 for (const auto& [name, idx] : mock_if_nametoindex)
138 {
139 ifinfomsg info{};
140 info.ifi_index = idx;
141 nlmsghdr hdr{};
142 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(info));
143 hdr.nlmsg_type = RTM_NEWLINK;
144 hdr.nlmsg_flags = NLM_F_MULTI;
145 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
146 memcpy(out.data(), &hdr, sizeof(hdr));
147 memcpy(NLMSG_DATA(out.data()), &info, sizeof(info));
148 }
149
150 nlmsghdr hdr{};
151 hdr.nlmsg_len = NLMSG_LENGTH(0);
152 hdr.nlmsg_type = NLMSG_DONE;
153 hdr.nlmsg_flags = NLM_F_MULTI;
154 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
155 memcpy(out.data(), &hdr, sizeof(hdr));
156 return ret;
157}
158
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700159ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
160{
161 nlmsgerr ack{};
162 nlmsghdr hdr{};
163 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
164 hdr.nlmsg_type = NLMSG_ERROR;
165 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
166 memcpy(out.data(), &hdr, sizeof(hdr));
167 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
168 return in.size();
169}
170
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700171extern "C" {
172
Gunnar Mills57d9c502018-09-14 14:42:34 -0500173int getifaddrs(ifaddrs** ifap)
Ratan Gupta8ab17922017-05-25 13:07:05 +0530174{
175 *ifap = mock_ifaddrs;
176 if (mock_ifaddrs == nullptr)
177 return -1;
178 return (0);
179}
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700180
181unsigned if_nametoindex(const char* ifname)
182{
183 auto it = mock_if_nametoindex.find(ifname);
184 if (it == mock_if_nametoindex.end())
185 {
186 errno = ENXIO;
187 return 0;
188 }
189 return it->second;
190}
191
192char* if_indextoname(unsigned ifindex, char* ifname)
193{
194 if (ifindex == 0)
195 {
196 errno = ENXIO;
197 return NULL;
198 }
199 auto it = mock_if_indextoname.find(ifindex);
200 if (it == mock_if_indextoname.end())
201 {
202 // TODO: Return ENXIO once other code is mocked out
203 return std::strcpy(ifname, "invalid");
204 }
205 return std::strcpy(ifname, it->second.c_str());
206}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700207
208int ioctl(int fd, unsigned long int request, ...)
209{
210 va_list vl;
211 va_start(vl, request);
212 void* data = va_arg(vl, void*);
213 va_end(vl);
214
215 if (request == SIOCGIFHWADDR)
216 {
217 auto req = reinterpret_cast<ifreq*>(data);
218 auto it = mock_macs.find(req->ifr_name);
219 if (it == mock_macs.end())
220 {
221 errno = ENXIO;
222 return -1;
223 }
224 std::memcpy(req->ifr_hwaddr.sa_data, &it->second, sizeof(it->second));
225 return 0;
226 }
227
228 static auto real_ioctl =
229 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
230 return real_ioctl(fd, request, data);
231}
232
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700233int socket(int domain, int type, int protocol)
234{
235 static auto real_socket =
236 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
237 int fd = real_socket(domain, type, protocol);
238 if (domain == AF_NETLINK && !(type & SOCK_RAW))
239 {
240 fprintf(stderr, "Netlink sockets must be RAW\n");
241 abort();
242 }
243 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
244 {
245 mock_rtnetlinks[fd] = {};
246 }
247 return fd;
248}
249
250int close(int fd)
251{
252 auto it = mock_rtnetlinks.find(fd);
253 if (it != mock_rtnetlinks.end())
254 {
255 mock_rtnetlinks.erase(it);
256 }
257
258 static auto real_close =
259 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
260 return real_close(fd);
261}
262
263ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
264{
265 auto it = mock_rtnetlinks.find(sockfd);
266 if (it == mock_rtnetlinks.end())
267 {
268 static auto real_sendmsg =
269 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
270 return real_sendmsg(sockfd, msg, flags);
271 }
272 auto& msgs = it->second;
273
274 validateMsgHdr(msg);
275 if (!msgs.empty())
276 {
277 fprintf(stderr, "Unread netlink responses\n");
278 abort();
279 }
280
281 ssize_t ret;
282 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
283 msg->msg_iov[0].iov_len);
284
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700285 ret = sendmsg_link_dump(msgs, iov);
286 if (ret != 0)
287 {
288 return ret;
289 }
290
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700291 ret = sendmsg_ack(msgs, iov);
292 if (ret != 0)
293 {
294 return ret;
295 }
296
297 errno = ENOSYS;
298 return -1;
299}
300
301ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
302{
303 auto it = mock_rtnetlinks.find(sockfd);
304 if (it == mock_rtnetlinks.end())
305 {
306 static auto real_recvmsg =
307 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
308 return real_recvmsg(sockfd, msg, flags);
309 }
310 auto& msgs = it->second;
311
312 validateMsgHdr(msg);
313 constexpr size_t required_buf_size = 8192;
314 if (msg->msg_iov[0].iov_len < required_buf_size)
315 {
316 fprintf(stderr, "recvmsg iov too short: %zu\n",
317 msg->msg_iov[0].iov_len);
318 abort();
319 }
320 if (msgs.empty())
321 {
322 fprintf(stderr, "No pending netlink responses\n");
323 abort();
324 }
325
326 ssize_t ret = 0;
327 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
328 while (!msgs.empty())
329 {
330 const auto& msg = msgs.front();
331 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
332 {
333 break;
334 }
335 ret = NLMSG_ALIGN(ret);
336 memcpy(data + ret, msg.data(), msg.size());
337 ret += msg.size();
338 msgs.pop();
339 }
340 return ret;
341}
342
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700343} // extern "C"