blob: e2bf8419509a3c7d6c4927afbdc6c13120d7d445 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <gmock/gmock.h>
#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>
#include "patchpanel/fake_shill_client.h"
#include "patchpanel/fake_system.h"
#include "patchpanel/guest_ipv6_service.h"
#include "patchpanel/mock_datapath.h"
#include "patchpanel/shill_client.h"
using ::testing::Args;
namespace patchpanel {
namespace {
class GuestIPv6ServiceUnderTest : public GuestIPv6Service {
public:
GuestIPv6ServiceUnderTest(Datapath* datapath,
ShillClient* shill_client,
System* system)
: GuestIPv6Service(nullptr, datapath, shill_client, system) {}
MOCK_METHOD3(SendNDProxyControl,
void(NDProxyControlMessage::NDProxyRequestType type,
int32_t if_id_primary,
int32_t if_id_secondary));
MOCK_METHOD2(StartRAServer,
bool(const std::string& ifname, const std::string& prefix));
MOCK_METHOD1(StopRAServer, bool(const std::string& ifname));
void FakeNDProxyNeighborDetectionSignal(int if_id, const in6_addr& ip6addr) {
NeighborDetectedSignal msg;
msg.set_if_id(if_id);
msg.set_ip(&ip6addr, sizeof(in6_addr));
NDProxySignalMessage nm;
*nm.mutable_neighbor_detected_signal() = msg;
FeedbackMessage fm;
*fm.mutable_ndproxy_signal() = nm;
OnNDProxyMessage(fm);
}
};
} // namespace
class GuestIPv6ServiceTest : public ::testing::Test {
protected:
void SetUp() override {
shill_client_ = shill_helper_.FakeClient();
shill_client_->SetFakeDeviceProperties(
"up1", ShillClient::Device{
ShillClient::Device::Type::kEthernet, 1, "up1", "", {}});
shill_client_->SetFakeDeviceProperties(
"up2", ShillClient::Device{
ShillClient::Device::Type::kEthernet, 2, "up2", "", {}});
system_ = std::make_unique<FakeSystem>();
datapath_ = std::make_unique<MockDatapath>();
ON_CALL(*datapath_, MaskInterfaceFlags).WillByDefault(Return(true));
}
FakeShillClientHelper shill_helper_;
std::unique_ptr<FakeShillClient> shill_client_;
std::unique_ptr<FakeSystem> system_;
std::unique_ptr<MockDatapath> datapath_;
};
TEST_F(GuestIPv6ServiceTest, SingleUpstreamSingleDownstream) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
EXPECT_CALL(*system_, IfNametoindex("up1")).WillOnce(Return(1));
EXPECT_CALL(*system_, IfNametoindex("down1")).WillOnce(Return(101));
EXPECT_CALL(*datapath_, MaskInterfaceFlags("up1", IFF_ALLMULTI, 0))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, MaskInterfaceFlags("down1", IFF_ALLMULTI, 0))
.WillOnce(Return(true));
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
target.StartForwarding("up1", "down1");
// This should work even IfNametoindex is returning 0 (netdevices can be
// already gone when StopForwarding() being called).
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(0));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(0));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, 1, 101));
target.StopForwarding("up1", "down1");
EXPECT_CALL(*system_, IfNametoindex("up1")).WillOnce(Return(1));
EXPECT_CALL(*system_, IfNametoindex("down1")).WillOnce(Return(101));
EXPECT_CALL(*datapath_, MaskInterfaceFlags("up1", IFF_ALLMULTI, 0))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_, MaskInterfaceFlags("down1", IFF_ALLMULTI, 0))
.WillOnce(Return(true));
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
target.StartForwarding("up1", "down1");
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, 1, 101));
target.StopUplink("up1");
}
MATCHER_P2(AreTheseTwo, a, b, "") {
return (a == std::get<0>(arg) && b == std::get<1>(arg)) ||
(b == std::get<0>(arg) && a == std::get<1>(arg));
}
TEST_F(GuestIPv6ServiceTest, MultipleUpstreamMultipleDownstream) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(1));
ON_CALL(*system_, IfNametoindex("up2")).WillByDefault(Return(2));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(101));
ON_CALL(*system_, IfNametoindex("down2")).WillByDefault(Return(102));
ON_CALL(*system_, IfNametoindex("down3")).WillByDefault(Return(103));
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
target.StartForwarding("up1", "down1");
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 2, 102));
target.StartForwarding("up2", "down2");
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 103));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NS_NA, _, _))
.With(Args<1, 2>(AreTheseTwo(101, 103)));
target.StartForwarding("up1", "down3");
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(1, 103)));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(101, 103)));
target.StopForwarding("up1", "down3");
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 2, 103));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NS_NA, _, _))
.With(Args<1, 2>(AreTheseTwo(102, 103)));
target.StartForwarding("up2", "down3");
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(2, 102)));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(2, 103)));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(102, 103)));
target.StopUplink("up2");
}
TEST_F(GuestIPv6ServiceTest, AdditionalDatapathSetup) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(1));
ON_CALL(*system_, IfNametoindex("up2")).WillByDefault(Return(2));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(101));
ON_CALL(*system_, IfIndextoname(101)).WillByDefault(Return("down1"));
// StartForwarding() and OnUplinkIPv6Changed() can be triggered in different
// order in different scenario so we need to verify both.
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
target.StartForwarding("up1", "down1");
EXPECT_CALL(*datapath_, AddIPv6NeighborProxy("down1", "2001:db8:0:100::1234"))
.WillOnce(Return(true));
target.OnUplinkIPv6Changed("up1", "2001:db8:0:100::1234");
EXPECT_CALL(*datapath_, AddIPv6HostRoute("down1", "2001:db8:0:100::abcd", 128,
"2001:db8:0:100::1234"));
target.FakeNDProxyNeighborDetectionSignal(
101, StringToIPv6Address("2001:db8:0:100::abcd"));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(1, 101)));
EXPECT_CALL(*datapath_,
RemoveIPv6NeighborProxy("down1", "2001:db8:0:100::1234"));
EXPECT_CALL(*datapath_, RemoveIPv6HostRoute("2001:db8:0:100::abcd", 128));
target.StopForwarding("up1", "down1");
// OnUplinkIPv6Changed -> StartForwarding
target.OnUplinkIPv6Changed("up1", "2001:db8:0:200::1234");
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
EXPECT_CALL(*datapath_, AddIPv6NeighborProxy("down1", "2001:db8:0:200::1234"))
.WillOnce(Return(true));
target.StartForwarding("up1", "down1");
EXPECT_CALL(*datapath_, AddIPv6HostRoute("down1", "2001:db8:0:200::abcd", 128,
"2001:db8:0:200::1234"));
target.FakeNDProxyNeighborDetectionSignal(
101, StringToIPv6Address("2001:db8:0:200::abcd"));
EXPECT_CALL(*datapath_, AddIPv6HostRoute("down1", "2001:db8:0:200::9876", 128,
"2001:db8:0:200::1234"));
target.FakeNDProxyNeighborDetectionSignal(
101, StringToIPv6Address("2001:db8:0:200::9876"));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(1, 101)));
EXPECT_CALL(*datapath_, RemoveIPv6HostRoute("2001:db8:0:200::abcd", 128));
EXPECT_CALL(*datapath_, RemoveIPv6HostRoute("2001:db8:0:200::9876", 128));
EXPECT_CALL(*datapath_,
RemoveIPv6NeighborProxy("down1", "2001:db8:0:200::1234"));
target.StopUplink("up1");
}
TEST_F(GuestIPv6ServiceTest, RAServer) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(1));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(101));
ON_CALL(*system_, IfNametoindex("down2")).WillByDefault(Return(102));
target.SetForwardMethod("up1",
GuestIPv6Service::ForwardMethod::kMethodRAServer);
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, _, _))
.Times(0);
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, _, _))
.Times(0);
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NEIGHBOR_MONITOR,
101, _));
target.StartForwarding("up1", "down1");
EXPECT_CALL(target, StartRAServer("down1", "2001:db8:0:200::"))
.WillOnce(Return(true));
target.OnUplinkIPv6Changed("up1", "2001:db8:0:200::1234");
EXPECT_CALL(target, StartRAServer("down2", "2001:db8:0:200::"))
.WillOnce(Return(true));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NS_NA, _, _))
.With(Args<1, 2>(AreTheseTwo(101, 102)));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NEIGHBOR_MONITOR,
102, _));
target.StartForwarding("up1", "down2");
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, _, _))
.With(Args<1, 2>(AreTheseTwo(101, 102)));
EXPECT_CALL(
target,
SendNDProxyControl(NDProxyControlMessage::STOP_NEIGHBOR_MONITOR, 101, _));
EXPECT_CALL(target, StopRAServer("down1")).WillOnce(Return(true));
EXPECT_CALL(
target,
SendNDProxyControl(NDProxyControlMessage::STOP_NEIGHBOR_MONITOR, 102, _));
EXPECT_CALL(target, StopRAServer("down2")).WillOnce(Return(true));
target.StopUplink("up1");
}
TEST_F(GuestIPv6ServiceTest, RAServerUplinkIPChange) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(1));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(101));
target.SetForwardMethod("up1",
GuestIPv6Service::ForwardMethod::kMethodRAServer);
target.StartForwarding("up1", "down1");
EXPECT_CALL(target, StartRAServer("down1", "2001:db8:0:200::"))
.WillOnce(Return(true));
target.OnUplinkIPv6Changed("up1", "2001:db8:0:200::1234");
EXPECT_CALL(target, StopRAServer("down1")).WillOnce(Return(true));
EXPECT_CALL(target, StartRAServer("down1", "2001:db8:0:100::"))
.WillOnce(Return(true));
target.OnUplinkIPv6Changed("up1", "2001:db8:0:100::abcd");
EXPECT_CALL(target, StopRAServer("down1")).WillOnce(Return(true));
target.StopUplink("up1");
}
TEST_F(GuestIPv6ServiceTest, SetMethodOnTheFly) {
GuestIPv6ServiceUnderTest target(datapath_.get(), shill_client_.get(),
system_.get());
ON_CALL(*system_, IfNametoindex("up1")).WillByDefault(Return(1));
ON_CALL(*system_, IfNametoindex("down1")).WillByDefault(Return(101));
target.OnUplinkIPv6Changed("up1", "2001:db8:0:200::1234");
EXPECT_CALL(target, SendNDProxyControl(
NDProxyControlMessage::START_NS_NA_RS_RA, 1, 101));
target.StartForwarding("up1", "down1");
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::STOP_PROXY, 1, 101));
EXPECT_CALL(target, StartRAServer("down1", "2001:db8:0:200::"))
.WillOnce(Return(true));
EXPECT_CALL(target,
SendNDProxyControl(NDProxyControlMessage::START_NEIGHBOR_MONITOR,
101, _));
target.SetForwardMethod("up1",
GuestIPv6Service::ForwardMethod::kMethodRAServer);
EXPECT_CALL(target, StopRAServer("down1")).WillOnce(Return(true));
EXPECT_CALL(
target,
SendNDProxyControl(NDProxyControlMessage::STOP_NEIGHBOR_MONITOR, 101, _));
target.StopForwarding("up1", "down1");
}
} // namespace patchpanel