| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // These tests run with an external network interface providing default route |
| // addresses. |
| |
| #include <arpa/inet.h> |
| #include <sys/utsname.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace { |
| |
| // This is the expected derived device name for the mac address |
| // aa:bb:cc:dd:ee:ff, specified in meta/netstack_external_network_test.cmx |
| // (see facets.fuchsia.netemul.networks.endpoints[0].mac). |
| // |
| // Since this only used on Fuchsia, it is conditionally compiled. |
| #if defined(__Fuchsia__) |
| const char kDerivedDeviceName[] = "train-cache-uncle-chill"; |
| #endif |
| |
| TEST(ExternalNetworkTest, ConnectToNonRoutableINET) { |
| int s; |
| ASSERT_GE(s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0), 0) << strerror(errno); |
| |
| struct sockaddr_in addr = {}; |
| addr.sin_family = AF_INET; |
| |
| // RFC5737#section-3 |
| // |
| // The blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2),and |
| // 203.0.113.0/24 (TEST-NET-3) are provided for use in documentation. |
| ASSERT_EQ(inet_pton(AF_INET, "192.0.2.55", &addr.sin_addr), 1) << strerror(errno); |
| |
| addr.sin_port = htons(1337); |
| |
| ASSERT_EQ(connect(s, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), -1); |
| |
| // The host env (linux) may have a route to the remote (e.g. default route), |
| // resulting in a TCP handshake being attempted and errno being set to |
| // EINPROGRESS. In a fuchsia environment, errno will never be set to |
| // EINPROGRESS because a TCP handshake will never be performed (the test is |
| // run without network configurations that make the remote routable). |
| // |
| // TODO(fxbug.dev/46817): Set errno to the same value as linux when a remote is |
| // unroutable. |
| #if defined(__linux__) |
| ASSERT_TRUE(errno == EINPROGRESS || errno == ENETUNREACH) << strerror(errno); |
| #else |
| ASSERT_EQ(errno, EHOSTUNREACH) << strerror(errno); |
| #endif |
| |
| ASSERT_EQ(close(s), 0) << strerror(errno); |
| } |
| |
| TEST(ExternalNetworkTest, ConnectToNonRoutableINET6) { |
| int s; |
| ASSERT_GE(s = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0), 0) << strerror(errno); |
| |
| struct sockaddr_in6 addr = {}; |
| addr.sin6_family = AF_INET6; |
| |
| // RFC3849#section-2 |
| // |
| // The prefix allocated for documentation purposes is 2001:DB8::/32. |
| ASSERT_EQ(inet_pton(AF_INET6, "2001:db8::55", &addr.sin6_addr), 1) << strerror(errno); |
| |
| addr.sin6_port = htons(1337); |
| |
| ASSERT_EQ(connect(s, reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), -1); |
| |
| // The host env (linux) may have a route to the remote (e.g. default route), |
| // resulting in a TCP handshake being attempted and errno being set to |
| // EINPROGRESS. In a fuchsia environment, errno will never be set to |
| // EINPROGRESS because a TCP handshake will never be performed (the test is |
| // run without network configurations that make the remote routable). |
| // |
| // TODO(fxbug.dev/46817): Set errno to the same value as linux when a remote is |
| // unroutable. |
| #if defined(__linux__) |
| ASSERT_TRUE(errno == EINPROGRESS || errno == ENETUNREACH) << strerror(errno); |
| #else |
| ASSERT_EQ(errno, EHOSTUNREACH) << strerror(errno); |
| #endif |
| |
| ASSERT_EQ(close(s), 0) << strerror(errno); |
| } |
| |
| TEST(ExternalNetworkTest, GetHostName) { |
| char hostname[HOST_NAME_MAX]; |
| EXPECT_GE(gethostname(hostname, sizeof(hostname)), 0) << strerror(errno); |
| #if defined(__Fuchsia__) |
| ASSERT_STREQ(hostname, kDerivedDeviceName); |
| #endif |
| } |
| |
| TEST(ExternalNetworkTest, Uname) { |
| utsname uts; |
| EXPECT_EQ(uname(&uts), 0) << strerror(errno); |
| #if defined(__Fuchsia__) |
| ASSERT_STREQ(uts.nodename, kDerivedDeviceName); |
| #endif |
| } |
| |
| TEST(ExternalNetworkTest, ConnectToRoutableNonexistentINET) { |
| int fd; |
| ASSERT_GE(fd = socket(AF_INET, SOCK_STREAM, 0), 0) << strerror(errno); |
| |
| struct sockaddr_in addr = {}; |
| addr.sin_family = AF_INET; |
| // Connect to a routable address to a non-existing remote. This triggers ARP resolution which is |
| // expected to fail. |
| addr.sin_addr.s_addr = htonl(0xd0e0a0d); |
| addr.sin_port = htons(1337); |
| |
| EXPECT_EQ(connect(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), -1); |
| // TODO(tamird): match linux. https://github.com/google/gvisor/issues/923. |
| #if defined(__linux__) |
| EXPECT_EQ(errno, ETIMEDOUT) << strerror(errno); |
| #else |
| EXPECT_EQ(errno, EHOSTUNREACH) << strerror(errno); |
| #endif |
| |
| EXPECT_EQ(close(fd), 0) << strerror(errno); |
| } |
| |
| // Test to ensure UDP send doesn`t error even with ARP timeouts. |
| // TODO(fxb.dev/35006): Test needs to be extended or replicated to test |
| // against other transport send errors. |
| TEST(ExternalNetworkTest, UDPErrSend) { |
| int sendfd; |
| ASSERT_GE(sendfd = socket(AF_INET, SOCK_DGRAM, 0), 0) << strerror(errno); |
| |
| struct sockaddr_in addr = {}; |
| addr.sin_family = AF_INET; |
| addr.sin_port = htons(1337); |
| char bytes[64]; |
| // Assign to a ssize_t variable to avoid compiler warnings for signedness in the EXPECTs below. |
| ssize_t len = sizeof(bytes); |
| |
| // Precondition sanity check: write completes without error. |
| addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| EXPECT_EQ(sendto(sendfd, bytes, sizeof(bytes), 0, reinterpret_cast<struct sockaddr*>(&addr), |
| sizeof(addr)), |
| len) |
| << strerror(errno); |
| |
| // Send to a routable address to a non-existing remote. This triggers ARP resolution which is |
| // expected to fail, but that failure is expected to leave the socket alive. Before the change |
| // that added this test, the socket would be incorrectly shut down. |
| addr.sin_addr.s_addr = htonl(0xd0e0a0d); |
| ssize_t ret = sendto(sendfd, bytes, sizeof(bytes), 0, reinterpret_cast<struct sockaddr*>(&addr), |
| sizeof(addr)); |
| |
| // TODO(tamird): Why does linux not signal an error? Should we do the same? |
| #if defined(__linux__) |
| EXPECT_EQ(ret, len) << strerror(errno); |
| #else |
| EXPECT_EQ(ret, -1); |
| EXPECT_EQ(errno, EHOSTUNREACH) << strerror(errno); |
| #endif |
| |
| // Postcondition sanity check: write completes without error. |
| addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| EXPECT_EQ(sendto(sendfd, bytes, sizeof(bytes), 0, reinterpret_cast<struct sockaddr*>(&addr), |
| sizeof(addr)), |
| len) |
| << strerror(errno); |
| |
| EXPECT_EQ(close(sendfd), 0) << strerror(errno); |
| } |
| |
| } // namespace |