// // Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "shill/connection_diagnostics.h" #include #include #include "shill/arp_client.h" #include "shill/arp_client_test_helper.h" #include "shill/icmp_session.h" #include "shill/mock_arp_client.h" #include "shill/mock_connection.h" #include "shill/mock_control.h" #include "shill/mock_device_info.h" #include "shill/mock_dns_client.h" #include "shill/mock_dns_client_factory.h" #include "shill/mock_event_dispatcher.h" #include "shill/mock_icmp_session.h" #include "shill/mock_icmp_session_factory.h" #include "shill/mock_manager.h" #include "shill/mock_metrics.h" #include "shill/mock_portal_detector.h" #include "shill/mock_routing_table.h" #include "shill/net/mock_rtnl_handler.h" using base::Bind; using base::Callback; using base::Unretained; using std::string; using std::vector; using testing::_; using testing::NiceMock; using testing::Return; using testing::ReturnRef; using testing::SetArgumentPointee; using testing::Test; namespace { const char kInterfaceName[] = "int0"; const char kDNSServer0[] = "8.8.8.8"; const char kDNSServer1[] = "8.8.4.4"; const char kURL[] = "http://www.gstatic.com/generate_204"; const char kLocalMacAddressASCIIString[] = "123456"; const char kArpReplySenderMacAddressASCIIString[] = "345678"; const char* kDNSServers[] = {kDNSServer0, kDNSServer1}; const shill::IPAddress kIPv4LocalAddress("100.200.43.22"); const shill::IPAddress kIPv4ServerAddress("8.8.8.8"); const shill::IPAddress kIPv6ServerAddress("fe80::1aa9:5ff:7ebf:14c5"); const shill::IPAddress kIPv4GatewayAddress("192.168.1.1"); const shill::IPAddress kIPv6GatewayAddress("fee2::11b2:53f:13be:125e"); const vector kEmptyResult; const vector kNonEmptyResult{ base::TimeDelta::FromMilliseconds(10)}; } // namespace namespace shill { MATCHER_P(IsSameIPAddress, ip_addr, "") { return arg.Equals(ip_addr); } MATCHER_P(IsEventList, expected_events, "") { // Match on type, phase, and result, but not message. if (arg.size() != expected_events.size()) { return false; } for (size_t i = 0; i < expected_events.size(); ++i) { if (expected_events[i].type != arg[i].type || expected_events[i].phase != arg[i].phase || expected_events[i].result != arg[i].result) { *result_listener << "\n=== Mismatch found on expected event index " << i << " ==="; *result_listener << "\nExpected: " << ConnectionDiagnostics::EventToString( expected_events[i]); *result_listener << "\n Actual: " << ConnectionDiagnostics::EventToString(arg[i]); *result_listener << "\nExpected connection diagnostics events:"; for (const auto& expected_event : expected_events) { *result_listener << "\n" << ConnectionDiagnostics::EventToString( expected_event); } *result_listener << "\nActual connection diagnostics events:"; for (const auto& actual_event : expected_events) { *result_listener << "\n" << ConnectionDiagnostics::EventToString(actual_event); } return false; } } return true; } MATCHER_P4(IsArpRequest, local_ip, remote_ip, local_mac, remote_mac, "") { if (local_ip.Equals(arg.local_ip_address()) && remote_ip.Equals(arg.remote_ip_address()) && local_mac.Equals(arg.local_mac_address()) && remote_mac.Equals(arg.remote_mac_address())) { return true; } if (!local_ip.Equals(arg.local_ip_address())) { *result_listener << "Local IP '" << arg.local_ip_address().ToString() << "' (expected '" << local_ip.ToString() << "')."; } if (!remote_ip.Equals(arg.remote_ip_address())) { *result_listener << "Remote IP '" << arg.remote_ip_address().ToString() << "' (expected '" << remote_ip.ToString() << "')."; } if (!local_mac.Equals(arg.local_mac_address())) { *result_listener << "Local MAC '" << arg.local_mac_address().HexEncode() << "' (expected " << local_mac.HexEncode() << ")'."; } if (!remote_mac.Equals(arg.remote_mac_address())) { *result_listener << "Remote MAC '" << arg.remote_mac_address().HexEncode() << "' (expected " << remote_mac.HexEncode() << ")'."; } return false; } class ConnectionDiagnosticsTest : public Test { public: ConnectionDiagnosticsTest() : interface_name_(kInterfaceName), dns_servers_(kDNSServers, kDNSServers + 2), local_ip_address_(kIPv4LocalAddress), gateway_ipv4_address_(kIPv4GatewayAddress), gateway_ipv6_address_(kIPv6GatewayAddress), local_mac_address_(string(kLocalMacAddressASCIIString), false), metrics_(&dispatcher_), manager_(&control_, &dispatcher_, &metrics_), device_info_(&control_, &dispatcher_, &metrics_, &manager_), connection_(new NiceMock(&device_info_)), connection_diagnostics_(connection_, &dispatcher_, &metrics_, &device_info_, callback_target_.result_callback()), portal_detector_(new NiceMock(connection_)) {} virtual ~ConnectionDiagnosticsTest() {} virtual void SetUp() { ASSERT_EQ(IPAddress::kFamilyIPv4, kIPv4LocalAddress.family()); ASSERT_EQ(IPAddress::kFamilyIPv4, kIPv4ServerAddress.family()); ASSERT_EQ(IPAddress::kFamilyIPv4, kIPv4GatewayAddress.family()); ASSERT_EQ(IPAddress::kFamilyIPv6, kIPv6ServerAddress.family()); ASSERT_EQ(IPAddress::kFamilyIPv6, kIPv6GatewayAddress.family()); arp_client_ = new NiceMock(); client_test_helper_.reset(new ArpClientTestHelper(arp_client_)); icmp_session_ = new NiceMock(&dispatcher_); connection_diagnostics_.arp_client_.reset(arp_client_); // Passes ownership connection_diagnostics_.icmp_session_.reset( icmp_session_); // Passes ownership connection_diagnostics_.portal_detector_.reset( portal_detector_); // Passes ownership connection_diagnostics_.routing_table_ = &routing_table_; connection_diagnostics_.rtnl_handler_ = &rtnl_handler_; ON_CALL(*connection_.get(), interface_name()) .WillByDefault(ReturnRef(interface_name_)); ON_CALL(*connection_.get(), dns_servers()) .WillByDefault(ReturnRef(dns_servers_)); ON_CALL(*connection_.get(), gateway()) .WillByDefault(ReturnRef(gateway_ipv4_address_)); ON_CALL(*connection_.get(), local()) .WillByDefault(ReturnRef(local_ip_address_)); connection_diagnostics_.dns_client_factory_ = MockDNSClientFactory::GetInstance(); connection_diagnostics_.icmp_session_factory_ = MockIcmpSessionFactory::GetInstance(); } virtual void TearDown() {} protected: class CallbackTarget { public: CallbackTarget() : result_callback_( Bind(&CallbackTarget::ResultCallback, Unretained(this))) {} MOCK_METHOD2(ResultCallback, void(const string&, const vector&)); Callback&)>& result_callback() { return result_callback_; } private: Callback&)> result_callback_; }; CallbackTarget& callback_target() { return callback_target_; } void UseIPv6Gateway() { EXPECT_CALL(*connection_.get(), gateway()) .WillRepeatedly(ReturnRef(gateway_ipv6_address_)); } void AddExpectedEvent(ConnectionDiagnostics::Type type, ConnectionDiagnostics::Phase phase, ConnectionDiagnostics::Result result) { expected_events_.push_back( ConnectionDiagnostics::Event(type, phase, result, "")); } void AddActualEvent(ConnectionDiagnostics::Type type, ConnectionDiagnostics::Phase phase, ConnectionDiagnostics::Result result) { connection_diagnostics_.diagnostic_events_.push_back( ConnectionDiagnostics::Event(type, phase, result, "")); } bool DoesPreviousEventMatch(ConnectionDiagnostics::Type type, ConnectionDiagnostics::Phase phase, ConnectionDiagnostics::Result result, size_t num_events_ago) { return connection_diagnostics_.DoesPreviousEventMatch(type, phase, result, num_events_ago); } // This direct call to ConnectionDiagnostics::Start does not mock the // return // value of MockPortalDetector::CreatePortalDetector, so this will crash // the // test if PortalDetector::Start is actually called. Use only for testing // bad input to ConnectionDiagnostics::Start. bool Start(const string& url_string) { return connection_diagnostics_.Start(url_string); } void VerifyStopped() { EXPECT_FALSE(connection_diagnostics_.running()); EXPECT_EQ(0, connection_diagnostics_.num_dns_attempts_); EXPECT_TRUE(connection_diagnostics_.diagnostic_events_.empty()); EXPECT_FALSE(connection_diagnostics_.dns_client_.get()); EXPECT_FALSE(connection_diagnostics_.arp_client_->IsStarted()); EXPECT_FALSE(connection_diagnostics_.icmp_session_->IsStarted()); EXPECT_FALSE(connection_diagnostics_.portal_detector_.get()); EXPECT_FALSE(connection_diagnostics_.receive_response_handler_.get()); EXPECT_FALSE(connection_diagnostics_.neighbor_msg_listener_.get()); EXPECT_TRUE( connection_diagnostics_.id_to_pending_dns_server_icmp_session_.empty()); EXPECT_FALSE(connection_diagnostics_.target_url_.get()); EXPECT_TRUE(connection_diagnostics_.route_query_callback_.IsCancelled()); EXPECT_TRUE( connection_diagnostics_.route_query_timeout_callback_.IsCancelled()); EXPECT_TRUE( connection_diagnostics_.arp_reply_timeout_callback_.IsCancelled()); EXPECT_TRUE(connection_diagnostics_.neighbor_request_timeout_callback_ .IsCancelled()); } void ExpectIcmpSessionStop() { EXPECT_CALL(*icmp_session_, Stop()); } void ExpectPortalDetectionStartSuccess(const string& url_string) { AddExpectedEvent(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); EXPECT_CALL(*portal_detector_, Start(url_string)).WillOnce(Return(true)); EXPECT_FALSE(connection_diagnostics_.running()); EXPECT_TRUE(connection_diagnostics_.diagnostic_events_.empty()); EXPECT_TRUE(Start(url_string)); EXPECT_TRUE(connection_diagnostics_.running()); } void ExpectPortalDetectionEndContentPhaseSuccess() { ExpectPortalDetectionEnd( ConnectionDiagnostics::kPhasePortalDetectionEndContent, ConnectionDiagnostics::kResultSuccess, ConnectivityTrial::kPhaseContent, ConnectivityTrial::kStatusSuccess); } void ExpectPortalDetectionEndContentPhaseFailure() { ExpectPortalDetectionEnd( ConnectionDiagnostics::kPhasePortalDetectionEndContent, ConnectionDiagnostics::kResultFailure, ConnectivityTrial::kPhaseContent, ConnectivityTrial::kStatusFailure); } void ExpectPortalDetectionEndDNSPhaseFailure() { ExpectPortalDetectionEnd(ConnectionDiagnostics::kPhasePortalDetectionEndDNS, ConnectionDiagnostics::kResultFailure, ConnectivityTrial::kPhaseDNS, ConnectivityTrial::kStatusFailure); } void ExpectPortalDetectionEndDNSPhaseTimeout() { ExpectPortalDetectionEnd(ConnectionDiagnostics::kPhasePortalDetectionEndDNS, ConnectionDiagnostics::kResultTimeout, ConnectivityTrial::kPhaseDNS, ConnectivityTrial::kStatusTimeout); } void ExpectPortalDetectionEndHTTPPhaseFailure() { ExpectPortalDetectionEnd( ConnectionDiagnostics::kPhasePortalDetectionEndOther, ConnectionDiagnostics::kResultFailure, ConnectivityTrial::kPhaseHTTP, ConnectivityTrial::kStatusFailure); } void ExpectPingDNSServersStartSuccess() { ExpectPingDNSSeversStart(true, ""); } void ExpectPingDNSSeversStartFailureAllAddressesInvalid() { ExpectPingDNSSeversStart(false, ConnectionDiagnostics::kIssueDNSServersInvalid); } void ExpectPingDNSSeversStartFailureAllIcmpSessionsFailed() { ExpectPingDNSSeversStart(false, ConnectionDiagnostics::kIssueInternalError); } void ExpectPingDNSServersEndSuccessRetriesLeft() { ExpectPingDNSServersEndSuccess(true); } void ExpectPingDNSServersEndSuccessNoRetriesLeft() { ExpectPingDNSServersEndSuccess(false); } void ExpectPingDNSServersEndFailure() { AddExpectedEvent(ConnectionDiagnostics::kTypePingDNSServers, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultFailure); // Post task to find DNS server route only after all (i.e. 2) pings are // done. connection_diagnostics_.OnPingDNSServerComplete(0, kEmptyResult); EXPECT_CALL(dispatcher_, PostTask(_)); connection_diagnostics_.OnPingDNSServerComplete(1, kEmptyResult); } void ExpectResolveTargetServerIPAddressStartSuccess( IPAddress::Family family) { AddExpectedEvent(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); ASSERT_FALSE(family == IPAddress::kFamilyUnknown); dns_client_ = new NiceMock(); EXPECT_CALL(*connection_.get(), IsIPv6()) .WillOnce(Return(family == IPAddress::kFamilyIPv6)); EXPECT_CALL( *MockDNSClientFactory::GetInstance(), CreateDNSClient(family, kInterfaceName, dns_servers_, ConnectionDiagnostics::kDNSTimeoutSeconds * 1000, &dispatcher_, _)) .WillOnce(Return(dns_client_)); // Passes ownership EXPECT_CALL(*dns_client_, Start(connection_diagnostics_.target_url_->host(), _)) .WillOnce(Return(true)); connection_diagnostics_.ResolveTargetServerIPAddress(dns_servers_); } void ExpectResolveTargetServerIPAddressEndSuccess( const IPAddress& resolved_address) { ExpectResolveTargetServerIPAddressEnd(ConnectionDiagnostics::kResultSuccess, resolved_address); } void ExpectResolveTargetServerIPAddressEndTimeout() { ExpectResolveTargetServerIPAddressEnd(ConnectionDiagnostics::kResultTimeout, IPAddress(IPAddress::kFamilyIPv4)); } void ExpectResolveTargetServerIPAddressEndFailure() { ExpectResolveTargetServerIPAddressEnd(ConnectionDiagnostics::kResultFailure, IPAddress(IPAddress::kFamilyIPv4)); } void ExpectPingHostStartSuccess(ConnectionDiagnostics::Type ping_event_type, const IPAddress& address) { AddExpectedEvent(ping_event_type, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); EXPECT_CALL(*icmp_session_, Start(IsSameIPAddress(address), _)) .WillOnce(Return(true)); connection_diagnostics_.PingHost(address); } void ExpectPingHostStartFailure(ConnectionDiagnostics::Type ping_event_type, const IPAddress& address) { AddExpectedEvent(ping_event_type, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultFailure); EXPECT_CALL(*icmp_session_, Start(IsSameIPAddress(address), _)) .WillOnce(Return(false)); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueInternalError)); EXPECT_CALL(callback_target(), ResultCallback(ConnectionDiagnostics::kIssueInternalError, IsEventList(expected_events_))); connection_diagnostics_.PingHost(address); } void ExpectPingHostEndSuccess(ConnectionDiagnostics::Type ping_event_type, const IPAddress& address) { AddExpectedEvent(ping_event_type, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); const string& issue = ping_event_type == ConnectionDiagnostics::kTypePingGateway ? ConnectionDiagnostics::kIssueGatewayUpstream : ConnectionDiagnostics::kIssueHTTPBrokenPortal; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL(callback_target(), ResultCallback(issue, IsEventList(expected_events_))); connection_diagnostics_.OnPingHostComplete(ping_event_type, address, kNonEmptyResult); } void ExpectPingHostEndFailure(ConnectionDiagnostics::Type ping_event_type, const IPAddress& address) { AddExpectedEvent(ping_event_type, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultFailure); // Next action is either to find a route to the target web server, find an // ARP entry for the IPv4 gateway, or find a neighbor table entry for the // IPv6 gateway. EXPECT_CALL(dispatcher_, PostTask(_)); connection_diagnostics_.OnPingHostComplete(ping_event_type, address, kEmptyResult); } void ExpectFindRouteToHostStartSuccess(const IPAddress& address) { AddExpectedEvent(ConnectionDiagnostics::kTypeFindRoute, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); EXPECT_CALL(routing_table_, RequestRouteToHost(IsSameIPAddress(address), connection_->interface_index(), _, _, connection_->table_id())) .WillOnce(Return(true)); EXPECT_CALL( dispatcher_, PostDelayedTask( _, ConnectionDiagnostics::kRouteQueryTimeoutSeconds * 1000)); connection_diagnostics_.FindRouteToHost(address); EXPECT_FALSE( connection_diagnostics_.route_query_timeout_callback_.IsCancelled()); } void ExpectFindRouteToHostEndSuccess(const IPAddress& address_queried, bool is_local_address) { AddExpectedEvent(ConnectionDiagnostics::kTypeFindRoute, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); IPAddress gateway(IPAddress::kFamilyIPv4); if (is_local_address) { gateway.SetAddressToDefault(); } else { // Could be an IPv6 address, but we instrument this later with the // argument passed to ExpectPingHostStartSuccess. gateway = gateway_ipv4_address_; } // Next action is either to ping the gateway, find an ARP table entry for // the local IPv4 web server, or find a neighbor table entry for the local // IPv6 web server. EXPECT_CALL(dispatcher_, PostTask(_)); RoutingTableEntry entry( address_queried, IPAddress(address_queried.family()), gateway, 0, RT_SCOPE_UNIVERSE, true, connection_->table_id(), -1); connection_diagnostics_.OnRouteQueryResponse(connection_->interface_index(), entry); } void ExpectFindRouteToHostEndFailure() { AddExpectedEvent(ConnectionDiagnostics::kTypeFindRoute, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultFailure); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueRouting)); EXPECT_CALL(callback_target(), ResultCallback(ConnectionDiagnostics::kIssueRouting, IsEventList(expected_events_))); connection_diagnostics_.OnRouteQueryTimeout(); } void ExpectArpTableLookupStartSuccessEndSuccess(const IPAddress& address, bool is_gateway) { ExpectArpTableLookup(address, true, is_gateway); } void ExpectArpTableLookupStartSuccessEndFailure(const IPAddress& address) { ExpectArpTableLookup(address, false, false); } void ExpectNeighborTableLookupStartSuccess(const IPAddress& address) { AddExpectedEvent(ConnectionDiagnostics::kTypeNeighborTableLookup, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); EXPECT_CALL(rtnl_handler_, RequestDump(RTNLHandler::kRequestNeighbor)); EXPECT_CALL( dispatcher_, PostDelayedTask( _, ConnectionDiagnostics::kNeighborTableRequestTimeoutSeconds * 1000)); connection_diagnostics_.FindNeighborTableEntry(address); } void ExpectNeighborTableLookupEndSuccess(const IPAddress& address_queried, bool is_gateway) { AddExpectedEvent(ConnectionDiagnostics::kTypeNeighborTableLookup, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); RTNLMessage msg(RTNLMessage::kTypeNeighbor, RTNLMessage::kModeAdd, 0, 0, 0, connection_->interface_index(), IPAddress::kFamilyIPv6); msg.set_neighbor_status( RTNLMessage::NeighborStatus(NUD_REACHABLE, 0, NDA_DST)); msg.SetAttribute(NDA_DST, address_queried.address()); const string& issue = is_gateway ? ConnectionDiagnostics::kIssueGatewayNotResponding : ConnectionDiagnostics::kIssueServerNotResponding; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL(callback_target(), ResultCallback(issue, IsEventList(expected_events_))); connection_diagnostics_.OnNeighborMsgReceived(address_queried, msg); } void ExpectNeighborTableLookupEndFailureNotReachable( const IPAddress& address_queried, bool is_gateway) { ExpectNeighborTableLookupEndFailure(address_queried, is_gateway, false); } void ExpectNeighborTableLookupEndFailureNoEntry( const IPAddress& address_queried, bool is_gateway) { ExpectNeighborTableLookupEndFailure(address_queried, is_gateway, true); } void ExpectCheckIPCollisionStartSuccess() { AddExpectedEvent(ConnectionDiagnostics::kTypeIPCollisionCheck, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); EXPECT_CALL(device_info_, GetMACAddress(connection_->interface_index(), _)) .WillOnce( DoAll(SetArgumentPointee<1>(local_mac_address_), Return(true))); EXPECT_CALL(*arp_client_, StartReplyListener()).WillOnce(Return(true)); // We should send an ARP request for our own local IP address. EXPECT_CALL(*arp_client_, TransmitRequest(IsArpRequest( local_ip_address_, local_ip_address_, local_mac_address_, ByteString()))) .WillOnce(Return(true)); EXPECT_CALL(dispatcher_, PostDelayedTask( _, ConnectionDiagnostics::kArpReplyTimeoutSeconds * 1000)); connection_diagnostics_.CheckIpCollision(); } void ExpectCheckIPCollisionEndSuccess() { AddExpectedEvent(ConnectionDiagnostics::kTypeIPCollisionCheck, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); // Simulate ARP response from a sender with the same IP address as our // connection, directed at our local IP address and local MAC address. client_test_helper_->GeneratePacket( ARPOP_REPLY, local_ip_address_, ByteString(string(kArpReplySenderMacAddressASCIIString), false), local_ip_address_, local_mac_address_); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueIPCollision)); EXPECT_CALL(callback_target(), ResultCallback(ConnectionDiagnostics::kIssueIPCollision, IsEventList(expected_events_))); connection_diagnostics_.OnArpReplyReceived(1); } void ExpectCheckIPCollisionEndFailureGatewayArpFailed() { ExpectCheckIPCollisionEndFailure( ConnectionDiagnostics::kIssueGatewayArpFailed); } void ExpectCheckIPCollisionEndFailureServerArpFailed() { ExpectCheckIPCollisionEndFailure( ConnectionDiagnostics::kIssueServerArpFailed); } private: void ExpectPortalDetectionEnd(ConnectionDiagnostics::Phase diag_phase, ConnectionDiagnostics::Result diag_result, ConnectivityTrial::Phase trial_phase, ConnectivityTrial::Status trial_status) { AddExpectedEvent(ConnectionDiagnostics::kTypePortalDetection, diag_phase, diag_result); if (diag_phase == ConnectionDiagnostics::kPhasePortalDetectionEndContent) { const string& issue = diag_result == ConnectionDiagnostics::kResultSuccess ? ConnectionDiagnostics::kIssueNone : ConnectionDiagnostics::kIssueCaptivePortal; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL(callback_target(), ResultCallback(issue, IsEventList(expected_events_))); } else if (diag_phase == ConnectionDiagnostics::kPhasePortalDetectionEndDNS && diag_result == ConnectionDiagnostics::kResultFailure) { EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueDNSServerMisconfig)); EXPECT_CALL( callback_target(), ResultCallback(ConnectionDiagnostics::kIssueDNSServerMisconfig, IsEventList(expected_events_))); } else { // Otherwise, we end in DNS phase with a timeout, or a HTTP phase failure. // Either of these cases warrant further diagnostic actions. EXPECT_CALL(dispatcher_, PostTask(_)); } connection_diagnostics_.StartAfterPortalDetectionInternal( PortalDetector::Result( ConnectivityTrial::Result(trial_phase, trial_status))); } // |expected_issue| only used if |is_success| is false. void ExpectPingDNSSeversStart(bool is_success, const string& expected_issue) { AddExpectedEvent(ConnectionDiagnostics::kTypePingDNSServers, ConnectionDiagnostics::kPhaseStart, is_success ? ConnectionDiagnostics::kResultSuccess : ConnectionDiagnostics::kResultFailure); const char* bad_addresses[] = {"110.2.3", "1.5"}; const vector bad_dns_servers(bad_addresses, bad_addresses + 2); if (!is_success && expected_issue == ConnectionDiagnostics::kIssueDNSServersInvalid) { // If the DNS server addresses are invalid, we will not even attempt to // start any ICMP sessions. EXPECT_CALL(*connection_.get(), dns_servers()) .WillRepeatedly(ReturnRef(bad_dns_servers)); } else { // We are either instrumenting the success case (started pinging all // DNS servers successfully) or the failure case where we fail to start // any pings. ASSERT_TRUE(is_success || expected_issue == ConnectionDiagnostics::kIssueInternalError); dns_server_icmp_session_0_ = new NiceMock(&dispatcher_); dns_server_icmp_session_1_ = new NiceMock(&dispatcher_); EXPECT_CALL(*MockIcmpSessionFactory::GetInstance(), CreateIcmpSession(&dispatcher_)) .WillOnce(Return(dns_server_icmp_session_0_)) .WillOnce(Return(dns_server_icmp_session_1_)); EXPECT_CALL(*dns_server_icmp_session_0_, Start(IsSameIPAddress(IPAddress(kDNSServer0)), _)) .WillOnce(Return(is_success)); EXPECT_CALL(*dns_server_icmp_session_1_, Start(IsSameIPAddress(IPAddress(kDNSServer1)), _)) .WillOnce(Return(is_success)); } if (is_success) { EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(_)).Times(0); EXPECT_CALL(callback_target(), ResultCallback(_, _)).Times(0); } else { EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(expected_issue)); EXPECT_CALL( callback_target(), ResultCallback(expected_issue, IsEventList(expected_events_))); } connection_diagnostics_.PingDNSServers(); if (is_success) { EXPECT_EQ(2, connection_diagnostics_ .id_to_pending_dns_server_icmp_session_.size()); } else { EXPECT_TRUE(connection_diagnostics_.id_to_pending_dns_server_icmp_session_ .empty()); } } void ExpectResolveTargetServerIPAddressEnd( ConnectionDiagnostics::Result result, const IPAddress& resolved_address) { AddExpectedEvent(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseEnd, result); Error error; if (result == ConnectionDiagnostics::kResultSuccess) { error.Populate(Error::kSuccess); EXPECT_CALL(dispatcher_, PostTask(_)); } else if (result == ConnectionDiagnostics::kResultTimeout) { error.Populate(Error::kOperationTimeout); EXPECT_CALL(dispatcher_, PostTask(_)); } else { error.Populate(Error::kOperationFailed); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueDNSServerMisconfig)); EXPECT_CALL( callback_target(), ResultCallback(ConnectionDiagnostics::kIssueDNSServerMisconfig, IsEventList(expected_events_))); } connection_diagnostics_.OnDNSResolutionComplete(error, resolved_address); } void ExpectPingDNSServersEndSuccess(bool retries_left) { AddExpectedEvent(ConnectionDiagnostics::kTypePingDNSServers, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); if (retries_left) { EXPECT_LT(connection_diagnostics_.num_dns_attempts_, ConnectionDiagnostics::kMaxDNSRetries); } else { EXPECT_GE(connection_diagnostics_.num_dns_attempts_, ConnectionDiagnostics::kMaxDNSRetries); } // Post retry task or report done only after all (i.e. 2) pings are done. connection_diagnostics_.OnPingDNSServerComplete(0, kNonEmptyResult); if (retries_left) { EXPECT_CALL(dispatcher_, PostTask(_)); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(_)).Times(0); EXPECT_CALL(callback_target(), ResultCallback(_, _)).Times(0); } else { EXPECT_CALL(dispatcher_, PostTask(_)).Times(0); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue( ConnectionDiagnostics::kIssueDNSServerNoResponse)); EXPECT_CALL( callback_target(), ResultCallback(ConnectionDiagnostics::kIssueDNSServerNoResponse, IsEventList(expected_events_))); } connection_diagnostics_.OnPingDNSServerComplete(1, kNonEmptyResult); } void ExpectArpTableLookup(const IPAddress& address, bool success, bool is_gateway) { AddExpectedEvent(ConnectionDiagnostics::kTypeArpTableLookup, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); AddExpectedEvent(ConnectionDiagnostics::kTypeArpTableLookup, ConnectionDiagnostics::kPhaseEnd, success ? ConnectionDiagnostics::kResultSuccess : ConnectionDiagnostics::kResultFailure); EXPECT_CALL(device_info_, GetMACAddressOfPeer(connection_->interface_index(), IsSameIPAddress(address), _)) .WillOnce(Return(success)); if (success) { const string& issue = is_gateway ? ConnectionDiagnostics::kIssueGatewayNotResponding : ConnectionDiagnostics::kIssueServerNotResponding; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL(callback_target(), ResultCallback(issue, IsEventList(expected_events_))); } else { // Checking for IP collision. EXPECT_CALL(dispatcher_, PostTask(_)); } connection_diagnostics_.FindArpTableEntry(address); } void ExpectCheckIPCollisionEndFailure(const string& expected_issue) { AddExpectedEvent(ConnectionDiagnostics::kTypeIPCollisionCheck, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultFailure); EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(expected_issue)); EXPECT_CALL(callback_target(), ResultCallback(expected_issue, IsEventList(expected_events_))); connection_diagnostics_.OnArpRequestTimeout(); } void ExpectNeighborTableLookupEndFailure(const IPAddress& address_queried, bool is_gateway, bool is_timeout) { AddExpectedEvent(ConnectionDiagnostics::kTypeNeighborTableLookup, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultFailure); string issue; if (is_timeout) { issue = is_gateway ? ConnectionDiagnostics::kIssueGatewayNoNeighborEntry : ConnectionDiagnostics::kIssueServerNoNeighborEntry; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL(callback_target(), ResultCallback(issue, IsEventList(expected_events_))); connection_diagnostics_.OnNeighborTableRequestTimeout(address_queried); } else { issue = is_gateway ? ConnectionDiagnostics::kIssueGatewayNeighborEntryNotConnected : ConnectionDiagnostics::kIssueServerNeighborEntryNotConnected; EXPECT_CALL(metrics_, NotifyConnectionDiagnosticsIssue(issue)); EXPECT_CALL( callback_target(), ResultCallback(issue, IsEventList(expected_events_))); RTNLMessage msg(RTNLMessage::kTypeNeighbor, RTNLMessage::kModeAdd, 0, 0, 0, connection_->interface_index(), IPAddress::kFamilyIPv6); msg.set_neighbor_status( RTNLMessage::NeighborStatus(NUD_FAILED, 0, NDA_DST)); msg.SetAttribute(NDA_DST, address_queried.address()); connection_diagnostics_.OnNeighborMsgReceived(address_queried, msg); } } const string interface_name_; const vector dns_servers_; const IPAddress local_ip_address_; const IPAddress gateway_ipv4_address_; const IPAddress gateway_ipv6_address_; ByteString local_mac_address_; CallbackTarget callback_target_; MockControl control_; NiceMock metrics_; MockManager manager_; NiceMock device_info_; scoped_refptr> connection_; ConnectionDiagnostics connection_diagnostics_; NiceMock dispatcher_; NiceMock routing_table_; NiceMock rtnl_handler_; std::unique_ptr client_test_helper_; // Used only for EXPECT_CALL(). Objects are owned by // |connection_diagnostics_|. NiceMock* arp_client_; NiceMock* dns_client_; NiceMock* icmp_session_; NiceMock* dns_server_icmp_session_0_; NiceMock* dns_server_icmp_session_1_; NiceMock* portal_detector_; // For each test, all events we expect to appear in the final result are // accumulated in this vector. vector expected_events_; }; TEST_F(ConnectionDiagnosticsTest, DoesPreviousEventMatch) { // If |diagnostic_events| is empty, we should always fail to match an event. EXPECT_FALSE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess, 0)); EXPECT_FALSE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess, 2)); AddActualEvent(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); AddActualEvent(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhasePortalDetectionEndOther, ConnectionDiagnostics::kResultFailure); AddActualEvent(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess); AddActualEvent(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess); // Matching out of bounds should fail. (4 events total, so 4 events before the // last event is out of bounds). EXPECT_FALSE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess, 4)); // Valid matches. EXPECT_TRUE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypePortalDetection, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess, 3)); EXPECT_TRUE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseStart, ConnectionDiagnostics::kResultSuccess, 1)); EXPECT_TRUE( DoesPreviousEventMatch(ConnectionDiagnostics::kTypeResolveTargetServerIP, ConnectionDiagnostics::kPhaseEnd, ConnectionDiagnostics::kResultSuccess, 0)); } TEST_F(ConnectionDiagnosticsTest, StartWhileRunning) { ExpectPortalDetectionStartSuccess(kURL); // Start diagnostics; EXPECT_FALSE(Start(kURL)); } TEST_F(ConnectionDiagnosticsTest, StartWithBadURL) { const string kBadURL("http://www.foo.com:x"); // Colon but no port // IcmpSession::Stop will be called once when the bad URL is rejected. ExpectIcmpSessionStop(); EXPECT_FALSE(Start(kBadURL)); // IcmpSession::Stop will be called a second time when // |connection_diagnostics_| is destructed. ExpectIcmpSessionStop(); } TEST_F(ConnectionDiagnosticsTest, EndWith_InternalError) { // Portal detection ends in HTTP phase, DNS resolution succeeds, and we // attempt to ping the target web server but fail because of an internal // error. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PortalDetectionContentPhase_Success) { // Portal detection ends successfully in content phase, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndContentPhaseSuccess(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PortalDetectionContentPhase_Failure) { // Portal detection ends unsuccessfully in content phase, so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndContentPhaseFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_DNSFailure_1) { // Portal detection ends with a DNS failure (not timeout), so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_DNSFailure_2) { // Portal detection ends in HTTP phase, DNS resolution fails (not timeout), so // we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingDNSServerStartFailure_1) { // Portal detection ends with a DNS timeout, and we attempt to pinging DNS // servers, but fail to start any IcmpSessions, so end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSSeversStartFailureAllIcmpSessionsFailed(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingDNSServerStartFailure_2) { // Portal detection ends with a DNS timeout, and we attempt to pinging DNS // servers, but all DNS servers configured for this connection have invalid IP // addresses, so we fail to start ping DNs servers, andend diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSSeversStartFailureAllAddressesInvalid(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingDNSServerEndSuccess_NoRetries_1) { // Portal detection ends with a DNS timeout, pinging DNS servers succeeds, DNS // resolution times out, pinging DNS servers succeeds again, and DNS // resolution times out again. End diagnostics because we have no more DNS // retries left. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessNoRetriesLeft(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingDNSServerEndSuccess_NoRetries_2) { // Portal detection ends in HTTP phase, DNS resolution times out, pinging DNS // servers succeeds, DNS resolution times out again, pinging DNS servers // succeeds. End diagnostics because we have no more DNS retries left. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessNoRetriesLeft(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingTargetIPSuccess_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, and pinging // the resolved IP address succeeds, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingTargetIPSuccess_2) { // Portal detection ends with a DNS timeout, pinging DNS servers succeeds, DNS // resolution succeeds, and pinging the resolved IP address succeeds, so we // end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingTargetIPSuccess_3) { // Portal detection ends in HTTP phase, DNS resolution times out, pinging DNS // servers succeeds, DNS resolution succeeds, and pinging the resolved IP // address succeeds, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_FindRouteFailure_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we fail to get a route for the IP address, // so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_FindRoute_Failure_2) { // Portal detection ends with a DNS timeout, pinging DNS servers succeeds, DNS // resolution succeeds, pinging the resolved IP address fails, and we fail to // get a route for the IP address, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_FindRouteFailure_3) { // Portal detection ends in HTTP phase, DNS resolution times out, pinging DNS // servers succeeds, DNS resolution succeeds, pinging the resolved IP address // fails, and we fail to get a route for the IP address, so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_FindRouteFailure_4) { // Portal detection ends with a DNS timeout, pinging DNS servers fails, get a // route for the first DNS server, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndFailure(); ExpectFindRouteToHostStartSuccess(kIPv4GatewayAddress); ExpectFindRouteToHostEndFailure(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingGatewaySuccess_1_IPv4) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, so ping the local gateway and succeed, so // we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingGatewaySuccess_1_IPv6) { // Same as above, but this time the resolved IP address of the target URL // is IPv6. UseIPv6Gateway(); ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv6); ExpectResolveTargetServerIPAddressEndSuccess(kIPv6ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv6ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv6ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingGatewaySuccess_2) { // Portal detection ends with a DNS timeout, pinging DNS servers succeeds, DNS // resolution succeeds, pinging the resolved IP address fails, and we // successfully get route for the IP address. This address is remote, so ping // the local gateway and succeed, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndDNSPhaseTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_PingGatewaySuccess_3) { // Portal detection ends in HTTP phase, DNS resolution times out, pinging DNS // servers succeeds, DNS resolution succeeds, pinging the resolved IP address // fails, and we successfully get route for the IP address. This address is // remote, so ping the local gateway. The ping succeeds, so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndTimeout(); ExpectPingDNSServersStartSuccess(); ExpectPingDNSServersEndSuccessRetriesLeft(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); VerifyStopped(); } // Note: for the test below, several other possible paths through the diagnostic // state machine that will lead us to end diagnostics at ARP table lookup or IP // collision check are not explicitly tested. We do this to avoid redundancy // since the above tests have already exercised these sub-paths extensively, TEST_F(ConnectionDiagnosticsTest, EndWith_FindArpTableEntrySuccess_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, pinging the local gateway fails, and we // find an ARP table entry for the gateway address, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectArpTableLookupStartSuccessEndSuccess(kIPv4GatewayAddress, true); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_FindArpTableEntrySuccess_2) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is local, and we find an ARP table entry for this // address, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, true); ExpectArpTableLookupStartSuccessEndSuccess(kIPv4ServerAddress, false); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_IPCollisionSuccess_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, pinging the local gateway fails, ARP table // lookup fails, we check for IP collision and find one, so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectArpTableLookupStartSuccessEndFailure(kIPv4GatewayAddress); ExpectCheckIPCollisionStartSuccess(); ExpectCheckIPCollisionEndSuccess(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_IPCollisionSuccess_2) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is local, ARP table lookup fails, we check for IP // collision and find one, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, true); ExpectArpTableLookupStartSuccessEndSuccess(kIPv4ServerAddress, false); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_IPCollisionFailure_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, pinging the local gateway fails, ARP table // lookup fails, we check for IP collision and do not find one, so we end // diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingGateway, kIPv4GatewayAddress); ExpectArpTableLookupStartSuccessEndFailure(kIPv4GatewayAddress); ExpectCheckIPCollisionStartSuccess(); ExpectCheckIPCollisionEndFailureGatewayArpFailed(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_IPCollisionFailure_2) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is local, ARP table lookup fails, we check for IP // collision and do not find one, so we end diagnostics. ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv4); ExpectResolveTargetServerIPAddressEndSuccess(kIPv4ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv4ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv4ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv4ServerAddress, true); ExpectArpTableLookupStartSuccessEndFailure(kIPv4ServerAddress); ExpectCheckIPCollisionStartSuccess(); ExpectCheckIPCollisionEndFailureServerArpFailed(); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_kTypeNeighborTableLookupSuccess_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, pinging the local IPv6 gateway fails, // and we find a neighbor table entry for the gateway. End diagnostics. UseIPv6Gateway(); ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv6); ExpectResolveTargetServerIPAddressEndSuccess(kIPv6ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv6ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv6ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); ExpectNeighborTableLookupStartSuccess(kIPv6GatewayAddress); ExpectNeighborTableLookupEndSuccess(kIPv6GatewayAddress, true); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_kTypeNeighborTableLookupSuccess_2) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, we succeed in getting a route for the IP // address. This address is a local IPv6 address, and we find a neighbor table // entry for it. End diagnostics. UseIPv6Gateway(); ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv6); ExpectResolveTargetServerIPAddressEndSuccess(kIPv6ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv6ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv6ServerAddress, true); ExpectNeighborTableLookupStartSuccess(kIPv6ServerAddress); ExpectNeighborTableLookupEndSuccess(kIPv6ServerAddress, false); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_kTypeNeighborTableLookupFailure_1) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, and we successfully get route for the IP // address. This address is remote, pinging the local IPv6 gateway fails, and // we find a neighbor table entry for the gateway, but it is not marked as // reachable. End diagnostics. UseIPv6Gateway(); ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv6); ExpectResolveTargetServerIPAddressEndSuccess(kIPv6ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv6ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv6ServerAddress, false); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingGateway, kIPv6GatewayAddress); ExpectNeighborTableLookupStartSuccess(kIPv6GatewayAddress); ExpectNeighborTableLookupEndFailureNotReachable(kIPv6GatewayAddress, true); VerifyStopped(); } TEST_F(ConnectionDiagnosticsTest, EndWith_kTypeNeighborTableLookupFailure_2) { // Portal detection ends in HTTP phase, DNS resolution succeeds, pinging the // resolved IP address fails, we succeed in getting a route for the IP // address. This address is a local IPv6 address, and we do not find a // neighbor table entry for it. End diagnostics. UseIPv6Gateway(); ExpectPortalDetectionStartSuccess(kURL); ExpectPortalDetectionEndHTTPPhaseFailure(); ExpectResolveTargetServerIPAddressStartSuccess(IPAddress::kFamilyIPv6); ExpectResolveTargetServerIPAddressEndSuccess(kIPv6ServerAddress); ExpectPingHostStartSuccess(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectPingHostEndFailure(ConnectionDiagnostics::kTypePingTargetServer, kIPv6ServerAddress); ExpectFindRouteToHostStartSuccess(kIPv6ServerAddress); ExpectFindRouteToHostEndSuccess(kIPv6ServerAddress, true); ExpectNeighborTableLookupStartSuccess(kIPv6ServerAddress); ExpectNeighborTableLookupEndFailureNoEntry(kIPv6ServerAddress, false); VerifyStopped(); } } // namespace shill