blob: bf68d474155ea190dadf3830725a3bb78a49457b [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 IIIfd862be2022-10-09 18:40:55 -070030using phosphor::network::system::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 -070033std::map<int, std::string> mock_if_indextoname;
34
William A. Kennington III77848562022-10-26 17:32:57 -070035void phosphor::network::system::mock_clear()
William A. Kennington III862275a2019-04-22 20:37:08 -070036{
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070037 mock_rtnetlinks.clear();
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070038 mock_if.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070039 mock_if_indextoname.clear();
William A. Kennington III862275a2019-04-22 20:37:08 -070040}
41
William A. Kennington III77848562022-10-26 17:32:57 -070042void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070043{
William A. Kennington III77848562022-10-26 17:32:57 -070044 if (info.idx == 0)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070045 {
46 throw std::invalid_argument("Bad interface index");
47 }
William A. Kennington III77848562022-10-26 17:32:57 -070048 for (const auto& [_, iinfo] : mock_if)
49 {
50 if (iinfo.idx == info.idx || iinfo.name == info.name)
51 {
52 throw std::invalid_argument("Interface already exists");
53 }
54 }
55 mock_if.emplace(info.name.value(), info);
56 mock_if_indextoname.emplace(info.idx, info.name.value());
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070057}
58
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070059void validateMsgHdr(const struct msghdr* msg)
60{
61 if (msg->msg_namelen != sizeof(sockaddr_nl))
62 {
63 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
64 abort();
65 }
66 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
67 if (from.nl_family != AF_NETLINK)
68 {
69 fprintf(stderr, "recvmsg bad family data\n");
70 abort();
71 }
72 if (msg->msg_iovlen != 1)
73 {
74 fprintf(stderr, "recvmsg unsupported iov configuration\n");
75 abort();
76 }
77}
78
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070079void appendRTAttr(std::string& msgBuf, unsigned short type,
80 std::string_view data)
81{
82 const auto rta_begin = msgBuf.size();
83 msgBuf.append(RTA_SPACE(data.size()), '\0');
84 auto& rta = *reinterpret_cast<rtattr*>(msgBuf.data() + rta_begin);
85 rta.rta_len = RTA_LENGTH(data.size());
86 rta.rta_type = type;
87 std::copy(data.begin(), data.end(),
88 msgBuf.data() + rta_begin + RTA_LENGTH(0));
89}
90
William A. Kennington III5bb7da92019-04-22 21:30:23 -070091ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
92{
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070093 if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
94 hdrin.nlmsg_type != RTM_GETLINK)
William A. Kennington III5bb7da92019-04-22 21:30:23 -070095 {
96 return 0;
97 }
98
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070099 std::string msgBuf;
100 msgBuf.reserve(8192);
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700101 for (const auto& [name, i] : mock_if)
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700102 {
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700103 if (msgBuf.size() > 4096)
104 {
105 msgs.emplace(std::move(msgBuf));
106 }
107 const auto nlbegin = msgBuf.size();
108 msgBuf.append(NLMSG_SPACE(sizeof(ifinfomsg)), '\0');
109 {
110 auto& info = *reinterpret_cast<ifinfomsg*>(msgBuf.data() + nlbegin +
111 NLMSG_HDRLEN);
112 info.ifi_index = i.idx;
113 info.ifi_flags = i.flags;
114 }
115 if (i.name)
116 {
117 appendRTAttr(msgBuf, IFLA_IFNAME, {name.data(), name.size() + 1});
118 }
119 if (i.mac)
120 {
121 appendRTAttr(msgBuf, IFLA_ADDRESS,
122 stdplus::raw::asView<char>(*i.mac));
123 }
124 if (i.mtu)
125 {
126 appendRTAttr(msgBuf, IFLA_MTU, stdplus::raw::asView<char>(*i.mtu));
127 }
128 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
129 hdr.nlmsg_len = msgBuf.size() - nlbegin;
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700130 hdr.nlmsg_type = RTM_NEWLINK;
131 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700132 msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700133 }
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700134 const auto nlbegin = msgBuf.size();
135 msgBuf.append(NLMSG_SPACE(0), '\0');
136 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700137 hdr.nlmsg_len = NLMSG_LENGTH(0);
138 hdr.nlmsg_type = NLMSG_DONE;
139 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700140
141 msgs.emplace(std::move(msgBuf));
142 return in.size();
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700143}
144
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700145ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
146{
147 nlmsgerr ack{};
148 nlmsghdr hdr{};
149 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
150 hdr.nlmsg_type = NLMSG_ERROR;
151 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
152 memcpy(out.data(), &hdr, sizeof(hdr));
153 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
154 return in.size();
155}
156
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700157extern "C" {
158
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700159char* if_indextoname(unsigned ifindex, char* ifname)
160{
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700161 auto it = mock_if_indextoname.find(ifindex);
162 if (it == mock_if_indextoname.end())
163 {
William A. Kennington IIId7946a72019-04-19 14:24:09 -0700164 errno = ENXIO;
165 return NULL;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -0700166 }
167 return std::strcpy(ifname, it->second.c_str());
168}
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700169
170int ioctl(int fd, unsigned long int request, ...)
171{
172 va_list vl;
173 va_start(vl, request);
174 void* data = va_arg(vl, void*);
175 va_end(vl);
176
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700177 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700178 if (request == SIOCGIFFLAGS)
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700179 {
180 auto it = mock_if.find(req->ifr_name);
181 if (it == mock_if.end())
182 {
183 errno = ENXIO;
184 return -1;
185 }
186 req->ifr_flags = it->second.flags;
187 return 0;
188 }
189 else if (request == SIOCGIFMTU)
190 {
191 auto it = mock_if.find(req->ifr_name);
192 if (it == mock_if.end())
193 {
194 errno = ENXIO;
195 return -1;
196 }
197 if (!it->second.mtu)
198 {
199 errno = EOPNOTSUPP;
200 return -1;
201 }
202 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700203 return 0;
204 }
205
206 static auto real_ioctl =
207 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
208 return real_ioctl(fd, request, data);
209}
210
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700211int socket(int domain, int type, int protocol)
212{
213 static auto real_socket =
214 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
215 int fd = real_socket(domain, type, protocol);
216 if (domain == AF_NETLINK && !(type & SOCK_RAW))
217 {
218 fprintf(stderr, "Netlink sockets must be RAW\n");
219 abort();
220 }
221 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
222 {
223 mock_rtnetlinks[fd] = {};
224 }
225 return fd;
226}
227
228int close(int fd)
229{
230 auto it = mock_rtnetlinks.find(fd);
231 if (it != mock_rtnetlinks.end())
232 {
233 mock_rtnetlinks.erase(it);
234 }
235
236 static auto real_close =
237 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
238 return real_close(fd);
239}
240
241ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
242{
243 auto it = mock_rtnetlinks.find(sockfd);
244 if (it == mock_rtnetlinks.end())
245 {
246 static auto real_sendmsg =
247 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
248 return real_sendmsg(sockfd, msg, flags);
249 }
250 auto& msgs = it->second;
251
252 validateMsgHdr(msg);
253 if (!msgs.empty())
254 {
255 fprintf(stderr, "Unread netlink responses\n");
256 abort();
257 }
258
259 ssize_t ret;
260 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
261 msg->msg_iov[0].iov_len);
262
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700263 ret = sendmsg_link_dump(msgs, iov);
264 if (ret != 0)
265 {
266 return ret;
267 }
268
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700269 ret = sendmsg_ack(msgs, iov);
270 if (ret != 0)
271 {
272 return ret;
273 }
274
275 errno = ENOSYS;
276 return -1;
277}
278
279ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
280{
281 auto it = mock_rtnetlinks.find(sockfd);
282 if (it == mock_rtnetlinks.end())
283 {
284 static auto real_recvmsg =
285 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
286 return real_recvmsg(sockfd, msg, flags);
287 }
288 auto& msgs = it->second;
289
290 validateMsgHdr(msg);
291 constexpr size_t required_buf_size = 8192;
292 if (msg->msg_iov[0].iov_len < required_buf_size)
293 {
294 fprintf(stderr, "recvmsg iov too short: %zu\n",
295 msg->msg_iov[0].iov_len);
296 abort();
297 }
298 if (msgs.empty())
299 {
300 fprintf(stderr, "No pending netlink responses\n");
301 abort();
302 }
303
304 ssize_t ret = 0;
305 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
306 while (!msgs.empty())
307 {
308 const auto& msg = msgs.front();
309 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
310 {
311 break;
312 }
313 ret = NLMSG_ALIGN(ret);
314 memcpy(data + ret, msg.data(), msg.size());
315 ret += msg.size();
316 msgs.pop();
317 }
318 return ret;
319}
320
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700321} // extern "C"