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 <stdplus/raw.hpp>
18
19 #include <cstdarg>
20 #include <cstdio>
21 #include <cstring>
22 #include <map>
23 #include <queue>
24 #include <stdexcept>
25 #include <string>
26 #include <string_view>
27 #include <vector>
28
29 std::map<int, std::queue<std::string>> mock_rtnetlinks;
30
31 using phosphor::network::InterfaceInfo;
32
33 std::map<std::string, InterfaceInfo> mock_if;
34
mock_clear()35 void phosphor::network::system::mock_clear()
36 {
37 mock_rtnetlinks.clear();
38 mock_if.clear();
39 }
40
mock_addIF(const InterfaceInfo & info)41 void phosphor::network::system::mock_addIF(const InterfaceInfo& info)
42 {
43 if (info.idx == 0)
44 {
45 throw std::invalid_argument("Bad interface index");
46 }
47 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);
55 }
56
validateMsgHdr(const struct msghdr * msg)57 void 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
appendRTAttr(std::string & msgBuf,unsigned short type,std::string_view data)77 void 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
sendmsg_link_dump(std::queue<std::string> & msgs,std::string_view in)89 ssize_t sendmsg_link_dump(std::queue<std::string>& msgs, std::string_view in)
90 {
91 if (const auto& hdrin = *reinterpret_cast<const nlmsghdr*>(in.data());
92 hdrin.nlmsg_type != RTM_GETLINK)
93 {
94 return 0;
95 }
96
97 std::string msgBuf;
98 msgBuf.reserve(8192);
99 for (const auto& [name, i] : mock_if)
100 {
101 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*>(
109 msgBuf.data() + nlbegin + 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;
128 hdr.nlmsg_type = RTM_NEWLINK;
129 hdr.nlmsg_flags = NLM_F_MULTI;
130 msgBuf.resize(NLMSG_ALIGN(msgBuf.size()), '\0');
131 }
132 const auto nlbegin = msgBuf.size();
133 msgBuf.append(NLMSG_SPACE(0), '\0');
134 auto& hdr = *reinterpret_cast<nlmsghdr*>(msgBuf.data() + nlbegin);
135 hdr.nlmsg_len = NLMSG_LENGTH(0);
136 hdr.nlmsg_type = NLMSG_DONE;
137 hdr.nlmsg_flags = NLM_F_MULTI;
138
139 msgs.emplace(std::move(msgBuf));
140 return in.size();
141 }
142
sendmsg_ack(std::queue<std::string> & msgs,std::string_view in)143 ssize_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
155 extern "C"
156 {
ioctl(int fd,unsigned long int request,...)157 int 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
164 auto req = reinterpret_cast<ifreq*>(data);
165 if (request == SIOCGIFFLAGS)
166 {
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;
190 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
socket(int domain,int type,int protocol)198 int 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
close(int fd)215 int 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
sendmsg(int sockfd,const struct msghdr * msg,int flags)228 ssize_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
250 ret = sendmsg_link_dump(msgs, iov);
251 if (ret != 0)
252 {
253 return ret;
254 }
255
256 ret = sendmsg_ack(msgs, iov);
257 if (ret != 0)
258 {
259 return ret;
260 }
261
262 errno = ENOSYS;
263 return -1;
264 }
265
recvmsg(int sockfd,struct msghdr * msg,int flags)266 ssize_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
308 } // extern "C"
309