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