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 35 void phosphor::network::system::mock_clear() 36 { 37 mock_rtnetlinks.clear(); 38 mock_if.clear(); 39 } 40 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 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 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 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 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 { 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 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 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 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 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