blob: 07048ce5e4b3958192f974553b67678be7d4ed2e [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.
#include <arpa/inet.h>
#include <fuchsia/net/cpp/fidl.h>
#include <fuchsia/netstack/cpp/fidl.h>
#include <garnet/lib/inet/ip_address.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <src/lib/fxl/strings/string_printf.h>
#include <sys/socket.h>
#include <future>
#include "src/lib/files/unique_fd.h"
#include "enclosed_guest.h"
#include "guest_test.h"
using ::testing::Each;
using ::testing::HasSubstr;
static constexpr char kVirtioNetUtil[] = "virtio_net_test_util";
static constexpr size_t kMtu = 1500;
static constexpr size_t kTestPacketSize = 1000;
// Includes ethernet + IPv4 + UDP headers.
static constexpr size_t kHeadersSize = 42;
template <class T>
T* GuestTest<T>::enclosed_guest_ = nullptr;
class VirtioNetZirconGuest : public ZirconEnclosedGuest {
public:
zx_status_t LaunchInfo(
fuchsia::virtualization::LaunchInfo* launch_info) override {
launch_info->url = kZirconGuestUrl;
launch_info->args.push_back("--virtio-gpu=false");
launch_info->args.push_back("--virtio-net=true");
launch_info->args.push_back("--cmdline-add=kernel.serial=none");
// Disable netsvc to avoid spamming the net device with logs.
launch_info->args.push_back("--cmdline-add=netsvc.disable=true");
launch_info->args.push_back("--legacy-net=false");
return ZX_OK;
}
};
class VirtioNetDebianGuest : public DebianEnclosedGuest {
public:
zx_status_t LaunchInfo(
fuchsia::virtualization::LaunchInfo* launch_info) override {
launch_info->url = kDebianGuestUrl;
launch_info->args.push_back("--virtio-gpu=false");
launch_info->args.push_back("--virtio-net=true");
launch_info->args.push_back("--legacy-net=false");
return ZX_OK;
}
};
static void TestThread(const MockNetstack& netstack, uint8_t receive_byte,
uint8_t send_byte, bool use_raw_packets) {
async::Loop loop(&kAsyncLoopConfigAttachToThread);
// This thread will loop indefinitely until it receives the correct packet.
// The test will time out via RunUtil in the test fixture if we fail to
// receive the correct packet.
uint8_t pkt[kMtu];
zx_status_t status;
while (true) {
size_t actual;
status = netstack.ReceivePacket(static_cast<void*>(pkt), kMtu, &actual);
ASSERT_EQ(status, ZX_OK);
bool match_test_packet = false;
size_t headers_size = use_raw_packets ? 0 : kHeadersSize;
if (actual == headers_size + kTestPacketSize) {
match_test_packet = true;
for (size_t i = headers_size; i != actual; ++i) {
if (pkt[i] != receive_byte) {
match_test_packet = false;
break;
}
}
}
if (match_test_packet) {
break;
}
}
memset(pkt, send_byte, kTestPacketSize);
if (use_raw_packets) {
status = netstack.SendPacket(static_cast<void*>(pkt), kTestPacketSize);
} else {
status = netstack.SendUdpPacket(static_cast<void*>(pkt), kTestPacketSize);
}
ASSERT_EQ(status, ZX_OK);
}
using VirtioNetZirconGuestTest = GuestTest<VirtioNetZirconGuest>;
TEST_F(VirtioNetZirconGuestTest, ReceiveAndSend) {
auto handle = std::async(std::launch::async, [this] {
MockNetstack* netstack = this->GetEnclosedGuest()->GetNetstack();
TestThread(*netstack, 0xab, 0xba, true /* use_raw_packets */);
});
std::string args =
fxl::StringPrintf("%u %u %zu", 0xab, 0xba, kTestPacketSize);
std::string result;
EXPECT_EQ(this->RunUtil(kVirtioNetUtil, args, &result), ZX_OK);
EXPECT_THAT(result, HasSubstr("PASS"));
handle.wait();
}
using VirtioNetDebianGuestTest = GuestTest<VirtioNetDebianGuest>;
TEST_F(VirtioNetDebianGuestTest, ReceiveAndSend) {
auto handle = std::async(std::launch::async, [this] {
MockNetstack* netstack = this->GetEnclosedGuest()->GetNetstack();
TestThread(*netstack, 0xab, 0xba, false /* use_raw_packets */);
});
// Configure the guest IPv4 address.
EXPECT_EQ(this->Execute("ifconfig enp0s5 192.168.0.10"), ZX_OK);
// Manually add a route to the host.
EXPECT_EQ(this->Execute("arp -s 192.168.0.1 02:1a:11:00:00:00"), ZX_OK);
std::string args =
fxl::StringPrintf("%u %u %zu", 0xab, 0xba, kTestPacketSize);
std::string result;
EXPECT_EQ(this->RunUtil(kVirtioNetUtil, args, &result), ZX_OK);
EXPECT_THAT(result, HasSubstr("PASS"));
handle.wait();
}