blob: 280c74d13b66255ed32114bd58468578053ad930 [file] [log] [blame]
Gunnar Mills57d9c502018-09-14 14:42:34 -05001#include <arpa/inet.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07002#include <dlfcn.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -05003#include <ifaddrs.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -07004#include <linux/netlink.h>
5#include <linux/rtnetlink.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07006#include <net/ethernet.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +05307#include <net/if.h>
8#include <netinet/in.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07009#include <sys/ioctl.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -050010#include <sys/socket.h>
11#include <sys/types.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070012#include <unistd.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +053013
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070014#include <cstdarg>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070015#include <cstdio>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070016#include <cstring>
17#include <map>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070018#include <queue>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070019#include <stdexcept>
20#include <string>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070021#include <string_view>
22#include <vector>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070023
Ratan Gupta8ab17922017-05-25 13:07:05 +053024#define MAX_IFADDRS 5
25
26int debugging = false;
27
28/* Data for mocking getifaddrs */
Gunnar Mills57d9c502018-09-14 14:42:34 -050029struct ifaddr_storage
30{
Ratan Gupta8ab17922017-05-25 13:07:05 +053031 struct ifaddrs ifaddr;
32 struct sockaddr_storage addr;
33 struct sockaddr_storage mask;
34 struct sockaddr_storage bcast;
35} mock_ifaddr_storage[MAX_IFADDRS];
36
Gunnar Mills57d9c502018-09-14 14:42:34 -050037struct ifaddrs* mock_ifaddrs = nullptr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053038
39int ifaddr_count = 0;
40
41/* Stub library functions */
Gunnar Mills57d9c502018-09-14 14:42:34 -050042void freeifaddrs(ifaddrs* ifp)
Ratan Gupta8ab17922017-05-25 13:07:05 +053043{
Gunnar Mills57d9c502018-09-14 14:42:34 -050044 return;
Ratan Gupta8ab17922017-05-25 13:07:05 +053045}
46
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070047std::map<int, std::queue<std::string>> mock_rtnetlinks;
48
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070049std::map<std::string, int> mock_if_nametoindex;
50std::map<int, std::string> mock_if_indextoname;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070051std::map<std::string, ether_addr> mock_macs;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070052
William A. Kennington III862275a2019-04-22 20:37:08 -070053void mock_clear()
54{
55 mock_ifaddrs = nullptr;
56 ifaddr_count = 0;
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070057 mock_rtnetlinks.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070058 mock_if_nametoindex.clear();
59 mock_if_indextoname.clear();
60 mock_macs.clear();
61}
62
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070063void mock_addIF(const std::string& name, int idx, const ether_addr& mac)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070064{
65 if (idx == 0)
66 {
67 throw std::invalid_argument("Bad interface index");
68 }
69
70 mock_if_nametoindex[name] = idx;
71 mock_if_indextoname[idx] = name;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070072 mock_macs[name] = mac;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070073}
74
Ratan Gupta8ab17922017-05-25 13:07:05 +053075void mock_addIP(const char* name, const char* addr, const char* mask,
76 unsigned int flags)
77{
Gunnar Mills57d9c502018-09-14 14:42:34 -050078 struct ifaddrs* ifaddr = &mock_ifaddr_storage[ifaddr_count].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +053079
Gunnar Mills57d9c502018-09-14 14:42:34 -050080 struct sockaddr_in* in =
81 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].addr);
82 struct sockaddr_in* mask_in =
83 reinterpret_cast<sockaddr_in*>(&mock_ifaddr_storage[ifaddr_count].mask);
Ratan Gupta8ab17922017-05-25 13:07:05 +053084
85 in->sin_family = AF_INET;
86 in->sin_port = 0;
87 in->sin_addr.s_addr = inet_addr(addr);
88
89 mask_in->sin_family = AF_INET;
90 mask_in->sin_port = 0;
91 mask_in->sin_addr.s_addr = inet_addr(mask);
92
93 ifaddr->ifa_next = nullptr;
94 ifaddr->ifa_name = const_cast<char*>(name);
95 ifaddr->ifa_flags = flags;
96 ifaddr->ifa_addr = reinterpret_cast<struct sockaddr*>(in);
97 ifaddr->ifa_netmask = reinterpret_cast<struct sockaddr*>(mask_in);
98 ifaddr->ifa_data = nullptr;
99
100 if (ifaddr_count > 0)
101 mock_ifaddr_storage[ifaddr_count - 1].ifaddr.ifa_next = ifaddr;
102 ifaddr_count++;
103 mock_ifaddrs = &mock_ifaddr_storage[0].ifaddr;
Ratan Gupta8ab17922017-05-25 13:07:05 +0530104}
105
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700106void validateMsgHdr(const struct msghdr* msg)
107{
108 if (msg->msg_namelen != sizeof(sockaddr_nl))
109 {
110 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
111 abort();
112 }
113 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
114 if (from.nl_family != AF_NETLINK)
115 {
116 fprintf(stderr, "recvmsg bad family data\n");
117 abort();
118 }
119 if (msg->msg_iovlen != 1)
120 {
121 fprintf(stderr, "recvmsg unsupported iov configuration\n");
122 abort();
123 }
124}
125
126ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
127{
128 nlmsgerr ack{};
129 nlmsghdr hdr{};
130 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
131 hdr.nlmsg_type = NLMSG_ERROR;
132 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
133 memcpy(out.data(), &hdr, sizeof(hdr));
134 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
135 return in.size();
136}
137
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700138extern "C" {
139
Gunnar Mills57d9c502018-09-14 14:42:34 -0500140int getifaddrs(ifaddrs** ifap)
Ratan Gupta8ab17922017-05-25 13:07:05 +0530141{
142 *ifap = mock_ifaddrs;
143 if (mock_ifaddrs == nullptr)
144 return -1;
145 return (0);
146}
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700147
148unsigned if_nametoindex(const char* ifname)
149{
150 auto it = mock_if_nametoindex.find(ifname);
151 if (it == mock_if_nametoindex.end())
152 {
153 errno = ENXIO;
154 return 0;
155 }
156 return it->second;
157}
158
159char* if_indextoname(unsigned ifindex, char* ifname)
160{
161 if (ifindex == 0)
162 {
163 errno = ENXIO;
164 return NULL;
165 }
166 auto it = mock_if_indextoname.find(ifindex);
167 if (it == mock_if_indextoname.end())
168 {
169 // TODO: Return ENXIO once other code is mocked out
170 return std::strcpy(ifname, "invalid");
171 }
172 return std::strcpy(ifname, it->second.c_str());
173}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700174
175int ioctl(int fd, unsigned long int request, ...)
176{
177 va_list vl;
178 va_start(vl, request);
179 void* data = va_arg(vl, void*);
180 va_end(vl);
181
182 if (request == SIOCGIFHWADDR)
183 {
184 auto req = reinterpret_cast<ifreq*>(data);
185 auto it = mock_macs.find(req->ifr_name);
186 if (it == mock_macs.end())
187 {
188 errno = ENXIO;
189 return -1;
190 }
191 std::memcpy(req->ifr_hwaddr.sa_data, &it->second, sizeof(it->second));
192 return 0;
193 }
194
195 static auto real_ioctl =
196 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
197 return real_ioctl(fd, request, data);
198}
199
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700200int socket(int domain, int type, int protocol)
201{
202 static auto real_socket =
203 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
204 int fd = real_socket(domain, type, protocol);
205 if (domain == AF_NETLINK && !(type & SOCK_RAW))
206 {
207 fprintf(stderr, "Netlink sockets must be RAW\n");
208 abort();
209 }
210 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
211 {
212 mock_rtnetlinks[fd] = {};
213 }
214 return fd;
215}
216
217int close(int fd)
218{
219 auto it = mock_rtnetlinks.find(fd);
220 if (it != mock_rtnetlinks.end())
221 {
222 mock_rtnetlinks.erase(it);
223 }
224
225 static auto real_close =
226 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
227 return real_close(fd);
228}
229
230ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
231{
232 auto it = mock_rtnetlinks.find(sockfd);
233 if (it == mock_rtnetlinks.end())
234 {
235 static auto real_sendmsg =
236 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
237 return real_sendmsg(sockfd, msg, flags);
238 }
239 auto& msgs = it->second;
240
241 validateMsgHdr(msg);
242 if (!msgs.empty())
243 {
244 fprintf(stderr, "Unread netlink responses\n");
245 abort();
246 }
247
248 ssize_t ret;
249 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
250 msg->msg_iov[0].iov_len);
251
252 ret = sendmsg_ack(msgs, iov);
253 if (ret != 0)
254 {
255 return ret;
256 }
257
258 errno = ENOSYS;
259 return -1;
260}
261
262ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
263{
264 auto it = mock_rtnetlinks.find(sockfd);
265 if (it == mock_rtnetlinks.end())
266 {
267 static auto real_recvmsg =
268 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
269 return real_recvmsg(sockfd, msg, flags);
270 }
271 auto& msgs = it->second;
272
273 validateMsgHdr(msg);
274 constexpr size_t required_buf_size = 8192;
275 if (msg->msg_iov[0].iov_len < required_buf_size)
276 {
277 fprintf(stderr, "recvmsg iov too short: %zu\n",
278 msg->msg_iov[0].iov_len);
279 abort();
280 }
281 if (msgs.empty())
282 {
283 fprintf(stderr, "No pending netlink responses\n");
284 abort();
285 }
286
287 ssize_t ret = 0;
288 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
289 while (!msgs.empty())
290 {
291 const auto& msg = msgs.front();
292 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
293 {
294 break;
295 }
296 ret = NLMSG_ALIGN(ret);
297 memcpy(data + ret, msg.data(), msg.size());
298 ret += msg.size();
299 msgs.pop();
300 }
301 return ret;
302}
303
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700304} // extern "C"