1 #include "mock_syscall.hpp"
2 
3 #include "util.hpp"
4 
5 #include <arpa/inet.h>
6 #include <dlfcn.h>
7 #include <linux/netlink.h>
8 #include <linux/rtnetlink.h>
9 #include <net/ethernet.h>
10 #include <net/if.h>
11 #include <netinet/in.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 
17 #include <cstdarg>
18 #include <cstdio>
19 #include <cstring>
20 #include <map>
21 #include <queue>
22 #include <stdexcept>
23 #include <stdplus/raw.hpp>
24 #include <string>
25 #include <string_view>
26 #include <vector>
27 
28 std::map<int, std::queue<std::string>> mock_rtnetlinks;
29 
30 using phosphor::network::InterfaceInfo;
31 
32 std::map<std::string, InterfaceInfo> mock_if;
33 
34 void phosphor::network::system::mock_clear()
35 {
36     mock_rtnetlinks.clear();
37     mock_if.clear();
38 }
39 
40 void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
41 {
42     if (info.idx == 0)
43     {
44         throw std::invalid_argument("Bad interface index");
45     }
46     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);
54 }
55 
56 void 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 
76 void 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 
88 ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
89 {
90     if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
91         hdrin.nlmsg_type != RTM_GETLINK)
92     {
93         return 0;
94     }
95 
96     std::string msgBuf;
97     msgBuf.reserve(8192);
98     for (const auto& [name, i] : mock_if)
99     {
100         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;
127         hdr.nlmsg_type = RTM_NEWLINK;
128         hdr.nlmsg_flags = NLM_F_MULTI;
129         msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
130     }
131     const auto nlbegin = msgBuf.size();
132     msgBuf.append(NLMSG_SPACE(0), '\0');
133     auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
134     hdr.nlmsg_len = NLMSG_LENGTH(0);
135     hdr.nlmsg_type = NLMSG_DONE;
136     hdr.nlmsg_flags = NLM_F_MULTI;
137 
138     msgs.emplace(std::move(msgBuf));
139     return in.size();
140 }
141 
142 ssize_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 
154 extern "C" {
155 
156 int 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 
163     auto req = reinterpret_cast<ifreq*>(data);
164     if (request == SIOCGIFFLAGS)
165     {
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;
189         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 
197 int 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 
214 int 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 
227 ssize_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 
249     ret = sendmsg_link_dump(msgs, iov);
250     if (ret != 0)
251     {
252         return ret;
253     }
254 
255     ret = sendmsg_ack(msgs, iov);
256     if (ret != 0)
257     {
258         return ret;
259     }
260 
261     errno = ENOSYS;
262     return -1;
263 }
264 
265 ssize_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 
307 } // extern "C"
308