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