blob: 0699cebb3c0d432e185a3c6e790edd61279b1a54 [file] [log] [blame]
William A. Kennington III77848562022-10-26 17:32:57 -07001#include "mock_syscall.hpp"
2
William A. Kennington III5bb7da92019-04-22 21:30:23 -07003#include "util.hpp"
4
Gunnar Mills57d9c502018-09-14 14:42:34 -05005#include <arpa/inet.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07006#include <dlfcn.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -07007#include <linux/netlink.h>
8#include <linux/rtnetlink.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -07009#include <net/ethernet.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +053010#include <net/if.h>
11#include <netinet/in.h>
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070012#include <sys/ioctl.h>
Gunnar Mills57d9c502018-09-14 14:42:34 -050013#include <sys/socket.h>
14#include <sys/types.h>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070015#include <unistd.h>
Ratan Gupta8ab17922017-05-25 13:07:05 +053016
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070017#include <cstdarg>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070018#include <cstdio>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070019#include <cstring>
20#include <map>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070021#include <queue>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070022#include <stdexcept>
William A. Kennington III12beaad2020-06-13 19:30:41 -070023#include <stdplus/raw.hpp>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070024#include <string>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070025#include <string_view>
26#include <vector>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070027
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070028std::map<int, std::queue<std::string>> mock_rtnetlinks;
29
William A. Kennington III454a0de2022-11-12 01:01:04 -080030using phosphor::network::InterfaceInfo;
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070031
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070032std::map<std::string, InterfaceInfo> mock_if;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070033
William A. Kennington III77848562022-10-26 17:32:57 -070034void phosphor::network::system::mock_clear()
William A. Kennington III862275a2019-04-22 20:37:08 -070035{
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}
39
William A. Kennington III77848562022-10-26 17:32:57 -070040void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070041{
William A. Kennington III77848562022-10-26 17:32:57 -070042 if (info.idx == 0)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070043 {
44 throw std::invalid_argument("Bad interface index");
45 }
William A. Kennington III77848562022-10-26 17:32:57 -070046 for (const auto& [_, iinfo] : mock_if)
47 {
48 if (iinfo.idx == info.idx || iinfo.name == info.name)
49 {
50 throw std::invalid_argument("Interface already exists");
51 }
52 }
53 mock_if.emplace(info.name.value(), info);
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 IIIcb64b992019-04-21 18:45:07 -0700156int ioctl(int fd, unsigned long int request, ...)
157{
158 va_list vl;
159 va_start(vl, request);
160 void* data = va_arg(vl, void*);
161 va_end(vl);
162
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700163 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700164 if (request == SIOCGIFFLAGS)
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700165 {
166 auto it = mock_if.find(req->ifr_name);
167 if (it == mock_if.end())
168 {
169 errno = ENXIO;
170 return -1;
171 }
172 req->ifr_flags = it->second.flags;
173 return 0;
174 }
175 else if (request == SIOCGIFMTU)
176 {
177 auto it = mock_if.find(req->ifr_name);
178 if (it == mock_if.end())
179 {
180 errno = ENXIO;
181 return -1;
182 }
183 if (!it->second.mtu)
184 {
185 errno = EOPNOTSUPP;
186 return -1;
187 }
188 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700189 return 0;
190 }
191
192 static auto real_ioctl =
193 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
194 return real_ioctl(fd, request, data);
195}
196
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700197int socket(int domain, int type, int protocol)
198{
199 static auto real_socket =
200 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
201 int fd = real_socket(domain, type, protocol);
202 if (domain == AF_NETLINK && !(type & SOCK_RAW))
203 {
204 fprintf(stderr, "Netlink sockets must be RAW\n");
205 abort();
206 }
207 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
208 {
209 mock_rtnetlinks[fd] = {};
210 }
211 return fd;
212}
213
214int close(int fd)
215{
216 auto it = mock_rtnetlinks.find(fd);
217 if (it != mock_rtnetlinks.end())
218 {
219 mock_rtnetlinks.erase(it);
220 }
221
222 static auto real_close =
223 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
224 return real_close(fd);
225}
226
227ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
228{
229 auto it = mock_rtnetlinks.find(sockfd);
230 if (it == mock_rtnetlinks.end())
231 {
232 static auto real_sendmsg =
233 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
234 return real_sendmsg(sockfd, msg, flags);
235 }
236 auto& msgs = it->second;
237
238 validateMsgHdr(msg);
239 if (!msgs.empty())
240 {
241 fprintf(stderr, "Unread netlink responses\n");
242 abort();
243 }
244
245 ssize_t ret;
246 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
247 msg->msg_iov[0].iov_len);
248
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700249 ret = sendmsg_link_dump(msgs, iov);
250 if (ret != 0)
251 {
252 return ret;
253 }
254
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700255 ret = sendmsg_ack(msgs, iov);
256 if (ret != 0)
257 {
258 return ret;
259 }
260
261 errno = ENOSYS;
262 return -1;
263}
264
265ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
266{
267 auto it = mock_rtnetlinks.find(sockfd);
268 if (it == mock_rtnetlinks.end())
269 {
270 static auto real_recvmsg =
271 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
272 return real_recvmsg(sockfd, msg, flags);
273 }
274 auto& msgs = it->second;
275
276 validateMsgHdr(msg);
277 constexpr size_t required_buf_size = 8192;
278 if (msg->msg_iov[0].iov_len < required_buf_size)
279 {
280 fprintf(stderr, "recvmsg iov too short: %zu\n",
281 msg->msg_iov[0].iov_len);
282 abort();
283 }
284 if (msgs.empty())
285 {
286 fprintf(stderr, "No pending netlink responses\n");
287 abort();
288 }
289
290 ssize_t ret = 0;
291 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
292 while (!msgs.empty())
293 {
294 const auto& msg = msgs.front();
295 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
296 {
297 break;
298 }
299 ret = NLMSG_ALIGN(ret);
300 memcpy(data + ret, msg.data(), msg.size());
301 ret += msg.size();
302 msgs.pop();
303 }
304 return ret;
305}
306
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700307} // extern "C"