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 
41 using base::Bind;
42 using base::FilePath;
43 using base::ScopedTempDir;
44 using base::Unretained;
45 using std::string;
46 using std::unique_ptr;
47 using std::vector;
48 using testing::_;
49 using testing::AnyNumber;
50 using testing::ContainsRegex;
51 using testing::InvokeWithoutArgs;
52 using testing::Mock;
53 using testing::Return;
54 using testing::SetArgumentPointee;
55 using testing::Test;
56 
57 namespace shill {
58 
59 namespace {
60 const char kDeviceName[] = "eth0";
61 const char kDhcpMethod[] = "dhcp";
62 const char kLeaseFileSuffix[] = "leasefilesuffix";
63 const bool kHasLeaseSuffix = true;
64 }  // namespace
65 
66 class TestDHCPConfig : public DHCPConfig {
67  public:
TestDHCPConfig(ControlInterface * control_interface,EventDispatcher * dispatcher,DHCPProvider * provider,const std::string & device_name,const std::string & type,const std::string & lease_file_suffix)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 
~TestDHCPConfig()81   ~TestDHCPConfig() {}
82 
ProcessEventSignal(const std::string & reason,const KeyValueStore & configuration)83   void ProcessEventSignal(const std::string& reason,
84                           const KeyValueStore& configuration) override {}
ProcessStatusChangeSignal(const std::string & status)85   void ProcessStatusChangeSignal(const std::string& status) override {}
86 
87   MOCK_METHOD0(ShouldFailOnAcquisitionTimeout, bool());
88   MOCK_METHOD0(ShouldKeepLeaseOnDisconnect, bool());
89 };
90 
91 typedef scoped_refptr<TestDHCPConfig> TestDHCPConfigRefPtr;
92 
93 class DHCPConfigTest : public PropertyStoreTest {
94  public:
DHCPConfigTest()95   DHCPConfigTest()
96       : proxy_(new MockDHCPProxy()),
97         config_(new TestDHCPConfig(&control_,
98                                    dispatcher(),
99                                    &provider_,
100                                    kDeviceName,
101                                    kDhcpMethod,
102                                    kLeaseFileSuffix)) {}
103 
SetUp()104   virtual void SetUp() {
105     config_->process_manager_ = &process_manager_;
106   }
107 
StopInstance()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 
124 const int DHCPConfigTest::kPID = 123456;
125 
CreateMockMinijailConfig(const string & lease_suffix)126 TestDHCPConfigRefPtr 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 
TEST_F(DHCPConfigTest,InitProxy)139 TEST_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 
TEST_F(DHCPConfigTest,StartFail)152 TEST_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 
160 MATCHER_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 
TEST_F(DHCPConfigTest,StartWithoutLeaseSuffix)173 TEST_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 
182 namespace {
183 
184 class DHCPConfigCallbackTest : public DHCPConfigTest {
185  public:
SetUp()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.
ConfigRef()202   const IPConfigRefPtr& ConfigRef() { return ip_config_; }
203 
204  private:
205   IPConfigRefPtr ip_config_;
206 };
207 
DoNothing()208 void DoNothing() {}
209 
210 }  // namespace
211 
TEST_F(DHCPConfigCallbackTest,NotifyFailure)212 TEST_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 
TEST_F(DHCPConfigCallbackTest,StoppedDuringFailureCallback)224 TEST_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 
TEST_F(DHCPConfigCallbackTest,StoppedDuringSuccessCallback)236 TEST_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 
TEST_F(DHCPConfigCallbackTest,ProcessAcquisitionTimeout)253 TEST_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 
TEST_F(DHCPConfigTest,ReleaseIP)271 TEST_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 
TEST_F(DHCPConfigTest,KeepLeaseOnDisconnect)279 TEST_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 
TEST_F(DHCPConfigTest,ReleaseLeaseOnDisconnect)291 TEST_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 
TEST_F(DHCPConfigTest,ReleaseIPStaticIPWithLease)303 TEST_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 
TEST_F(DHCPConfigTest,ReleaseIPStaticIPWithoutLease)313 TEST_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 
TEST_F(DHCPConfigTest,RenewIP)325 TEST_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 
TEST_F(DHCPConfigTest,RequestIP)345 TEST_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 
TEST_F(DHCPConfigCallbackTest,RequestIPTimeout)355 TEST_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 
TEST_F(DHCPConfigTest,Restart)371 TEST_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 
TEST_F(DHCPConfigTest,RestartNoClient)385 TEST_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 
TEST_F(DHCPConfigCallbackTest,StartTimeout)396 TEST_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 
TEST_F(DHCPConfigTest,Stop)411 TEST_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 
TEST_F(DHCPConfigTest,StopDuringRequestIP)427 TEST_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 
TEST_F(DHCPConfigTest,SetProperty)438 TEST_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