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:
DHCPv4ConfigTest()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
SetUp()92 virtual void SetUp() {
93 config_->process_manager_ = &process_manager_;
94 }
95
StartInstance(DHCPv4ConfigRefPtr config)96 bool StartInstance(DHCPv4ConfigRefPtr config) {
97 return config->Start();
98 }
99
StopInstance()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
CreateMockMinijailConfig(const string & hostname,const string & vendorclass,const string & lease_suffix,bool arp_gateway)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
CreateRunningConfig(const string & hostname,const string & vendorclass,const string & lease_suffix,bool arp_gateway)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
StopRunningConfigAndExpect(DHCPv4ConfigRefPtr config,bool lease_file_exists)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
TEST_F(DHCPv4ConfigTest,GetIPv4AddressString)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
TEST_F(DHCPv4ConfigTest,ParseClasslessStaticRoutes)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
TEST_F(DHCPv4ConfigTest,ParseConfiguration)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
TEST_F(DHCPv4ConfigTest,ParseConfigurationWithMinimumMTU)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
TEST_F(DHCPv4ConfigTest,StartWithHostname)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
TEST_F(DHCPv4ConfigTest,StartWithoutHostname)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
TEST_F(DHCPv4ConfigTest,StartWithEmptyHostname)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
TEST_F(DHCPv4ConfigTest,StartWithVendorClass)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
TEST_F(DHCPv4ConfigTest,StartWithoutVendorClass)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
TEST_F(DHCPv4ConfigTest,StartWithoutArpGateway)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:
SetUp()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.
ConfigRef()523 const IPConfigRefPtr& ConfigRef() { return ip_config_; }
524
525 private:
526 IPConfigRefPtr ip_config_;
527 };
528
529 } // namespace
530
TEST_F(DHCPv4ConfigCallbackTest,ProcessEventSignalFail)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
TEST_F(DHCPv4ConfigCallbackTest,ProcessEventSignalSuccess)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
TEST_F(DHCPv4ConfigCallbackTest,StoppedDuringFailureCallback)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
TEST_F(DHCPv4ConfigCallbackTest,StoppedDuringSuccessCallback)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
TEST_F(DHCPv4ConfigCallbackTest,ProcessEventSignalUnknown)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
TEST_F(DHCPv4ConfigCallbackTest,ProcessEventSignalGatewayArp)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
TEST_F(DHCPv4ConfigCallbackTest,ProcessEventSignalGatewayArpNak)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
TEST_F(DHCPv4ConfigTest,ProcessStatusChangeSingal)647 TEST_F(DHCPv4ConfigTest, ProcessStatusChangeSingal) {
648 EXPECT_CALL(metrics_, NotifyDhcpClientStatus(
649 Metrics::kDhcpClientStatusBound));
650 config_->ProcessStatusChangeSignal(DHCPv4Config::kStatusBound);
651 }
652
TEST_F(DHCPv4ConfigTest,StartSuccessEphemeral)653 TEST_F(DHCPv4ConfigTest, StartSuccessEphemeral) {
654 DHCPv4ConfigRefPtr config =
655 CreateRunningConfig(kHostName, kVendorClass, kDeviceName, kArpGateway);
656 StopRunningConfigAndExpect(config, false);
657 }
658
TEST_F(DHCPv4ConfigTest,StartSuccessPersistent)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