Home | History | Annotate | Download | only in dhcp
      1 //
      2 // Copyright (C) 2015 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/dhcp/dhcpv4_config.h"
     18 
     19 #include <memory>
     20 #include <string>
     21 #include <vector>
     22 
     23 #include <base/bind.h>
     24 #include <base/files/file_util.h>
     25 #include <base/files/scoped_temp_dir.h>
     26 #include <base/strings/stringprintf.h>
     27 #if defined(__ANDROID__)
     28 #include <dbus/service_constants.h>
     29 #else
     30 #include <chromeos/dbus/service_constants.h>
     31 #endif  // __ANDROID__
     32 
     33 #include "shill/dhcp/mock_dhcp_provider.h"
     34 #include "shill/dhcp/mock_dhcp_proxy.h"
     35 #include "shill/event_dispatcher.h"
     36 #include "shill/mock_control.h"
     37 #include "shill/mock_dhcp_properties.h"
     38 #include "shill/mock_log.h"
     39 #include "shill/mock_metrics.h"
     40 #include "shill/mock_process_manager.h"
     41 #include "shill/mock_store.h"
     42 #include "shill/property_store_unittest.h"
     43 #include "shill/testing.h"
     44 
     45 using base::Bind;
     46 using base::FilePath;
     47 using base::ScopedTempDir;
     48 using base::Unretained;
     49 using std::string;
     50 using std::unique_ptr;
     51 using std::vector;
     52 using testing::_;
     53 using testing::AnyNumber;
     54 using testing::ContainsRegex;
     55 using testing::DoAll;
     56 using testing::InvokeWithoutArgs;
     57 using testing::Mock;
     58 using testing::Return;
     59 using testing::SetArgumentPointee;
     60 using testing::Test;
     61 
     62 namespace shill {
     63 
     64 namespace {
     65 const char kDeviceName[] = "eth0";
     66 const char kHostName[] = "hostname";
     67 const char kVendorClass[] = "vendorclass";
     68 const char kLeaseFileSuffix[] = "leasefilesuffix";
     69 const bool kArpGateway = true;
     70 const bool kHasHostname = true;
     71 const bool kHasVendorClass = true;
     72 const bool kHasLeaseSuffix = true;
     73 const char kStorageID[] = "dhcp_service_id";
     74 }  // namespace
     75 
     76 typedef scoped_refptr<DHCPv4Config> DHCPv4ConfigRefPtr;
     77 
     78 class DHCPv4ConfigTest : public PropertyStoreTest {
     79  public:
     80   DHCPv4ConfigTest()
     81       : proxy_(new MockDHCPProxy()),
     82         metrics_(dispatcher()),
     83         config_(new DHCPv4Config(&control_,
     84                                  dispatcher(),
     85                                  &provider_,
     86                                  kDeviceName,
     87                                  kLeaseFileSuffix,
     88                                  kArpGateway,
     89                                  dhcp_props_,
     90                                  &metrics_)) {}
     91 
     92   virtual void SetUp() {
     93     config_->process_manager_ = &process_manager_;
     94   }
     95 
     96   bool StartInstance(DHCPv4ConfigRefPtr config) {
     97     return config->Start();
     98   }
     99 
    100   void StopInstance() {
    101     config_->Stop("In test");
    102   }
    103 
    104   DHCPv4ConfigRefPtr CreateMockMinijailConfig(const string& hostname,
    105                                               const string& vendorclass,
    106                                               const string& lease_suffix,
    107                                               bool arp_gateway);
    108   DHCPv4ConfigRefPtr CreateRunningConfig(const string& hostname,
    109                                          const string& vendorclass,
    110                                          const string& lease_suffix,
    111                                          bool arp_gateway);
    112   void StopRunningConfigAndExpect(DHCPv4ConfigRefPtr config,
    113                                   bool lease_file_exists);
    114 
    115  protected:
    116   static const int kPID;
    117 
    118   FilePath lease_file_;
    119   FilePath pid_file_;
    120   ScopedTempDir temp_dir_;
    121   unique_ptr<MockDHCPProxy> proxy_;
    122   MockControl control_;
    123   MockProcessManager process_manager_;
    124   MockMetrics metrics_;
    125   MockDHCPProvider provider_;
    126   MockDhcpProperties dhcp_props_;
    127   DHCPv4ConfigRefPtr config_;
    128 };
    129 
    130 const int DHCPv4ConfigTest::kPID = 123456;
    131 
    132 DHCPv4ConfigRefPtr DHCPv4ConfigTest::CreateMockMinijailConfig(
    133     const string& hostname,
    134     const string& vendorclass,
    135     const string& lease_suffix,
    136     bool arp_gateway) {
    137   MockStore storage;
    138   DhcpProperties dhcp_props;
    139   if (!hostname.empty()) {
    140     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.Hostname", _))
    141         .WillOnce(DoAll(SetArgumentPointee<2>(string(kHostName)),
    142                         Return(true)));
    143   } else {
    144     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.Hostname", _))
    145         .WillOnce(Return(false));
    146   }
    147   if (!vendorclass.empty()) {
    148     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.VendorClass", _))
    149         .WillOnce(DoAll(SetArgumentPointee<2>(string(kVendorClass)),
    150                         Return(true)));
    151   } else {
    152     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.VendorClass", _))
    153         .WillOnce(Return(false));
    154   }
    155   dhcp_props.Load(&storage, kStorageID);
    156   DHCPv4ConfigRefPtr config(new DHCPv4Config(&control_,
    157                                              dispatcher(),
    158                                              &provider_,
    159                                              kDeviceName,
    160                                              lease_suffix,
    161                                              arp_gateway,
    162                                              dhcp_props,
    163                                              &metrics_));
    164   config->process_manager_ = &process_manager_;
    165 
    166   return config;
    167 }
    168 
    169 DHCPv4ConfigRefPtr DHCPv4ConfigTest::CreateRunningConfig(
    170     const string& hostname, const string& vendorclass,
    171     const string& lease_suffix, bool arp_gateway) {
    172   MockStore storage;
    173   DhcpProperties dhcp_props;
    174   if (!hostname.empty()) {
    175     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.Hostname", _))
    176         .WillOnce(DoAll(SetArgumentPointee<2>(string(kHostName)),
    177                         Return(true)));
    178   } else {
    179     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.Hostname", _))
    180         .WillOnce(Return(false));
    181   }
    182   if (!vendorclass.empty()) {
    183     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.VendorClass", _))
    184         .WillOnce(DoAll(SetArgumentPointee<2>(string(kVendorClass)),
    185                         Return(true)));
    186   } else {
    187     EXPECT_CALL(storage, GetString(kStorageID, "DHCPProperty.VendorClass", _))
    188         .WillOnce(Return(false));
    189   }
    190   dhcp_props.Load(&storage, kStorageID);
    191   DHCPv4ConfigRefPtr config(new DHCPv4Config(&control_,
    192                                              dispatcher(),
    193                                              &provider_,
    194                                              kDeviceName,
    195                                              lease_suffix,
    196                                              arp_gateway,
    197                                              dhcp_props,
    198                                              &metrics_));
    199   config->process_manager_ = &process_manager_;
    200   EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _))
    201       .WillOnce(Return(kPID));
    202   EXPECT_CALL(provider_, BindPID(kPID, IsRefPtrTo(config)));
    203   EXPECT_TRUE(config->Start());
    204   EXPECT_EQ(kPID, config->pid_);
    205   EXPECT_EQ(config->hostname_, hostname);
    206   EXPECT_EQ(config->vendor_class_, vendorclass);
    207 
    208   EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
    209   config->root_ = temp_dir_.path();
    210   FilePath varrun = temp_dir_.path().Append("var/run/dhcpcd");
    211   EXPECT_TRUE(base::CreateDirectory(varrun));
    212   pid_file_ = varrun.Append(base::StringPrintf("dhcpcd-%s-4.pid", kDeviceName));
    213   FilePath varlib = temp_dir_.path().Append("var/lib/dhcpcd");
    214   EXPECT_TRUE(base::CreateDirectory(varlib));
    215   lease_file_ =
    216       varlib.Append(base::StringPrintf("dhcpcd-%s.lease", kDeviceName));
    217   EXPECT_EQ(0, base::WriteFile(pid_file_, "", 0));
    218   EXPECT_EQ(0, base::WriteFile(lease_file_, "", 0));
    219   EXPECT_TRUE(base::PathExists(pid_file_));
    220   EXPECT_TRUE(base::PathExists(lease_file_));
    221 
    222   return config;
    223 }
    224 
    225 void DHCPv4ConfigTest::StopRunningConfigAndExpect(DHCPv4ConfigRefPtr config,
    226                                                   bool lease_file_exists) {
    227   ScopedMockLog log;
    228   // We use a non-zero exit status so that we get the log message.
    229   EXPECT_CALL(log, Log(_, _, ::testing::EndsWith("status 10")));
    230   EXPECT_CALL(provider_, UnbindPID(kPID));
    231   config->OnProcessExited(10);
    232 
    233   EXPECT_FALSE(base::PathExists(pid_file_));
    234   EXPECT_EQ(lease_file_exists, base::PathExists(lease_file_));
    235 }
    236 
    237 TEST_F(DHCPv4ConfigTest, GetIPv4AddressString) {
    238   EXPECT_EQ("255.255.255.255", config_->GetIPv4AddressString(0xffffffff));
    239   EXPECT_EQ("0.0.0.0", config_->GetIPv4AddressString(0));
    240   EXPECT_EQ("1.2.3.4", config_->GetIPv4AddressString(0x04030201));
    241 }
    242 
    243 TEST_F(DHCPv4ConfigTest, ParseClasslessStaticRoutes) {
    244   const string kDefaultAddress = "0.0.0.0";
    245   const string kDefaultDestination = kDefaultAddress + "/0";
    246   const string kRouter0 = "10.0.0.254";
    247   const string kAddress1 = "192.168.1.0";
    248   const string kDestination1 = kAddress1 + "/24";
    249   // Last gateway missing, leaving an odd number of parameters.
    250   const string kBrokenClasslessRoutes0 = kDefaultDestination + " " + kRouter0 +
    251       " " + kDestination1;
    252   IPConfig::Properties properties;
    253   EXPECT_FALSE(DHCPv4Config::ParseClasslessStaticRoutes(kBrokenClasslessRoutes0,
    254                                                         &properties));
    255   EXPECT_TRUE(properties.routes.empty());
    256   EXPECT_TRUE(properties.gateway.empty());
    257 
    258   // Gateway argument for the second route is malformed, but we were able
    259   // to salvage a default gateway.
    260   const string kBrokenRouter1 = "10.0.0";
    261   const string kBrokenClasslessRoutes1 = kBrokenClasslessRoutes0 + " " +
    262       kBrokenRouter1;
    263   EXPECT_FALSE(DHCPv4Config::ParseClasslessStaticRoutes(kBrokenClasslessRoutes1,
    264                                                         &properties));
    265   EXPECT_TRUE(properties.routes.empty());
    266   EXPECT_EQ(kRouter0, properties.gateway);
    267 
    268   const string kRouter1 = "10.0.0.253";
    269   const string kRouter2 = "10.0.0.252";
    270   const string kClasslessRoutes0 = kDefaultDestination + " " + kRouter2 + " " +
    271       kDestination1 + " " + kRouter1;
    272   EXPECT_TRUE(DHCPv4Config::ParseClasslessStaticRoutes(kClasslessRoutes0,
    273                                                        &properties));
    274   // The old default route is preserved.
    275   EXPECT_EQ(kRouter0, properties.gateway);
    276 
    277   // The two routes (including the one which would have otherwise been
    278   // classified as a default route) are added to the routing table.
    279   EXPECT_EQ(2, properties.routes.size());
    280   const IPConfig::Route& route0 = properties.routes[0];
    281   EXPECT_EQ(kDefaultAddress, route0.host);
    282   EXPECT_EQ("0.0.0.0", route0.netmask);
    283   EXPECT_EQ(kRouter2, route0.gateway);
    284 
    285   const IPConfig::Route& route1 = properties.routes[1];
    286   EXPECT_EQ(kAddress1, route1.host);
    287   EXPECT_EQ("255.255.255.0", route1.netmask);
    288   EXPECT_EQ(kRouter1, route1.gateway);
    289 
    290   // A malformed routing table should not affect the current table.
    291   EXPECT_FALSE(DHCPv4Config::ParseClasslessStaticRoutes(kBrokenClasslessRoutes1,
    292                                                         &properties));
    293   EXPECT_EQ(2, properties.routes.size());
    294   EXPECT_EQ(kRouter0, properties.gateway);
    295 }
    296 
    297 TEST_F(DHCPv4ConfigTest, ParseConfiguration) {
    298   KeyValueStore conf;
    299   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    300   conf.SetUint8(DHCPv4Config::kConfigurationKeySubnetCIDR, 16);
    301   conf.SetUint(DHCPv4Config::kConfigurationKeyBroadcastAddress, 0x10203040);
    302   {
    303     vector<uint32_t> routers;
    304     routers.push_back(0x02040608);
    305     routers.push_back(0x03050709);
    306     conf.SetUint32s(DHCPv4Config::kConfigurationKeyRouters, routers);
    307   }
    308   {
    309     vector<uint32_t> dns;
    310     dns.push_back(0x09070503);
    311     dns.push_back(0x08060402);
    312     conf.SetUint32s(DHCPv4Config::kConfigurationKeyDNS, dns);
    313   }
    314   conf.SetString(DHCPv4Config::kConfigurationKeyDomainName, "domain-name");
    315   {
    316     vector<string> search;
    317     search.push_back("foo.com");
    318     search.push_back("bar.com");
    319     conf.SetStrings(DHCPv4Config::kConfigurationKeyDomainSearch, search);
    320   }
    321   conf.SetUint16(DHCPv4Config::kConfigurationKeyMTU, 600);
    322   conf.SetString(DHCPv4Config::kConfigurationKeyHostname, "hostname");
    323   conf.SetString("UnknownKey", "UnknownValue");
    324 
    325   EXPECT_CALL(metrics_,
    326               SendSparseToUMA(Metrics::kMetricDhcpClientMTUValue, 600));
    327   IPConfig::Properties properties;
    328   ASSERT_TRUE(config_->ParseConfiguration(conf, &properties));
    329   EXPECT_EQ("4.3.2.1", properties.address);
    330   EXPECT_EQ(16, properties.subnet_prefix);
    331   EXPECT_EQ("64.48.32.16", properties.broadcast_address);
    332   EXPECT_EQ("8.6.4.2", properties.gateway);
    333   ASSERT_EQ(2, properties.dns_servers.size());
    334   EXPECT_EQ("3.5.7.9", properties.dns_servers[0]);
    335   EXPECT_EQ("2.4.6.8", properties.dns_servers[1]);
    336   EXPECT_EQ("domain-name", properties.domain_name);
    337   ASSERT_EQ(2, properties.domain_search.size());
    338   EXPECT_EQ("foo.com", properties.domain_search[0]);
    339   EXPECT_EQ("bar.com", properties.domain_search[1]);
    340   EXPECT_EQ(600, properties.mtu);
    341   EXPECT_EQ("hostname", properties.accepted_hostname);
    342 }
    343 
    344 TEST_F(DHCPv4ConfigTest, ParseConfigurationWithMinimumMTU) {
    345   // Even without a minimum MTU set, we should ignore a 576 value.
    346   KeyValueStore conf;
    347   conf.SetUint16(DHCPv4Config::kConfigurationKeyMTU, 576);
    348 
    349   IPConfig::Properties properties;
    350   EXPECT_CALL(metrics_,
    351               SendSparseToUMA(Metrics::kMetricDhcpClientMTUValue, 576));
    352   ASSERT_TRUE(config_->ParseConfiguration(conf, &properties));
    353   EXPECT_EQ(IPConfig::kUndefinedMTU, properties.mtu);
    354   Mock::VerifyAndClearExpectations(&metrics_);
    355 
    356   // With a minimum MTU set, values below the minimum should be ignored.
    357   config_->set_minimum_mtu(1500);
    358   conf.RemoveUint16(DHCPv4Config::kConfigurationKeyMTU);
    359   conf.SetUint16(DHCPv4Config::kConfigurationKeyMTU, 1499);
    360   EXPECT_CALL(metrics_,
    361               SendSparseToUMA(Metrics::kMetricDhcpClientMTUValue, 1499));
    362   ASSERT_TRUE(config_->ParseConfiguration(conf, &properties));
    363   EXPECT_EQ(IPConfig::kUndefinedMTU, properties.mtu);
    364   Mock::VerifyAndClearExpectations(&metrics_);
    365 
    366   // A value (other than 576) should be accepted if it is >= mimimum_mtu.
    367   config_->set_minimum_mtu(577);
    368   conf.RemoveUint16(DHCPv4Config::kConfigurationKeyMTU);
    369   conf.SetUint16(DHCPv4Config::kConfigurationKeyMTU, 577);
    370   EXPECT_CALL(metrics_,
    371               SendSparseToUMA(Metrics::kMetricDhcpClientMTUValue, 577));
    372   ASSERT_TRUE(config_->ParseConfiguration(conf, &properties));
    373   EXPECT_EQ(577, properties.mtu);
    374 }
    375 
    376 MATCHER_P4(IsDHCPCDArgs,
    377            has_hostname,
    378            has_vendorclass,
    379            has_arp_gateway,
    380            has_lease_suffix, "") {
    381   if (arg[0] != "-B" ||
    382       arg[1] != "-q" ||
    383       arg[2] != "-4") {
    384     return false;
    385   }
    386 
    387   int end_offset = 3;
    388   if (has_hostname) {
    389     if (arg[end_offset] != "-h" ||
    390         arg[end_offset + 1] != kHostName) {
    391       return false;
    392     }
    393     end_offset += 2;
    394   }
    395 
    396   if (has_vendorclass){
    397     if (arg[end_offset] != "-i" ||
    398         arg[end_offset + 1] != kVendorClass) {
    399       return false;
    400     }
    401     end_offset += 2;
    402   }
    403 
    404   if (has_arp_gateway) {
    405     if (arg[end_offset] != "-R" ||
    406         arg[end_offset + 1] != "-P") {
    407       return false;
    408     }
    409     end_offset += 2;
    410   }
    411 
    412   string device_arg = has_lease_suffix ?
    413       string(kDeviceName) + "=" + string(kLeaseFileSuffix) : kDeviceName;
    414   return arg[end_offset] == device_arg;
    415 }
    416 
    417 TEST_F(DHCPv4ConfigTest, StartWithHostname) {
    418   config_->hostname_ = kHostName;
    419   EXPECT_CALL(process_manager_,
    420               StartProcessInMinijail(_, _,
    421                                      IsDHCPCDArgs(kHasHostname,
    422                                                   !kHasVendorClass,
    423                                                   kArpGateway,
    424                                                   kHasLeaseSuffix), _, _, _, _))
    425       .WillOnce(Return(-1));
    426   EXPECT_FALSE(StartInstance(config_));
    427 }
    428 
    429 TEST_F(DHCPv4ConfigTest, StartWithoutHostname) {
    430   DHCPv4ConfigRefPtr config = CreateMockMinijailConfig("",
    431                                                        "",
    432                                                        kLeaseFileSuffix,
    433                                                        kArpGateway);
    434   EXPECT_CALL(process_manager_,
    435               StartProcessInMinijail(_, _,
    436                                      IsDHCPCDArgs(!kHasHostname,
    437                                                   !kHasVendorClass,
    438                                                   kArpGateway,
    439                                                   kHasLeaseSuffix), _, _, _, _))
    440       .WillOnce(Return(-1));
    441   EXPECT_FALSE(StartInstance(config));
    442 }
    443 
    444 TEST_F(DHCPv4ConfigTest, StartWithEmptyHostname) {
    445   DHCPv4ConfigRefPtr config = CreateMockMinijailConfig("",
    446                                                        "",
    447                                                        kLeaseFileSuffix,
    448                                                        kArpGateway);
    449   EXPECT_CALL(process_manager_,
    450               StartProcessInMinijail(_, _,
    451                                      IsDHCPCDArgs(!kHasHostname,
    452                                                   !kHasVendorClass,
    453                                                   kArpGateway,
    454                                                   kHasLeaseSuffix), _, _, _, _))
    455       .WillOnce(Return(-1));
    456   EXPECT_FALSE(StartInstance(config));
    457 }
    458 
    459 TEST_F(DHCPv4ConfigTest, StartWithVendorClass) {
    460   config_->hostname_ = kHostName;
    461   config_->vendor_class_ = kVendorClass;
    462   EXPECT_CALL(process_manager_,
    463               StartProcessInMinijail(_, _,
    464                                      IsDHCPCDArgs(kHasHostname,
    465                                                   kHasVendorClass,
    466                                                   kArpGateway,
    467                                                   kHasLeaseSuffix), _, _, _, _))
    468       .WillOnce(Return(-1));
    469   EXPECT_FALSE(StartInstance(config_));
    470 }
    471 
    472 TEST_F(DHCPv4ConfigTest, StartWithoutVendorClass) {
    473   DHCPv4ConfigRefPtr config = CreateMockMinijailConfig(kHostName,
    474                                                        "",
    475                                                        kLeaseFileSuffix,
    476                                                        kArpGateway);
    477   EXPECT_CALL(process_manager_,
    478               StartProcessInMinijail(_, _,
    479                                      IsDHCPCDArgs(kHasHostname,
    480                                                   !kHasVendorClass,
    481                                                   kArpGateway,
    482                                                   kHasLeaseSuffix), _, _, _, _))
    483       .WillOnce(Return(-1));
    484   EXPECT_FALSE(StartInstance(config));
    485 }
    486 
    487 
    488 TEST_F(DHCPv4ConfigTest, StartWithoutArpGateway) {
    489   DHCPv4ConfigRefPtr config = CreateMockMinijailConfig(kHostName,
    490                                                        "",
    491                                                        kLeaseFileSuffix,
    492                                                        !kArpGateway);
    493   EXPECT_CALL(process_manager_,
    494               StartProcessInMinijail(_, _,
    495                                      IsDHCPCDArgs(kHasHostname,
    496                                                   !kHasVendorClass,
    497                                                   !kArpGateway,
    498                                                   kHasLeaseSuffix), _, _, _, _))
    499       .WillOnce(Return(-1));
    500   EXPECT_FALSE(StartInstance(config));
    501 }
    502 
    503 namespace {
    504 
    505 class DHCPv4ConfigCallbackTest : public DHCPv4ConfigTest {
    506  public:
    507   virtual void SetUp() {
    508     DHCPv4ConfigTest::SetUp();
    509     config_->RegisterUpdateCallback(
    510         Bind(&DHCPv4ConfigCallbackTest::SuccessCallback, Unretained(this)));
    511     config_->RegisterFailureCallback(
    512         Bind(&DHCPv4ConfigCallbackTest::FailureCallback, Unretained(this)));
    513     ip_config_ = config_;
    514   }
    515 
    516   MOCK_METHOD2(SuccessCallback,
    517                void(const IPConfigRefPtr& ipconfig, bool new_lease_acquired));
    518   MOCK_METHOD1(FailureCallback, void(const IPConfigRefPtr& ipconfig));
    519 
    520   // The mock methods above take IPConfigRefPtr because this is the type
    521   // that the registered callbacks take.  This conversion of the DHCP
    522   // config ref pointer eases our work in setting up expectations.
    523   const IPConfigRefPtr& ConfigRef() { return ip_config_; }
    524 
    525  private:
    526   IPConfigRefPtr ip_config_;
    527 };
    528 
    529 }  // namespace
    530 
    531 TEST_F(DHCPv4ConfigCallbackTest, ProcessEventSignalFail) {
    532   KeyValueStore conf;
    533   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    534   EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0);
    535   EXPECT_CALL(*this, FailureCallback(ConfigRef()));
    536   config_->ProcessEventSignal(DHCPv4Config::kReasonFail, conf);
    537   Mock::VerifyAndClearExpectations(this);
    538   EXPECT_TRUE(config_->properties().address.empty());
    539 }
    540 
    541 TEST_F(DHCPv4ConfigCallbackTest, ProcessEventSignalSuccess) {
    542   for (const auto& reason : { DHCPv4Config::kReasonBound,
    543                               DHCPv4Config::kReasonRebind,
    544                               DHCPv4Config::kReasonReboot,
    545                               DHCPv4Config::kReasonRenew }) {
    546     int address_octet = 0;
    547     for (const auto lease_time_given : { false, true }) {
    548       KeyValueStore conf;
    549       conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, ++address_octet);
    550       if (lease_time_given) {
    551         const uint32_t kLeaseTime = 1;
    552         conf.SetUint(DHCPv4Config::kConfigurationKeyLeaseTime, kLeaseTime);
    553       }
    554       EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true));
    555       EXPECT_CALL(*this, FailureCallback(_)).Times(0);
    556       config_->ProcessEventSignal(reason, conf);
    557       string failure_message = string(reason) + " failed with lease time " +
    558           (lease_time_given ? "given" : "not given");
    559       EXPECT_TRUE(Mock::VerifyAndClearExpectations(this)) << failure_message;
    560       EXPECT_EQ(base::StringPrintf("%d.0.0.0", address_octet),
    561                 config_->properties().address) << failure_message;
    562     }
    563   }
    564 }
    565 
    566 TEST_F(DHCPv4ConfigCallbackTest, StoppedDuringFailureCallback) {
    567   KeyValueStore conf;
    568   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    569   // Stop the DHCP config while it is calling the failure callback.  We
    570   // need to ensure that no callbacks are left running inadvertently as
    571   // a result.
    572   EXPECT_CALL(*this, FailureCallback(ConfigRef()))
    573       .WillOnce(InvokeWithoutArgs(this, &DHCPv4ConfigTest::StopInstance));
    574   config_->ProcessEventSignal(DHCPv4Config::kReasonFail, conf);
    575   EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
    576 }
    577 
    578 TEST_F(DHCPv4ConfigCallbackTest, StoppedDuringSuccessCallback) {
    579   KeyValueStore conf;
    580   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    581   const uint32_t kLeaseTime = 1;
    582   conf.SetUint(DHCPv4Config::kConfigurationKeyLeaseTime, kLeaseTime);
    583   // Stop the DHCP config while it is calling the success callback.  This
    584   // can happen if the device has a static IP configuration and releases
    585   // the lease after accepting other network parameters from the DHCP
    586   // IPConfig properties.  We need to ensure that no callbacks are left
    587   // running inadvertently as a result.
    588   EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true))
    589       .WillOnce(InvokeWithoutArgs(this, &DHCPv4ConfigTest::StopInstance));
    590   config_->ProcessEventSignal(DHCPv4Config::kReasonBound, conf);
    591   EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
    592 }
    593 
    594 TEST_F(DHCPv4ConfigCallbackTest, ProcessEventSignalUnknown) {
    595   KeyValueStore conf;
    596   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    597   static const char kReasonUnknown[] = "UNKNOWN_REASON";
    598   EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0);
    599   EXPECT_CALL(*this, FailureCallback(_)).Times(0);
    600   config_->ProcessEventSignal(kReasonUnknown, conf);
    601   Mock::VerifyAndClearExpectations(this);
    602   EXPECT_TRUE(config_->properties().address.empty());
    603 }
    604 
    605 TEST_F(DHCPv4ConfigCallbackTest, ProcessEventSignalGatewayArp) {
    606   KeyValueStore conf;
    607   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    608   EXPECT_CALL(*this, SuccessCallback(ConfigRef(), false));
    609   EXPECT_CALL(*this, FailureCallback(_)).Times(0);
    610   EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _))
    611       .WillOnce(Return(0));
    612   StartInstance(config_);
    613   config_->ProcessEventSignal(DHCPv4Config::kReasonGatewayArp, conf);
    614   Mock::VerifyAndClearExpectations(this);
    615   EXPECT_EQ("4.3.2.1", config_->properties().address);
    616   EXPECT_TRUE(config_->is_gateway_arp_active_);
    617   // Will not fail on acquisition timeout since Gateway ARP is active.
    618   EXPECT_FALSE(config_->ShouldFailOnAcquisitionTimeout());
    619 
    620   // An official reply from a DHCP server should reset our GatewayArp state.
    621   EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true));
    622   EXPECT_CALL(*this, FailureCallback(_)).Times(0);
    623   config_->ProcessEventSignal(DHCPv4Config::kReasonRenew, conf);
    624   Mock::VerifyAndClearExpectations(this);
    625   EXPECT_FALSE(config_->is_gateway_arp_active_);
    626   // Will fail on acquisition timeout since Gateway ARP is not active.
    627   EXPECT_TRUE(config_->ShouldFailOnAcquisitionTimeout());
    628 }
    629 
    630 TEST_F(DHCPv4ConfigCallbackTest, ProcessEventSignalGatewayArpNak) {
    631   KeyValueStore conf;
    632   conf.SetUint(DHCPv4Config::kConfigurationKeyIPAddress, 0x01020304);
    633   EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _))
    634       .WillOnce(Return(0));
    635   StartInstance(config_);
    636   config_->ProcessEventSignal(DHCPv4Config::kReasonGatewayArp, conf);
    637   EXPECT_TRUE(config_->is_gateway_arp_active_);
    638 
    639   // Sending a NAK should clear is_gateway_arp_active_.
    640   config_->ProcessEventSignal(DHCPv4Config::kReasonNak, conf);
    641   EXPECT_FALSE(config_->is_gateway_arp_active_);
    642   // Will fail on acquisition timeout since Gateway ARP is not active.
    643   EXPECT_TRUE(config_->ShouldFailOnAcquisitionTimeout());
    644   Mock::VerifyAndClearExpectations(this);
    645 }
    646 
    647 TEST_F(DHCPv4ConfigTest, ProcessStatusChangeSingal) {
    648   EXPECT_CALL(metrics_, NotifyDhcpClientStatus(
    649       Metrics::kDhcpClientStatusBound));
    650   config_->ProcessStatusChangeSignal(DHCPv4Config::kStatusBound);
    651 }
    652 
    653 TEST_F(DHCPv4ConfigTest, StartSuccessEphemeral) {
    654   DHCPv4ConfigRefPtr config =
    655       CreateRunningConfig(kHostName, kVendorClass, kDeviceName, kArpGateway);
    656   StopRunningConfigAndExpect(config, false);
    657 }
    658 
    659 TEST_F(DHCPv4ConfigTest, StartSuccessPersistent) {
    660   DHCPv4ConfigRefPtr config =
    661       CreateRunningConfig(kHostName, kVendorClass,
    662                           kLeaseFileSuffix, kArpGateway);
    663   StopRunningConfigAndExpect(config, true);
    664 }
    665 
    666 }  // namespace shill
    667