xref: /openbmc/phosphor-networkd/test/mock_syscall.cpp (revision ad205028c6e4e9deaf4f9ea886d38081b6f26f37)
17784856dSWilliam A. Kennington III #include "mock_syscall.hpp"
27784856dSWilliam A. Kennington III 
35bb7da95SWilliam A. Kennington III #include "util.hpp"
45bb7da95SWilliam A. Kennington III 
557d9c506SGunnar Mills #include <arpa/inet.h>
6cb64b99fSWilliam A. Kennington III #include <dlfcn.h>
7bc2059f1SWilliam A. Kennington III #include <linux/netlink.h>
8bc2059f1SWilliam A. Kennington III #include <linux/rtnetlink.h>
9cb64b99fSWilliam A. Kennington III #include <net/ethernet.h>
108ab17923SRatan Gupta #include <net/if.h>
118ab17923SRatan Gupta #include <netinet/in.h>
12cb64b99fSWilliam A. Kennington III #include <sys/ioctl.h>
1357d9c506SGunnar Mills #include <sys/socket.h>
1457d9c506SGunnar Mills #include <sys/types.h>
15bc2059f1SWilliam A. Kennington III #include <unistd.h>
168ab17923SRatan Gupta 
1789d734b9SPatrick Williams #include <stdplus/raw.hpp>
1889d734b9SPatrick Williams 
19cb64b99fSWilliam A. Kennington III #include <cstdarg>
20bc2059f1SWilliam A. Kennington III #include <cstdio>
21ebb1ad0cSWilliam A. Kennington III #include <cstring>
22ebb1ad0cSWilliam A. Kennington III #include <map>
23bc2059f1SWilliam A. Kennington III #include <queue>
24ebb1ad0cSWilliam A. Kennington III #include <stdexcept>
25ebb1ad0cSWilliam A. Kennington III #include <string>
26bc2059f1SWilliam A. Kennington III #include <string_view>
27bc2059f1SWilliam A. Kennington III #include <vector>
28ebb1ad0cSWilliam A. Kennington III 
29bc2059f1SWilliam A. Kennington III std::map<int, std::queue<std::string>> mock_rtnetlinks;
30bc2059f1SWilliam A. Kennington III 
31454a0deeSWilliam A. Kennington III using phosphor::network::InterfaceInfo;
329ecb90ebSWilliam A. Kennington III 
33fd862be8SWilliam A. Kennington III std::map<std::string, InterfaceInfo> mock_if;
34ebb1ad0cSWilliam A. Kennington III 
mock_clear()357784856dSWilliam A. Kennington III void phosphor::network::system::mock_clear()
36862275aaSWilliam A. Kennington III {
37bc2059f1SWilliam A. Kennington III     mock_rtnetlinks.clear();
389ecb90ebSWilliam A. Kennington III     mock_if.clear();
39862275aaSWilliam A. Kennington III }
40862275aaSWilliam A. Kennington III 
mock_addIF(const InterfaceInfo & info)417784856dSWilliam A. Kennington III void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
42ebb1ad0cSWilliam A. Kennington III {
437784856dSWilliam A. Kennington III     if (info.idx == 0)
44ebb1ad0cSWilliam A. Kennington III     {
45ebb1ad0cSWilliam A. Kennington III         throw std::invalid_argument("Bad interface index");
46ebb1ad0cSWilliam A. Kennington III     }
477784856dSWilliam A. Kennington III     for (const auto& [_, iinfo] : mock_if)
487784856dSWilliam A. Kennington III     {
497784856dSWilliam A. Kennington III         if (iinfo.idx == info.idx || iinfo.name == info.name)
507784856dSWilliam A. Kennington III         {
517784856dSWilliam A. Kennington III             throw std::invalid_argument("Interface already exists");
527784856dSWilliam A. Kennington III         }
537784856dSWilliam A. Kennington III     }
547784856dSWilliam A. Kennington III     mock_if.emplace(info.name.value(), info);
55ebb1ad0cSWilliam A. Kennington III }
56ebb1ad0cSWilliam A. Kennington III 
validateMsgHdr(const struct msghdr * msg)57bc2059f1SWilliam A. Kennington III void validateMsgHdr(const struct msghdr* msg)
58bc2059f1SWilliam A. Kennington III {
59bc2059f1SWilliam A. Kennington III     if (msg->msg_namelen != sizeof(sockaddr_nl))
60bc2059f1SWilliam A. Kennington III     {
61bc2059f1SWilliam A. Kennington III         fprintf(stderr, "bad namelen: %u\n", msg->msg_namelen);
62bc2059f1SWilliam A. Kennington III         abort();
63bc2059f1SWilliam A. Kennington III     }
64bc2059f1SWilliam A. Kennington III     const auto& from = *reinterpret_cast<sockaddr_nl*>(msg->msg_name);
65bc2059f1SWilliam A. Kennington III     if (from.nl_family != AF_NETLINK)
66bc2059f1SWilliam A. Kennington III     {
67bc2059f1SWilliam A. Kennington III         fprintf(stderr, "recvmsg bad family data\n");
68bc2059f1SWilliam A. Kennington III         abort();
69bc2059f1SWilliam A. Kennington III     }
70bc2059f1SWilliam A. Kennington III     if (msg->msg_iovlen != 1)
71bc2059f1SWilliam A. Kennington III     {
72bc2059f1SWilliam A. Kennington III         fprintf(stderr, "recvmsg unsupported iov configuration\n");
73bc2059f1SWilliam A. Kennington III         abort();
74bc2059f1SWilliam A. Kennington III     }
75bc2059f1SWilliam A. Kennington III }
76bc2059f1SWilliam A. Kennington III 
appendRTAttr(std::string & msgBuf,unsigned short type,std::string_view data)77fd862be8SWilliam A. Kennington III void appendRTAttr(std::string& msgBuf, unsigned short type,
78fd862be8SWilliam A. Kennington III                   std::string_view data)
79fd862be8SWilliam A. Kennington III {
80fd862be8SWilliam A. Kennington III     const auto rta_begin = msgBuf.size();
81fd862be8SWilliam A. Kennington III     msgBuf.append(RTA_SPACE(data.size()), '\0');
82fd862be8SWilliam A. Kennington III     auto& rta = *reinterpret_cast<rtattr*>(msgBuf.data() + rta_begin);
83fd862be8SWilliam A. Kennington III     rta.rta_len = RTA_LENGTH(data.size());
84fd862be8SWilliam A. Kennington III     rta.rta_type = type;
85fd862be8SWilliam A. Kennington III     std::copy(data.begin(), data.end(),
86fd862be8SWilliam A. Kennington III               msgBuf.data() + rta_begin + RTA_LENGTH(0));
87fd862be8SWilliam A. Kennington III }
88fd862be8SWilliam A. Kennington III 
sendmsg_link_dump(std::queue<std::string> & msgs,std::string_view in)895bb7da95SWilliam A. Kennington III ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
905bb7da95SWilliam A. Kennington III {
91fd862be8SWilliam A. Kennington III     if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
92fd862be8SWilliam A. Kennington III         hdrin.nlmsg_type != RTM_GETLINK)
935bb7da95SWilliam A. Kennington III     {
945bb7da95SWilliam A. Kennington III         return 0;
955bb7da95SWilliam A. Kennington III     }
965bb7da95SWilliam A. Kennington III 
97fd862be8SWilliam A. Kennington III     std::string msgBuf;
98fd862be8SWilliam A. Kennington III     msgBuf.reserve(8192);
999ecb90ebSWilliam A. Kennington III     for (const auto& [name, i] : mock_if)
1005bb7da95SWilliam A. Kennington III     {
101fd862be8SWilliam A. Kennington III         if (msgBuf.size() > 4096)
102fd862be8SWilliam A. Kennington III         {
103fd862be8SWilliam A. Kennington III             msgs.emplace(std::move(msgBuf));
104fd862be8SWilliam A. Kennington III         }
105fd862be8SWilliam A. Kennington III         const auto nlbegin = msgBuf.size();
106fd862be8SWilliam A. Kennington III         msgBuf.append(NLMSG_SPACE(sizeof(ifinfomsg)), '\0');
107fd862be8SWilliam A. Kennington III         {
108*ad205028SPatrick Williams             auto& info = *reinterpret_cast<ifinfomsg*>(
109*ad205028SPatrick Williams                 msgBuf.data() + nlbegin + NLMSG_HDRLEN);
1109ecb90ebSWilliam A. Kennington III             info.ifi_index = i.idx;
1119ecb90ebSWilliam A. Kennington III             info.ifi_flags = i.flags;
112fd862be8SWilliam A. Kennington III         }
113fd862be8SWilliam A. Kennington III         if (i.name)
114fd862be8SWilliam A. Kennington III         {
115fd862be8SWilliam A. Kennington III             appendRTAttr(msgBuf, IFLA_IFNAME, {name.data(), name.size() + 1});
116fd862be8SWilliam A. Kennington III         }
117fd862be8SWilliam A. Kennington III         if (i.mac)
118fd862be8SWilliam A. Kennington III         {
119fd862be8SWilliam A. Kennington III             appendRTAttr(msgBuf, IFLA_ADDRESS,
120fd862be8SWilliam A. Kennington III                          stdplus::raw::asView<char>(*i.mac));
121fd862be8SWilliam A. Kennington III         }
122fd862be8SWilliam A. Kennington III         if (i.mtu)
123fd862be8SWilliam A. Kennington III         {
124fd862be8SWilliam A. Kennington III             appendRTAttr(msgBuf, IFLA_MTU, stdplus::raw::asView<char>(*i.mtu));
125fd862be8SWilliam A. Kennington III         }
126fd862be8SWilliam A. Kennington III         auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
127fd862be8SWilliam A. Kennington III         hdr.nlmsg_len = msgBuf.size() - nlbegin;
1285bb7da95SWilliam A. Kennington III         hdr.nlmsg_type = RTM_NEWLINK;
1295bb7da95SWilliam A. Kennington III         hdr.nlmsg_flags = NLM_F_MULTI;
130fd862be8SWilliam A. Kennington III         msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
1315bb7da95SWilliam A. Kennington III     }
132fd862be8SWilliam A. Kennington III     const auto nlbegin = msgBuf.size();
133fd862be8SWilliam A. Kennington III     msgBuf.append(NLMSG_SPACE(0), '\0');
134fd862be8SWilliam A. Kennington III     auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
1355bb7da95SWilliam A. Kennington III     hdr.nlmsg_len = NLMSG_LENGTH(0);
1365bb7da95SWilliam A. Kennington III     hdr.nlmsg_type = NLMSG_DONE;
1375bb7da95SWilliam A. Kennington III     hdr.nlmsg_flags = NLM_F_MULTI;
138fd862be8SWilliam A. Kennington III 
139fd862be8SWilliam A. Kennington III     msgs.emplace(std::move(msgBuf));
140fd862be8SWilliam A. Kennington III     return in.size();
1415bb7da95SWilliam A. Kennington III }
1425bb7da95SWilliam A. Kennington III 
sendmsg_ack(std::queue<std::string> & msgs,std::string_view in)143bc2059f1SWilliam A. Kennington III ssize_t sendmsg_ack(std::queue<std::string>& msgs, std::string_view in)
144bc2059f1SWilliam A. Kennington III {
145bc2059f1SWilliam A. Kennington III     nlmsgerr ack{};
146bc2059f1SWilliam A. Kennington III     nlmsghdr hdr{};
147bc2059f1SWilliam A. Kennington III     hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack));
148bc2059f1SWilliam A. Kennington III     hdr.nlmsg_type = NLMSG_ERROR;
149bc2059f1SWilliam A. Kennington III     auto& out = msgs.emplace(hdr.nlmsg_len, '\0');
150bc2059f1SWilliam A. Kennington III     memcpy(out.data(), &hdr, sizeof(hdr));
151bc2059f1SWilliam A. Kennington III     memcpy(NLMSG_DATA(out.data()), &ack, sizeof(ack));
152bc2059f1SWilliam A. Kennington III     return in.size();
153bc2059f1SWilliam A. Kennington III }
154bc2059f1SWilliam A. Kennington III 
15589d734b9SPatrick Williams extern "C"
15689d734b9SPatrick Williams {
ioctl(int fd,unsigned long int request,...)157cb64b99fSWilliam A. Kennington III int ioctl(int fd, unsigned long int request, ...)
158cb64b99fSWilliam A. Kennington III {
159cb64b99fSWilliam A. Kennington III     va_list vl;
160cb64b99fSWilliam A. Kennington III     va_start(vl, request);
161cb64b99fSWilliam A. Kennington III     void* data = va_arg(vl, void*);
162cb64b99fSWilliam A. Kennington III     va_end(vl);
163cb64b99fSWilliam A. Kennington III 
1649ecb90ebSWilliam A. Kennington III     auto req = reinterpret_cast<ifreq*>(data);
165fd862be8SWilliam A. Kennington III     if (request == SIOCGIFFLAGS)
1669ecb90ebSWilliam A. Kennington III     {
1679ecb90ebSWilliam A. Kennington III         auto it = mock_if.find(req->ifr_name);
1689ecb90ebSWilliam A. Kennington III         if (it == mock_if.end())
1699ecb90ebSWilliam A. Kennington III         {
1709ecb90ebSWilliam A. Kennington III             errno = ENXIO;
1719ecb90ebSWilliam A. Kennington III             return -1;
1729ecb90ebSWilliam A. Kennington III         }
1739ecb90ebSWilliam A. Kennington III         req->ifr_flags = it->second.flags;
1749ecb90ebSWilliam A. Kennington III         return 0;
1759ecb90ebSWilliam A. Kennington III     }
1769ecb90ebSWilliam A. Kennington III     else if (request == SIOCGIFMTU)
1779ecb90ebSWilliam A. Kennington III     {
1789ecb90ebSWilliam A. Kennington III         auto it = mock_if.find(req->ifr_name);
1799ecb90ebSWilliam A. Kennington III         if (it == mock_if.end())
1809ecb90ebSWilliam A. Kennington III         {
1819ecb90ebSWilliam A. Kennington III             errno = ENXIO;
1829ecb90ebSWilliam A. Kennington III             return -1;
1839ecb90ebSWilliam A. Kennington III         }
1849ecb90ebSWilliam A. Kennington III         if (!it->second.mtu)
1859ecb90ebSWilliam A. Kennington III         {
1869ecb90ebSWilliam A. Kennington III             errno = EOPNOTSUPP;
1879ecb90ebSWilliam A. Kennington III             return -1;
1889ecb90ebSWilliam A. Kennington III         }
1899ecb90ebSWilliam A. Kennington III         req->ifr_mtu = *it->second.mtu;
190cb64b99fSWilliam A. Kennington III         return 0;
191cb64b99fSWilliam A. Kennington III     }
192cb64b99fSWilliam A. Kennington III 
193cb64b99fSWilliam A. Kennington III     static auto real_ioctl =
194cb64b99fSWilliam A. Kennington III         reinterpret_cast<decltype(&ioctl)>(dlsym(RTLD_NEXT, "ioctl"));
195cb64b99fSWilliam A. Kennington III     return real_ioctl(fd, request, data);
196cb64b99fSWilliam A. Kennington III }
197cb64b99fSWilliam A. Kennington III 
socket(int domain,int type,int protocol)198bc2059f1SWilliam A. Kennington III int socket(int domain, int type, int protocol)
199bc2059f1SWilliam A. Kennington III {
200bc2059f1SWilliam A. Kennington III     static auto real_socket =
201bc2059f1SWilliam A. Kennington III         reinterpret_cast<decltype(&socket)>(dlsym(RTLD_NEXT, "socket"));
202bc2059f1SWilliam A. Kennington III     int fd = real_socket(domain, type, protocol);
203bc2059f1SWilliam A. Kennington III     if (domain == AF_NETLINK && !(type & SOCK_RAW))
204bc2059f1SWilliam A. Kennington III     {
205bc2059f1SWilliam A. Kennington III         fprintf(stderr, "Netlink sockets must be RAW\n");
206bc2059f1SWilliam A. Kennington III         abort();
207bc2059f1SWilliam A. Kennington III     }
208bc2059f1SWilliam A. Kennington III     if (domain == AF_NETLINK && protocol == NETLINK_ROUTE)
209bc2059f1SWilliam A. Kennington III     {
210bc2059f1SWilliam A. Kennington III         mock_rtnetlinks[fd] = {};
211bc2059f1SWilliam A. Kennington III     }
212bc2059f1SWilliam A. Kennington III     return fd;
213bc2059f1SWilliam A. Kennington III }
214bc2059f1SWilliam A. Kennington III 
close(int fd)215bc2059f1SWilliam A. Kennington III int close(int fd)
216bc2059f1SWilliam A. Kennington III {
217bc2059f1SWilliam A. Kennington III     auto it = mock_rtnetlinks.find(fd);
218bc2059f1SWilliam A. Kennington III     if (it != mock_rtnetlinks.end())
219bc2059f1SWilliam A. Kennington III     {
220bc2059f1SWilliam A. Kennington III         mock_rtnetlinks.erase(it);
221bc2059f1SWilliam A. Kennington III     }
222bc2059f1SWilliam A. Kennington III 
223bc2059f1SWilliam A. Kennington III     static auto real_close =
224bc2059f1SWilliam A. Kennington III         reinterpret_cast<decltype(&close)>(dlsym(RTLD_NEXT, "close"));
225bc2059f1SWilliam A. Kennington III     return real_close(fd);
226bc2059f1SWilliam A. Kennington III }
227bc2059f1SWilliam A. Kennington III 
sendmsg(int sockfd,const struct msghdr * msg,int flags)228bc2059f1SWilliam A. Kennington III ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags)
229bc2059f1SWilliam A. Kennington III {
230bc2059f1SWilliam A. Kennington III     auto it = mock_rtnetlinks.find(sockfd);
231bc2059f1SWilliam A. Kennington III     if (it == mock_rtnetlinks.end())
232bc2059f1SWilliam A. Kennington III     {
233bc2059f1SWilliam A. Kennington III         static auto real_sendmsg =
234bc2059f1SWilliam A. Kennington III             reinterpret_cast<decltype(&sendmsg)>(dlsym(RTLD_NEXT, "sendmsg"));
235bc2059f1SWilliam A. Kennington III         return real_sendmsg(sockfd, msg, flags);
236bc2059f1SWilliam A. Kennington III     }
237bc2059f1SWilliam A. Kennington III     auto& msgs = it->second;
238bc2059f1SWilliam A. Kennington III 
239bc2059f1SWilliam A. Kennington III     validateMsgHdr(msg);
240bc2059f1SWilliam A. Kennington III     if (!msgs.empty())
241bc2059f1SWilliam A. Kennington III     {
242bc2059f1SWilliam A. Kennington III         fprintf(stderr, "Unread netlink responses\n");
243bc2059f1SWilliam A. Kennington III         abort();
244bc2059f1SWilliam A. Kennington III     }
245bc2059f1SWilliam A. Kennington III 
246bc2059f1SWilliam A. Kennington III     ssize_t ret;
247bc2059f1SWilliam A. Kennington III     std::string_view iov(reinterpret_cast<char*>(msg->msg_iov[0].iov_base),
248bc2059f1SWilliam A. Kennington III                          msg->msg_iov[0].iov_len);
249bc2059f1SWilliam A. Kennington III 
2505bb7da95SWilliam A. Kennington III     ret = sendmsg_link_dump(msgs, iov);
2515bb7da95SWilliam A. Kennington III     if (ret != 0)
2525bb7da95SWilliam A. Kennington III     {
2535bb7da95SWilliam A. Kennington III         return ret;
2545bb7da95SWilliam A. Kennington III     }
2555bb7da95SWilliam A. Kennington III 
256bc2059f1SWilliam A. Kennington III     ret = sendmsg_ack(msgs, iov);
257bc2059f1SWilliam A. Kennington III     if (ret != 0)
258bc2059f1SWilliam A. Kennington III     {
259bc2059f1SWilliam A. Kennington III         return ret;
260bc2059f1SWilliam A. Kennington III     }
261bc2059f1SWilliam A. Kennington III 
262bc2059f1SWilliam A. Kennington III     errno = ENOSYS;
263bc2059f1SWilliam A. Kennington III     return -1;
264bc2059f1SWilliam A. Kennington III }
265bc2059f1SWilliam A. Kennington III 
recvmsg(int sockfd,struct msghdr * msg,int flags)266bc2059f1SWilliam A. Kennington III ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags)
267bc2059f1SWilliam A. Kennington III {
268bc2059f1SWilliam A. Kennington III     auto it = mock_rtnetlinks.find(sockfd);
269bc2059f1SWilliam A. Kennington III     if (it == mock_rtnetlinks.end())
270bc2059f1SWilliam A. Kennington III     {
271bc2059f1SWilliam A. Kennington III         static auto real_recvmsg =
272bc2059f1SWilliam A. Kennington III             reinterpret_cast<decltype(&recvmsg)>(dlsym(RTLD_NEXT, "recvmsg"));
273bc2059f1SWilliam A. Kennington III         return real_recvmsg(sockfd, msg, flags);
274bc2059f1SWilliam A. Kennington III     }
275bc2059f1SWilliam A. Kennington III     auto& msgs = it->second;
276bc2059f1SWilliam A. Kennington III 
277bc2059f1SWilliam A. Kennington III     validateMsgHdr(msg);
278bc2059f1SWilliam A. Kennington III     constexpr size_t required_buf_size = 8192;
279bc2059f1SWilliam A. Kennington III     if (msg->msg_iov[0].iov_len < required_buf_size)
280bc2059f1SWilliam A. Kennington III     {
281bc2059f1SWilliam A. Kennington III         fprintf(stderr, "recvmsg iov too short: %zu\n",
282bc2059f1SWilliam A. Kennington III                 msg->msg_iov[0].iov_len);
283bc2059f1SWilliam A. Kennington III         abort();
284bc2059f1SWilliam A. Kennington III     }
285bc2059f1SWilliam A. Kennington III     if (msgs.empty())
286bc2059f1SWilliam A. Kennington III     {
287bc2059f1SWilliam A. Kennington III         fprintf(stderr, "No pending netlink responses\n");
288bc2059f1SWilliam A. Kennington III         abort();
289bc2059f1SWilliam A. Kennington III     }
290bc2059f1SWilliam A. Kennington III 
291bc2059f1SWilliam A. Kennington III     ssize_t ret = 0;
292bc2059f1SWilliam A. Kennington III     auto data = reinterpret_cast<char*>(msg->msg_iov[0].iov_base);
293bc2059f1SWilliam A. Kennington III     while (!msgs.empty())
294bc2059f1SWilliam A. Kennington III     {
295bc2059f1SWilliam A. Kennington III         const auto& msg = msgs.front();
296bc2059f1SWilliam A. Kennington III         if (NLMSG_ALIGN(ret) + msg.size() > required_buf_size)
297bc2059f1SWilliam A. Kennington III         {
298bc2059f1SWilliam A. Kennington III             break;
299bc2059f1SWilliam A. Kennington III         }
300bc2059f1SWilliam A. Kennington III         ret = NLMSG_ALIGN(ret);
301bc2059f1SWilliam A. Kennington III         memcpy(data + ret, msg.data(), msg.size());
302bc2059f1SWilliam A. Kennington III         ret += msg.size();
303bc2059f1SWilliam A. Kennington III         msgs.pop();
304bc2059f1SWilliam A. Kennington III     }
305bc2059f1SWilliam A. Kennington III     return ret;
306bc2059f1SWilliam A. Kennington III }
307bc2059f1SWilliam A. Kennington III 
308cb64b99fSWilliam A. Kennington III } // extern "C"
309