blob: 62465e794c0e35688a1eeb5cbb9f97b79d0d31a8 [file] [log] [blame]
// 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