Home | History | Annotate | Download | only in dhcp
      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:
     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 
    112 const int DHCPv6ConfigTest::kPID = 123456;
    113 const unsigned int DHCPv6ConfigTest::kTag = 77;
    114 
    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 
    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 
    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 
    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 
    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:
    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 
    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 
    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 
    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 
    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 
    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 
    351 TEST_F(DHCPv6ConfigTest, StartSuccessEphemeral) {
    352   DHCPv6ConfigRefPtr config =
    353       CreateRunningConfig(kDeviceName);
    354   StopRunningConfigAndExpect(config, false);
    355 }
    356 
    357 TEST_F(DHCPv6ConfigTest, StartSuccessPersistent) {
    358   DHCPv6ConfigRefPtr config =
    359       CreateRunningConfig(kLeaseFileSuffix);
    360   StopRunningConfigAndExpect(config, true);
    361 }
    362 
    363 }  // namespace shill
    364