blob: fb8780e0e65cd0cf3b2965261217e044affdc07e [file] [log] [blame]
Samuel Tanf66080e2015-06-18 15:53:00 -07001// Copyright 2015 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/icmp_session.h"
6
7#include <base/test/simple_test_tick_clock.h>
8#include <gtest/gtest.h>
9
10#include "shill/mock_event_dispatcher.h"
11#include "shill/mock_icmp.h"
12#include "shill/net/ip_address.h"
13
14using base::Bind;
15using base::Unretained;
16using testing::_;
17using testing::NiceMock;
18using testing::Return;
19using testing::StrictMock;
20using testing::Test;
21
22namespace shill {
23
24namespace {
25
26// ICMP echo replies with 0 bytes of data and and echo ID 0. Sequence numbers
27// are 0x8, 0x9, and 0xa respectively to simulate replies to a sequence of sent
28// echo requests.
29const uint8_t kIcmpEchoReply1[] = {0x00, 0x00, 0xf7, 0xff,
30 0x00, 0x00, 0x08, 0x00};
31const uint16_t kIcmpEchoReply1_SeqNum = 0x08;
32const uint8_t kIcmpEchoReply2[] = {0x00, 0x00, 0xf6, 0xff,
33 0x00, 0x00, 0x09, 0x00};
34const uint16_t kIcmpEchoReply2_SeqNum = 0x09;
35const uint8_t kIcmpEchoReply3[] = {0x00, 0x00, 0xf5, 0xff,
36 0x00, 0x00, 0x0a, 0x00};
37const uint16_t kIcmpEchoReply3_SeqNum = 0x0a;
38
39// This ICMP echo reply has an echo ID of 0xe, which is different from the
40// echo ID used in the unit tests (0).
41const uint8_t kIcmpEchoReplyDifferentEchoID[] = {0x00, 0x00, 0xea, 0xff,
42 0x0e, 0x00, 0x0b, 0x00};
43
44} // namespace
45
46MATCHER_P(IsIPAddress, address, "") {
47 // IPAddress objects don't support the "==" operator as per style, so we need
48 // a custom matcher.
49 return address.Equals(arg);
50}
51
52class IcmpSessionTest : public Test {
53 public:
54 IcmpSessionTest() : icmp_session_(&dispatcher_) {}
55 virtual ~IcmpSessionTest() {}
56
57 virtual void SetUp() {
58 icmp_session_.tick_clock_ = &testing_clock_;
59 icmp_ = new NiceMock<MockIcmp>();
60 // Passes ownership.
61 icmp_session_.icmp_.reset(icmp_);
62 ON_CALL(*icmp_, IsStarted()).WillByDefault(Return(false));
63 }
64
65 virtual void TearDown() {
66 EXPECT_CALL(*icmp_, IsStarted());
67 IcmpSession::kNextUniqueEchoId = 0;
68 }
69
70 MOCK_METHOD1(ResultCallback, void(const IcmpSession::IcmpSessionResult&));
71
72 protected:
73 static const char kIPAddress[];
74
75 void StartAndVerify(const IPAddress& destination) {
76 EXPECT_CALL(*icmp_, IsStarted());
77 EXPECT_CALL(*icmp_, Start()).WillOnce(Return(true));
78 EXPECT_CALL(dispatcher_, CreateInputHandler(icmp_->socket(), _, _));
79 EXPECT_CALL(dispatcher_, PostDelayedTask(_, GetTimeoutSeconds() * 1000));
80 EXPECT_CALL(dispatcher_, PostTask(_));
81 EXPECT_TRUE(Start(destination));
82 EXPECT_TRUE(GetSeqNumToSentRecvTime()->empty());
83 EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty());
84 EXPECT_CALL(*icmp_, IsStarted()).WillRepeatedly(Return(true));
85 }
86
87 bool Start(const IPAddress& destination) {
88 return icmp_session_.Start(
89 destination, Bind(&IcmpSessionTest::ResultCallback, Unretained(this)));
90 }
91
92 void Stop() {
93 icmp_session_.Stop();
94 }
95
96 bool SeqNumToSentRecvTimeContains(uint16_t seq_num) {
97 return icmp_session_.seq_num_to_sent_recv_time_.find(seq_num) !=
98 icmp_session_.seq_num_to_sent_recv_time_.end();
99 }
100
101 bool ReceivedEchoReplySeqNumbersContains(uint16_t seq_num) {
102 return icmp_session_.received_echo_reply_seq_numbers_.find(seq_num) !=
103 icmp_session_.received_echo_reply_seq_numbers_.end();
104 }
105
106 void TransmitEchoRequestTask(const IPAddress& destination,
107 bool transmit_request_success) {
108 EXPECT_CALL(*icmp_, TransmitEchoRequest(IsIPAddress(destination),
109 icmp_session_.echo_id_,
110 GetCurrentSequenceNumber()))
111 .WillOnce(Return(transmit_request_success));
112 icmp_session_.TransmitEchoRequestTask(destination);
113 }
114
Samuel Tan42c33a42015-07-14 17:52:57 -0700115 void ReportResultAndStopSession() {
116 icmp_session_.ReportResultAndStopSession();
117 }
118
Samuel Tanf66080e2015-06-18 15:53:00 -0700119 void VerifyIcmpSessionStopped() {
120 EXPECT_TRUE(icmp_session_.timeout_callback_.IsCancelled());
121 EXPECT_TRUE(icmp_session_.result_callback_.is_null());
122 EXPECT_FALSE(icmp_session_.echo_reply_handler_);
123 }
124
125 void OnEchoReplyReceived(InputData* data) {
126 icmp_session_.OnEchoReplyReceived(data);
127 }
128
129 IcmpSession::IcmpSessionResult GenerateIcmpResult() {
130 return icmp_session_.GenerateIcmpResult();
131 }
132
133 std::map<uint16_t, IcmpSession::SentRecvTimePair>* GetSeqNumToSentRecvTime() {
134 return &icmp_session_.seq_num_to_sent_recv_time_;
135 }
136 std::set<uint16_t>* GetReceivedEchoReplySeqNumbers() {
137 return &icmp_session_.received_echo_reply_seq_numbers_;
138 }
139 uint16_t GetNextUniqueEchoId() const {
140 return IcmpSession::kNextUniqueEchoId;
141 }
142 int GetTotalNumEchoRequests() const {
143 return IcmpSession::kTotalNumEchoRequests;
144 }
145 int GetCurrentSequenceNumber() const {
146 return icmp_session_.current_sequence_number_;
147 }
148 void SetCurrentSequenceNumber(uint16_t val) {
149 icmp_session_.current_sequence_number_ = val;
150 }
151 size_t GetTimeoutSeconds() const { return IcmpSession::kTimeoutSeconds; }
152 int GetEchoRequestIntervalSeconds() const {
153 return IcmpSession::kEchoRequestIntervalSeconds;
154 }
155
156 MockIcmp* icmp_;
157 StrictMock<MockEventDispatcher> dispatcher_;
158 IcmpSession icmp_session_;
159 base::SimpleTestTickClock testing_clock_;
160};
161
162const char IcmpSessionTest::kIPAddress[] = "10.0.1.1";
163
164TEST_F(IcmpSessionTest, Constructor) {
165 // |icmp_session_| should have been assigned the value of |kNextUniqueEchoId|
166 // on construction, and caused the value of this static variable to be
167 // incremented.
168 uint16_t saved_echo_id = GetNextUniqueEchoId();
169 EXPECT_EQ(saved_echo_id - 1, icmp_session_.echo_id_);
170
171 // The next IcmpSession object constructed, |session| should get the next
172 // unique value of |kNextUniqueEchoId|, and further increment this variable.
173 IcmpSession session(&dispatcher_);
174 EXPECT_EQ(saved_echo_id, session.echo_id_);
175 EXPECT_EQ(saved_echo_id + 1, GetNextUniqueEchoId());
176}
177
178TEST_F(IcmpSessionTest, StartWhileAlreadyStarted) {
179 IPAddress ipv4_destination(IPAddress::kFamilyIPv4);
180 EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress));
181 StartAndVerify(ipv4_destination);
182
183 // Since an ICMP session is already started, we should fail to start it again.
184 EXPECT_CALL(*icmp_, Start()).Times(0);
185 EXPECT_CALL(dispatcher_, CreateInputHandler(_, _, _)).Times(0);
186 EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
187 EXPECT_CALL(dispatcher_, PostTask(_)).Times(0);
188 EXPECT_FALSE(Start(ipv4_destination));
189}
190
191TEST_F(IcmpSessionTest, StopWhileNotStarted) {
192 // Attempting to stop the ICMP session while it is not started should do
193 // nothing.
194 EXPECT_CALL(*icmp_, IsStarted()).WillOnce(Return(false));
195 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
196 EXPECT_CALL(*icmp_, Stop()).Times(0);
197 Stop();
198}
199
200TEST_F(IcmpSessionTest, SessionSuccess) {
201 // Test a successful ICMP session where the sending of requests and receiving
202 // of replies are interleaved. Moreover, test the case where transmitting an
203 // echo request fails.
204
205 base::TimeTicks now = testing_clock_.NowTicks();
206 base::TimeTicks kSentTime1 = base::TimeTicks::FromInternalValue(10);
207 base::TimeTicks kRecvTime1 = base::TimeTicks::FromInternalValue(20);
208 base::TimeTicks kSentTime2 = base::TimeTicks::FromInternalValue(30);
209 base::TimeTicks kSentTime3 = base::TimeTicks::FromInternalValue(40);
210 base::TimeTicks kRecvTime2 = base::TimeTicks::FromInternalValue(50);
211 base::TimeTicks kWrongEchoIDRecvTime = base::TimeTicks::FromInternalValue(60);
212 base::TimeTicks kRecvTime3 = base::TimeTicks::FromInternalValue(70);
213
214 IcmpSession::IcmpSessionResult expected_result;
215 expected_result.push_back(kRecvTime1 - kSentTime1);
216 expected_result.push_back(kRecvTime2 - kSentTime2);
217 expected_result.push_back(kRecvTime3 - kSentTime3);
218
219 // Initiate session.
220 IPAddress ipv4_destination(IPAddress::kFamilyIPv4);
221 EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress));
222 StartAndVerify(ipv4_destination);
223
224 // Send the first echo request.
225 testing_clock_.Advance(kSentTime1 - now);
226 now = testing_clock_.NowTicks();
227 SetCurrentSequenceNumber(kIcmpEchoReply1_SeqNum);
228 EXPECT_CALL(dispatcher_,
229 PostDelayedTask(_, GetEchoRequestIntervalSeconds() * 1000));
230 TransmitEchoRequestTask(ipv4_destination, true);
231 EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty());
232 EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size());
233 EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply1_SeqNum));
234 EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply1_SeqNum).first);
235 EXPECT_EQ(kIcmpEchoReply2_SeqNum, GetCurrentSequenceNumber());
236
237 // Receive first reply.
238 testing_clock_.Advance(kRecvTime1 - now);
239 now = testing_clock_.NowTicks();
240 uint8_t buffer_1[sizeof(kIcmpEchoReply1)];
241 memcpy(buffer_1, kIcmpEchoReply1, sizeof(kIcmpEchoReply1));
242 InputData data_1(reinterpret_cast<unsigned char*>(buffer_1),
243 sizeof(buffer_1));
244 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
245 OnEchoReplyReceived(&data_1);
246 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
247 EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply1_SeqNum));
248
249 // Send the second echo request.
250 testing_clock_.Advance(kSentTime2 - now);
251 now = testing_clock_.NowTicks();
252 EXPECT_CALL(dispatcher_,
253 PostDelayedTask(_, GetEchoRequestIntervalSeconds() * 1000));
254 TransmitEchoRequestTask(ipv4_destination, true);
255 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
256 EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size());
257 EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply2_SeqNum));
258 EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply2_SeqNum).first);
259 EXPECT_EQ(kIcmpEchoReply3_SeqNum, GetCurrentSequenceNumber());
260
261 // Sending final request.
262 testing_clock_.Advance(kSentTime3 - now);
263 now = testing_clock_.NowTicks();
264 EXPECT_CALL(dispatcher_, PostDelayedTask(_, _)).Times(0);
265 EXPECT_CALL(*icmp_, Stop()).Times(0);
266 TransmitEchoRequestTask(ipv4_destination, true);
267 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
268 EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size());
269 EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply3_SeqNum));
270 EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply3_SeqNum).first);
271 EXPECT_EQ(kIcmpEchoReply3_SeqNum + 1, GetCurrentSequenceNumber());
272
273 // Receive second reply.
274 testing_clock_.Advance(kRecvTime2 - now);
275 now = testing_clock_.NowTicks();
276 uint8_t buffer_2[sizeof(kIcmpEchoReply2)];
277 memcpy(buffer_2, kIcmpEchoReply2, sizeof(kIcmpEchoReply2));
278 InputData data_2(reinterpret_cast<unsigned char*>(buffer_2),
279 sizeof(buffer_2));
280 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
281 EXPECT_CALL(*icmp_, Stop()).Times(0);
282 OnEchoReplyReceived(&data_2);
283 EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size());
284 EXPECT_EQ(2, GetReceivedEchoReplySeqNumbers()->size());
285 EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply2_SeqNum));
286
287 // Receive a reply that has an echo ID that does not match that of this
288 // ICMP session. This reply will not be processed.
289 testing_clock_.Advance(kWrongEchoIDRecvTime - now);
290 now = testing_clock_.NowTicks();
291 uint8_t buffer_3[sizeof(kIcmpEchoReplyDifferentEchoID)];
292 memcpy(buffer_3, kIcmpEchoReplyDifferentEchoID,
293 sizeof(kIcmpEchoReplyDifferentEchoID));
294 InputData data_3(reinterpret_cast<unsigned char*>(buffer_3),
295 sizeof(buffer_3));
296 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
297 EXPECT_CALL(*icmp_, Stop()).Times(0);
298 OnEchoReplyReceived(&data_3);
299 EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size());
300 EXPECT_EQ(2, GetReceivedEchoReplySeqNumbers()->size());
301
302 // Receive third reply, which concludes the ICMP session.
303 testing_clock_.Advance(kRecvTime3 - now);
304 now = testing_clock_.NowTicks();
305 uint8_t buffer_4[sizeof(kIcmpEchoReply3)];
306 memcpy(buffer_4, kIcmpEchoReply3, sizeof(kIcmpEchoReply3));
307 InputData data_4(reinterpret_cast<unsigned char*>(buffer_4),
308 sizeof(buffer_4));
309 EXPECT_CALL(*this, ResultCallback(expected_result));
310 EXPECT_CALL(*icmp_, Stop());
311 OnEchoReplyReceived(&data_4);
312 EXPECT_EQ(3, GetSeqNumToSentRecvTime()->size());
313 EXPECT_EQ(3, GetReceivedEchoReplySeqNumbers()->size());
314 EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply3_SeqNum));
315
316 VerifyIcmpSessionStopped();
317}
318
319TEST_F(IcmpSessionTest, SessionTimeoutOrInterrupted) {
320 // Test a failed ICMP session where we neither send out all echo requests nor
321 // receive all echo replies before stopping the ICMP session (because of a
322 // timeout or a manually-triggered stop). Moreover, test that echo requests
323 // that are sent unsuccessfully are sent again.
324
325 base::TimeTicks now = testing_clock_.NowTicks();
326 base::TimeTicks kSentTime1 = base::TimeTicks::FromInternalValue(10);
327 base::TimeTicks kSentTime2 = base::TimeTicks::FromInternalValue(20);
328 base::TimeTicks kRecvTime1 = base::TimeTicks::FromInternalValue(30);
329 base::TimeTicks kResendTime1 = base::TimeTicks::FromInternalValue(40);
330
331 IcmpSession::IcmpSessionResult expected_partial_result;
332 expected_partial_result.push_back(kRecvTime1 - kSentTime1);
333 expected_partial_result.push_back(base::TimeDelta());
334
335 // Initiate session.
336 IPAddress ipv4_destination(IPAddress::kFamilyIPv4);
337 EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress));
338 StartAndVerify(ipv4_destination);
339
340 // Send the first echo request successfully.
341 testing_clock_.Advance(kSentTime1 - now);
342 now = testing_clock_.NowTicks();
343 SetCurrentSequenceNumber(kIcmpEchoReply1_SeqNum);
344 EXPECT_CALL(dispatcher_,
345 PostDelayedTask(_, GetEchoRequestIntervalSeconds() * 1000));
346 TransmitEchoRequestTask(ipv4_destination, true);
347 EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty());
348 EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size());
349 EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply1_SeqNum));
350 EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply1_SeqNum).first);
351 EXPECT_EQ(kIcmpEchoReply2_SeqNum, GetCurrentSequenceNumber());
352
353 // Send the second echo request unsuccessfully.
354 testing_clock_.Advance(kSentTime2 - now);
355 now = testing_clock_.NowTicks();
356 EXPECT_CALL(dispatcher_,
357 PostDelayedTask(_, GetEchoRequestIntervalSeconds() * 1000));
358 TransmitEchoRequestTask(ipv4_destination, false);
359 EXPECT_TRUE(GetReceivedEchoReplySeqNumbers()->empty());
360 EXPECT_EQ(1, GetSeqNumToSentRecvTime()->size());
361 EXPECT_FALSE(SeqNumToSentRecvTimeContains(kIcmpEchoReply2_SeqNum));
362 // The sequence number should still be incremented when we fail to transmit an
363 // echo request.
364 EXPECT_EQ(kIcmpEchoReply3_SeqNum, GetCurrentSequenceNumber());
365
366 // Receive first reply.
367 testing_clock_.Advance(kRecvTime1 - now);
368 now = testing_clock_.NowTicks();
369 uint8_t buffer_1[sizeof(kIcmpEchoReply1)];
370 memcpy(buffer_1, kIcmpEchoReply1, sizeof(kIcmpEchoReply1));
371 InputData data_1(reinterpret_cast<unsigned char*>(buffer_1),
372 sizeof(buffer_1));
373 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
374 OnEchoReplyReceived(&data_1);
375 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
376 EXPECT_TRUE(ReceivedEchoReplySeqNumbersContains(kIcmpEchoReply1_SeqNum));
377
378 // Resend second echo request successfully.
379 testing_clock_.Advance(kResendTime1 - now);
380 now = testing_clock_.NowTicks();
381 EXPECT_CALL(dispatcher_,
382 PostDelayedTask(_, GetEchoRequestIntervalSeconds() * 1000));
383 TransmitEchoRequestTask(ipv4_destination, true);
384 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
385 EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size());
386 EXPECT_TRUE(SeqNumToSentRecvTimeContains(kIcmpEchoReply3_SeqNum));
387 EXPECT_EQ(now, GetSeqNumToSentRecvTime()->at(kIcmpEchoReply3_SeqNum).first);
388 EXPECT_EQ(kIcmpEchoReply3_SeqNum + 1, GetCurrentSequenceNumber());
389
Samuel Tan42c33a42015-07-14 17:52:57 -0700390 // Timeout triggered, so report partial results.
Samuel Tanf66080e2015-06-18 15:53:00 -0700391 EXPECT_CALL(*this, ResultCallback(expected_partial_result));
392 EXPECT_CALL(*icmp_, Stop());
Samuel Tan42c33a42015-07-14 17:52:57 -0700393 ReportResultAndStopSession();
Samuel Tanf66080e2015-06-18 15:53:00 -0700394 EXPECT_EQ(2, GetSeqNumToSentRecvTime()->size());
395 EXPECT_EQ(1, GetReceivedEchoReplySeqNumbers()->size());
Samuel Tan42c33a42015-07-14 17:52:57 -0700396 VerifyIcmpSessionStopped();
397}
Samuel Tanf66080e2015-06-18 15:53:00 -0700398
Samuel Tan42c33a42015-07-14 17:52:57 -0700399TEST_F(IcmpSessionTest, DoNotReportResultsOnStop) {
400 // Initiate session.
401 IPAddress ipv4_destination(IPAddress::kFamilyIPv4);
402 EXPECT_TRUE(ipv4_destination.SetAddressFromString(kIPAddress));
403 StartAndVerify(ipv4_destination);
404
405 // Session interrupted manually by calling Stop(), so do not report results.
406 EXPECT_CALL(*this, ResultCallback(_)).Times(0);
407 EXPECT_CALL(*icmp_, Stop());
408 Stop();
Samuel Tanf66080e2015-06-18 15:53:00 -0700409 VerifyIcmpSessionStopped();
410}
411
Samuel Tan83375982015-06-30 14:35:20 -0700412TEST_F(IcmpSessionTest, AnyRepliesReceived) {
413 IcmpSession::IcmpSessionResult none_sent;
414 EXPECT_FALSE(IcmpSession::AnyRepliesReceived(none_sent));
415
416 IcmpSession::IcmpSessionResult two_sent_none_received;
417 two_sent_none_received.push_back(base::TimeDelta());
418 two_sent_none_received.push_back(base::TimeDelta());
419 EXPECT_FALSE(IcmpSession::AnyRepliesReceived(two_sent_none_received));
420
421 IcmpSession::IcmpSessionResult one_sent_one_received;
422 one_sent_one_received.push_back(base::TimeDelta::FromSeconds(10));
423 EXPECT_TRUE(IcmpSession::AnyRepliesReceived(one_sent_one_received));
424
425 IcmpSession::IcmpSessionResult two_sent_one_received;
426 two_sent_one_received.push_back(base::TimeDelta::FromSeconds(20));
427 two_sent_one_received.push_back(base::TimeDelta());
428 EXPECT_TRUE(IcmpSession::AnyRepliesReceived(two_sent_one_received));
429}
430
Samuel Tanf66080e2015-06-18 15:53:00 -0700431} // namespace shill