blob: fa2ae2d357603221eb9453859fdfbd63203d8ced [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
Patrick Williams89d734b2023-05-10 07:50:25 -050017#include <stdplus/raw.hpp>
18
William A. Kennington IIIcb64b992019-04-21 18:45:07 -070019#include <cstdarg>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070020#include <cstdio>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070021#include <cstring>
22#include <map>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070023#include <queue>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070024#include <stdexcept>
25#include <string>
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070026#include <string_view>
27#include <vector>
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070028
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070029std::map<int, std::queue<std::string>> mock_rtnetlinks;
30
William A. Kennington III454a0de2022-11-12 01:01:04 -080031using phosphor::network::InterfaceInfo;
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070032
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070033std::map<std::string, InterfaceInfo> mock_if;
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070034
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}
40
William A. Kennington III77848562022-10-26 17:32:57 -070041void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070042{
William A. Kennington III77848562022-10-26 17:32:57 -070043 if (info.idx == 0)
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070044 {
45 throw std::invalid_argument("Bad interface index");
46 }
William A. Kennington III77848562022-10-26 17:32:57 -070047 for (const auto& [_, iinfo] : mock_if)
48 {
49 if (iinfo.idx == info.idx || iinfo.name == info.name)
50 {
51 throw std::invalid_argument("Interface already exists");
52 }
53 }
54 mock_if.emplace(info.name.value(), info);
William A. Kennington IIIebb1ad02019-04-21 18:02:49 -070055}
56
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -070057void validateMsgHdr(const struct msghdr* msg)
58{
59 if (msg->msg_namelen != sizeof(sockaddr_nl))
60 {
61 fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
62 abort();
63 }
64 const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
65 if (from.nl_family != AF_NETLINK)
66 {
67 fprintf(stderr, "recvmsg bad family data\n");
68 abort();
69 }
70 if (msg->msg_iovlen != 1)
71 {
72 fprintf(stderr, "recvmsg unsupported iov configuration\n");
73 abort();
74 }
75}
76
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070077void appendRTAttr(std::string& msgBuf, unsigned short type,
78 std::string_view data)
79{
80 const auto rta_begin = msgBuf.size();
81 msgBuf.append(RTA_SPACE(data.size()), '\0');
82 auto& rta = *reinterpret_cast<rtattr*>(msgBuf.data() + rta_begin);
83 rta.rta_len = RTA_LENGTH(data.size());
84 rta.rta_type = type;
85 std::copy(data.begin(), data.end(),
86 msgBuf.data() + rta_begin + RTA_LENGTH(0));
87}
88
William A. Kennington III5bb7da92019-04-22 21:30:23 -070089ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
90{
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070091 if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
92 hdrin.nlmsg_type != RTM_GETLINK)
William A. Kennington III5bb7da92019-04-22 21:30:23 -070093 {
94 return 0;
95 }
96
William A. Kennington IIIfd862be2022-10-09 18:40:55 -070097 std::string msgBuf;
98 msgBuf.reserve(8192);
William A. Kennington III9ecb90e2022-10-14 03:12:43 -070099 for (const auto& [name, i] : mock_if)
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700100 {
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700101 if (msgBuf.size() > 4096)
102 {
103 msgs.emplace(std::move(msgBuf));
104 }
105 const auto nlbegin = msgBuf.size();
106 msgBuf.append(NLMSG_SPACE(sizeof(ifinfomsg)), '\0');
107 {
108 auto& info = *reinterpret_cast<ifinfomsg*>(msgBuf.data() + nlbegin +
109 NLMSG_HDRLEN);
110 info.ifi_index = i.idx;
111 info.ifi_flags = i.flags;
112 }
113 if (i.name)
114 {
115 appendRTAttr(msgBuf, IFLA_IFNAME, {name.data(), name.size() + 1});
116 }
117 if (i.mac)
118 {
119 appendRTAttr(msgBuf, IFLA_ADDRESS,
120 stdplus::raw::asView<char>(*i.mac));
121 }
122 if (i.mtu)
123 {
124 appendRTAttr(msgBuf, IFLA_MTU, stdplus::raw::asView<char>(*i.mtu));
125 }
126 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
127 hdr.nlmsg_len = msgBuf.size() - nlbegin;
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700128 hdr.nlmsg_type = RTM_NEWLINK;
129 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700130 msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700131 }
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700132 const auto nlbegin = msgBuf.size();
133 msgBuf.append(NLMSG_SPACE(0), '\0');
134 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700135 hdr.nlmsg_len = NLMSG_LENGTH(0);
136 hdr.nlmsg_type = NLMSG_DONE;
137 hdr.nlmsg_flags = NLM_F_MULTI;
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700138
139 msgs.emplace(std::move(msgBuf));
140 return in.size();
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700141}
142
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700143ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
144{
145 nlmsgerr ack{};
146 nlmsghdr hdr{};
147 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
148 hdr.nlmsg_type = NLMSG_ERROR;
149 auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
150 memcpy(out.data(), &hdr, sizeof(hdr));
151 memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
152 return in.size();
153}
154
Patrick Williams89d734b2023-05-10 07:50:25 -0500155extern "C"
156{
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700157int ioctl(int fd, unsigned long int request, ...)
158{
159 va_list vl;
160 va_start(vl, request);
161 void* data = va_arg(vl, void*);
162 va_end(vl);
163
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700164 auto req = reinterpret_cast<ifreq*>(data);
William A. Kennington IIIfd862be2022-10-09 18:40:55 -0700165 if (request == SIOCGIFFLAGS)
William A. Kennington III9ecb90e2022-10-14 03:12:43 -0700166 {
167 auto it = mock_if.find(req->ifr_name);
168 if (it == mock_if.end())
169 {
170 errno = ENXIO;
171 return -1;
172 }
173 req->ifr_flags = it->second.flags;
174 return 0;
175 }
176 else if (request == SIOCGIFMTU)
177 {
178 auto it = mock_if.find(req->ifr_name);
179 if (it == mock_if.end())
180 {
181 errno = ENXIO;
182 return -1;
183 }
184 if (!it->second.mtu)
185 {
186 errno = EOPNOTSUPP;
187 return -1;
188 }
189 req->ifr_mtu = *it->second.mtu;
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700190 return 0;
191 }
192
193 static auto real_ioctl =
194 reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
195 return real_ioctl(fd, request, data);
196}
197
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700198int socket(int domain, int type, int protocol)
199{
200 static auto real_socket =
201 reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
202 int fd = real_socket(domain, type, protocol);
203 if (domain == AF_NETLINK && !(type & SOCK_RAW))
204 {
205 fprintf(stderr, "Netlink sockets must be RAW\n");
206 abort();
207 }
208 if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
209 {
210 mock_rtnetlinks[fd] = {};
211 }
212 return fd;
213}
214
215int close(int fd)
216{
217 auto it = mock_rtnetlinks.find(fd);
218 if (it != mock_rtnetlinks.end())
219 {
220 mock_rtnetlinks.erase(it);
221 }
222
223 static auto real_close =
224 reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
225 return real_close(fd);
226}
227
228ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
229{
230 auto it = mock_rtnetlinks.find(sockfd);
231 if (it == mock_rtnetlinks.end())
232 {
233 static auto real_sendmsg =
234 reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
235 return real_sendmsg(sockfd, msg, flags);
236 }
237 auto& msgs = it->second;
238
239 validateMsgHdr(msg);
240 if (!msgs.empty())
241 {
242 fprintf(stderr, "Unread netlink responses\n");
243 abort();
244 }
245
246 ssize_t ret;
247 std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
248 msg->msg_iov[0].iov_len);
249
William A. Kennington III5bb7da92019-04-22 21:30:23 -0700250 ret = sendmsg_link_dump(msgs, iov);
251 if (ret != 0)
252 {
253 return ret;
254 }
255
William A. Kennington IIIbc2059f2019-04-22 13:31:54 -0700256 ret = sendmsg_ack(msgs, iov);
257 if (ret != 0)
258 {
259 return ret;
260 }
261
262 errno = ENOSYS;
263 return -1;
264}
265
266ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
267{
268 auto it = mock_rtnetlinks.find(sockfd);
269 if (it == mock_rtnetlinks.end())
270 {
271 static auto real_recvmsg =
272 reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
273 return real_recvmsg(sockfd, msg, flags);
274 }
275 auto& msgs = it->second;
276
277 validateMsgHdr(msg);
278 constexpr size_t required_buf_size = 8192;
279 if (msg->msg_iov[0].iov_len < required_buf_size)
280 {
281 fprintf(stderr, "recvmsg iov too short: %zu\n",
282 msg->msg_iov[0].iov_len);
283 abort();
284 }
285 if (msgs.empty())
286 {
287 fprintf(stderr, "No pending netlink responses\n");
288 abort();
289 }
290
291 ssize_t ret = 0;
292 auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
293 while (!msgs.empty())
294 {
295 const auto& msg = msgs.front();
296 if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
297 {
298 break;
299 }
300 ret = NLMSG_ALIGN(ret);
301 memcpy(data + ret, msg.data(), msg.size());
302 ret += msg.size();
303 msgs.pop();
304 }
305 return ret;
306}
307
William A. Kennington IIIcb64b992019-04-21 18:45:07 -0700308} // extern "C"