1 #include "mock_syscall.hpp" 2 #include "netlink.hpp" 3 #include "util.hpp" 4 5 #include <linux/netlink.h> 6 #include <linux/rtnetlink.h> 7 8 #include <cstring> 9 #include <stdexcept> 10 #include <stdplus/raw.hpp> 11 #include <string_view> 12 13 #include <gtest/gtest.h> 14 15 namespace phosphor 16 { 17 namespace network 18 { 19 namespace netlink 20 { 21 namespace detail 22 { 23 24 TEST(ExtractMsgs, TooSmall) 25 { 26 const char buf[] = {'1'}; 27 static_assert(sizeof(buf) < sizeof(nlmsghdr)); 28 std::string_view data(buf, sizeof(buf)); 29 30 size_t cbCalls = 0; 31 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 32 bool done = true; 33 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error); 34 EXPECT_EQ(1, data.size()); 35 EXPECT_EQ(0, cbCalls); 36 EXPECT_TRUE(done); 37 } 38 39 TEST(ExtractMsgs, SmallAttrLen) 40 { 41 nlmsghdr hdr{}; 42 hdr.nlmsg_len = NLMSG_LENGTH(0) - 1; 43 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 44 45 size_t cbCalls = 0; 46 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 47 bool done = true; 48 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error); 49 EXPECT_EQ(NLMSG_SPACE(0), data.size()); 50 EXPECT_EQ(0, cbCalls); 51 EXPECT_TRUE(done); 52 } 53 54 TEST(ExtractMsgs, LargeAttrLen) 55 { 56 nlmsghdr hdr{}; 57 hdr.nlmsg_len = NLMSG_LENGTH(0) + 1; 58 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 59 60 size_t cbCalls = 0; 61 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 62 bool done = true; 63 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error); 64 EXPECT_EQ(NLMSG_SPACE(0), data.size()); 65 EXPECT_EQ(0, cbCalls); 66 EXPECT_TRUE(done); 67 } 68 69 TEST(ExtractMsgs, NoopMsg) 70 { 71 nlmsghdr hdr{}; 72 hdr.nlmsg_len = NLMSG_LENGTH(0); 73 hdr.nlmsg_type = NLMSG_NOOP; 74 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 75 76 size_t cbCalls = 0; 77 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 78 bool done = true; 79 processMsg(data, done, cb); 80 EXPECT_EQ(0, data.size()); 81 EXPECT_EQ(0, cbCalls); 82 EXPECT_TRUE(done); 83 } 84 85 TEST(ExtractMsgs, AckMsg) 86 { 87 nlmsgerr ack{}; 88 nlmsghdr hdr{}; 89 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(ack)); 90 hdr.nlmsg_type = NLMSG_ERROR; 91 char buf[NLMSG_ALIGN(hdr.nlmsg_len)]; 92 std::memcpy(buf, &hdr, sizeof(hdr)); 93 std::memcpy(NLMSG_DATA(buf), &ack, sizeof(ack)); 94 std::string_view data(reinterpret_cast<char*>(&buf), sizeof(buf)); 95 96 size_t cbCalls = 0; 97 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 98 bool done = true; 99 processMsg(data, done, cb); 100 EXPECT_EQ(0, data.size()); 101 EXPECT_EQ(0, cbCalls); 102 EXPECT_TRUE(done); 103 } 104 105 TEST(ExtractMsgs, ErrMsg) 106 { 107 nlmsgerr err{}; 108 err.error = EINVAL; 109 nlmsghdr hdr{}; 110 hdr.nlmsg_len = NLMSG_LENGTH(sizeof(err)); 111 hdr.nlmsg_type = NLMSG_ERROR; 112 char buf[NLMSG_ALIGN(hdr.nlmsg_len)]; 113 std::memcpy(buf, &hdr, sizeof(hdr)); 114 std::memcpy(NLMSG_DATA(buf), &err, sizeof(err)); 115 std::string_view data(reinterpret_cast<char*>(&buf), sizeof(buf)); 116 117 size_t cbCalls = 0; 118 nlmsghdr hdrOut; 119 std::string_view dataOut; 120 auto cb = [&](const nlmsghdr& hdr, std::string_view data) { 121 hdrOut = hdr; 122 dataOut = data; 123 cbCalls++; 124 }; 125 bool done = true; 126 processMsg(data, done, cb); 127 EXPECT_EQ(0, data.size()); 128 EXPECT_EQ(1, cbCalls); 129 EXPECT_TRUE(stdplus::raw::equal(hdr, hdrOut)); 130 EXPECT_TRUE( 131 stdplus::raw::equal(err, stdplus::raw::extract<nlmsgerr>(dataOut))); 132 EXPECT_EQ(0, dataOut.size()); 133 EXPECT_TRUE(done); 134 } 135 136 TEST(ExtractMsgs, DoneNoMulti) 137 { 138 nlmsghdr hdr{}; 139 hdr.nlmsg_len = NLMSG_LENGTH(0); 140 hdr.nlmsg_type = NLMSG_DONE; 141 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 142 143 size_t cbCalls = 0; 144 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 145 bool done = true; 146 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error); 147 EXPECT_EQ(0, data.size()); 148 EXPECT_EQ(0, cbCalls); 149 EXPECT_TRUE(done); 150 } 151 152 TEST(ExtractMsg, TwoMultiMsgs) 153 { 154 nlmsghdr hdr{}; 155 hdr.nlmsg_len = NLMSG_LENGTH(0); 156 hdr.nlmsg_type = RTM_NEWLINK; 157 hdr.nlmsg_flags = NLM_F_MULTI; 158 std::string buf; 159 buf.append(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 160 buf.append(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 161 162 std::string_view data = buf; 163 size_t cbCalls = 0; 164 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 165 bool done = true; 166 processMsg(data, done, cb); 167 EXPECT_EQ(NLMSG_SPACE(0), data.size()); 168 EXPECT_EQ(1, cbCalls); 169 EXPECT_FALSE(done); 170 171 processMsg(data, done, cb); 172 EXPECT_EQ(0, data.size()); 173 EXPECT_EQ(2, cbCalls); 174 EXPECT_FALSE(done); 175 } 176 177 TEST(ExtractMsgs, MultiMsgValid) 178 { 179 nlmsghdr hdr{}; 180 hdr.nlmsg_len = NLMSG_LENGTH(0); 181 hdr.nlmsg_type = RTM_NEWLINK; 182 hdr.nlmsg_flags = NLM_F_MULTI; 183 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 184 185 size_t cbCalls = 0; 186 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 187 bool done = true; 188 processMsg(data, done, cb); 189 EXPECT_EQ(0, data.size()); 190 EXPECT_EQ(1, cbCalls); 191 EXPECT_FALSE(done); 192 193 hdr.nlmsg_type = NLMSG_DONE; 194 hdr.nlmsg_flags = 0; 195 data = std::string_view(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 196 processMsg(data, done, cb); 197 EXPECT_EQ(0, data.size()); 198 EXPECT_EQ(1, cbCalls); 199 EXPECT_TRUE(done); 200 } 201 202 TEST(ExtractMsgs, MultiMsgInvalid) 203 { 204 nlmsghdr hdr{}; 205 hdr.nlmsg_len = NLMSG_LENGTH(0); 206 hdr.nlmsg_type = RTM_NEWLINK; 207 hdr.nlmsg_flags = NLM_F_MULTI; 208 std::string_view data(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 209 210 size_t cbCalls = 0; 211 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 212 bool done = true; 213 processMsg(data, done, cb); 214 EXPECT_EQ(0, data.size()); 215 EXPECT_EQ(1, cbCalls); 216 EXPECT_FALSE(done); 217 218 hdr.nlmsg_flags = 0; 219 data = std::string_view(reinterpret_cast<char*>(&hdr), NLMSG_SPACE(0)); 220 EXPECT_THROW(processMsg(data, done, cb), std::runtime_error); 221 EXPECT_EQ(0, data.size()); 222 EXPECT_EQ(1, cbCalls); 223 EXPECT_FALSE(done); 224 } 225 226 } // namespace detail 227 228 TEST(ExtractRtAttr, TooSmall) 229 { 230 const char buf[] = {'1'}; 231 static_assert(sizeof(buf) < sizeof(rtattr)); 232 std::string_view data(buf, sizeof(buf)); 233 234 EXPECT_THROW(extractRtAttr(data), std::runtime_error); 235 EXPECT_EQ(1, data.size()); 236 } 237 238 TEST(ExtractRtAttr, SmallAttrLen) 239 { 240 rtattr rta{}; 241 rta.rta_len = RTA_LENGTH(0) - 1; 242 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0)); 243 244 EXPECT_THROW(extractRtAttr(data), std::runtime_error); 245 EXPECT_EQ(RTA_SPACE(0), data.size()); 246 } 247 248 TEST(ExtractRtAttr, LargeAttrLen) 249 { 250 rtattr rta{}; 251 rta.rta_len = RTA_LENGTH(0) + 1; 252 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0)); 253 254 EXPECT_THROW(extractRtAttr(data), std::runtime_error); 255 EXPECT_EQ(RTA_SPACE(0), data.size()); 256 } 257 258 TEST(ExtractRtAttr, NoData) 259 { 260 rtattr rta{}; 261 rta.rta_len = RTA_LENGTH(0); 262 std::string_view data(reinterpret_cast<char*>(&rta), RTA_SPACE(0)); 263 264 auto [hdr, attr] = extractRtAttr(data); 265 EXPECT_EQ(0, data.size()); 266 EXPECT_EQ(0, attr.size()); 267 EXPECT_EQ(0, std::memcmp(&rta, &hdr, sizeof(rta))); 268 } 269 270 TEST(ExtractRtAttr, SomeData) 271 { 272 const char attrbuf[] = "abcd"; 273 const char nextbuf[] = "efgh"; 274 rtattr rta{}; 275 rta.rta_len = RTA_LENGTH(sizeof(attrbuf)); 276 277 char buf[RTA_SPACE(sizeof(attrbuf)) + sizeof(nextbuf)]; 278 memcpy(buf, &rta, sizeof(rta)); 279 memcpy(RTA_DATA(buf), &attrbuf, sizeof(attrbuf)); 280 memcpy(buf + RTA_SPACE(sizeof(attrbuf)), &nextbuf, sizeof(nextbuf)); 281 std::string_view data(buf, sizeof(buf)); 282 283 auto [hdr, attr] = extractRtAttr(data); 284 EXPECT_EQ(0, memcmp(&rta, &hdr, sizeof(rta))); 285 EXPECT_EQ(sizeof(attrbuf), attr.size()); 286 EXPECT_EQ(0, memcmp(&attrbuf, attr.data(), sizeof(attrbuf))); 287 EXPECT_EQ(sizeof(nextbuf), data.size()); 288 EXPECT_EQ(0, memcmp(&nextbuf, data.data(), sizeof(nextbuf))); 289 } 290 291 class PerformRequest : public testing::Test 292 { 293 public: 294 void doLinkDump(size_t ifs) 295 { 296 mock_clear(); 297 for (size_t i = 0; i < ifs; ++i) 298 { 299 mock_addIF("eth" + std::to_string(i), 1 + i); 300 } 301 302 size_t cbCalls = 0; 303 auto cb = [&](const nlmsghdr&, std::string_view) { cbCalls++; }; 304 ifinfomsg msg{}; 305 netlink::performRequest(NETLINK_ROUTE, RTM_GETLINK, NLM_F_DUMP, msg, 306 cb); 307 EXPECT_EQ(ifs, cbCalls); 308 } 309 }; 310 311 TEST_F(PerformRequest, NoResponse) 312 { 313 doLinkDump(0); 314 } 315 316 TEST_F(PerformRequest, SingleResponse) 317 { 318 doLinkDump(1); 319 } 320 321 TEST_F(PerformRequest, MultiResponse) 322 { 323 doLinkDump(3); 324 } 325 326 TEST_F(PerformRequest, MultiMsg) 327 { 328 doLinkDump(1000); 329 } 330 331 } // namespace netlink 332 } // namespace network 333 } // namespace phosphor 334