blob: 459131f48fb57507f6f7e9c010c8df951625470a [file] [log] [blame]
// Copyright 2021 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 "src/connectivity/network/mdns/service/transport/mdns_transceiver.h"
#include <fuchsia/hardware/network/cpp/fidl.h>
#include <fuchsia/net/interfaces/cpp/fidl.h>
#include <fuchsia/net/interfaces/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h>
#include "src/connectivity/network/mdns/service/common/mdns_addresses.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/lib/testing/predicates/status.h"
namespace mdns::test {
constexpr std::array<uint8_t, 4> kIPv4Address1 = {1, 2, 3, 1};
constexpr std::array<uint8_t, 4> kIPv4Address2 = {4, 5, 6, 1};
constexpr std::array<uint8_t, 16> kIPv6Address1 = {0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 3, 4};
constexpr std::array<uint8_t, 16> kIPv6Address2 = {0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 3, 2, 1};
constexpr std::array<uint8_t, 16> kIPv6AddressNotLinkLocal = {0xff, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 3, 2, 1};
constexpr std::array<uint8_t, 16> kIPv6AddressNotLinkLocal2 = {0xff, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 3, 2, 2};
constexpr std::array<uint8_t, 16> kIPv6AddressNotLinkLocal3 = {0xff, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 3, 2, 3};
constexpr std::array<uint8_t, 16> kIPv6AddressNotLinkLocal4 = {0xff, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 4, 3, 2, 4};
constexpr uint8_t kIPv4PrefixLength = 24;
constexpr uint8_t kIPv6PrefixLength = 64;
constexpr uint8_t kID = 1;
constexpr const char kName[] = "test01";
constexpr const char kHostName[] = "testhostname";
namespace {
fuchsia::net::Ipv4Address ToFIDL(const std::array<uint8_t, 4>& bytes) {
fuchsia::net::Ipv4Address addr;
std::copy(bytes.cbegin(), bytes.cend(), addr.addr.begin());
return addr;
}
fuchsia::net::Ipv6Address ToFIDL(const std::array<uint8_t, 16>& bytes) {
fuchsia::net::Ipv6Address addr;
std::copy(bytes.cbegin(), bytes.cend(), addr.addr.begin());
return addr;
}
void InitAddress(fuchsia::net::interfaces::Address& addr, const std::array<uint8_t, 4>& bytes) {
fuchsia::net::Subnet subnet{
.prefix_len = kIPv4PrefixLength,
};
subnet.addr.set_ipv4(ToFIDL(bytes));
addr.set_addr(std::move(subnet));
}
void InitAddress(fuchsia::net::interfaces::Address& addr, const std::array<uint8_t, 16>& bytes) {
fuchsia::net::Subnet subnet{
.prefix_len = kIPv6PrefixLength,
};
subnet.addr.set_ipv6(ToFIDL(bytes));
addr.set_addr(std::move(subnet));
}
std::vector<fuchsia::net::interfaces::Address> Addresses1() {
std::vector<fuchsia::net::interfaces::Address> addresses;
addresses.reserve(2);
InitAddress(addresses.emplace_back(), kIPv4Address1);
InitAddress(addresses.emplace_back(), kIPv6Address1);
// To verify that non-link-local addresses do not cause a transceiver to be created.
InitAddress(addresses.emplace_back(), kIPv6AddressNotLinkLocal);
return addresses;
}
std::vector<fuchsia::net::interfaces::Address> Addresses1Extra() {
std::vector<fuchsia::net::interfaces::Address> addresses;
addresses.reserve(2);
InitAddress(addresses.emplace_back(), kIPv4Address1);
InitAddress(addresses.emplace_back(), kIPv6Address1);
InitAddress(addresses.emplace_back(), kIPv6AddressNotLinkLocal);
InitAddress(addresses.emplace_back(), kIPv6AddressNotLinkLocal2);
InitAddress(addresses.emplace_back(), kIPv6AddressNotLinkLocal3);
InitAddress(addresses.emplace_back(), kIPv6AddressNotLinkLocal4);
return addresses;
}
std::vector<fuchsia::net::interfaces::Address> Addresses2() {
std::vector<fuchsia::net::interfaces::Address> addresses;
addresses.reserve(2);
InitAddress(addresses.emplace_back(), kIPv4Address2);
InitAddress(addresses.emplace_back(), kIPv6Address2);
return addresses;
}
bool VerifyAddressResource(std::shared_ptr<DnsResource> resource, inet::IpAddress expected_address,
bool cache_flush) {
EXPECT_TRUE(!!resource);
if (!resource) {
return false;
}
EXPECT_EQ(kHostName, resource->name_.dotted_string_);
EXPECT_EQ(expected_address.is_v4() ? DnsType::kA : DnsType::kAaaa, resource->type_);
EXPECT_EQ(DnsClass::kIn, resource->class_);
EXPECT_EQ(cache_flush, resource->cache_flush_);
EXPECT_EQ(DnsResource::kShortTimeToLive, resource->time_to_live_);
if (resource->type_ == DnsType::kA) {
EXPECT_EQ(expected_address, resource->a_.address_.address_);
if (expected_address != resource->a_.address_.address_) {
return false;
}
} else {
EXPECT_EQ(expected_address, resource->aaaa_.address_.address_);
if (expected_address != resource->aaaa_.address_.address_) {
return false;
}
}
return (kHostName == resource->name_.dotted_string_) &&
((expected_address.is_v4() ? DnsType::kA : DnsType::kAaaa) == resource->type_) &&
(DnsClass::kIn == resource->class_) && (cache_flush == resource->cache_flush_) &&
(DnsResource::kShortTimeToLive == resource->time_to_live_);
}
} // namespace
class InterfacesWatcherImpl : public fuchsia::net::interfaces::testing::Watcher_TestBase {
public:
void NotImplemented_(const std::string& name) override {
std::cout << "Not implemented: " << name << std::endl;
}
void Watch(WatchCallback callback) override {
ASSERT_FALSE(watch_cb_.has_value());
watch_cb_ = std::move(callback);
}
bool CompleteWatchCallback(fuchsia::net::interfaces::Event event) {
if (watch_cb_.has_value()) {
(*watch_cb_)(std::move(event));
watch_cb_.reset();
return true;
} else {
return false;
}
}
private:
std::optional<WatchCallback> watch_cb_;
};
class StubInterfaceTransceiver : public MdnsInterfaceTransceiver {
public:
StubInterfaceTransceiver(inet::IpAddress address, const std::string& name, uint32_t id,
Media media)
: MdnsInterfaceTransceiver(address, name, id, media),
ip_versions_(address.is_v4() ? IpVersions::kV4 : IpVersions::kV6) {}
static std::unique_ptr<MdnsInterfaceTransceiver> Create(inet::IpAddress address,
const std::string& name, uint32_t id,
Media media) {
return std::make_unique<StubInterfaceTransceiver>(address, name, id, media);
}
const std::vector<std::shared_ptr<DnsResource>>& GetInterfaceAddressResources(
const std::string& host_full_name) {
return MdnsInterfaceTransceiver::GetInterfaceAddressResources(host_full_name);
}
protected:
enum IpVersions IpVersions() override { return ip_versions_; }
int SetOptionDisableMulticastLoop() override { return 0; }
int SetOptionJoinMulticastGroup() override { return 0; }
int SetOptionOutboundInterface() override { return 0; }
int SetOptionUnicastTtl() override { return 0; }
int SetOptionMulticastTtl() override { return 0; }
int SetOptionFamilySpecific() override { return 0; }
int Bind() override { return 0; }
ssize_t SendTo(const void* buffer, size_t size, const inet::SocketAddress& address) override {
return 0;
}
bool Start(InboundMessageCallback callback) override { return true; }
void Stop() override {}
private:
enum IpVersions ip_versions_;
};
class MdnsTransceiverTests : public gtest::TestLoopFixture {
public:
MdnsTransceiverTests()
: binding_(std::make_unique<fidl::Binding<fuchsia::net::interfaces::Watcher>>(
&fake_watcher_impl_)),
v4_address1_(inet::IpAddress(ToFIDL(kIPv4Address1))),
v4_address2_(inet::IpAddress(ToFIDL(kIPv4Address2))),
v6_address1_(inet::IpAddress(ToFIDL(kIPv6Address1))),
v6_address2_(inet::IpAddress(ToFIDL(kIPv6Address2))),
v6_address_not_link_local_(inet::IpAddress(ToFIDL(kIPv6AddressNotLinkLocal))),
v6_address_not_link_local_2_(inet::IpAddress(ToFIDL(kIPv6AddressNotLinkLocal2))),
v6_address_not_link_local_3_(inet::IpAddress(ToFIDL(kIPv6AddressNotLinkLocal3))),
v6_address_not_link_local_4_(inet::IpAddress(ToFIDL(kIPv6AddressNotLinkLocal4))) {
properties_ = fuchsia::net::interfaces::Properties();
properties_.set_id(kID);
properties_.set_name(kName);
properties_.set_device_class(fuchsia::net::interfaces::DeviceClass::WithDevice(
fuchsia::hardware::network::DeviceClass::WLAN));
properties_.set_online(true);
properties_.set_has_default_ipv4_route(false);
properties_.set_has_default_ipv6_route(false);
properties_.set_addresses(Addresses1());
}
protected:
void SetUp() override {
TestLoopFixture::SetUp();
fuchsia::net::interfaces::WatcherPtr watcher;
ASSERT_OK(binding_->Bind(watcher.NewRequest()));
transceiver_.Start(
std::move(watcher), []() {}, [](auto, auto) {}, StubInterfaceTransceiver::Create);
}
void TearDown() override {
transceiver_.Stop();
TestLoopFixture::TearDown();
}
MdnsTransceiver transceiver_;
InterfacesWatcherImpl fake_watcher_impl_;
const std::unique_ptr<fidl::Binding<fuchsia::net::interfaces::Watcher>> binding_;
fuchsia::net::interfaces::Properties properties_;
const inet::IpAddress v4_address1_, v4_address2_, v6_address1_, v6_address2_,
v6_address_not_link_local_, v6_address_not_link_local_2_, v6_address_not_link_local_3_,
v6_address_not_link_local_4_;
};
TEST_F(MdnsTransceiverTests, IgnoreLoopback) {
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
properties_.set_device_class(
fuchsia::net::interfaces::DeviceClass::WithLoopback(fuchsia::net::interfaces::Empty()));
RunLoopUntilIdle();
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithExisting(std::move(properties_))));
RunLoopUntilIdle();
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
}
TEST_F(MdnsTransceiverTests, OnlineChange) {
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
RunLoopUntilIdle();
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithExisting(std::move(properties_))));
RunLoopUntilIdle();
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
fuchsia::net::interfaces::Properties online_false;
online_false.set_id(kID);
online_false.set_online(false);
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(online_false))));
RunLoopUntilIdle();
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
fuchsia::net::interfaces::Properties online_true;
online_false.set_id(kID);
online_false.set_online(true);
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(online_false))));
RunLoopUntilIdle();
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
}
TEST_F(MdnsTransceiverTests, AddressesChange) {
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
RunLoopUntilIdle();
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithExisting(std::move(properties_))));
RunLoopUntilIdle();
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
fuchsia::net::interfaces::Properties addresses_change;
addresses_change.set_id(kID);
addresses_change.set_addresses(Addresses2());
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(addresses_change))));
RunLoopUntilIdle();
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
}
TEST_F(MdnsTransceiverTests, OnlineAndAddressesChange) {
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
RunLoopUntilIdle();
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithExisting(std::move(properties_))));
RunLoopUntilIdle();
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
fuchsia::net::interfaces::Properties offline_and_addresses_change;
offline_and_addresses_change.set_id(kID);
offline_and_addresses_change.set_online(false);
offline_and_addresses_change.set_addresses(Addresses2());
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(offline_and_addresses_change))));
RunLoopUntilIdle();
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
fuchsia::net::interfaces::Properties online_and_addresses_change;
online_and_addresses_change.set_id(kID);
online_and_addresses_change.set_online(true);
online_and_addresses_change.set_addresses(Addresses1());
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(online_and_addresses_change))));
RunLoopUntilIdle();
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v4_address1_), nullptr);
EXPECT_NE(transceiver_.GetInterfaceTransceiver(v6_address1_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v4_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address2_), nullptr);
EXPECT_EQ(transceiver_.GetInterfaceTransceiver(v6_address_not_link_local_), nullptr);
}
// Tests that interface address resources at the transceivers are updated properly when address
// changes occur and that cache_flush and other resource properties are properly set.
TEST_F(MdnsTransceiverTests, InterfaceAddressResources) {
RunLoopUntilIdle();
// Send the initial NIC configuration. Initial addresses are |v4_address1_|, |v6_address1_| and
// |v6_address_not_link_local_|.
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithExisting(std::move(properties_))));
RunLoopUntilIdle();
// Get the expected transceivers created based on the NIC configuration.
auto v4_transceiver = reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v4_address1_));
auto v6_transceiver = reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v6_address1_));
EXPECT_NE(v4_transceiver, nullptr);
EXPECT_NE(v6_transceiver, nullptr);
// Expect that the correct address resources are generated by the transceivers with the
// correct cache_flush bits. For a given transceiver, the first A (V4) and the first AAAA (V6)
// resource should have cache_flush set and the remainder of the A/AAAA resources should not.
auto& v4_addr = v4_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(3u, v4_addr.size());
EXPECT_TRUE(VerifyAddressResource(v4_addr[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr[2], v6_address_not_link_local_, false));
auto& v6_addr = v6_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(3u, v6_addr.size());
EXPECT_TRUE(VerifyAddressResource(v6_addr[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr[2], v6_address_not_link_local_, false));
// Send an address change that adds new V6 addresses.
fuchsia::net::interfaces::Properties addresses_change;
addresses_change.set_id(kID);
addresses_change.set_addresses(Addresses1Extra());
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(addresses_change))));
RunLoopUntilIdle();
// Ensure that the transceivers have not been deleted or replaced.
EXPECT_EQ(v4_transceiver, reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v4_address1_)));
EXPECT_EQ(v6_transceiver, reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v6_address1_)));
// Expect that the correct address resources are generated by the transceivers with the
// correct cache_flush bits. For a given transceiver, the first A (V4) and the first AAAA (V6)
// resource should have cache_flush set and the remainder of the A/AAAA resources should not.
auto& v4_addr_2 = v4_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(6u, v4_addr.size());
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[2], v6_address_not_link_local_, false));
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[3], v6_address_not_link_local_2_, false));
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[4], v6_address_not_link_local_3_, false));
EXPECT_TRUE(VerifyAddressResource(v4_addr_2[5], v6_address_not_link_local_4_, false));
auto& v6_addr_2 = v6_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(6u, v6_addr_2.size());
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[2], v6_address_not_link_local_, false));
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[3], v6_address_not_link_local_2_, false));
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[4], v6_address_not_link_local_3_, false));
EXPECT_TRUE(VerifyAddressResource(v6_addr_2[5], v6_address_not_link_local_4_, false));
// Send an address change that remove some V6 addresses.
fuchsia::net::interfaces::Properties addresses_change_2;
addresses_change_2.set_id(kID);
addresses_change_2.set_addresses(Addresses1());
ASSERT_TRUE(fake_watcher_impl_.CompleteWatchCallback(
fuchsia::net::interfaces::Event::WithChanged(std::move(addresses_change_2))));
RunLoopUntilIdle();
// Ensure that the transceivers have not been deleted or replaced.
EXPECT_EQ(v4_transceiver, reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v4_address1_)));
EXPECT_EQ(v6_transceiver, reinterpret_cast<StubInterfaceTransceiver*>(
transceiver_.GetInterfaceTransceiver(v6_address1_)));
// Expect that the correct address resources are generated by the transceivers with the
// correct cache_flush bits. For a given transceiver, the first A (V4) and the first AAAA (V6)
// resource should have cache_flush set and the remainder of the A/AAAA resources should not.
auto& v4_addr_3 = v4_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(3u, v4_addr_3.size());
EXPECT_TRUE(VerifyAddressResource(v4_addr_3[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr_3[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v4_addr_3[2], v6_address_not_link_local_, false));
auto& v6_addr_3 = v6_transceiver->GetInterfaceAddressResources(kHostName);
EXPECT_EQ(3u, v6_addr_3.size());
EXPECT_TRUE(VerifyAddressResource(v6_addr_3[0], v4_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr_3[1], v6_address1_, true));
EXPECT_TRUE(VerifyAddressResource(v6_addr_3[2], v6_address_not_link_local_, false));
}
} // namespace mdns::test