• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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