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/dhcpv6_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_log.h"
38 #include "shill/mock_metrics.h"
39 #include "shill/mock_process_manager.h"
40 #include "shill/property_store_unittest.h"
41 #include "shill/testing.h"
42
43 using base::Bind;
44 using base::FilePath;
45 using base::ScopedTempDir;
46 using base::Unretained;
47 using std::string;
48 using std::unique_ptr;
49 using std::vector;
50 using testing::_;
51 using testing::AnyNumber;
52 using testing::ContainsRegex;
53 using testing::InvokeWithoutArgs;
54 using testing::Mock;
55 using testing::Return;
56 using testing::SetArgumentPointee;
57 using testing::Test;
58
59 namespace shill {
60
61 namespace {
62 const char kDeviceName[] = "eth0";
63 const char kLeaseFileSuffix[] = "leasefilesuffix";
64 const bool kHasLeaseSuffix = true;
65 const char kIPAddress[] = "2001:db8:0:1::1";
66 const char kDelegatedPrefix[] = "2001:db8:0:100::";
67 } // namespace
68
69 typedef scoped_refptr<DHCPv6Config> DHCPv6ConfigRefPtr;
70
71 class DHCPv6ConfigTest : public PropertyStoreTest {
72 public:
DHCPv6ConfigTest()73 DHCPv6ConfigTest()
74 : proxy_(new MockDHCPProxy()),
75 config_(new DHCPv6Config(&control_,
76 dispatcher(),
77 &provider_,
78 kDeviceName,
79 kLeaseFileSuffix)) {}
80
SetUp()81 virtual void SetUp() {
82 config_->process_manager_ = &process_manager_;
83 }
84
StartInstance(DHCPv6ConfigRefPtr config)85 bool StartInstance(DHCPv6ConfigRefPtr config) {
86 return config->Start();
87 }
88
StopInstance()89 void StopInstance() {
90 config_->Stop("In test");
91 }
92
93 DHCPv6ConfigRefPtr CreateMockMinijailConfig(const string& lease_suffix);
94 DHCPv6ConfigRefPtr CreateRunningConfig(const string& lease_suffix);
95 void StopRunningConfigAndExpect(DHCPv6ConfigRefPtr config,
96 bool lease_file_exists);
97
98 protected:
99 static const int kPID;
100 static const unsigned int kTag;
101
102 FilePath lease_file_;
103 FilePath pid_file_;
104 ScopedTempDir temp_dir_;
105 unique_ptr<MockDHCPProxy> proxy_;
106 MockControl control_;
107 MockProcessManager process_manager_;
108 MockDHCPProvider provider_;
109 DHCPv6ConfigRefPtr config_;
110 };
111
112 const int DHCPv6ConfigTest::kPID = 123456;
113 const unsigned int DHCPv6ConfigTest::kTag = 77;
114
CreateMockMinijailConfig(const string & lease_suffix)115 DHCPv6ConfigRefPtr DHCPv6ConfigTest::CreateMockMinijailConfig(
116 const string& lease_suffix) {
117 DHCPv6ConfigRefPtr config(new DHCPv6Config(&control_,
118 dispatcher(),
119 &provider_,
120 kDeviceName,
121 lease_suffix));
122 config->process_manager_ = &process_manager_;
123
124 return config;
125 }
126
CreateRunningConfig(const string & lease_suffix)127 DHCPv6ConfigRefPtr DHCPv6ConfigTest::CreateRunningConfig(
128 const string& lease_suffix) {
129 DHCPv6ConfigRefPtr config(new DHCPv6Config(&control_,
130 dispatcher(),
131 &provider_,
132 kDeviceName,
133 lease_suffix));
134 config->process_manager_ = &process_manager_;
135 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _))
136 .WillOnce(Return(kPID));
137 EXPECT_CALL(provider_, BindPID(kPID, IsRefPtrTo(config)));
138 EXPECT_TRUE(config->Start());
139 EXPECT_EQ(kPID, config->pid_);
140
141 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
142 config->root_ = temp_dir_.path();
143 FilePath varrun = temp_dir_.path().Append("var/run/dhcpcd");
144 EXPECT_TRUE(base::CreateDirectory(varrun));
145 pid_file_ = varrun.Append(base::StringPrintf("dhcpcd-%s-6.pid", kDeviceName));
146 FilePath varlib = temp_dir_.path().Append("var/lib/dhcpcd");
147 EXPECT_TRUE(base::CreateDirectory(varlib));
148 lease_file_ =
149 varlib.Append(base::StringPrintf("dhcpcd-%s.lease6", kDeviceName));
150 EXPECT_EQ(0, base::WriteFile(pid_file_, "", 0));
151 EXPECT_EQ(0, base::WriteFile(lease_file_, "", 0));
152 EXPECT_TRUE(base::PathExists(pid_file_));
153 EXPECT_TRUE(base::PathExists(lease_file_));
154
155 return config;
156 }
157
StopRunningConfigAndExpect(DHCPv6ConfigRefPtr config,bool lease_file_exists)158 void DHCPv6ConfigTest::StopRunningConfigAndExpect(DHCPv6ConfigRefPtr config,
159 bool lease_file_exists) {
160 ScopedMockLog log;
161 // We use a non-zero exit status so that we get the log message.
162 EXPECT_CALL(log, Log(_, _, ::testing::EndsWith("status 10")));
163 EXPECT_CALL(provider_, UnbindPID(kPID));
164 config->OnProcessExited(10);
165
166 EXPECT_FALSE(base::PathExists(pid_file_));
167 EXPECT_EQ(lease_file_exists, base::PathExists(lease_file_));
168 }
169
TEST_F(DHCPv6ConfigTest,ParseConfiguration)170 TEST_F(DHCPv6ConfigTest, ParseConfiguration) {
171 const char kConfigIPAddress[] = "2001:db8:0:1::129";
172 const char kConfigDelegatedPrefix[] = "2001:db8:1:100::";
173 const char kConfigNameServer[] = "fec8:0::1";
174 const char kConfigDomainSearch[] = "example.domain";
175 const uint32_t kConfigDelegatedPrefixLength = 56;
176 const uint32_t kConfigIPAddressLeaseTime = 5;
177 const uint32_t kConfigDelegatedPrefixLeaseTime = 10;
178
179 KeyValueStore conf;
180 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kConfigIPAddress);
181 conf.SetUint(DHCPv6Config::kConfigurationKeyIPAddressLeaseTime,
182 kConfigIPAddressLeaseTime);
183 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
184 kConfigDelegatedPrefix);
185 conf.SetUint(DHCPv6Config::kConfigurationKeyDelegatedPrefixLength,
186 kConfigDelegatedPrefixLength);
187 conf.SetUint(DHCPv6Config::kConfigurationKeyDelegatedPrefixLeaseTime,
188 kConfigDelegatedPrefixLeaseTime);
189 {
190 vector<string> dns;
191 dns.push_back(kConfigNameServer);
192 conf.SetStrings(DHCPv6Config::kConfigurationKeyDNS, dns);
193 }
194 {
195 vector<string> domain_search;
196 domain_search.push_back(kConfigDomainSearch);
197 conf.SetStrings(DHCPv6Config::kConfigurationKeyDomainSearch, domain_search);
198 }
199 conf.SetString("UnknownKey", "UnknownValue");
200
201 ASSERT_TRUE(config_->ParseConfiguration(conf));
202 EXPECT_EQ(kConfigIPAddress, config_->properties_.address);
203 EXPECT_EQ(kConfigDelegatedPrefix, config_->properties_.delegated_prefix);
204 EXPECT_EQ(kConfigDelegatedPrefixLength,
205 config_->properties_.delegated_prefix_length);
206 ASSERT_EQ(1, config_->properties_.dns_servers.size());
207 EXPECT_EQ(kConfigNameServer, config_->properties_.dns_servers[0]);
208 ASSERT_EQ(1, config_->properties_.domain_search.size());
209 EXPECT_EQ(kConfigDomainSearch, config_->properties_.domain_search[0]);
210 // Use IP address lease time since it is shorter.
211 EXPECT_EQ(kConfigIPAddressLeaseTime,
212 config_->properties_.lease_duration_seconds);
213 }
214
215 MATCHER_P(IsDHCPCDv6Args, has_lease_suffix, "") {
216 if (arg[0] != "-B" ||
217 arg[1] != "-q" ||
218 arg[2] != "-6" ||
219 arg[3] != "-a") {
220 return false;
221 }
222
223 int end_offset = 4;
224
225 string device_arg = has_lease_suffix ?
226 string(kDeviceName) + "=" + string(kLeaseFileSuffix) : kDeviceName;
227 return arg[end_offset] == device_arg;
228 }
229
TEST_F(DHCPv6ConfigTest,StartDhcpcd)230 TEST_F(DHCPv6ConfigTest, StartDhcpcd) {
231 EXPECT_CALL(process_manager_,
232 StartProcessInMinijail(_, _, IsDHCPCDv6Args(kHasLeaseSuffix),
233 _, _, _, _))
234 .WillOnce(Return(-1));
235 EXPECT_FALSE(StartInstance(config_));
236 }
237
238
239 namespace {
240
241 class DHCPv6ConfigCallbackTest : public DHCPv6ConfigTest {
242 public:
SetUp()243 virtual void SetUp() {
244 DHCPv6ConfigTest::SetUp();
245 config_->RegisterUpdateCallback(
246 Bind(&DHCPv6ConfigCallbackTest::SuccessCallback, Unretained(this)));
247 config_->RegisterFailureCallback(
248 Bind(&DHCPv6ConfigCallbackTest::FailureCallback, Unretained(this)));
249 ip_config_ = config_;
250 }
251
252 MOCK_METHOD2(SuccessCallback,
253 void(const IPConfigRefPtr& ipconfig, bool new_lease_acquired));
254 MOCK_METHOD1(FailureCallback, void(const IPConfigRefPtr& ipconfig));
255
256 // The mock methods above take IPConfigRefPtr because this is the type
257 // that the registered callbacks take. This conversion of the DHCP
258 // config ref pointer eases our work in setting up expectations.
ConfigRef()259 const IPConfigRefPtr& ConfigRef() { return ip_config_; }
260
261 private:
262 IPConfigRefPtr ip_config_;
263 };
264
265 } // namespace
266
TEST_F(DHCPv6ConfigCallbackTest,ProcessEventSignalFail)267 TEST_F(DHCPv6ConfigCallbackTest, ProcessEventSignalFail) {
268 KeyValueStore conf;
269 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kIPAddress);
270 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
271 kDelegatedPrefix);
272 EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0);
273 EXPECT_CALL(*this, FailureCallback(ConfigRef()));
274 config_->ProcessEventSignal(DHCPv6Config::kReasonFail, conf);
275 Mock::VerifyAndClearExpectations(this);
276 EXPECT_TRUE(config_->properties().address.empty());
277 }
278
TEST_F(DHCPv6ConfigCallbackTest,ProcessEventSignalSuccess)279 TEST_F(DHCPv6ConfigCallbackTest, ProcessEventSignalSuccess) {
280 for (const auto& reason : { DHCPv6Config::kReasonBound,
281 DHCPv6Config::kReasonRebind,
282 DHCPv6Config::kReasonReboot,
283 DHCPv6Config::kReasonRenew }) {
284 for (const auto lease_time_given : { false, true }) {
285 KeyValueStore conf;
286 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kIPAddress);
287 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
288 kDelegatedPrefix);
289 if (lease_time_given) {
290 const uint32_t kLeaseTime = 1;
291 conf.SetUint(DHCPv6Config::kConfigurationKeyIPAddressLeaseTime,
292 kLeaseTime);
293 }
294 EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true));
295 EXPECT_CALL(*this, FailureCallback(_)).Times(0);
296 config_->ProcessEventSignal(reason, conf);
297 string failure_message = string(reason) + " failed with lease time " +
298 (lease_time_given ? "given" : "not given");
299 EXPECT_TRUE(Mock::VerifyAndClearExpectations(this)) << failure_message;
300 EXPECT_EQ("2001:db8:0:1::1", config_->properties().address)
301 << failure_message;
302 }
303 }
304 }
305
TEST_F(DHCPv6ConfigCallbackTest,StoppedDuringFailureCallback)306 TEST_F(DHCPv6ConfigCallbackTest, StoppedDuringFailureCallback) {
307 KeyValueStore conf;
308 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kIPAddress);
309 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
310 kDelegatedPrefix);
311 // Stop the DHCP config while it is calling the failure callback. We
312 // need to ensure that no callbacks are left running inadvertently as
313 // a result.
314 EXPECT_CALL(*this, FailureCallback(ConfigRef()))
315 .WillOnce(InvokeWithoutArgs(this, &DHCPv6ConfigTest::StopInstance));
316 config_->ProcessEventSignal(DHCPv6Config::kReasonFail, conf);
317 EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
318 }
319
TEST_F(DHCPv6ConfigCallbackTest,StoppedDuringSuccessCallback)320 TEST_F(DHCPv6ConfigCallbackTest, StoppedDuringSuccessCallback) {
321 KeyValueStore conf;
322 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kIPAddress);
323 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
324 kDelegatedPrefix);
325 const uint32_t kLeaseTime = 1;
326 conf.SetUint(DHCPv6Config::kConfigurationKeyIPAddressLeaseTime, kLeaseTime);
327 // Stop the DHCP config while it is calling the success callback. This
328 // can happen if the device has a static IP configuration and releases
329 // the lease after accepting other network parameters from the DHCP
330 // IPConfig properties. We need to ensure that no callbacks are left
331 // running inadvertently as a result.
332 EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true))
333 .WillOnce(InvokeWithoutArgs(this, &DHCPv6ConfigTest::StopInstance));
334 config_->ProcessEventSignal(DHCPv6Config::kReasonBound, conf);
335 EXPECT_TRUE(Mock::VerifyAndClearExpectations(this));
336 }
337
TEST_F(DHCPv6ConfigCallbackTest,ProcessEventSignalUnknown)338 TEST_F(DHCPv6ConfigCallbackTest, ProcessEventSignalUnknown) {
339 KeyValueStore conf;
340 conf.SetString(DHCPv6Config::kConfigurationKeyIPAddress, kIPAddress);
341 conf.SetString(DHCPv6Config::kConfigurationKeyDelegatedPrefix,
342 kDelegatedPrefix);
343 static const char kReasonUnknown[] = "UNKNOWN_REASON";
344 EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0);
345 EXPECT_CALL(*this, FailureCallback(_)).Times(0);
346 config_->ProcessEventSignal(kReasonUnknown, conf);
347 Mock::VerifyAndClearExpectations(this);
348 EXPECT_TRUE(config_->properties().address.empty());
349 }
350
TEST_F(DHCPv6ConfigTest,StartSuccessEphemeral)351 TEST_F(DHCPv6ConfigTest, StartSuccessEphemeral) {
352 DHCPv6ConfigRefPtr config =
353 CreateRunningConfig(kDeviceName);
354 StopRunningConfigAndExpect(config, false);
355 }
356
TEST_F(DHCPv6ConfigTest,StartSuccessPersistent)357 TEST_F(DHCPv6ConfigTest, StartSuccessPersistent) {
358 DHCPv6ConfigRefPtr config =
359 CreateRunningConfig(kLeaseFileSuffix);
360 StopRunningConfigAndExpect(config, true);
361 }
362
363 } // namespace shill
364