blob: 349a79af8082c3d3846a585ff4ef81038723386b [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>
William A. Kennington III12beaad2020-06-13 19:30:41 -070022#include <stdplus/raw.hpp>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070023#include <string>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070024#include <string_view>
25#include <vector>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070026
Ratan Gupta8ab17922017-05-25 13:07:05 +053027#define MAX_IFADDRS 5
28
29int debugging = false;
30
31/* Data for mocking getifaddrs */
Gunnar Mills57d9c502018-09-14 14:42:34 -050032struct ifaddr_storage
33{
Ratan Gupta8ab17922017-05-25 13:07:05 +053034 struct ifaddrs ifaddr;
35 struct sockaddr_storage addr;
36 struct sockaddr_storage mask;
37 struct sockaddr_storage bcast;
38} mock_ifaddr_storage[MAX_IFADDRS];
39
Gunnar Mills57d9c502018-09-14 14:42:34 -050040struct ifaddrs* mock_ifaddrs = nullptr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053041
42int ifaddr_count = 0;
43
44/* Stub library functions */
Manojkiran Edaaa57fa52020-06-13 14:59:53 +053045void freeifaddrs(ifaddrs* /*ifp*/)
Ratan Gupta8ab17922017-05-25 13:07:05 +053046{
Gunnar Mills57d9c502018-09-14 14:42:34 -050047 return;
Ratan Gupta8ab17922017-05-25 13:07:05 +053048}
49
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070050std::map<int, std::queue<std::string>> mock_rtnetlinks;
51
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070052struct MockInfo
53{
54 unsigned idx;
55 unsigned flags;
56 std::optional<ether_addr> mac;
57 std::optional<unsigned> mtu;
58};
59
60std::map<std::string, MockInfo> mock_if;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070061std::map<int, std::string> mock_if_indextoname;
62
William A. Kennington III862275a2019-04-22 20:37:08 -070063void mock_clear()
64{
65 mock_ifaddrs = nullptr;
66 ifaddr_count = 0;
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070067 mock_rtnetlinks.clear();
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070068 mock_if.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070069 mock_if_indextoname.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070070}
71
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070072void mock_addIF(const std::string& name, unsigned idx, unsigned flags,
73 std::optional<ether_addr> mac, std::optional<unsigned> mtu)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070074{
75 if (idx == 0)
76 {
77 throw std::invalid_argument("Bad interface index");
78 }
79
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070080 mock_if.emplace(
81 name, MockInfo{.idx = idx, .flags = flags, .mac = mac, .mtu = mtu});
82 mock_if_indextoname.emplace(idx, name);
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070083}
84
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070085void mock_addIP(const char* name, const char* addr, const char* mask)
Ratan Gupta8ab17922017-05-25 13:07:05 +053086{
Gunnar Mills57d9c502018-09-14 14:42:34 -050087 struct ifaddrs* ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053088
Gunnar Mills57d9c502018-09-14 14:42:34 -050089 struct sockaddr_in* in =
90 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].addr);
91 struct sockaddr_in* mask_in =
92 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].mask);
Ratan Gupta8ab17922017-05-25 13:07:05 +053093
94 in->sin_family = AF_INET;
95 in->sin_port = 0;
96 in->sin_addr.s_addr = inet_addr(addr);
97
98 mask_in->sin_family = AF_INET;
99 mask_in->sin_port = 0;
100 mask_in->sin_addr.s_addr = inet_addr(mask);
101
102 ifaddr->ifa_next = nullptr;
103 ifaddr->ifa_name = const_cast<char*>(name);
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700104 ifaddr->ifa_flags = 0;
Ratan Gupta8ab17922017-05-25 13:07:05 +0530105 ifaddr->ifa_addr = reinterpret_cast<struct sockaddr*>(in);
106 ifaddr->ifa_netmask = reinterpret_cast<struct sockaddr*>(mask_in);
107 ifaddr->ifa_data = nullptr;
108
109 if (ifaddr_count > 0)
110 mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr;
111 ifaddr_count++;
112 mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +0530113}
114
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700115void validateMsgHdr(const struct msghdr* msg)
116{
117 if (msg->msg_namelen != sizeof(sockaddr_nl))
118 {
119 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
120 abort();
121 }
122 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
123 if (from.nl_family != AF_NETLINK)
124 {
125 fprintf(stderr, "recvmsg bad family data\n");
126 abort();
127 }
128 if (msg->msg_iovlen != 1)
129 {
130 fprintf(stderr, "recvmsg unsupported iov configuration\n");
131 abort();
132 }
133}
134
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700135ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
136{
137 const ssize_t ret = in.size();
William A. Kennington III12beaad2020-06-13 19:30:41 -0700138 const auto& hdrin = stdplus::raw::copyFrom<nlmsghdr>(in);
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700139 if (hdrin.nlmsg_type != RTM_GETLINK)
140 {
141 return 0;
142 }
143
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700144 for (const auto& [name, i] : mock_if)
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700145 {
146 ifinfomsg info{};
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700147 info.ifi_index = i.idx;
148 info.ifi_flags = i.flags;
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700149 nlmsghdr hdr{};
150 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(info));
151 hdr.nlmsg_type = RTM_NEWLINK;
152 hdr.nlmsg_flags = NLM_F_MULTI;
153 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
154 memcpy(out.data(), &hdr, sizeof(hdr));
155 memcpy(NLMSG_DATA(out.data()), &info, sizeof(info));
156 }
157
158 nlmsghdr hdr{};
159 hdr.nlmsg_len = NLMSG_LENGTH(0);
160 hdr.nlmsg_type = NLMSG_DONE;
161 hdr.nlmsg_flags = NLM_F_MULTI;
162 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
163 memcpy(out.data(), &hdr, sizeof(hdr));
164 return ret;
165}
166
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700167ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
168{
169 nlmsgerr ack{};
170 nlmsghdr hdr{};
171 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
172 hdr.nlmsg_type = NLMSG_ERROR;
173 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
174 memcpy(out.data(), &hdr, sizeof(hdr));
175 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
176 return in.size();
177}
178
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700179extern "C" {
180
Gunnar Mills57d9c502018-09-14 14:42:34 -0500181int getifaddrs(ifaddrs** ifap)
Ratan Gupta8ab17922017-05-25 13:07:05 +0530182{
183 *ifap = mock_ifaddrs;
184 if (mock_ifaddrs == nullptr)
185 return -1;
186 return (0);
187}
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700188
189unsigned if_nametoindex(const char* ifname)
190{
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700191 auto it = mock_if.find(ifname);
192 if (it == mock_if.end())
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700193 {
194 errno = ENXIO;
195 return 0;
196 }
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700197 return it->second.idx;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700198}
199
200char* if_indextoname(unsigned ifindex, char* ifname)
201{
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700202 auto it = mock_if_indextoname.find(ifindex);
203 if (it == mock_if_indextoname.end())
204 {
William A. Kennington IIId7946a72019-04-19 14:24:09 -0700205 errno = ENXIO;
206 return NULL;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700207 }
208 return std::strcpy(ifname, it->second.c_str());
209}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700210
211int ioctl(int fd, unsigned long int request, ...)
212{
213 va_list vl;
214 va_start(vl, request);
215 void* data = va_arg(vl, void*);
216 va_end(vl);
217
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700218 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700219 if (request == SIOCGIFHWADDR)
220 {
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700221 auto it = mock_if.find(req->ifr_name);
222 if (it == mock_if.end())
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700223 {
224 errno = ENXIO;
225 return -1;
226 }
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700227 if (!it->second.mac)
228 {
229 errno = EOPNOTSUPP;
230 return -1;
231 }
232 std::memcpy(req->ifr_hwaddr.sa_data, &*it->second.mac,
233 sizeof(*it->second.mac));
234 return 0;
235 }
236 else if (request == SIOCGIFFLAGS)
237 {
238 auto it = mock_if.find(req->ifr_name);
239 if (it == mock_if.end())
240 {
241 errno = ENXIO;
242 return -1;
243 }
244 req->ifr_flags = it->second.flags;
245 return 0;
246 }
247 else if (request == SIOCGIFMTU)
248 {
249 auto it = mock_if.find(req->ifr_name);
250 if (it == mock_if.end())
251 {
252 errno = ENXIO;
253 return -1;
254 }
255 if (!it->second.mtu)
256 {
257 errno = EOPNOTSUPP;
258 return -1;
259 }
260 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700261 return 0;
262 }
263
264 static auto real_ioctl =
265 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
266 return real_ioctl(fd, request, data);
267}
268
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700269int socket(int domain, int type, int protocol)
270{
271 static auto real_socket =
272 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
273 int fd = real_socket(domain, type, protocol);
274 if (domain == AF_NETLINK && !(type & SOCK_RAW))
275 {
276 fprintf(stderr, "Netlink sockets must be RAW\n");
277 abort();
278 }
279 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
280 {
281 mock_rtnetlinks[fd] = {};
282 }
283 return fd;
284}
285
286int close(int fd)
287{
288 auto it = mock_rtnetlinks.find(fd);
289 if (it != mock_rtnetlinks.end())
290 {
291 mock_rtnetlinks.erase(it);
292 }
293
294 static auto real_close =
295 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
296 return real_close(fd);
297}
298
299ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
300{
301 auto it = mock_rtnetlinks.find(sockfd);
302 if (it == mock_rtnetlinks.end())
303 {
304 static auto real_sendmsg =
305 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
306 return real_sendmsg(sockfd, msg, flags);
307 }
308 auto& msgs = it->second;
309
310 validateMsgHdr(msg);
311 if (!msgs.empty())
312 {
313 fprintf(stderr, "Unread netlink responses\n");
314 abort();
315 }
316
317 ssize_t ret;
318 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
319 msg->msg_iov[0].iov_len);
320
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700321 ret = sendmsg_link_dump(msgs, iov);
322 if (ret != 0)
323 {
324 return ret;
325 }
326
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700327 ret = sendmsg_ack(msgs, iov);
328 if (ret != 0)
329 {
330 return ret;
331 }
332
333 errno = ENOSYS;
334 return -1;
335}
336
337ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
338{
339 auto it = mock_rtnetlinks.find(sockfd);
340 if (it == mock_rtnetlinks.end())
341 {
342 static auto real_recvmsg =
343 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
344 return real_recvmsg(sockfd, msg, flags);
345 }
346 auto& msgs = it->second;
347
348 validateMsgHdr(msg);
349 constexpr size_t required_buf_size = 8192;
350 if (msg->msg_iov[0].iov_len < required_buf_size)
351 {
352 fprintf(stderr, "recvmsg iov too short: %zu\n",
353 msg->msg_iov[0].iov_len);
354 abort();
355 }
356 if (msgs.empty())
357 {
358 fprintf(stderr, "No pending netlink responses\n");
359 abort();
360 }
361
362 ssize_t ret = 0;
363 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
364 while (!msgs.empty())
365 {
366 const auto& msg = msgs.front();
367 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
368 {
369 break;
370 }
371 ret = NLMSG_ALIGN(ret);
372 memcpy(data + ret, msg.data(), msg.size());
373 ret += msg.size();
374 msgs.pop();
375 }
376 return ret;
377}
378
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700379} // extern "C"