blob: e481afd9f7b32ff1372d565017c933835a2859d1 [file] [log] [blame]
William A. Kennington IIIfd862be2022-10-09 18:40:55 -07001#include "system_queries.hpp"
William A. Kennington III5bb7da92019-04-22 21:30:23 -07002#include "util.hpp"
3
Gunnar Mills57d9c502018-09-14 14:42:34 -05004#include <arpa/inet.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07005#include <dlfcn.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
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070027std::map<int, std::queue<std::string>> mock_rtnetlinks;
28
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070029using phosphor::network::system::InterfaceInfo;
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070030
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070031std::map<std::string, InterfaceInfo> mock_if;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070032std::map<int, std::string> mock_if_indextoname;
33
William A. Kennington III862275a2019-04-22 20:37:08 -070034void mock_clear()
35{
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070036 mock_rtnetlinks.clear();
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070037 mock_if.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070038 mock_if_indextoname.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070039}
40
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070041void mock_addIF(const std::string& name, unsigned idx, unsigned flags,
42 std::optional<ether_addr> mac, std::optional<unsigned> mtu)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070043{
44 if (idx == 0)
45 {
46 throw std::invalid_argument("Bad interface index");
47 }
48
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070049 mock_if.emplace(
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070050 name,
51 InterfaceInfo{
52 .idx = idx, .flags = flags, .name = name, .mac = mac, .mtu = mtu});
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070053 mock_if_indextoname.emplace(idx, name);
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070054}
55
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070056void validateMsgHdr(const struct msghdr* msg)
57{
58 if (msg->msg_namelen != sizeof(sockaddr_nl))
59 {
60 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
61 abort();
62 }
63 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
64 if (from.nl_family != AF_NETLINK)
65 {
66 fprintf(stderr, "recvmsg bad family data\n");
67 abort();
68 }
69 if (msg->msg_iovlen != 1)
70 {
71 fprintf(stderr, "recvmsg unsupported iov configuration\n");
72 abort();
73 }
74}
75
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070076void appendRTAttr(std::string& msgBuf, unsigned short type,
77 std::string_view data)
78{
79 const auto rta_begin = msgBuf.size();
80 msgBuf.append(RTA_SPACE(data.size()), '\0');
81 auto& rta = *reinterpret_cast<rtattr*>(msgBuf.data() + rta_begin);
82 rta.rta_len = RTA_LENGTH(data.size());
83 rta.rta_type = type;
84 std::copy(data.begin(), data.end(),
85 msgBuf.data() + rta_begin + RTA_LENGTH(0));
86}
87
William A. Kennington III5bb7da92019-04-22 21:30:23 -070088ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
89{
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070090 if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
91 hdrin.nlmsg_type != RTM_GETLINK)
William A. Kennington III5bb7da92019-04-22 21:30:23 -070092 {
93 return 0;
94 }
95
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070096 std::string msgBuf;
97 msgBuf.reserve(8192);
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070098 for (const auto& [name, i] : mock_if)
William A. Kennington III5bb7da92019-04-22 21:30:23 -070099 {
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700100 if (msgBuf.size() > 4096)
101 {
102 msgs.emplace(std::move(msgBuf));
103 }
104 const auto nlbegin = msgBuf.size();
105 msgBuf.append(NLMSG_SPACE(sizeof(ifinfomsg)), '\0');
106 {
107 auto& info = *reinterpret_cast<ifinfomsg*>(msgBuf.data() + nlbegin +
108 NLMSG_HDRLEN);
109 info.ifi_index = i.idx;
110 info.ifi_flags = i.flags;
111 }
112 if (i.name)
113 {
114 appendRTAttr(msgBuf, IFLA_IFNAME, {name.data(), name.size() + 1});
115 }
116 if (i.mac)
117 {
118 appendRTAttr(msgBuf, IFLA_ADDRESS,
119 stdplus::raw::asView<char>(*i.mac));
120 }
121 if (i.mtu)
122 {
123 appendRTAttr(msgBuf, IFLA_MTU, stdplus::raw::asView<char>(*i.mtu));
124 }
125 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
126 hdr.nlmsg_len = msgBuf.size() - nlbegin;
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700127 hdr.nlmsg_type = RTM_NEWLINK;
128 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700129 msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700130 }
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700131 const auto nlbegin = msgBuf.size();
132 msgBuf.append(NLMSG_SPACE(0), '\0');
133 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700134 hdr.nlmsg_len = NLMSG_LENGTH(0);
135 hdr.nlmsg_type = NLMSG_DONE;
136 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700137
138 msgs.emplace(std::move(msgBuf));
139 return in.size();
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700140}
141
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700142ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
143{
144 nlmsgerr ack{};
145 nlmsghdr hdr{};
146 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
147 hdr.nlmsg_type = NLMSG_ERROR;
148 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
149 memcpy(out.data(), &hdr, sizeof(hdr));
150 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
151 return in.size();
152}
153
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700154extern "C" {
155
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700156char* if_indextoname(unsigned ifindex, char* ifname)
157{
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700158 auto it = mock_if_indextoname.find(ifindex);
159 if (it == mock_if_indextoname.end())
160 {
William A. Kennington IIId7946a72019-04-19 14:24:09 -0700161 errno = ENXIO;
162 return NULL;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700163 }
164 return std::strcpy(ifname, it->second.c_str());
165}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700166
167int ioctl(int fd, unsigned long int request, ...)
168{
169 va_list vl;
170 va_start(vl, request);
171 void* data = va_arg(vl, void*);
172 va_end(vl);
173
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700174 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700175 if (request == SIOCGIFFLAGS)
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700176 {
177 auto it = mock_if.find(req->ifr_name);
178 if (it == mock_if.end())
179 {
180 errno = ENXIO;
181 return -1;
182 }
183 req->ifr_flags = it->second.flags;
184 return 0;
185 }
186 else if (request == SIOCGIFMTU)
187 {
188 auto it = mock_if.find(req->ifr_name);
189 if (it == mock_if.end())
190 {
191 errno = ENXIO;
192 return -1;
193 }
194 if (!it->second.mtu)
195 {
196 errno = EOPNOTSUPP;
197 return -1;
198 }
199 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700200 return 0;
201 }
202
203 static auto real_ioctl =
204 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
205 return real_ioctl(fd, request, data);
206}
207
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700208int socket(int domain, int type, int protocol)
209{
210 static auto real_socket =
211 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
212 int fd = real_socket(domain, type, protocol);
213 if (domain == AF_NETLINK && !(type & SOCK_RAW))
214 {
215 fprintf(stderr, "Netlink sockets must be RAW\n");
216 abort();
217 }
218 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
219 {
220 mock_rtnetlinks[fd] = {};
221 }
222 return fd;
223}
224
225int close(int fd)
226{
227 auto it = mock_rtnetlinks.find(fd);
228 if (it != mock_rtnetlinks.end())
229 {
230 mock_rtnetlinks.erase(it);
231 }
232
233 static auto real_close =
234 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
235 return real_close(fd);
236}
237
238ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
239{
240 auto it = mock_rtnetlinks.find(sockfd);
241 if (it == mock_rtnetlinks.end())
242 {
243 static auto real_sendmsg =
244 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
245 return real_sendmsg(sockfd, msg, flags);
246 }
247 auto& msgs = it->second;
248
249 validateMsgHdr(msg);
250 if (!msgs.empty())
251 {
252 fprintf(stderr, "Unread netlink responses\n");
253 abort();
254 }
255
256 ssize_t ret;
257 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
258 msg->msg_iov[0].iov_len);
259
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700260 ret = sendmsg_link_dump(msgs, iov);
261 if (ret != 0)
262 {
263 return ret;
264 }
265
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700266 ret = sendmsg_ack(msgs, iov);
267 if (ret != 0)
268 {
269 return ret;
270 }
271
272 errno = ENOSYS;
273 return -1;
274}
275
276ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
277{
278 auto it = mock_rtnetlinks.find(sockfd);
279 if (it == mock_rtnetlinks.end())
280 {
281 static auto real_recvmsg =
282 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
283 return real_recvmsg(sockfd, msg, flags);
284 }
285 auto& msgs = it->second;
286
287 validateMsgHdr(msg);
288 constexpr size_t required_buf_size = 8192;
289 if (msg->msg_iov[0].iov_len < required_buf_size)
290 {
291 fprintf(stderr, "recvmsg iov too short: %zu\n",
292 msg->msg_iov[0].iov_len);
293 abort();
294 }
295 if (msgs.empty())
296 {
297 fprintf(stderr, "No pending netlink responses\n");
298 abort();
299 }
300
301 ssize_t ret = 0;
302 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
303 while (!msgs.empty())
304 {
305 const auto& msg = msgs.front();
306 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
307 {
308 break;
309 }
310 ret = NLMSG_ALIGN(ret);
311 memcpy(data + ret, msg.data(), msg.size());
312 ret += msg.size();
313 msgs.pop();
314 }
315 return ret;
316}
317
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700318} // extern "C"