patchpanel: Implement private functions for ClatService

This CL introduces the internal logic of ClatService.

This CL doesn't have impact on the existing networking because the
feature of ClatService is disabled by feature flag introduced in
crrev.com/c/4835372.

I verified by ping command that ICMP packets are correctly delivered
between Borealis and IPv4 destination in the following cases.
  OnDefaultLogicalDeviceChanged
	- change default network from dual stack to IPv6-only
	- change default network from IPv6-only one to dual stack
	- change default network from IPv6-only one to no network
	- reboot OS and connect DUT to ipv6-only network
  OnDefaultLogicalDeviceIPConfigChanged
	- IPv6-only device gets IPv4 address
	- Dual stack device loses IPv4 address

BUG=b:278970851
TEST=enable ClatService and test it manually

Change-Id: I26b693f831d8017052ca30206326b666a8ae67a3
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/4842130
Commit-Queue: Naoya Tezuka <[email protected]>
Tested-by: Naoya Tezuka <[email protected]>
Reviewed-by: Taoyu Li <[email protected]>
diff --git a/patchpanel/clat_service_test.cc b/patchpanel/clat_service_test.cc
index 656bbbc..d6b2c9b 100644
--- a/patchpanel/clat_service_test.cc
+++ b/patchpanel/clat_service_test.cc
@@ -4,6 +4,7 @@
 
 #include "patchpanel/clat_service.h"
 
+#include <memory>
 #include <optional>
 #include <string>
 #include <vector>
@@ -13,12 +14,22 @@
 #include <net-base/ip_address_utils.h>
 #include <net-base/ipv4_address.h>
 #include <net-base/ipv6_address.h>
+#include <shill/net/mock_process_manager.h>
+#include <shill/net/process_manager.h>
 
+#include "patchpanel/datapath.h"
+#include "patchpanel/fake_system.h"
+#include "patchpanel/mock_datapath.h"
 #include "patchpanel/shill_client.h"
+#include "patchpanel/system.h"
 
 using testing::_;
+using testing::Eq;
 using testing::Exactly;
 using testing::Invoke;
+using testing::IsEmpty;
+using testing::Return;
+using testing::StrEq;
 
 namespace patchpanel {
 namespace {
@@ -27,11 +38,22 @@
   return arg.ifname == expected_ifname;
 }
 
+MATCHER_P(CIDRHasPrefix, expected_prefix_str, "") {
+  return net_base::IPv6CIDR::CreateFromCIDRString(expected_prefix_str)
+      ->InSameSubnetWith(arg.address());
+}
+
+MATCHER_P(AddressHasPrefix, expected_prefix_str, "") {
+  return net_base::IPv6CIDR::CreateFromCIDRString(expected_prefix_str)
+      ->InSameSubnetWith(arg);
+}
+
 class ClatServiceUnderTest : public ClatService {
  public:
-  // TODO(b/278970851): Do the actual implementation. ClatService class needs to
-  // take Datapath* argument in constructor.
-  ClatServiceUnderTest() {
+  ClatServiceUnderTest(Datapath* datapath,
+                       shill::ProcessManager* process_manager,
+                       System* system)
+      : ClatService(datapath, process_manager, system) {
     ON_CALL(*this, StartClat(_))
         .WillByDefault(
             (Invoke(this, &ClatServiceUnderTest::SetClatRunningDeviceForTest)));
@@ -92,244 +114,225 @@
   dev.ipconfig.ipv6_cidr = net_base::IPv6CIDR::CreateFromCIDRString(ipv6_cidr);
 
   return dev;
-}
-
 }  // namespace
 
+class ClatServiceTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    target_ = std::make_unique<ClatServiceUnderTest>(
+        &datapath_, &process_manager_, &system_);
+  }
+  MockDatapath datapath_ = MockDatapath();
+  shill::MockProcessManager process_manager_ = shill::MockProcessManager();
+  FakeSystem system_ = FakeSystem();
+  std::unique_ptr<ClatServiceUnderTest> target_;
+};
+
 // TODO(b/278970851): Merge tests for OnShillDefaultLogicalDeviceChanged into a
 // single test with a testcase data array.
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromIPv4DeviceToIPv6OnlyDevice) {
+TEST_F(ClatServiceTest, ChangeFromIPv4DeviceToIPv6OnlyDevice) {
   const auto v4only_dev = MakeFakeIPv4OnlyShillDevice("v4only", 1);
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
 
-  ClatServiceUnderTest target;
-
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, &v4only_dev);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, &v4only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromIPv6OnlyDeviceToIPv4Device) {
+TEST_F(ClatServiceTest, ChangeFromIPv6OnlyDeviceToIPv4Device) {
   const auto v4only_dev = MakeFakeIPv4OnlyShillDevice("v4only", 1);
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
 
-  ClatServiceUnderTest target;
-  // Start CLAT on the new_device.
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  //  Start CLAT on the new_device.
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(true));
-  target.OnShillDefaultLogicalDeviceChanged(&v4only_dev, &v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  target_->OnShillDefaultLogicalDeviceChanged(&v4only_dev, &v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromIPv6OnlyDeviceToAnother) {
+TEST_F(ClatServiceTest, ChangeFromIPv6OnlyDeviceToAnother) {
   const auto new_v6only_dev =
       MakeFakeIPv6OnlyShillDevice("new_v6only", 1, "1020:db8::1/64");
   const auto prev_v6only_dev =
       MakeFakeIPv6OnlyShillDevice("prev_v6only", 1, "2001:db8::2/64");
 
-  ClatServiceUnderTest target;
-  // Start CLAT on the new_device.
-  target.OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev, nullptr);
+  //  Start CLAT on the new_device.
+  target_->OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(true));
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("new_v6only")));
+  EXPECT_CALL(*target_, StopClat(true));
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("new_v6only")));
 
-  target.OnShillDefaultLogicalDeviceChanged(&new_v6only_dev, &prev_v6only_dev);
+  target_->OnShillDefaultLogicalDeviceChanged(&new_v6only_dev,
+                                              &prev_v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromDualStackDeviceToIPv4OnlyDevice) {
+TEST_F(ClatServiceTest, ChangeFromDualStackDeviceToIPv4OnlyDevice) {
   const auto dual_dev = MakeFakeDualStackShillDevice("dual_dev", 1);
   const auto v4only_dev = MakeFakeIPv4OnlyShillDevice("v4only", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(&v4only_dev, &dual_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(&v4only_dev, &dual_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromIPv4OnlyDeviceToDualStackDevice) {
+TEST_F(ClatServiceTest, ChangeFromIPv4OnlyDeviceToDualStackDevice) {
   const auto dual_dev = MakeFakeDualStackShillDevice("dual_dev", 1);
   const auto v4only_dev = MakeFakeIPv4OnlyShillDevice("v4only", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&v4only_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&v4only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(&dual_dev, &v4only_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(&dual_dev, &v4only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromDualStackDeviceToIPv6OnlyDevice) {
+TEST_F(ClatServiceTest, ChangeFromDualStackDeviceToIPv6OnlyDevice) {
   const auto dual_dev = MakeFakeDualStackShillDevice("dual_dev", 1);
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromIPv6OnlyDeviceToDualStackDevice) {
+TEST_F(ClatServiceTest, ChangeFromIPv6OnlyDeviceToDualStackDevice) {
   const auto dual_dev = MakeFakeDualStackShillDevice("dual_dev", 1);
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(true));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(&dual_dev, &v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(&dual_dev, &v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromDualStackDeviceToAnother) {
+TEST_F(ClatServiceTest, ChangeFromDualStackDeviceToAnother) {
   const auto new_v6only_dev = MakeFakeDualStackShillDevice(
       "new_dual_dev", 1, "10.10.0.2/24", "1020:db8::1/64");
 
   const auto prev_v6only_dev = MakeFakeDualStackShillDevice(
       "prev_dual_dev", 2, "10.20.0.2/24", "2001:db8::1/64");
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(&new_v6only_dev, &prev_v6only_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(&new_v6only_dev,
+                                              &prev_v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromNonExstingDeviceToExistingDevice) {
+TEST_F(ClatServiceTest, ChangeFromNonExstingDeviceToExistingDevice) {
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only");
 
-  ClatServiceUnderTest target;
-
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromExstingDeviceToNonExistingDevice) {
+TEST_F(ClatServiceTest, ChangeFromExstingDeviceToNonExistingDevice) {
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only");
 
-  ClatServiceUnderTest target;
-  // Start CLAT on the new_device.
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  //  Start CLAT on the new_device.
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(true));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(nullptr, &v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(nullptr, &v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     DefaultDeviceChangeWhileClatIsRunningOnDifferentDevice) {
+TEST_F(ClatServiceTest,
+       DefaultDeviceChangeWhileClatIsRunningOnDifferentDevice) {
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 1);
   const auto prev_v6only_dev =
       MakeFakeIPv6OnlyShillDevice("new_v6only", 2, "1020:db8::1/64");
   const auto new_v6only_dev =
       MakeFakeIPv6OnlyShillDevice("prev_v6only", 3, "1030:db8::1/64");
 
-  ClatServiceUnderTest target;
-  // Start CLAT on device "v6only_dev1".
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  //  Start CLAT on device "v6only_dev1".
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 
   // Unexpectedly the default logical device changes from an device different
   // from v6only_dev1 to another.
-  EXPECT_CALL(target, StopClat(true));
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("new_v6only")));
-  target.OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev, &prev_v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("new_v6only")));
+  target_->OnShillDefaultLogicalDeviceChanged(&prev_v6only_dev,
+                                              &prev_v6only_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     NewDefaultDeviceIsTheSameWithClatDevice) {
+TEST_F(ClatServiceTest, NewDefaultDeviceIsTheSameWithClatDevice) {
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 1);
   const auto dual_dev = MakeFakeDualStackShillDevice("dual", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
 }
 
-TEST(ClatServiceDefaultLogicalDeviceChangeTest,
-     ChangeFromDualStackDeviceToIPv6OnlyDeviceWhileDisabled) {
+TEST_F(ClatServiceTest,
+       ChangeFromDualStackDeviceToIPv6OnlyDeviceWhileDisabled) {
   const auto dual_dev = MakeFakeDualStackShillDevice("dual", 1);
   const auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
 
-  ClatServiceUnderTest target;
-  target.OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
+  target_->OnShillDefaultLogicalDeviceChanged(&dual_dev, nullptr);
 
-  EXPECT_CALL(target, StopClat(false));
-  target.Disable();
+  EXPECT_CALL(*target_, StopClat(false));
+  target_->Disable();
 
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnShillDefaultLogicalDeviceChanged(&v6only_dev, &dual_dev);
 
   // The default logical device is IPv6-only, so CLAT starts immdiately after
   // it's enabled.
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.Enable();
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->Enable();
 }
 
-TEST(ClatServiceIPConfigChangeTest, IPv6OnlyDeviceGetIPv4Address) {
+TEST_F(ClatServiceTest, IPv6OnlyDeviceGetIPv4Address) {
   auto default_logical_device = MakeFakeIPv6OnlyShillDevice("v6only");
 
-  ClatServiceUnderTest target;
-
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
 
   // The default logical device gets IPv4 address because of IPConfig changes.
   default_logical_device.ipconfig.ipv4_cidr =
       net_base::IPv4CIDR::CreateFromCIDRString(kIPv4CIDR);
 
-  EXPECT_CALL(target, StopClat(true));
-  target.OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
+  EXPECT_CALL(*target_, StopClat(true));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
 }
 
-TEST(ClatServiceClatServiceIPConfigChangeTest, DeviceLoseIPv4Address) {
+TEST_F(ClatServiceTest, DeviceLoseIPv4Address) {
   auto default_logical_device = MakeFakeDualStackShillDevice("dual_stack", 1);
 
-  ClatServiceUnderTest target;
-
   // The default logical device loses IPv4 address because of IPConfig changes.
   default_logical_device.ipconfig.ipv4_cidr.reset();
 
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("dual_stack")));
-  target.OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("dual_stack")));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(default_logical_device);
 }
 
-TEST(ClatServiceIPConfigChangeTest, IPConfigChangeWithoutIPv6AddressChange) {
+TEST_F(ClatServiceTest, IPConfigChangeWithoutIPv6AddressChange) {
   auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only");
   v6only_dev.ipconfig.ipv4_dns_addresses = std::vector<std::string>{"8.8.8.8"};
 
-  ClatServiceUnderTest target;
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 
   v6only_dev.ipconfig.ipv4_dns_addresses = std::vector<std::string>{"1.1.1.1"};
 
   // This change has nothing with CLAT.
-  EXPECT_CALL(target, StopClat(_)).Times(Exactly(0));
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  EXPECT_CALL(*target_, StopClat(_)).Times(Exactly(0));
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 }
 
-TEST(ClatServiceIPConfigChangeTest, IPv6AddressChangeInTheSamePrefix) {
+TEST_F(ClatServiceTest, IPv6AddressChangeInTheSamePrefix) {
   auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 1, "2001:db8::1/64");
 
-  ClatServiceUnderTest target;
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 
   v6only_dev.ipconfig.ipv6_cidr =
       net_base::IPv6CIDR::CreateFromCIDRString("2001:db8::2/64");
@@ -337,50 +340,88 @@
   // Even the new IPn6 address of the default logical device has the same prefix
   // as the old one, CLAT needs to be reconfigured because the new address
   // conflict with the IPv6 address used by CLAT.
-  EXPECT_CALL(target, StopClat(true));
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("v6only")));
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("v6only")));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 }
 
-TEST(ClatServiceIPConfigChangeTest,
-     EnabledAfterGettingIPv4AddressWhileDisabled) {
+TEST_F(ClatServiceTest, EnabledAfterGettingIPv4AddressWhileDisabled) {
   auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only");
 
-  ClatServiceUnderTest target;
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 
-  EXPECT_CALL(target, StopClat(false));
-  target.Disable();
+  EXPECT_CALL(*target_, StopClat(false));
+  target_->Disable();
 
   v6only_dev.ipconfig.ipv4_cidr =
       net_base::IPv4CIDR::CreateFromCIDRString(kIPv4CIDR);
 
-  EXPECT_CALL(target, StopClat(true));
-  target.OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
+  EXPECT_CALL(*target_, StopClat(true));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(v6only_dev);
 
-  EXPECT_CALL(target, StartClat(_)).Times(Exactly(0));
-  target.Enable();
+  EXPECT_CALL(*target_, StartClat(_)).Times(Exactly(0));
+  target_->Enable();
 }
 
-TEST(ClatServiceClatServiceIPConfigChangeTest,
-     EnabledAfterBecomingIPv6OnlyWhileDisabled) {
+TEST_F(ClatServiceTest, EnabledAfterBecomingIPv6OnlyWhileDisabled) {
   auto dual_dev = MakeFakeDualStackShillDevice("dual");
 
-  ClatServiceUnderTest target;
-  target.OnDefaultLogicalDeviceIPConfigChanged(dual_dev);
+  target_->OnDefaultLogicalDeviceIPConfigChanged(dual_dev);
 
-  EXPECT_CALL(target, StopClat(false));
-  target.Disable();
+  EXPECT_CALL(*target_, StopClat(false));
+  target_->Disable();
 
   dual_dev.ipconfig.ipv4_cidr.reset();
 
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("dual")));
-  target.OnDefaultLogicalDeviceIPConfigChanged(dual_dev);
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("dual")));
+  target_->OnDefaultLogicalDeviceIPConfigChanged(dual_dev);
 
   // The default logical device is IPv6-only, so CLAT starts immdiately after
   // it's enabled.
-  EXPECT_CALL(target, StartClat(ShillDeviceHasInterfaceName("dual")));
-  target.Enable();
+  EXPECT_CALL(*target_, StartClat(ShillDeviceHasInterfaceName("dual")));
+  target_->Enable();
 }
 
+TEST_F(ClatServiceTest, VerifyStartAndStopClat) {
+  ClatService target(&datapath_, &process_manager_, &system_);
+  target.Enable();
+
+  auto v4only_dev = MakeFakeIPv4OnlyShillDevice("v4only", 1);
+  auto v6only_dev = MakeFakeIPv6OnlyShillDevice("v6only", 2);
+
+  target.OnShillDefaultLogicalDeviceChanged(&v4only_dev, nullptr);
+
+  EXPECT_CALL(system_, WriteConfigFile(_, _)).WillOnce(Return(true));
+  EXPECT_CALL(
+      datapath_,
+      AddTunTap(StrEq("tun_nat64"), Eq(std::nullopt),
+                net_base::IPv4CIDR::CreateFromCIDRString("192.0.0.1/29"),
+                IsEmpty(), DeviceMode::kTun))
+      .WillOnce(Return("tun_nat64"));
+  EXPECT_CALL(datapath_, AddIPv6HostRoute(StrEq("tun_nat64"),
+                                          CIDRHasPrefix("2001:db8::/64"),
+                                          Eq(std::nullopt)))
+      .WillOnce(Return(true));
+  EXPECT_CALL(
+      datapath_,
+      AddIPv6NeighborProxy(StrEq("v6only"), AddressHasPrefix("2001:db8::/64")))
+      .WillOnce(Return(true));
+  EXPECT_CALL(datapath_, AddIPv4RouteToTable(StrEq("tun_nat64"),
+                                             net_base::IPv4CIDR(), 249))
+      .WillOnce(Return(true));
+  // StartClat() is called.
+  target.OnShillDefaultLogicalDeviceChanged(&v6only_dev, &v4only_dev);
+
+  EXPECT_CALL(datapath_, DeleteIPv4RouteFromTable(StrEq("tun_nat64"),
+                                                  net_base::IPv4CIDR(), 249));
+  EXPECT_CALL(datapath_,
+              RemoveIPv6NeighborProxy(StrEq("v6only"),
+                                      AddressHasPrefix("2001:db8::/64")));
+  EXPECT_CALL(datapath_, RemoveIPv6HostRoute(CIDRHasPrefix("2001:db8::/64")));
+  EXPECT_CALL(datapath_, RemoveTunTap(StrEq("tun_nat64"), DeviceMode::kTun));
+  // StopClat() is called.
+  target.OnShillDefaultLogicalDeviceChanged(&v4only_dev, &v6only_dev);
+}
+
+}  // namespace
 }  // namespace patchpanel