gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/socket_netlink_route.cc (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <arpa/inet.h> 16 #include <fcntl.h> 17 #include <ifaddrs.h> 18 #include <linux/fib_rules.h> 19 #include <linux/if.h> 20 #include <linux/netlink.h> 21 #include <linux/rtnetlink.h> 22 #include <sys/socket.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 #include <cerrno> 27 #include <cstdint> 28 #include <iostream> 29 #include <utility> 30 #include <vector> 31 32 #include "gmock/gmock.h" 33 #include "gtest/gtest.h" 34 #include "absl/strings/str_format.h" 35 #include "test/syscalls/linux/socket_netlink_route_util.h" 36 #include "test/syscalls/linux/socket_netlink_util.h" 37 #include "test/util/cleanup.h" 38 #include "test/util/file_descriptor.h" 39 #include "test/util/linux_capability_util.h" 40 #include "test/util/posix_error.h" 41 #include "test/util/save_util.h" 42 #include "test/util/socket_util.h" 43 #include "test/util/test_util.h" 44 45 // Tests for NETLINK_ROUTE sockets. 46 47 namespace gvisor { 48 namespace testing { 49 50 namespace { 51 52 constexpr uint32_t kSeq = 12345; 53 54 using ::testing::_; 55 using ::testing::AnyOf; 56 using ::testing::Eq; 57 58 // Parameters for SockOptTest. They are: 59 // 0: Socket option to query. 60 // 1: A predicate to run on the returned sockopt value. Should return true if 61 // the value is considered ok. 62 // 2: A description of what the sockopt value is expected to be. Should complete 63 // the sentence "<value> was unexpected, expected <description>" 64 using SockOptTest = ::testing::TestWithParam< 65 std::tuple<int, std::function<bool(int)>, std::string>>; 66 67 TEST_P(SockOptTest, GetSockOpt) { 68 int sockopt = std::get<0>(GetParam()); 69 auto verifier = std::get<1>(GetParam()); 70 std::string verifier_description = std::get<2>(GetParam()); 71 72 FileDescriptor fd = 73 ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); 74 75 int res; 76 socklen_t len = sizeof(res); 77 78 EXPECT_THAT(getsockopt(fd.get(), SOL_SOCKET, sockopt, &res, &len), 79 SyscallSucceeds()); 80 81 EXPECT_EQ(len, sizeof(res)); 82 EXPECT_TRUE(verifier(res)) << absl::StrFormat( 83 "getsockopt(%d, SOL_SOCKET, %d, &res, &len) => res=%d was unexpected, " 84 "expected %s", 85 fd.get(), sockopt, res, verifier_description); 86 } 87 88 std::function<bool(int)> IsPositive() { 89 return [](int val) { return val > 0; }; 90 } 91 92 std::function<bool(int)> IsEqual(int target) { 93 return [target](int val) { return val == target; }; 94 } 95 96 INSTANTIATE_TEST_SUITE_P( 97 NetlinkRouteTest, SockOptTest, 98 ::testing::Values( 99 std::make_tuple(SO_SNDBUF, IsPositive(), "positive send buffer size"), 100 std::make_tuple(SO_RCVBUF, IsPositive(), 101 "positive receive buffer size"), 102 std::make_tuple(SO_TYPE, IsEqual(SOCK_RAW), 103 absl::StrFormat("SOCK_RAW (%d)", SOCK_RAW)), 104 std::make_tuple(SO_DOMAIN, IsEqual(AF_NETLINK), 105 absl::StrFormat("AF_NETLINK (%d)", AF_NETLINK)), 106 std::make_tuple(SO_PROTOCOL, IsEqual(NETLINK_ROUTE), 107 absl::StrFormat("NETLINK_ROUTE (%d)", NETLINK_ROUTE)), 108 std::make_tuple(SO_PASSCRED, IsEqual(0), "0"))); 109 110 // Validates the reponses to RTM_GETLINK + NLM_F_DUMP. 111 void CheckGetLinkResponse(const struct nlmsghdr* hdr, int seq, int port) { 112 EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWLINK), Eq(NLMSG_DONE))); 113 114 EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) 115 << std::hex << hdr->nlmsg_flags; 116 117 EXPECT_EQ(hdr->nlmsg_seq, seq); 118 EXPECT_EQ(hdr->nlmsg_pid, port); 119 120 if (hdr->nlmsg_type != RTM_NEWLINK) { 121 return; 122 } 123 124 // RTM_NEWLINK contains at least the header and ifinfomsg. 125 EXPECT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); 126 127 // TODO(mpratt): Check ifinfomsg contents and following attrs. 128 } 129 130 TEST(NetlinkRouteTest, GetLinkDump) { 131 FileDescriptor fd = 132 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 133 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 134 135 // Loopback is common among all tests, check that it's found. 136 bool loopbackFound = false; 137 ASSERT_NO_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) { 138 CheckGetLinkResponse(hdr, kSeq, port); 139 if (hdr->nlmsg_type != RTM_NEWLINK) { 140 return; 141 } 142 ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); 143 const struct ifinfomsg* msg = 144 reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); 145 std::cout << "Found interface idx=" << msg->ifi_index 146 << ", type=" << std::hex << msg->ifi_type << std::endl; 147 if (msg->ifi_type == ARPHRD_LOOPBACK) { 148 loopbackFound = true; 149 EXPECT_NE(msg->ifi_flags & IFF_LOOPBACK, 0); 150 } 151 })); 152 EXPECT_TRUE(loopbackFound); 153 } 154 155 // CheckLinkMsg checks a netlink message against an expected link. 156 void CheckLinkMsg(const struct nlmsghdr* hdr, const Link& link) { 157 ASSERT_THAT(hdr->nlmsg_type, Eq(RTM_NEWLINK)); 158 ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); 159 const struct ifinfomsg* msg = 160 reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); 161 EXPECT_EQ(msg->ifi_index, link.index); 162 163 const struct rtattr* rta = FindRtAttr(hdr, msg, IFLA_IFNAME); 164 EXPECT_NE(nullptr, rta) << "IFLA_IFNAME not found in message."; 165 if (rta != nullptr) { 166 std::string name(reinterpret_cast<const char*>(RTA_DATA(rta))); 167 EXPECT_EQ(name, link.name); 168 } 169 } 170 171 TEST(NetlinkRouteTest, GetLinkByIndex) { 172 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 173 174 FileDescriptor fd = 175 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 176 177 struct request { 178 struct nlmsghdr hdr; 179 struct ifinfomsg ifm; 180 }; 181 182 struct request req = {}; 183 req.hdr.nlmsg_len = sizeof(req); 184 req.hdr.nlmsg_type = RTM_GETLINK; 185 req.hdr.nlmsg_flags = NLM_F_REQUEST; 186 req.hdr.nlmsg_seq = kSeq; 187 req.ifm.ifi_family = AF_UNSPEC; 188 req.ifm.ifi_index = loopback_link.index; 189 190 bool found = false; 191 ASSERT_NO_ERRNO(NetlinkRequestResponse( 192 fd, &req, sizeof(req), 193 [&](const struct nlmsghdr* hdr) { 194 CheckLinkMsg(hdr, loopback_link); 195 found = true; 196 }, 197 false)); 198 EXPECT_TRUE(found) << "Netlink response does not contain any links."; 199 } 200 201 TEST(NetlinkRouteTest, LinkUp) { 202 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 203 SKIP_IF(IsRunningWithHostinet()); 204 205 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 206 207 FileDescriptor fd = 208 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 209 210 struct request { 211 struct nlmsghdr hdr; 212 struct ifinfomsg ifm; 213 }; 214 215 struct request req = {}; 216 req.hdr.nlmsg_len = sizeof(req); 217 req.hdr.nlmsg_type = RTM_NEWLINK; 218 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 219 req.hdr.nlmsg_seq = kSeq; 220 req.ifm.ifi_family = AF_UNSPEC; 221 req.ifm.ifi_index = loopback_link.index; 222 req.ifm.ifi_change = IFF_UP; 223 req.ifm.ifi_flags = IFF_UP; 224 EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req))); 225 } 226 227 TEST(NetlinkRouteTest, GetLinkByName) { 228 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 229 230 FileDescriptor fd = 231 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 232 233 struct request { 234 struct nlmsghdr hdr; 235 struct ifinfomsg ifm; 236 struct rtattr rtattr; 237 char ifname[IFNAMSIZ]; 238 char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; 239 }; 240 241 struct request req = {}; 242 req.hdr.nlmsg_type = RTM_GETLINK; 243 req.hdr.nlmsg_flags = NLM_F_REQUEST; 244 req.hdr.nlmsg_seq = kSeq; 245 req.ifm.ifi_family = AF_UNSPEC; 246 req.rtattr.rta_type = IFLA_IFNAME; 247 req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1); 248 strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname)); 249 req.hdr.nlmsg_len = 250 NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); 251 252 bool found = false; 253 ASSERT_NO_ERRNO(NetlinkRequestResponse( 254 fd, &req, sizeof(req), 255 [&](const struct nlmsghdr* hdr) { 256 CheckLinkMsg(hdr, loopback_link); 257 found = true; 258 }, 259 false)); 260 EXPECT_TRUE(found) << "Netlink response does not contain any links."; 261 } 262 263 TEST(NetlinkRouteTest, GetLinkByIndexNotFound) { 264 FileDescriptor fd = 265 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 266 267 struct request { 268 struct nlmsghdr hdr; 269 struct ifinfomsg ifm; 270 }; 271 272 struct request req = {}; 273 req.hdr.nlmsg_len = sizeof(req); 274 req.hdr.nlmsg_type = RTM_GETLINK; 275 req.hdr.nlmsg_flags = NLM_F_REQUEST; 276 req.hdr.nlmsg_seq = kSeq; 277 req.ifm.ifi_family = AF_UNSPEC; 278 req.ifm.ifi_index = 1234590; 279 280 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 281 PosixErrorIs(ENODEV, _)); 282 } 283 284 TEST(NetlinkRouteTest, GetLinkByNameNotFound) { 285 const std::string name = "nodevice?!"; 286 287 FileDescriptor fd = 288 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 289 290 struct request { 291 struct nlmsghdr hdr; 292 struct ifinfomsg ifm; 293 struct rtattr rtattr; 294 char ifname[IFNAMSIZ]; 295 char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; 296 }; 297 298 struct request req = {}; 299 req.hdr.nlmsg_type = RTM_GETLINK; 300 req.hdr.nlmsg_flags = NLM_F_REQUEST; 301 req.hdr.nlmsg_seq = kSeq; 302 req.ifm.ifi_family = AF_UNSPEC; 303 req.rtattr.rta_type = IFLA_IFNAME; 304 req.rtattr.rta_len = RTA_LENGTH(name.size() + 1); 305 strncpy(req.ifname, name.c_str(), sizeof(req.ifname)); 306 req.hdr.nlmsg_len = 307 NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); 308 309 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 310 PosixErrorIs(ENODEV, _)); 311 } 312 313 TEST(NetlinkRouteTest, RemoveLoopbackByName) { 314 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 315 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 316 317 FileDescriptor fd = 318 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 319 320 struct request { 321 struct nlmsghdr hdr; 322 struct ifinfomsg ifm; 323 struct rtattr rtattr; 324 char ifname[IFNAMSIZ]; 325 char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; 326 }; 327 328 struct request req = {}; 329 req.hdr.nlmsg_type = RTM_DELLINK; 330 req.hdr.nlmsg_flags = NLM_F_REQUEST; 331 req.hdr.nlmsg_seq = kSeq; 332 req.ifm.ifi_family = AF_UNSPEC; 333 req.rtattr.rta_type = IFLA_IFNAME; 334 req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1); 335 strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname)); 336 req.hdr.nlmsg_len = 337 NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); 338 339 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 340 PosixErrorIs(ENOTSUP, _)); 341 } 342 343 TEST(NetlinkRouteTest, RemoveLoopbackByIndex) { 344 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 345 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 346 FileDescriptor fd = 347 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 348 349 struct request { 350 struct nlmsghdr hdr; 351 struct ifinfomsg ifm; 352 }; 353 354 struct request req = {}; 355 req.hdr.nlmsg_len = sizeof(req); 356 req.hdr.nlmsg_type = RTM_DELLINK; 357 req.hdr.nlmsg_flags = NLM_F_REQUEST; 358 req.hdr.nlmsg_seq = kSeq; 359 req.ifm.ifi_family = AF_UNSPEC; 360 req.ifm.ifi_index = loopback_link.index; 361 362 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 363 PosixErrorIs(ENOTSUP, _)); 364 } 365 366 TEST(NetlinkRouteTest, RemoveLinkByIndexNotFound) { 367 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 368 FileDescriptor fd = 369 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 370 371 struct request { 372 struct nlmsghdr hdr; 373 struct ifinfomsg ifm; 374 }; 375 376 struct request req = {}; 377 req.hdr.nlmsg_len = sizeof(req); 378 req.hdr.nlmsg_type = RTM_GETLINK; 379 req.hdr.nlmsg_flags = NLM_F_REQUEST; 380 req.hdr.nlmsg_seq = kSeq; 381 req.ifm.ifi_family = AF_UNSPEC; 382 req.ifm.ifi_index = 1234590; 383 384 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 385 PosixErrorIs(ENODEV, _)); 386 } 387 388 TEST(NetlinkRouteTest, RemoveLinkByNameNotFound) { 389 const std::string name = "nodevice?!"; 390 391 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 392 FileDescriptor fd = 393 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 394 395 struct request { 396 struct nlmsghdr hdr; 397 struct ifinfomsg ifm; 398 struct rtattr rtattr; 399 char ifname[IFNAMSIZ]; 400 char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; 401 }; 402 403 struct request req = {}; 404 req.hdr.nlmsg_type = RTM_DELLINK; 405 req.hdr.nlmsg_flags = NLM_F_REQUEST; 406 req.hdr.nlmsg_seq = kSeq; 407 req.ifm.ifi_family = AF_UNSPEC; 408 req.rtattr.rta_type = IFLA_IFNAME; 409 req.rtattr.rta_len = RTA_LENGTH(name.size() + 1); 410 strncpy(req.ifname, name.c_str(), sizeof(req.ifname)); 411 req.hdr.nlmsg_len = 412 NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); 413 414 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 415 PosixErrorIs(ENODEV, _)); 416 } 417 418 TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) { 419 FileDescriptor fd = 420 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 421 422 struct request { 423 struct nlmsghdr hdr; 424 struct ifinfomsg ifm; 425 }; 426 427 struct request req = {}; 428 req.hdr.nlmsg_len = sizeof(req); 429 // If type & 0x3 is equal to 0x2, this means a get request 430 // which doesn't require CAP_SYS_ADMIN. 431 req.hdr.nlmsg_type = ((__RTM_MAX + 1024) & (~0x3)) | 0x2; 432 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 433 req.hdr.nlmsg_seq = kSeq; 434 req.ifm.ifi_family = AF_UNSPEC; 435 436 EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), 437 PosixErrorIs(EOPNOTSUPP, _)); 438 } 439 440 TEST(NetlinkRouteTest, MsgHdrMsgTrunc) { 441 FileDescriptor fd = 442 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 443 444 struct request { 445 struct nlmsghdr hdr; 446 struct ifinfomsg ifm; 447 }; 448 449 struct request req = {}; 450 req.hdr.nlmsg_len = sizeof(req); 451 req.hdr.nlmsg_type = RTM_GETLINK; 452 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 453 req.hdr.nlmsg_seq = kSeq; 454 req.ifm.ifi_family = AF_UNSPEC; 455 456 struct iovec iov = {}; 457 iov.iov_base = &req; 458 iov.iov_len = sizeof(req); 459 460 struct msghdr msg = {}; 461 msg.msg_iov = &iov; 462 msg.msg_iovlen = 1; 463 // No destination required; it defaults to pid 0, the kernel. 464 465 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 466 467 // Small enough to ensure that the response doesn't fit. 468 constexpr size_t kBufferSize = 10; 469 std::vector<char> buf(kBufferSize); 470 iov.iov_base = buf.data(); 471 iov.iov_len = buf.size(); 472 473 ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), 474 SyscallSucceedsWithValue(kBufferSize)); 475 EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC); 476 } 477 478 TEST(NetlinkRouteTest, SpliceFromPipe) { 479 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 480 FileDescriptor fd = 481 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 482 483 int fds[2]; 484 ASSERT_THAT(pipe(fds), SyscallSucceeds()); 485 FileDescriptor rfd(fds[0]); 486 FileDescriptor wfd(fds[1]); 487 488 struct request { 489 struct nlmsghdr hdr; 490 struct ifinfomsg ifm; 491 }; 492 493 struct request req = {}; 494 req.hdr.nlmsg_len = sizeof(req); 495 req.hdr.nlmsg_type = RTM_GETLINK; 496 req.hdr.nlmsg_flags = NLM_F_REQUEST; 497 req.hdr.nlmsg_seq = kSeq; 498 req.ifm.ifi_family = AF_UNSPEC; 499 req.ifm.ifi_index = loopback_link.index; 500 501 ASSERT_THAT(write(wfd.get(), &req, sizeof(req)), 502 SyscallSucceedsWithValue(sizeof(req))); 503 504 EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), 505 SyscallSucceedsWithValue(sizeof(req))); 506 close(wfd.release()); 507 EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), 508 SyscallSucceedsWithValue(0)); 509 510 bool found = false; 511 ASSERT_NO_ERRNO(NetlinkResponse( 512 fd, 513 [&](const struct nlmsghdr* hdr) { 514 CheckLinkMsg(hdr, loopback_link); 515 found = true; 516 }, 517 false)); 518 EXPECT_TRUE(found) << "Netlink response does not contain any links."; 519 } 520 521 TEST(NetlinkRouteTest, MsgTruncMsgHdrMsgTrunc) { 522 FileDescriptor fd = 523 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 524 525 struct request { 526 struct nlmsghdr hdr; 527 struct ifinfomsg ifm; 528 }; 529 530 struct request req = {}; 531 req.hdr.nlmsg_len = sizeof(req); 532 req.hdr.nlmsg_type = RTM_GETLINK; 533 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 534 req.hdr.nlmsg_seq = kSeq; 535 req.ifm.ifi_family = AF_UNSPEC; 536 537 struct iovec iov = {}; 538 iov.iov_base = &req; 539 iov.iov_len = sizeof(req); 540 541 struct msghdr msg = {}; 542 msg.msg_iov = &iov; 543 msg.msg_iovlen = 1; 544 // No destination required; it defaults to pid 0, the kernel. 545 546 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 547 548 // Small enough to ensure that the response doesn't fit. 549 constexpr size_t kBufferSize = 10; 550 std::vector<char> buf(kBufferSize); 551 iov.iov_base = buf.data(); 552 iov.iov_len = buf.size(); 553 554 int res = 0; 555 ASSERT_THAT(res = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), 556 SyscallSucceeds()); 557 EXPECT_GT(res, kBufferSize); 558 EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC); 559 } 560 561 TEST(NetlinkRouteTest, ControlMessageIgnored) { 562 FileDescriptor fd = 563 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 564 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 565 566 struct request { 567 struct nlmsghdr control_hdr; 568 struct nlmsghdr message_hdr; 569 struct ifinfomsg ifm; 570 }; 571 572 struct request req = {}; 573 574 // This control message is ignored. We still receive a response for the 575 // following RTM_GETLINK. 576 req.control_hdr.nlmsg_len = sizeof(req.control_hdr); 577 req.control_hdr.nlmsg_type = NLMSG_DONE; 578 req.control_hdr.nlmsg_seq = kSeq; 579 580 req.message_hdr.nlmsg_len = sizeof(req.message_hdr) + sizeof(req.ifm); 581 req.message_hdr.nlmsg_type = RTM_GETLINK; 582 req.message_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 583 req.message_hdr.nlmsg_seq = kSeq; 584 585 req.ifm.ifi_family = AF_UNSPEC; 586 587 ASSERT_NO_ERRNO(NetlinkRequestResponse( 588 fd, &req, sizeof(req), 589 [&](const struct nlmsghdr* hdr) { 590 CheckGetLinkResponse(hdr, kSeq, port); 591 }, 592 false)); 593 } 594 595 TEST(NetlinkRouteTest, GetAddrDump) { 596 FileDescriptor fd = 597 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 598 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 599 600 struct request { 601 struct nlmsghdr hdr; 602 struct rtgenmsg rgm; 603 }; 604 605 struct request req; 606 req.hdr.nlmsg_len = sizeof(req); 607 req.hdr.nlmsg_type = RTM_GETADDR; 608 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 609 req.hdr.nlmsg_seq = kSeq; 610 req.rgm.rtgen_family = AF_UNSPEC; 611 612 ASSERT_NO_ERRNO(NetlinkRequestResponse( 613 fd, &req, sizeof(req), 614 [&](const struct nlmsghdr* hdr) { 615 EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE))); 616 617 EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) 618 << std::hex << hdr->nlmsg_flags; 619 620 EXPECT_EQ(hdr->nlmsg_seq, kSeq); 621 EXPECT_EQ(hdr->nlmsg_pid, port); 622 623 if (hdr->nlmsg_type != RTM_NEWADDR) { 624 return; 625 } 626 627 // RTM_NEWADDR contains at least the header and ifaddrmsg. 628 EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); 629 630 // TODO(mpratt): Check ifaddrmsg contents and following attrs. 631 }, 632 false)); 633 } 634 635 TEST(NetlinkRouteTest, LookupAll) { 636 struct ifaddrs* if_addr_list = nullptr; 637 auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); }); 638 639 // Not a syscall but we can use the syscall matcher as glibc sets errno. 640 ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds()); 641 642 int count = 0; 643 for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) { 644 if (!i->ifa_addr || (i->ifa_addr->sa_family != AF_INET && 645 i->ifa_addr->sa_family != AF_INET6)) { 646 continue; 647 } 648 count++; 649 } 650 ASSERT_GT(count, 0); 651 } 652 653 TEST(NetlinkRouteTest, AddAndRemoveAddr) { 654 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 655 // Don't do cooperative save/restore because netstack state is not restored. 656 // TODO(gvisor.dev/issue/4595): enable cooperative save tests. 657 const DisableSave ds; 658 659 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 660 661 struct in_addr addr; 662 ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1); 663 664 // Create should succeed, as no such address in kernel. 665 ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, 666 /*prefixlen=*/24, &addr, sizeof(addr))); 667 668 Cleanup defer_addr_removal = Cleanup( 669 [loopback_link = std::move(loopback_link), addr = std::move(addr)] { 670 // First delete should succeed, as address exists. 671 EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, 672 /*prefixlen=*/24, &addr, 673 sizeof(addr))); 674 675 // Second delete should fail, as address no longer exists. 676 EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, 677 /*prefixlen=*/24, &addr, sizeof(addr)), 678 PosixErrorIs(EADDRNOTAVAIL, _)); 679 }); 680 681 // Replace an existing address should succeed. 682 ASSERT_NO_ERRNO(LinkReplaceLocalAddr(loopback_link.index, AF_INET, 683 /*prefixlen=*/24, &addr, sizeof(addr))); 684 685 // Create exclusive should fail, as we created the address above. 686 EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET, 687 /*prefixlen=*/24, &addr, sizeof(addr)), 688 PosixErrorIs(EEXIST, _)); 689 } 690 691 // GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request. 692 TEST(NetlinkRouteTest, GetRouteDump) { 693 FileDescriptor fd = 694 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 695 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 696 697 struct request { 698 struct nlmsghdr hdr; 699 struct rtmsg rtm; 700 }; 701 702 struct request req = {}; 703 req.hdr.nlmsg_len = sizeof(req); 704 req.hdr.nlmsg_type = RTM_GETROUTE; 705 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 706 req.hdr.nlmsg_seq = kSeq; 707 req.rtm.rtm_family = AF_UNSPEC; 708 709 bool routeFound = false; 710 bool dstFound = true; 711 bool defaultRouteDstFound = false; 712 ASSERT_NO_ERRNO(NetlinkRequestResponse( 713 fd, &req, sizeof(req), 714 [&](const struct nlmsghdr* hdr) { 715 // Validate the reponse to RTM_GETROUTE + NLM_F_DUMP. 716 EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWROUTE), Eq(NLMSG_DONE))); 717 718 EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) 719 << std::hex << hdr->nlmsg_flags; 720 721 EXPECT_EQ(hdr->nlmsg_seq, kSeq); 722 EXPECT_EQ(hdr->nlmsg_pid, port); 723 724 // The test should not proceed if it's not a RTM_NEWROUTE message. 725 if (hdr->nlmsg_type != RTM_NEWROUTE) { 726 return; 727 } 728 729 // RTM_NEWROUTE contains at least the header and rtmsg. 730 ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg))); 731 const struct rtmsg* msg = 732 reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr)); 733 // NOTE: rtmsg fields are char fields. 734 std::cout << "Found route table=" << static_cast<int>(msg->rtm_table) 735 << ", protocol=" << static_cast<int>(msg->rtm_protocol) 736 << ", scope=" << static_cast<int>(msg->rtm_scope) 737 << ", type=" << static_cast<int>(msg->rtm_type) 738 << ", prefixlen=" << static_cast<int>(msg->rtm_dst_len); 739 740 int len = RTM_PAYLOAD(hdr); 741 bool rtDstFound = false; 742 for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len); 743 attr = RTA_NEXT(attr, len)) { 744 if (attr->rta_type == RTA_DST) { 745 char address[INET_ADDRSTRLEN] = {}; 746 inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address)); 747 std::cout << ", dst=" << address; 748 rtDstFound = true; 749 } 750 } 751 752 std::cout << std::endl; 753 754 // If the test is running in a new network namespace, it will have only 755 // the local route table. 756 if (msg->rtm_table == RT_TABLE_MAIN || 757 (!IsRunningOnGvisor() && msg->rtm_table == RT_TABLE_LOCAL)) { 758 routeFound = true; 759 if (msg->rtm_dst_len) { 760 dstFound = rtDstFound && dstFound; 761 } else { 762 defaultRouteDstFound = rtDstFound || defaultRouteDstFound; 763 } 764 } 765 }, 766 false)); 767 // At least one route found in main route table. 768 EXPECT_TRUE(routeFound); 769 // Found RTA_DST for each route in main table. 770 EXPECT_TRUE(dstFound); 771 // RTA_DST should not be present for default routes. 772 EXPECT_FALSE(defaultRouteDstFound); 773 } 774 775 // GetRouteRequest tests a RTM_GETROUTE request with RTM_F_LOOKUP_TABLE flag. 776 TEST(NetlinkRouteTest, GetRouteRequest) { 777 FileDescriptor fd = 778 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 779 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 780 781 struct request { 782 struct nlmsghdr hdr; 783 struct rtmsg rtm; 784 struct nlattr nla; 785 struct in_addr sin_addr; 786 }; 787 788 constexpr uint32_t kSeq = 12345; 789 790 struct request req = {}; 791 req.hdr.nlmsg_len = sizeof(req); 792 req.hdr.nlmsg_type = RTM_GETROUTE; 793 req.hdr.nlmsg_flags = NLM_F_REQUEST; 794 req.hdr.nlmsg_seq = kSeq; 795 796 req.rtm.rtm_family = AF_INET; 797 req.rtm.rtm_dst_len = 32; 798 req.rtm.rtm_src_len = 0; 799 req.rtm.rtm_tos = 0; 800 req.rtm.rtm_table = RT_TABLE_UNSPEC; 801 req.rtm.rtm_protocol = RTPROT_UNSPEC; 802 req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; 803 req.rtm.rtm_type = RTN_UNSPEC; 804 req.rtm.rtm_flags = RTM_F_LOOKUP_TABLE; 805 806 req.nla.nla_len = 8; 807 req.nla.nla_type = RTA_DST; 808 inet_aton("127.0.0.2", &req.sin_addr); 809 810 bool rtDstFound = false; 811 ASSERT_NO_ERRNO(NetlinkRequestResponseSingle( 812 fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) { 813 // Validate the reponse to RTM_GETROUTE request with RTM_F_LOOKUP_TABLE 814 // flag. 815 EXPECT_THAT(hdr->nlmsg_type, RTM_NEWROUTE); 816 817 EXPECT_TRUE(hdr->nlmsg_flags == 0) << std::hex << hdr->nlmsg_flags; 818 819 EXPECT_EQ(hdr->nlmsg_seq, kSeq); 820 EXPECT_EQ(hdr->nlmsg_pid, port); 821 822 // RTM_NEWROUTE contains at least the header and rtmsg. 823 ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg))); 824 const struct rtmsg* msg = 825 reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr)); 826 827 // NOTE: rtmsg fields are char fields. 828 std::cout << "Found route table=" << static_cast<int>(msg->rtm_table) 829 << ", protocol=" << static_cast<int>(msg->rtm_protocol) 830 << ", scope=" << static_cast<int>(msg->rtm_scope) 831 << ", type=" << static_cast<int>(msg->rtm_type); 832 833 EXPECT_EQ(msg->rtm_family, AF_INET); 834 EXPECT_EQ(msg->rtm_dst_len, 32); 835 EXPECT_TRUE((msg->rtm_flags & RTM_F_CLONED) == RTM_F_CLONED) 836 << std::hex << msg->rtm_flags; 837 838 int len = RTM_PAYLOAD(hdr); 839 std::cout << ", len=" << len; 840 for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len); 841 attr = RTA_NEXT(attr, len)) { 842 if (attr->rta_type == RTA_DST) { 843 char address[INET_ADDRSTRLEN] = {}; 844 inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address)); 845 std::cout << ", dst=" << address; 846 rtDstFound = true; 847 } else if (attr->rta_type == RTA_OIF) { 848 const char* oif = reinterpret_cast<const char*>(RTA_DATA(attr)); 849 std::cout << ", oif=" << oif; 850 } 851 } 852 853 std::cout << std::endl; 854 })); 855 // Found RTA_DST for RTM_F_LOOKUP_TABLE. 856 EXPECT_TRUE(rtDstFound); 857 } 858 859 // NetlinkRouteTest with a single parameter that must be AF_INET or AF_INET6. 860 using NetlinkRouteIpInvariantTest = ::testing::TestWithParam<int>; 861 862 INSTANTIATE_TEST_SUITE_P(NetlinkRouteIpv4AndIpv6Tests, 863 NetlinkRouteIpInvariantTest, 864 ::testing::Values(AF_INET, AF_INET6)); 865 866 TEST_P(NetlinkRouteIpInvariantTest, AddAndRemoveRoute) { 867 // Gvisor does not support `RTM_NEWROUTE` or `RTM_DELROUTE`. 868 SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix); 869 // CAP_NET_ADMIN is required to modify the routing table. 870 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 871 872 // Based on the test parameter, build an IPv4 or IPv6 destination subnet. 873 int family = GetParam(); 874 struct in_addr dst_v4; 875 struct in6_addr dst_v6; 876 void* dst = nullptr; 877 int dst_len; 878 int prefixlen; 879 switch (family) { 880 case AF_INET: 881 ASSERT_EQ(inet_pton(family, "192.0.2.0", &dst_v4), 1); 882 prefixlen = 24; 883 dst = &dst_v4; 884 dst_len = sizeof(dst_v4); 885 break; 886 case AF_INET6: 887 ASSERT_EQ(inet_pton(family, "2001:db8::", &dst_v6), 1); 888 prefixlen = 64; 889 dst = &dst_v6; 890 dst_len = sizeof(dst_v6); 891 break; 892 default: 893 FAIL() << "address family must be AF_INET or AF_INET6"; 894 } 895 896 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 897 898 // Create should succeed, as no such route in kernel. 899 ASSERT_NO_ERRNO( 900 AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len)); 901 902 // Second create should fail, as we already created the route above. 903 EXPECT_THAT( 904 AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len), 905 PosixErrorIs(EEXIST, _)); 906 907 // First delete should succeed, as route exists. 908 EXPECT_NO_ERRNO( 909 DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len)); 910 911 // Second delete should fail, as route no longer exists. 912 EXPECT_THAT( 913 DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len), 914 PosixErrorIs(ESRCH, _)); 915 } 916 917 // GetRuleDump tests a RTM_GETRULE + NLM_F_DUMP request. 918 TEST(NetlinkRouteTest, GetRuleDump) { 919 // Gvisor does not support `RTM_GETRULE` 920 SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix); 921 922 FileDescriptor fd = 923 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 924 uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); 925 926 struct request { 927 struct nlmsghdr hdr; 928 struct rtmsg rtm; 929 }; 930 931 struct request req = {}; 932 req.hdr.nlmsg_len = sizeof(req); 933 req.hdr.nlmsg_type = RTM_GETRULE; 934 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 935 req.hdr.nlmsg_seq = kSeq; 936 req.rtm.rtm_family = AF_UNSPEC; 937 938 bool ruleFound = false; 939 ASSERT_NO_ERRNO(NetlinkRequestResponse( 940 fd, &req, sizeof(req), 941 [&](const struct nlmsghdr* hdr) { 942 // Validate the response to RTM_GETRULE + NLM_F_DUMP. 943 EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWRULE), Eq(NLMSG_DONE))); 944 945 EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) 946 << std::hex << hdr->nlmsg_flags; 947 948 EXPECT_EQ(hdr->nlmsg_seq, kSeq); 949 EXPECT_EQ(hdr->nlmsg_pid, port); 950 951 // The test should not proceed if the multipart message is done. 952 if (hdr->nlmsg_type == NLMSG_DONE) { 953 return; 954 } 955 956 // RTM_NEWRULE contains at least the header and rule. 957 ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct fib_rule_hdr))); 958 const struct fib_rule_hdr* rule = 959 reinterpret_cast<const struct fib_rule_hdr*>(NLMSG_DATA(hdr)); 960 std::cout << std::dec << "Found rule" 961 << ": family=" << static_cast<int>(rule->family) 962 << ", table=" << static_cast<int>(rule->table) 963 << ", action=" << static_cast<int>(rule->action) << std::endl; 964 // All rules should have a non-zero action. 965 EXPECT_NE(rule->action, 0); 966 ruleFound = true; 967 }, 968 false)); 969 // At least one rule found. 970 EXPECT_TRUE(ruleFound); 971 } 972 973 TEST_P(NetlinkRouteIpInvariantTest, AddAndRemoveRule) { 974 // Gvisor does not support `RTM_NEWRULE` or `RTM_DELRULE`. 975 SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix); 976 // CAP_NET_ADMIN is required to modify the rule table. 977 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 978 979 // Based on the test parameter, build an IPv4 or IPv6 destination subnet. 980 int family = GetParam(); 981 struct in_addr dst_v4; 982 struct in6_addr dst_v6; 983 void* dst = nullptr; 984 int dst_len; 985 int prefixlen; 986 switch (family) { 987 case AF_INET: 988 ASSERT_EQ(inet_pton(family, "192.0.2.0", &dst_v4), 1); 989 prefixlen = 24; 990 dst = &dst_v4; 991 dst_len = sizeof(dst_v4); 992 break; 993 case AF_INET6: 994 ASSERT_EQ(inet_pton(family, "2001:db8::", &dst_v6), 1); 995 prefixlen = 64; 996 dst = &dst_v6; 997 dst_len = sizeof(dst_v6); 998 break; 999 default: 1000 FAIL() << "address family must be AF_INET or AF_INET6"; 1001 } 1002 1003 // Unique values for table and priority fields to ensure the new rule does not 1004 // collide with any of the default rules installed by Linux. 1005 const int kTable = 99; 1006 const int kPriority = 12345; 1007 1008 Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); 1009 1010 // Create should succeed, as no such rule in the kernel. 1011 ASSERT_NO_ERRNO(AddExclusiveLookupInTableRule(family, kTable, kPriority, 1012 prefixlen, dst, dst_len)); 1013 1014 // Second create should fail, as we already created the rule above. 1015 EXPECT_THAT(AddExclusiveLookupInTableRule(family, kTable, kPriority, 1016 prefixlen, dst, dst_len), 1017 PosixErrorIs(EEXIST, _)); 1018 1019 // First delete should succeed, as rule exists. 1020 EXPECT_NO_ERRNO( 1021 DelLookupInTableRule(family, kTable, kPriority, prefixlen, dst, dst_len)); 1022 1023 // Second delete should fail, as rule no longer exists. 1024 EXPECT_THAT( 1025 DelLookupInTableRule(family, kTable, kPriority, prefixlen, dst, dst_len), 1026 PosixErrorIs(ENOENT, _)); 1027 } 1028 1029 // RecvmsgTrunc tests the recvmsg MSG_TRUNC flag with zero length output 1030 // buffer. MSG_TRUNC with a zero length buffer should consume subsequent 1031 // messages off the socket. 1032 TEST(NetlinkRouteTest, RecvmsgTrunc) { 1033 FileDescriptor fd = 1034 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 1035 1036 struct request { 1037 struct nlmsghdr hdr; 1038 struct rtgenmsg rgm; 1039 }; 1040 1041 struct request req; 1042 req.hdr.nlmsg_len = sizeof(req); 1043 req.hdr.nlmsg_type = RTM_GETADDR; 1044 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 1045 req.hdr.nlmsg_seq = kSeq; 1046 req.rgm.rtgen_family = AF_UNSPEC; 1047 1048 struct iovec iov = {}; 1049 iov.iov_base = &req; 1050 iov.iov_len = sizeof(req); 1051 1052 struct msghdr msg = {}; 1053 msg.msg_iov = &iov; 1054 msg.msg_iovlen = 1; 1055 1056 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1057 1058 iov.iov_base = NULL; 1059 iov.iov_len = 0; 1060 1061 int trunclen, trunclen2; 1062 1063 // Note: This test assumes at least two messages are returned by the 1064 // RTM_GETADDR request. That means at least one RTM_NEWLINK message and one 1065 // NLMSG_DONE message. We cannot read all the messages without blocking 1066 // because we would need to read the message into a buffer and check the 1067 // nlmsg_type for NLMSG_DONE. However, the test depends on reading into a 1068 // zero-length buffer. 1069 1070 // First, call recvmsg with MSG_TRUNC. This will read the full message from 1071 // the socket and return it's full length. Subsequent calls to recvmsg will 1072 // read the next messages from the socket. 1073 ASSERT_THAT(trunclen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), 1074 SyscallSucceeds()); 1075 1076 // Message should always be truncated. However, While the destination iov is 1077 // zero length, MSG_TRUNC returns the size of the next message so it should 1078 // not be zero. 1079 ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); 1080 ASSERT_NE(trunclen, 0); 1081 // Returned length is at least the header and ifaddrmsg. 1082 EXPECT_GE(trunclen, sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg)); 1083 1084 // Reset the msg_flags to make sure that the recvmsg call is setting them 1085 // properly. 1086 msg.msg_flags = 0; 1087 1088 // Make a second recvvmsg call to get the next message. 1089 ASSERT_THAT(trunclen2 = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), 1090 SyscallSucceeds()); 1091 ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); 1092 ASSERT_NE(trunclen2, 0); 1093 1094 // Assert that the received messages are not the same. 1095 // 1096 // We are calling recvmsg with a zero length buffer so we have no way to 1097 // inspect the messages to make sure they are not equal in value. The best 1098 // we can do is to compare their lengths. 1099 ASSERT_NE(trunclen, trunclen2); 1100 } 1101 1102 // RecvmsgTruncPeek tests recvmsg with the combination of the MSG_TRUNC and 1103 // MSG_PEEK flags and a zero length output buffer. This is normally used to 1104 // read the full length of the next message on the socket without consuming 1105 // it, so a properly sized buffer can be allocated to store the message. This 1106 // test tests that scenario. 1107 TEST(NetlinkRouteTest, RecvmsgTruncPeek) { 1108 FileDescriptor fd = 1109 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 1110 1111 struct request { 1112 struct nlmsghdr hdr; 1113 struct rtgenmsg rgm; 1114 }; 1115 1116 struct request req; 1117 req.hdr.nlmsg_len = sizeof(req); 1118 req.hdr.nlmsg_type = RTM_GETADDR; 1119 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 1120 req.hdr.nlmsg_seq = kSeq; 1121 req.rgm.rtgen_family = AF_UNSPEC; 1122 1123 struct iovec iov = {}; 1124 iov.iov_base = &req; 1125 iov.iov_len = sizeof(req); 1126 1127 struct msghdr msg = {}; 1128 msg.msg_iov = &iov; 1129 msg.msg_iovlen = 1; 1130 1131 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1132 1133 int type = -1; 1134 do { 1135 int peeklen; 1136 int len; 1137 1138 iov.iov_base = NULL; 1139 iov.iov_len = 0; 1140 1141 // Call recvmsg with MSG_PEEK and MSG_TRUNC. This will peek at the message 1142 // and return it's full length. 1143 // See: MSG_TRUNC http://man7.org/linux/man-pages/man2/recv.2.html 1144 ASSERT_THAT( 1145 peeklen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_PEEK | MSG_TRUNC), 1146 SyscallSucceeds()); 1147 1148 // Message should always be truncated. 1149 ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); 1150 ASSERT_NE(peeklen, 0); 1151 1152 // Reset the message flags for the next call. 1153 msg.msg_flags = 0; 1154 1155 // Make the actual call to recvmsg to get the actual data. We will use 1156 // the length returned from the peek call for the allocated buffer size.. 1157 std::vector<char> buf(peeklen); 1158 iov.iov_base = buf.data(); 1159 iov.iov_len = buf.size(); 1160 ASSERT_THAT(len = RetryEINTR(recvmsg)(fd.get(), &msg, 0), 1161 SyscallSucceeds()); 1162 1163 // Message should not be truncated since we allocated the correct buffer 1164 // size. 1165 EXPECT_NE(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); 1166 1167 // MSG_PEEK should have left data on the socket and the subsequent call 1168 // with should have retrieved the same data. Both calls should have 1169 // returned the message's full length so they should be equal. 1170 ASSERT_NE(len, 0); 1171 ASSERT_EQ(peeklen, len); 1172 1173 for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data()); 1174 NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { 1175 type = hdr->nlmsg_type; 1176 } 1177 } while (type != NLMSG_DONE && type != NLMSG_ERROR); 1178 } 1179 1180 // No SCM_CREDENTIALS are received without SO_PASSCRED set. 1181 TEST(NetlinkRouteTest, NoPasscredNoCreds) { 1182 FileDescriptor fd = 1183 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 1184 1185 ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOff, 1186 sizeof(kSockOptOff)), 1187 SyscallSucceeds()); 1188 1189 struct request { 1190 struct nlmsghdr hdr; 1191 struct rtgenmsg rgm; 1192 }; 1193 1194 struct request req; 1195 req.hdr.nlmsg_len = sizeof(req); 1196 req.hdr.nlmsg_type = RTM_GETADDR; 1197 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 1198 req.hdr.nlmsg_seq = kSeq; 1199 req.rgm.rtgen_family = AF_UNSPEC; 1200 1201 struct iovec iov = {}; 1202 iov.iov_base = &req; 1203 iov.iov_len = sizeof(req); 1204 1205 struct msghdr msg = {}; 1206 msg.msg_iov = &iov; 1207 msg.msg_iovlen = 1; 1208 1209 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1210 1211 iov.iov_base = NULL; 1212 iov.iov_len = 0; 1213 1214 char control[CMSG_SPACE(sizeof(struct ucred))] = {}; 1215 msg.msg_control = control; 1216 msg.msg_controllen = sizeof(control); 1217 1218 // Note: This test assumes at least one message is returned by the 1219 // RTM_GETADDR request. 1220 ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1221 1222 // No control messages. 1223 EXPECT_EQ(CMSG_FIRSTHDR(&msg), nullptr); 1224 } 1225 1226 // SCM_CREDENTIALS are received with SO_PASSCRED set. 1227 TEST(NetlinkRouteTest, PasscredCreds) { 1228 FileDescriptor fd = 1229 ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); 1230 1231 ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOn, 1232 sizeof(kSockOptOn)), 1233 SyscallSucceeds()); 1234 1235 struct request { 1236 struct nlmsghdr hdr; 1237 struct rtgenmsg rgm; 1238 }; 1239 1240 struct request req; 1241 req.hdr.nlmsg_len = sizeof(req); 1242 req.hdr.nlmsg_type = RTM_GETADDR; 1243 req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 1244 req.hdr.nlmsg_seq = kSeq; 1245 req.rgm.rtgen_family = AF_UNSPEC; 1246 1247 struct iovec iov = {}; 1248 iov.iov_base = &req; 1249 iov.iov_len = sizeof(req); 1250 1251 struct msghdr msg = {}; 1252 msg.msg_iov = &iov; 1253 msg.msg_iovlen = 1; 1254 1255 ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1256 1257 iov.iov_base = NULL; 1258 iov.iov_len = 0; 1259 1260 char control[CMSG_SPACE(sizeof(struct ucred))] = {}; 1261 msg.msg_control = control; 1262 msg.msg_controllen = sizeof(control); 1263 1264 // Note: This test assumes at least one message is returned by the 1265 // RTM_GETADDR request. 1266 ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds()); 1267 1268 struct ucred creds; 1269 struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); 1270 ASSERT_NE(cmsg, nullptr); 1271 ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(creds))); 1272 ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); 1273 ASSERT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); 1274 1275 memcpy(&creds, CMSG_DATA(cmsg), sizeof(creds)); 1276 1277 // The peer is the kernel, which is "PID" 0. 1278 EXPECT_EQ(creds.pid, 0); 1279 // The kernel identifies as root. Also allow nobody in case this test is 1280 // running in a userns without root mapped. 1281 EXPECT_THAT(creds.uid, AnyOf(Eq(0), Eq(65534))); 1282 EXPECT_THAT(creds.gid, AnyOf(Eq(0), Eq(65534))); 1283 } 1284 1285 } // namespace 1286 1287 } // namespace testing 1288 } // namespace gvisor