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
43using base::Bind;
44using base::FilePath;
45using base::ScopedTempDir;
46using base::Unretained;
47using std::string;
48using std::unique_ptr;
49using std::vector;
50using testing::_;
51using testing::AnyNumber;
52using testing::ContainsRegex;
53using testing::InvokeWithoutArgs;
54using testing::Mock;
55using testing::Return;
56using testing::SetArgumentPointee;
57using testing::Test;
58
59namespace shill {
60
61namespace {
62const char kDeviceName[] = "eth0";
63const char kLeaseFileSuffix[] = "leasefilesuffix";
64const bool kHasLeaseSuffix = true;
65const char kIPAddress[] = "2001:db8:0:1::1";
66const char kDelegatedPrefix[] = "2001:db8:0:100::";
67}  // namespace
68
69typedef scoped_refptr<DHCPv6Config> DHCPv6ConfigRefPtr;
70
71class DHCPv6ConfigTest : public PropertyStoreTest {
72 public:
73  DHCPv6ConfigTest()
74      : proxy_(new MockDHCPProxy()),
75        config_(new DHCPv6Config(&control_,
76                                 dispatcher(),
77                                 &provider_,
78                                 kDeviceName,
79                                 kLeaseFileSuffix)) {}
80
81  virtual void SetUp() {
82    config_->process_manager_ = &process_manager_;
83  }
84
85  bool StartInstance(DHCPv6ConfigRefPtr config) {
86    return config->Start();
87  }
88
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
112const int DHCPv6ConfigTest::kPID = 123456;
113const unsigned int DHCPv6ConfigTest::kTag = 77;
114
115DHCPv6ConfigRefPtr 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
127DHCPv6ConfigRefPtr 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
158void 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
170TEST_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
215MATCHER_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
230TEST_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
239namespace {
240
241class DHCPv6ConfigCallbackTest : public DHCPv6ConfigTest {
242 public:
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.
259  const IPConfigRefPtr& ConfigRef() { return ip_config_; }
260
261 private:
262  IPConfigRefPtr ip_config_;
263};
264
265}  // namespace
266
267TEST_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
279TEST_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
306TEST_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
320TEST_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
338TEST_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
351TEST_F(DHCPv6ConfigTest, StartSuccessEphemeral) {
352  DHCPv6ConfigRefPtr config =
353      CreateRunningConfig(kDeviceName);
354  StopRunningConfigAndExpect(config, false);
355}
356
357TEST_F(DHCPv6ConfigTest, StartSuccessPersistent) {
358  DHCPv6ConfigRefPtr config =
359      CreateRunningConfig(kLeaseFileSuffix);
360  StopRunningConfigAndExpect(config, true);
361}
362
363}  // namespace shill
364