1// 2// Copyright (C) 2012 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/dhcp_config.h" 18 19#include <memory> 20#include <string> 21#include <vector> 22 23#include <base/bind.h> 24#include <base/strings/stringprintf.h> 25#if defined(__ANDROID__) 26#include <dbus/service_constants.h> 27#else 28#include <chromeos/dbus/service_constants.h> 29#endif // __ANDROID__ 30 31#include "shill/dhcp/mock_dhcp_provider.h" 32#include "shill/dhcp/mock_dhcp_proxy.h" 33#include "shill/event_dispatcher.h" 34#include "shill/mock_control.h" 35#include "shill/mock_log.h" 36#include "shill/mock_metrics.h" 37#include "shill/mock_process_manager.h" 38#include "shill/property_store_unittest.h" 39#include "shill/testing.h" 40 41using base::Bind; 42using base::FilePath; 43using base::ScopedTempDir; 44using base::Unretained; 45using std::string; 46using std::unique_ptr; 47using std::vector; 48using testing::_; 49using testing::AnyNumber; 50using testing::ContainsRegex; 51using testing::InvokeWithoutArgs; 52using testing::Mock; 53using testing::Return; 54using testing::SetArgumentPointee; 55using testing::Test; 56 57namespace shill { 58 59namespace { 60const char kDeviceName[] = "eth0"; 61const char kDhcpMethod[] = "dhcp"; 62const char kLeaseFileSuffix[] = "leasefilesuffix"; 63const bool kHasLeaseSuffix = true; 64} // namespace 65 66class TestDHCPConfig : public DHCPConfig { 67 public: 68 TestDHCPConfig(ControlInterface* control_interface, 69 EventDispatcher* dispatcher, 70 DHCPProvider* provider, 71 const std::string& device_name, 72 const std::string& type, 73 const std::string& lease_file_suffix) 74 : DHCPConfig(control_interface, 75 dispatcher, 76 provider, 77 device_name, 78 type, 79 lease_file_suffix) {} 80 81 ~TestDHCPConfig() {} 82 83 void ProcessEventSignal(const std::string& reason, 84 const KeyValueStore& configuration) override {} 85 void ProcessStatusChangeSignal(const std::string& status) override {} 86 87 MOCK_METHOD0(ShouldFailOnAcquisitionTimeout, bool()); 88 MOCK_METHOD0(ShouldKeepLeaseOnDisconnect, bool()); 89}; 90 91typedef scoped_refptr<TestDHCPConfig> TestDHCPConfigRefPtr; 92 93class DHCPConfigTest : public PropertyStoreTest { 94 public: 95 DHCPConfigTest() 96 : proxy_(new MockDHCPProxy()), 97 config_(new TestDHCPConfig(&control_, 98 dispatcher(), 99 &provider_, 100 kDeviceName, 101 kDhcpMethod, 102 kLeaseFileSuffix)) {} 103 104 virtual void SetUp() { 105 config_->process_manager_ = &process_manager_; 106 } 107 108 void StopInstance() { 109 config_->Stop("In test"); 110 } 111 112 TestDHCPConfigRefPtr CreateMockMinijailConfig(const string& lease_suffix); 113 114 protected: 115 static const int kPID; 116 117 unique_ptr<MockDHCPProxy> proxy_; 118 MockControl control_; 119 MockProcessManager process_manager_; 120 TestDHCPConfigRefPtr config_; 121 MockDHCPProvider provider_; 122}; 123 124const int DHCPConfigTest::kPID = 123456; 125 126TestDHCPConfigRefPtr DHCPConfigTest::CreateMockMinijailConfig( 127 const string& lease_suffix) { 128 TestDHCPConfigRefPtr config(new TestDHCPConfig(&control_, 129 dispatcher(), 130 &provider_, 131 kDeviceName, 132 kDhcpMethod, 133 lease_suffix)); 134 config->process_manager_ = &process_manager_; 135 136 return config; 137} 138 139TEST_F(DHCPConfigTest, InitProxy) { 140 static const char kService[] = ":1.200"; 141 EXPECT_TRUE(proxy_.get()); 142 EXPECT_FALSE(config_->proxy_.get()); 143 EXPECT_CALL(control_, CreateDHCPProxy(kService)) 144 .WillOnce(ReturnAndReleasePointee(&proxy_)); 145 config_->InitProxy(kService); 146 EXPECT_FALSE(proxy_.get()); 147 EXPECT_TRUE(config_->proxy_.get()); 148 149 config_->InitProxy(kService); 150} 151 152TEST_F(DHCPConfigTest, StartFail) { 153 EXPECT_CALL(process_manager_, 154 StartProcessInMinijail(_, _, _, _, _, _, _)) 155 .WillOnce(Return(-1)); 156 EXPECT_FALSE(config_->Start()); 157 EXPECT_EQ(0, config_->pid_); 158} 159 160MATCHER_P(IsDHCPCDArgs, has_lease_suffix, "") { 161 if (arg[0] != "-B" || 162 arg[1] != "-q") { 163 return false; 164 } 165 166 int end_offset = 2; 167 168 string device_arg = has_lease_suffix ? 169 string(kDeviceName) + "=" + string(kLeaseFileSuffix) : kDeviceName; 170 return arg[end_offset] == device_arg; 171} 172 173TEST_F(DHCPConfigTest, StartWithoutLeaseSuffix) { 174 TestDHCPConfigRefPtr config = CreateMockMinijailConfig(kDeviceName); 175 EXPECT_CALL(process_manager_, 176 StartProcessInMinijail(_, _, IsDHCPCDArgs(!kHasLeaseSuffix), 177 _, _, _, _)) 178 .WillOnce(Return(-1)); 179 EXPECT_FALSE(config->Start()); 180} 181 182namespace { 183 184class DHCPConfigCallbackTest : public DHCPConfigTest { 185 public: 186 virtual void SetUp() { 187 DHCPConfigTest::SetUp(); 188 config_->RegisterUpdateCallback( 189 Bind(&DHCPConfigCallbackTest::SuccessCallback, Unretained(this))); 190 config_->RegisterFailureCallback( 191 Bind(&DHCPConfigCallbackTest::FailureCallback, Unretained(this))); 192 ip_config_ = config_; 193 } 194 195 MOCK_METHOD2(SuccessCallback, 196 void(const IPConfigRefPtr& ipconfig, bool new_lease_acquired)); 197 MOCK_METHOD1(FailureCallback, void(const IPConfigRefPtr& ipconfig)); 198 199 // The mock methods above take IPConfigRefPtr because this is the type 200 // that the registered callbacks take. This conversion of the DHCP 201 // config ref pointer eases our work in setting up expectations. 202 const IPConfigRefPtr& ConfigRef() { return ip_config_; } 203 204 private: 205 IPConfigRefPtr ip_config_; 206}; 207 208void DoNothing() {} 209 210} // namespace 211 212TEST_F(DHCPConfigCallbackTest, NotifyFailure) { 213 EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0); 214 EXPECT_CALL(*this, FailureCallback(ConfigRef())); 215 config_->lease_acquisition_timeout_callback_.Reset(base::Bind(&DoNothing)); 216 config_->lease_expiration_callback_.Reset(base::Bind(&DoNothing)); 217 config_->NotifyFailure(); 218 Mock::VerifyAndClearExpectations(this); 219 EXPECT_TRUE(config_->properties().address.empty()); 220 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 221 EXPECT_TRUE(config_->lease_expiration_callback_.IsCancelled()); 222} 223 224TEST_F(DHCPConfigCallbackTest, StoppedDuringFailureCallback) { 225 // Stop the DHCP config while it is calling the failure callback. We 226 // need to ensure that no callbacks are left running inadvertently as 227 // a result. 228 EXPECT_CALL(*this, FailureCallback(ConfigRef())) 229 .WillOnce(InvokeWithoutArgs(this, &DHCPConfigTest::StopInstance)); 230 config_->NotifyFailure(); 231 EXPECT_TRUE(Mock::VerifyAndClearExpectations(this)); 232 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 233 EXPECT_TRUE(config_->lease_expiration_callback_.IsCancelled()); 234} 235 236TEST_F(DHCPConfigCallbackTest, StoppedDuringSuccessCallback) { 237 IPConfig::Properties properties; 238 properties.address = "1.2.3.4"; 239 properties.lease_duration_seconds = 1; 240 // Stop the DHCP config while it is calling the success callback. This 241 // can happen if the device has a static IP configuration and releases 242 // the lease after accepting other network parameters from the DHCP 243 // IPConfig properties. We need to ensure that no callbacks are left 244 // running inadvertently as a result. 245 EXPECT_CALL(*this, SuccessCallback(ConfigRef(), true)) 246 .WillOnce(InvokeWithoutArgs(this, &DHCPConfigTest::StopInstance)); 247 config_->UpdateProperties(properties, true); 248 EXPECT_TRUE(Mock::VerifyAndClearExpectations(this)); 249 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 250 EXPECT_TRUE(config_->lease_expiration_callback_.IsCancelled()); 251} 252 253TEST_F(DHCPConfigCallbackTest, ProcessAcquisitionTimeout) { 254 // Do not fail on acquisition timeout (e.g. ARP gateway is active). 255 EXPECT_CALL(*config_.get(), ShouldFailOnAcquisitionTimeout()) 256 .WillOnce(Return(false)); 257 EXPECT_CALL(*this, FailureCallback(_)).Times(0); 258 config_->ProcessAcquisitionTimeout(); 259 Mock::VerifyAndClearExpectations(this); 260 Mock::VerifyAndClearExpectations(config_.get()); 261 262 // Fail on acquisition timeout. 263 EXPECT_CALL(*config_.get(), ShouldFailOnAcquisitionTimeout()) 264 .WillOnce(Return(true)); 265 EXPECT_CALL(*this, FailureCallback(_)).Times(1); 266 config_->ProcessAcquisitionTimeout(); 267 Mock::VerifyAndClearExpectations(this); 268 Mock::VerifyAndClearExpectations(config_.get()); 269} 270 271TEST_F(DHCPConfigTest, ReleaseIP) { 272 config_->pid_ = 1 << 18; // Ensure unknown positive PID. 273 EXPECT_CALL(*proxy_, Release(kDeviceName)).Times(1); 274 config_->proxy_.reset(proxy_.release()); 275 EXPECT_TRUE(config_->ReleaseIP(IPConfig::kReleaseReasonDisconnect)); 276 config_->pid_ = 0; 277} 278 279TEST_F(DHCPConfigTest, KeepLeaseOnDisconnect) { 280 config_->pid_ = 1 << 18; // Ensure unknown positive PID. 281 282 // Keep lease on disconnect (e.g. ARP gateway is enabled). 283 EXPECT_CALL(*config_.get(), ShouldKeepLeaseOnDisconnect()) 284 .WillOnce(Return(true)); 285 EXPECT_CALL(*proxy_, Release(kDeviceName)).Times(0); 286 config_->proxy_.reset(proxy_.release()); 287 EXPECT_TRUE(config_->ReleaseIP(IPConfig::kReleaseReasonDisconnect)); 288 config_->pid_ = 0; 289} 290 291TEST_F(DHCPConfigTest, ReleaseLeaseOnDisconnect) { 292 config_->pid_ = 1 << 18; // Ensure unknown positive PID. 293 294 // Release lease on disconnect. 295 EXPECT_CALL(*config_.get(), ShouldKeepLeaseOnDisconnect()) 296 .WillOnce(Return(false)); 297 EXPECT_CALL(*proxy_, Release(kDeviceName)).Times(1); 298 config_->proxy_.reset(proxy_.release()); 299 EXPECT_TRUE(config_->ReleaseIP(IPConfig::kReleaseReasonDisconnect)); 300 config_->pid_ = 0; 301} 302 303TEST_F(DHCPConfigTest, ReleaseIPStaticIPWithLease) { 304 config_->pid_ = 1 << 18; // Ensure unknown positive PID. 305 config_->is_lease_active_ = true; 306 EXPECT_CALL(*proxy_, Release(kDeviceName)); 307 config_->proxy_.reset(proxy_.release()); 308 EXPECT_TRUE(config_->ReleaseIP(IPConfig::kReleaseReasonStaticIP)); 309 EXPECT_EQ(nullptr, config_->proxy_.get()); 310 config_->pid_ = 0; 311} 312 313TEST_F(DHCPConfigTest, ReleaseIPStaticIPWithoutLease) { 314 config_->pid_ = 1 << 18; // Ensure unknown positive PID. 315 config_->is_lease_active_ = false; 316 EXPECT_CALL(*proxy_, Release(kDeviceName)).Times(0); 317 MockDHCPProxy* proxy_pointer = proxy_.get(); 318 config_->proxy_.reset(proxy_.release()); 319 EXPECT_TRUE(config_->ReleaseIP(IPConfig::kReleaseReasonStaticIP)); 320 // Expect that proxy has not been released. 321 EXPECT_EQ(proxy_pointer, config_->proxy_.get()); 322 config_->pid_ = 0; 323} 324 325TEST_F(DHCPConfigTest, RenewIP) { 326 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _)) 327 .WillOnce(Return(-1)); 328 config_->pid_ = 0; 329 EXPECT_FALSE(config_->RenewIP()); // Expect a call to Start() if pid_ is 0. 330 Mock::VerifyAndClearExpectations(&process_manager_); 331 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _)) 332 .Times(0); 333 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 334 config_->lease_expiration_callback_.Reset(base::Bind(&DoNothing)); 335 config_->pid_ = 456; 336 EXPECT_FALSE(config_->RenewIP()); // Expect no crash with NULL proxy. 337 EXPECT_CALL(*proxy_, Rebind(kDeviceName)).Times(1); 338 config_->proxy_.reset(proxy_.release()); 339 EXPECT_TRUE(config_->RenewIP()); 340 EXPECT_FALSE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 341 EXPECT_TRUE(config_->lease_expiration_callback_.IsCancelled()); 342 config_->pid_ = 0; 343} 344 345TEST_F(DHCPConfigTest, RequestIP) { 346 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 347 config_->pid_ = 567; 348 EXPECT_CALL(*proxy_, Rebind(kDeviceName)).Times(1); 349 config_->proxy_.reset(proxy_.release()); 350 EXPECT_TRUE(config_->RenewIP()); 351 EXPECT_FALSE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 352 config_->pid_ = 0; 353} 354 355TEST_F(DHCPConfigCallbackTest, RequestIPTimeout) { 356 EXPECT_CALL(*config_.get(), ShouldFailOnAcquisitionTimeout()) 357 .WillOnce(Return(true)); 358 EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0); 359 EXPECT_CALL(*this, FailureCallback(ConfigRef())); 360 config_->lease_acquisition_timeout_seconds_ = 0; 361 config_->pid_ = 567; 362 EXPECT_CALL(*proxy_, Rebind(kDeviceName)).Times(1); 363 config_->proxy_.reset(proxy_.release()); 364 config_->RenewIP(); 365 config_->dispatcher_->DispatchPendingEvents(); 366 Mock::VerifyAndClearExpectations(this); 367 Mock::VerifyAndClearExpectations(config_.get()); 368 config_->pid_ = 0; 369} 370 371TEST_F(DHCPConfigTest, Restart) { 372 const int kPID1 = 1 << 17; // Ensure unknown positive PID. 373 const int kPID2 = 987; 374 config_->pid_ = kPID1; 375 EXPECT_CALL(provider_, UnbindPID(kPID1)); 376 EXPECT_CALL(process_manager_, StopProcessAndBlock(kPID1)).WillOnce(Return(true)); 377 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _)) 378 .WillOnce(Return(kPID2)); 379 EXPECT_CALL(provider_, BindPID(kPID2, IsRefPtrTo(config_))); 380 EXPECT_TRUE(config_->Restart()); 381 EXPECT_EQ(kPID2, config_->pid_); 382 config_->pid_ = 0; 383} 384 385TEST_F(DHCPConfigTest, RestartNoClient) { 386 const int kPID = 777; 387 EXPECT_CALL(process_manager_, StopProcessAndBlock(_)).Times(0); 388 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _)) 389 .WillOnce(Return(kPID)); 390 EXPECT_CALL(provider_, BindPID(kPID, IsRefPtrTo(config_))); 391 EXPECT_TRUE(config_->Restart()); 392 EXPECT_EQ(kPID, config_->pid_); 393 config_->pid_ = 0; 394} 395 396TEST_F(DHCPConfigCallbackTest, StartTimeout) { 397 EXPECT_CALL(*config_.get(), ShouldFailOnAcquisitionTimeout()) 398 .WillOnce(Return(true)); 399 EXPECT_CALL(*this, SuccessCallback(_, _)).Times(0); 400 EXPECT_CALL(*this, FailureCallback(ConfigRef())); 401 config_->lease_acquisition_timeout_seconds_ = 0; 402 config_->proxy_.reset(proxy_.release()); 403 EXPECT_CALL(process_manager_, StartProcessInMinijail(_, _, _, _, _, _, _)) 404 .WillOnce(Return(0)); 405 config_->Start(); 406 config_->dispatcher_->DispatchPendingEvents(); 407 Mock::VerifyAndClearExpectations(this); 408 Mock::VerifyAndClearExpectations(config_.get()); 409} 410 411TEST_F(DHCPConfigTest, Stop) { 412 const int kPID = 1 << 17; // Ensure unknown positive PID. 413 ScopedMockLog log; 414 EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); 415 EXPECT_CALL(log, Log(_, _, ContainsRegex( 416 base::StringPrintf("Stopping.+%s", __func__)))); 417 config_->pid_ = kPID; 418 config_->lease_acquisition_timeout_callback_.Reset(base::Bind(&DoNothing)); 419 config_->lease_expiration_callback_.Reset(base::Bind(&DoNothing)); 420 EXPECT_CALL(provider_, UnbindPID(kPID)); 421 config_->Stop(__func__); 422 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 423 EXPECT_TRUE(config_->lease_expiration_callback_.IsCancelled()); 424 EXPECT_FALSE(config_->pid_); 425} 426 427TEST_F(DHCPConfigTest, StopDuringRequestIP) { 428 config_->pid_ = 567; 429 EXPECT_CALL(*proxy_, Rebind(kDeviceName)).Times(1); 430 config_->proxy_.reset(proxy_.release()); 431 EXPECT_TRUE(config_->RenewIP()); 432 EXPECT_FALSE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 433 config_->pid_ = 0; // Keep Stop from killing a real process. 434 config_->Stop(__func__); 435 EXPECT_TRUE(config_->lease_acquisition_timeout_callback_.IsCancelled()); 436} 437 438TEST_F(DHCPConfigTest, SetProperty) { 439 Error error; 440 // Ensure that an attempt to write a R/O property returns InvalidArgs error. 441 EXPECT_FALSE(config_->mutable_store()->SetAnyProperty( 442 kAddressProperty, PropertyStoreTest::kStringV, &error)); 443 EXPECT_TRUE(error.IsFailure()); 444 EXPECT_EQ(Error::kInvalidArguments, error.type()); 445} 446 447} // namespace shill 448