1//
2// Copyright (C) 2011 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/ipconfig.h"
18
19#include <sys/time.h>
20
21#include <base/bind.h>
22#if defined(__ANDROID__)
23#include <dbus/service_constants.h>
24#else
25#include <chromeos/dbus/service_constants.h>
26#endif  // __ANDROID__
27#include <gmock/gmock.h>
28#include <gtest/gtest.h>
29
30#include "shill/logging.h"
31#include "shill/mock_adaptors.h"
32#include "shill/mock_control.h"
33#include "shill/mock_log.h"
34#include "shill/mock_store.h"
35#include "shill/net/mock_time.h"
36#include "shill/static_ip_parameters.h"
37
38using base::Bind;
39using base::Unretained;
40using std::string;
41using testing::_;
42using testing::EndsWith;
43using testing::DoAll;
44using testing::Mock;
45using testing::Return;
46using testing::SaveArg;
47using testing::SetArgPointee;
48using testing::SetArgumentPointee;
49using testing::StrictMock;
50using testing::Test;
51
52namespace shill {
53
54namespace {
55const char kDeviceName[] = "testdevice";
56const uint32_t kTimeNow = 10;
57}  // namespace
58
59class IPConfigTest : public Test {
60 public:
61  IPConfigTest() : ipconfig_(new IPConfig(&control_, kDeviceName)) {
62    ipconfig_->time_ = &time_;
63  }
64
65  virtual void SetUp() {
66    ScopeLogger::GetInstance()->EnableScopesByName("inet");
67    ScopeLogger::GetInstance()->set_verbose_level(3);
68  }
69
70  virtual void TearDown() {
71    ScopeLogger::GetInstance()->EnableScopesByName("-inet");
72    ScopeLogger::GetInstance()->set_verbose_level(0);
73  }
74
75  void DropRef(const IPConfigRefPtr & /*ipconfig*/,
76               bool /*new_lease_acquired*/) {
77    ipconfig_ = nullptr;
78  }
79
80  MOCK_METHOD2(OnIPConfigUpdated,
81               void(const IPConfigRefPtr& ipconfig, bool new_lease_acquired));
82  MOCK_METHOD1(OnIPConfigFailed, void(const IPConfigRefPtr& ipconfig));
83  MOCK_METHOD1(OnIPConfigRefreshed, void(const IPConfigRefPtr& ipconfig));
84  MOCK_METHOD1(OnIPConfigExpired, void(const IPConfigRefPtr& ipconfig));
85
86 protected:
87  IPConfigMockAdaptor* GetAdaptor() {
88    return static_cast<IPConfigMockAdaptor*>(ipconfig_->adaptor_.get());
89  }
90
91  void UpdateProperties(const IPConfig::Properties& properties) {
92    ipconfig_->UpdateProperties(properties, true);
93  }
94
95  void NotifyFailure() {
96    ipconfig_->NotifyFailure();
97  }
98
99  void NotifyExpiry() {
100    ipconfig_->NotifyExpiry();
101  }
102
103  void ExpectPropertiesEqual(const IPConfig::Properties& properties) {
104    EXPECT_EQ(properties.address, ipconfig_->properties().address);
105    EXPECT_EQ(properties.subnet_prefix, ipconfig_->properties().subnet_prefix);
106    EXPECT_EQ(properties.broadcast_address,
107              ipconfig_->properties().broadcast_address);
108    EXPECT_EQ(properties.dns_servers.size(),
109              ipconfig_->properties().dns_servers.size());
110    if (properties.dns_servers.size() ==
111        ipconfig_->properties().dns_servers.size()) {
112      for (size_t i = 0; i < properties.dns_servers.size(); ++i) {
113        EXPECT_EQ(properties.dns_servers[i],
114                  ipconfig_->properties().dns_servers[i]);
115      }
116    }
117    EXPECT_EQ(properties.domain_search.size(),
118              ipconfig_->properties().domain_search.size());
119    if (properties.domain_search.size() ==
120        ipconfig_->properties().domain_search.size()) {
121      for (size_t i = 0; i < properties.domain_search.size(); ++i) {
122        EXPECT_EQ(properties.domain_search[i],
123                  ipconfig_->properties().domain_search[i]);
124      }
125    }
126    EXPECT_EQ(properties.gateway, ipconfig_->properties().gateway);
127    EXPECT_EQ(properties.blackhole_ipv6,
128              ipconfig_->properties().blackhole_ipv6);
129    EXPECT_EQ(properties.mtu, ipconfig_->properties().mtu);
130  }
131
132  MockControl control_;
133  MockTime time_;
134  IPConfigRefPtr ipconfig_;
135};
136
137TEST_F(IPConfigTest, DeviceName) {
138  EXPECT_EQ(kDeviceName, ipconfig_->device_name());
139}
140
141TEST_F(IPConfigTest, RequestIP) {
142  EXPECT_FALSE(ipconfig_->RequestIP());
143}
144
145TEST_F(IPConfigTest, RenewIP) {
146  EXPECT_FALSE(ipconfig_->RenewIP());
147}
148
149TEST_F(IPConfigTest, ReleaseIP) {
150  EXPECT_FALSE(ipconfig_->ReleaseIP(IPConfig::kReleaseReasonDisconnect));
151}
152
153TEST_F(IPConfigTest, UpdateProperties) {
154  IPConfig::Properties properties;
155  properties.address = "1.2.3.4";
156  properties.subnet_prefix = 24;
157  properties.broadcast_address = "11.22.33.44";
158  properties.dns_servers.push_back("10.20.30.40");
159  properties.dns_servers.push_back("20.30.40.50");
160  properties.domain_name = "foo.org";
161  properties.domain_search.push_back("zoo.org");
162  properties.domain_search.push_back("zoo.com");
163  properties.gateway = "5.6.7.8";
164  properties.blackhole_ipv6 = true;
165  properties.mtu = 700;
166  UpdateProperties(properties);
167  ExpectPropertiesEqual(properties);
168
169  // We should not reset on NotifyFailure.
170  NotifyFailure();
171  ExpectPropertiesEqual(properties);
172
173  // We should not reset on NotifyExpiry.
174  NotifyExpiry();
175  ExpectPropertiesEqual(properties);
176
177  // We should reset if ResetProperties is called.
178  ipconfig_->ResetProperties();
179  ExpectPropertiesEqual(IPConfig::Properties());
180}
181
182TEST_F(IPConfigTest, Callbacks) {
183  ipconfig_->RegisterUpdateCallback(
184      Bind(&IPConfigTest::OnIPConfigUpdated, Unretained(this)));
185  ipconfig_->RegisterFailureCallback(
186      Bind(&IPConfigTest::OnIPConfigFailed, Unretained(this)));
187  ipconfig_->RegisterRefreshCallback(
188      Bind(&IPConfigTest::OnIPConfigRefreshed, Unretained(this)));
189  ipconfig_->RegisterExpireCallback(
190      Bind(&IPConfigTest::OnIPConfigExpired, Unretained(this)));
191
192  EXPECT_CALL(*this, OnIPConfigUpdated(ipconfig_, true));
193  EXPECT_CALL(*this, OnIPConfigFailed(ipconfig_)).Times(0);
194  EXPECT_CALL(*this, OnIPConfigRefreshed(ipconfig_)).Times(0);
195  EXPECT_CALL(*this, OnIPConfigExpired(ipconfig_)).Times(0);
196  UpdateProperties(IPConfig::Properties());
197  Mock::VerifyAndClearExpectations(this);
198
199  EXPECT_CALL(*this, OnIPConfigUpdated(ipconfig_, true)).Times(0);
200  EXPECT_CALL(*this, OnIPConfigFailed(ipconfig_));
201  EXPECT_CALL(*this, OnIPConfigRefreshed(ipconfig_)).Times(0);
202  EXPECT_CALL(*this, OnIPConfigExpired(ipconfig_)).Times(0);
203  NotifyFailure();
204  Mock::VerifyAndClearExpectations(this);
205
206  EXPECT_CALL(*this, OnIPConfigUpdated(ipconfig_, true)).Times(0);
207  EXPECT_CALL(*this, OnIPConfigFailed(ipconfig_)).Times(0);
208  EXPECT_CALL(*this, OnIPConfigRefreshed(ipconfig_));
209  EXPECT_CALL(*this, OnIPConfigExpired(ipconfig_)).Times(0);
210  ipconfig_->Refresh(nullptr);
211  Mock::VerifyAndClearExpectations(this);
212
213  EXPECT_CALL(*this, OnIPConfigUpdated(ipconfig_, true)).Times(0);
214  EXPECT_CALL(*this, OnIPConfigFailed(ipconfig_)).Times(0);
215  EXPECT_CALL(*this, OnIPConfigRefreshed(ipconfig_)).Times(0);
216  EXPECT_CALL(*this, OnIPConfigExpired(ipconfig_));
217  NotifyExpiry();
218  Mock::VerifyAndClearExpectations(this);
219}
220
221TEST_F(IPConfigTest, UpdatePropertiesWithDropRef) {
222  // The UpdateCallback should be able to drop a reference to the
223  // IPConfig object without crashing.
224  ipconfig_->RegisterUpdateCallback(
225      Bind(&IPConfigTest::DropRef, Unretained(this)));
226  UpdateProperties(IPConfig::Properties());
227}
228
229TEST_F(IPConfigTest, PropertyChanges) {
230  IPConfigMockAdaptor* adaptor = GetAdaptor();
231
232  StaticIPParameters static_ip_params;
233  EXPECT_CALL(*adaptor, EmitStringChanged(kAddressProperty, _));
234  EXPECT_CALL(*adaptor, EmitStringsChanged(kNameServersProperty, _));
235  ipconfig_->ApplyStaticIPParameters(&static_ip_params);
236  Mock::VerifyAndClearExpectations(adaptor);
237
238  EXPECT_CALL(*adaptor, EmitStringChanged(kAddressProperty, _));
239  EXPECT_CALL(*adaptor, EmitStringsChanged(kNameServersProperty, _));
240  ipconfig_->RestoreSavedIPParameters(&static_ip_params);
241  Mock::VerifyAndClearExpectations(adaptor);
242
243  IPConfig::Properties ip_properties;
244  EXPECT_CALL(*adaptor, EmitStringChanged(kAddressProperty, _));
245  EXPECT_CALL(*adaptor, EmitStringsChanged(kNameServersProperty, _));
246  UpdateProperties(ip_properties);
247  Mock::VerifyAndClearExpectations(adaptor);
248
249  // It is the callback's responsibility for resetting the IPConfig
250  // properties (via IPConfig::ResetProperties()).  Since NotifyFailure
251  // by itself doesn't change any properties, it should not emit any
252  // property change events either.
253  EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0);
254  EXPECT_CALL(*adaptor, EmitStringsChanged(_, _)).Times(0);
255  NotifyFailure();
256  Mock::VerifyAndClearExpectations(adaptor);
257
258  // Similarly, NotifyExpiry() should have no property change side effects.
259  EXPECT_CALL(*adaptor, EmitStringChanged(_, _)).Times(0);
260  EXPECT_CALL(*adaptor, EmitStringsChanged(_, _)).Times(0);
261  NotifyExpiry();
262  Mock::VerifyAndClearExpectations(adaptor);
263
264  EXPECT_CALL(*adaptor, EmitStringChanged(kAddressProperty, _));
265  EXPECT_CALL(*adaptor, EmitStringsChanged(kNameServersProperty, _));
266  ipconfig_->ResetProperties();
267  Mock::VerifyAndClearExpectations(adaptor);
268}
269
270TEST_F(IPConfigTest, UpdateLeaseExpirationTime) {
271  const struct timeval expected_time_now = {kTimeNow , 0};
272  uint32_t lease_duration = 1;
273  EXPECT_CALL(time_, GetTimeBoottime(_))
274      .WillOnce(DoAll(SetArgPointee<0>(expected_time_now), Return(0)));
275  ipconfig_->UpdateLeaseExpirationTime(lease_duration);
276  EXPECT_EQ(kTimeNow + lease_duration,
277            ipconfig_->current_lease_expiration_time_.tv_sec);
278}
279
280TEST_F(IPConfigTest, TimeToLeaseExpiry_NoDHCPLease) {
281  ScopedMockLog log;
282  uint32_t time_left = 0;
283  // |current_lease_expiration_time_| has not been set, so expect an error.
284  EXPECT_CALL(log, Log(_, _,
285                       EndsWith("No current DHCP lease")));
286  EXPECT_FALSE(ipconfig_->TimeToLeaseExpiry(&time_left));
287  EXPECT_EQ(0, time_left);
288}
289
290TEST_F(IPConfigTest, TimeToLeaseExpiry_CurrentLeaseExpired) {
291  ScopedMockLog log;
292  const struct timeval time_now = {kTimeNow, 0};
293  uint32_t time_left = 0;
294  // Set |current_lease_expiration_time_| so it is expired (i.e. earlier than
295  // current time).
296  ipconfig_->current_lease_expiration_time_ = {kTimeNow - 1, 0};
297  EXPECT_CALL(time_, GetTimeBoottime(_))
298      .WillOnce(DoAll(SetArgPointee<0>(time_now), Return(0)));
299  EXPECT_CALL(log, Log(_, _,
300                       EndsWith("Current DHCP lease has already expired")));
301  EXPECT_FALSE(ipconfig_->TimeToLeaseExpiry(&time_left));
302  EXPECT_EQ(0, time_left);
303}
304
305TEST_F(IPConfigTest, TimeToLeaseExpiry_Success) {
306  const uint32_t expected_time_to_expiry = 10;
307  const struct timeval time_now = {kTimeNow, 0};
308  uint32_t time_left;
309  // Set |current_lease_expiration_time_| so it appears like we already
310  // have obtained a DHCP lease before.
311  ipconfig_->current_lease_expiration_time_ = {
312      kTimeNow + expected_time_to_expiry, 0};
313  EXPECT_CALL(time_, GetTimeBoottime(_))
314      .WillOnce(DoAll(SetArgPointee<0>(time_now), Return(0)));
315  EXPECT_TRUE(ipconfig_->TimeToLeaseExpiry(&time_left));
316  EXPECT_EQ(expected_time_to_expiry, time_left);
317}
318
319}  // namespace shill
320