| // 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(); |
| } |