blob: 5493c58a36191dbccae3403cc165eb9b372bfaa5 [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{
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700194 auto it = mock_if_indextoname.find(ifindex);
195 if (it == mock_if_indextoname.end())
196 {
William A. Kennington IIId7946a72019-04-19 14:24:09 -0700197 errno = ENXIO;
198 return NULL;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700199 }
200 return std::strcpy(ifname, it->second.c_str());
201}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700202
203int ioctl(int fd, unsigned long int request, ...)
204{
205 va_list vl;
206 va_start(vl, request);
207 void* data = va_arg(vl, void*);
208 va_end(vl);
209
210 if (request == SIOCGIFHWADDR)
211 {
212 auto req = reinterpret_cast<ifreq*>(data);
213 auto it = mock_macs.find(req->ifr_name);
214 if (it == mock_macs.end())
215 {
216 errno = ENXIO;
217 return -1;
218 }
219 std::memcpy(req->ifr_hwaddr.sa_data, &it->second, sizeof(it->second));
220 return 0;
221 }
222
223 static auto real_ioctl =
224 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
225 return real_ioctl(fd, request, data);
226}
227
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700228int socket(int domain, int type, int protocol)
229{
230 static auto real_socket =
231 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
232 int fd = real_socket(domain, type, protocol);
233 if (domain == AF_NETLINK && !(type & SOCK_RAW))
234 {
235 fprintf(stderr, "Netlink sockets must be RAW\n");
236 abort();
237 }
238 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
239 {
240 mock_rtnetlinks[fd] = {};
241 }
242 return fd;
243}
244
245int close(int fd)
246{
247 auto it = mock_rtnetlinks.find(fd);
248 if (it != mock_rtnetlinks.end())
249 {
250 mock_rtnetlinks.erase(it);
251 }
252
253 static auto real_close =
254 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
255 return real_close(fd);
256}
257
258ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
259{
260 auto it = mock_rtnetlinks.find(sockfd);
261 if (it == mock_rtnetlinks.end())
262 {
263 static auto real_sendmsg =
264 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
265 return real_sendmsg(sockfd, msg, flags);
266 }
267 auto& msgs = it->second;
268
269 validateMsgHdr(msg);
270 if (!msgs.empty())
271 {
272 fprintf(stderr, "Unread netlink responses\n");
273 abort();
274 }
275
276 ssize_t ret;
277 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
278 msg->msg_iov[0].iov_len);
279
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700280 ret = sendmsg_link_dump(msgs, iov);
281 if (ret != 0)
282 {
283 return ret;
284 }
285
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700286 ret = sendmsg_ack(msgs, iov);
287 if (ret != 0)
288 {
289 return ret;
290 }
291
292 errno = ENOSYS;
293 return -1;
294}
295
296ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
297{
298 auto it = mock_rtnetlinks.find(sockfd);
299 if (it == mock_rtnetlinks.end())
300 {
301 static auto real_recvmsg =
302 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
303 return real_recvmsg(sockfd, msg, flags);
304 }
305 auto& msgs = it->second;
306
307 validateMsgHdr(msg);
308 constexpr size_t required_buf_size = 8192;
309 if (msg->msg_iov[0].iov_len < required_buf_size)
310 {
311 fprintf(stderr, "recvmsg iov too short: %zu\n",
312 msg->msg_iov[0].iov_len);
313 abort();
314 }
315 if (msgs.empty())
316 {
317 fprintf(stderr, "No pending netlink responses\n");
318 abort();
319 }
320
321 ssize_t ret = 0;
322 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
323 while (!msgs.empty())
324 {
325 const auto& msg = msgs.front();
326 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
327 {
328 break;
329 }
330 ret = NLMSG_ALIGN(ret);
331 memcpy(data + ret, msg.data(), msg.size());
332 ret += msg.size();
333 msgs.pop();
334 }
335 return ret;
336}
337
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700338} // extern "C"