blob: e5f97be8c75544413da788514fe02da2d7ab8999 [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;
William A. Kennington III2e09d272022-10-14 17:15:00 -0700184 return 0;
Ratan Gupta8ab17922017-05-25 13:07:05 +0530185}
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700186
187unsigned if_nametoindex(const char* ifname)
188{
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700189 auto it = mock_if.find(ifname);
190 if (it == mock_if.end())
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700191 {
192 errno = ENXIO;
193 return 0;
194 }
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700195 return it->second.idx;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700196}
197
198char* if_indextoname(unsigned ifindex, char* ifname)
199{
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700200 auto it = mock_if_indextoname.find(ifindex);
201 if (it == mock_if_indextoname.end())
202 {
William A. Kennington IIId7946a72019-04-19 14:24:09 -0700203 errno = ENXIO;
204 return NULL;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700205 }
206 return std::strcpy(ifname, it->second.c_str());
207}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700208
209int ioctl(int fd, unsigned long int request, ...)
210{
211 va_list vl;
212 va_start(vl, request);
213 void* data = va_arg(vl, void*);
214 va_end(vl);
215
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700216 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700217 if (request == SIOCGIFHWADDR)
218 {
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700219 auto it = mock_if.find(req->ifr_name);
220 if (it == mock_if.end())
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700221 {
222 errno = ENXIO;
223 return -1;
224 }
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700225 if (!it->second.mac)
226 {
227 errno = EOPNOTSUPP;
228 return -1;
229 }
230 std::memcpy(req->ifr_hwaddr.sa_data, &*it->second.mac,
231 sizeof(*it->second.mac));
232 return 0;
233 }
234 else if (request == SIOCGIFFLAGS)
235 {
236 auto it = mock_if.find(req->ifr_name);
237 if (it == mock_if.end())
238 {
239 errno = ENXIO;
240 return -1;
241 }
242 req->ifr_flags = it->second.flags;
243 return 0;
244 }
245 else if (request == SIOCGIFMTU)
246 {
247 auto it = mock_if.find(req->ifr_name);
248 if (it == mock_if.end())
249 {
250 errno = ENXIO;
251 return -1;
252 }
253 if (!it->second.mtu)
254 {
255 errno = EOPNOTSUPP;
256 return -1;
257 }
258 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700259 return 0;
260 }
261
262 static auto real_ioctl =
263 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
264 return real_ioctl(fd, request, data);
265}
266
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700267int socket(int domain, int type, int protocol)
268{
269 static auto real_socket =
270 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
271 int fd = real_socket(domain, type, protocol);
272 if (domain == AF_NETLINK && !(type & SOCK_RAW))
273 {
274 fprintf(stderr, "Netlink sockets must be RAW\n");
275 abort();
276 }
277 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
278 {
279 mock_rtnetlinks[fd] = {};
280 }
281 return fd;
282}
283
284int close(int fd)
285{
286 auto it = mock_rtnetlinks.find(fd);
287 if (it != mock_rtnetlinks.end())
288 {
289 mock_rtnetlinks.erase(it);
290 }
291
292 static auto real_close =
293 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
294 return real_close(fd);
295}
296
297ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
298{
299 auto it = mock_rtnetlinks.find(sockfd);
300 if (it == mock_rtnetlinks.end())
301 {
302 static auto real_sendmsg =
303 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
304 return real_sendmsg(sockfd, msg, flags);
305 }
306 auto& msgs = it->second;
307
308 validateMsgHdr(msg);
309 if (!msgs.empty())
310 {
311 fprintf(stderr, "Unread netlink responses\n");
312 abort();
313 }
314
315 ssize_t ret;
316 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
317 msg->msg_iov[0].iov_len);
318
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700319 ret = sendmsg_link_dump(msgs, iov);
320 if (ret != 0)
321 {
322 return ret;
323 }
324
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700325 ret = sendmsg_ack(msgs, iov);
326 if (ret != 0)
327 {
328 return ret;
329 }
330
331 errno = ENOSYS;
332 return -1;
333}
334
335ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
336{
337 auto it = mock_rtnetlinks.find(sockfd);
338 if (it == mock_rtnetlinks.end())
339 {
340 static auto real_recvmsg =
341 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
342 return real_recvmsg(sockfd, msg, flags);
343 }
344 auto& msgs = it->second;
345
346 validateMsgHdr(msg);
347 constexpr size_t required_buf_size = 8192;
348 if (msg->msg_iov[0].iov_len < required_buf_size)
349 {
350 fprintf(stderr, "recvmsg iov too short: %zu\n",
351 msg->msg_iov[0].iov_len);
352 abort();
353 }
354 if (msgs.empty())
355 {
356 fprintf(stderr, "No pending netlink responses\n");
357 abort();
358 }
359
360 ssize_t ret = 0;
361 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
362 while (!msgs.empty())
363 {
364 const auto& msg = msgs.front();
365 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
366 {
367 break;
368 }
369 ret = NLMSG_ALIGN(ret);
370 memcpy(data + ret, msg.data(), msg.size());
371 ret += msg.size();
372 msgs.pop();
373 }
374 return ret;
375}
376
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700377} // extern "C"