Commit ea64ccad authored by Zhongyi Shi's avatar Zhongyi Shi Committed by Commit Bot

Add logic to receive connectivity probing packet. Server will send a

connectivity probing packet back to the source address of received
connectivity probing packet without latching on the new peer address
from received connectivity probing packet.

relnote: n/a current unused code, flag protected by
quic_server_reply_to_connectivity_probing which is on by default.

Manual merge internal change: 175728966

Bug: 774622
Change-Id: If0f958cdd2f0540be435cd222ba6f848acb9e185
Reviewed-on: https://chromium-review.googlesource.com/775993Reviewed-by: default avatarRyan Hamilton <rch@chromium.org>
Commit-Queue: Zhongyi Shi <zhongyi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517240}
parent 5a496c3e
...@@ -182,6 +182,10 @@ QuicConnection::QuicConnection( ...@@ -182,6 +182,10 @@ QuicConnection::QuicConnection(
: framer_(supported_versions, : framer_(supported_versions,
helper->GetClock()->ApproximateNow(), helper->GetClock()->ApproximateNow(),
perspective), perspective),
server_reply_to_connectivity_probes_(
FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing),
current_packet_content_(NO_FRAMES_RECEIVED),
current_peer_migration_type_(NO_CHANGE),
helper_(helper), helper_(helper),
alarm_factory_(alarm_factory), alarm_factory_(alarm_factory),
per_packet_options_(nullptr), per_packet_options_(nullptr),
...@@ -640,6 +644,13 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { ...@@ -640,6 +644,13 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
return false; return false;
} }
if (server_reply_to_connectivity_probes_) {
QUIC_FLAG_COUNT_N(
quic_reloadable_flag_quic_server_reply_to_connectivity_probing, 1, 4);
// Initialize the current packet content stats.
current_packet_content_ = NO_FRAMES_RECEIVED;
current_peer_migration_type_ = NO_CHANGE;
}
PeerAddressChangeType peer_migration_type = PeerAddressChangeType peer_migration_type =
QuicUtils::DetermineAddressChangeType(peer_address_, QuicUtils::DetermineAddressChangeType(peer_address_,
last_packet_source_address_); last_packet_source_address_);
...@@ -647,15 +658,25 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { ...@@ -647,15 +658,25 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
// new address. // new address.
if (header.packet_number > received_packet_manager_.GetLargestObserved() && if (header.packet_number > received_packet_manager_.GetLargestObserved() &&
peer_migration_type != NO_CHANGE) { peer_migration_type != NO_CHANGE) {
QUIC_DLOG(INFO) << ENDPOINT << "Peer's ip:port changed from "
<< peer_address_.ToString() << " to "
<< last_packet_source_address_.ToString();
if (perspective_ == Perspective::IS_CLIENT) { if (perspective_ == Perspective::IS_CLIENT) {
QUIC_DLOG(INFO) << ENDPOINT << "Peer's ip:port changed from "
<< peer_address_.ToString() << " to "
<< last_packet_source_address_.ToString();
peer_address_ = last_packet_source_address_; peer_address_ = last_packet_source_address_;
} else if (active_peer_migration_type_ == NO_CHANGE) { } else if (active_peer_migration_type_ == NO_CHANGE) {
// Only migrate connection to a new peer address if there is no // Only migrate connection to a new peer address if there is no
// pending change underway. // pending change underway.
StartPeerMigration(peer_migration_type); if (server_reply_to_connectivity_probes_) {
// Cache the current migration change type, which will start peer
// migration immediately if this packet is not a connectivity probing
// packet.
QUIC_FLAG_COUNT_N(
quic_reloadable_flag_quic_server_reply_to_connectivity_probing, 2,
4);
current_peer_migration_type_ = peer_migration_type;
} else {
StartPeerMigration(peer_migration_type);
}
} }
} }
...@@ -676,6 +697,11 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { ...@@ -676,6 +697,11 @@ bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a stream frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnStreamFrame(frame); debug_visitor_->OnStreamFrame(frame);
} }
...@@ -707,6 +733,11 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { ...@@ -707,6 +733,11 @@ bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
DCHECK(connected_); DCHECK(connected_);
// Since an ack frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnAckFrame(incoming_ack); debug_visitor_->OnAckFrame(incoming_ack);
} }
...@@ -756,6 +787,11 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { ...@@ -756,6 +787,11 @@ bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a stop waiting frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (no_stop_waiting_frames_) { if (no_stop_waiting_frames_) {
return true; return true;
} }
...@@ -783,6 +819,8 @@ bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) { ...@@ -783,6 +819,8 @@ bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
UpdatePacketContent(SECOND_FRAME_IS_PADDING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnPaddingFrame(frame); debug_visitor_->OnPaddingFrame(frame);
} }
...@@ -791,6 +829,8 @@ bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) { ...@@ -791,6 +829,8 @@ bool QuicConnection::OnPaddingFrame(const QuicPaddingFrame& frame) {
bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) { bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
UpdatePacketContent(FIRST_FRAME_IS_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnPingFrame(frame); debug_visitor_->OnPingFrame(frame);
} }
...@@ -858,6 +898,11 @@ const char* QuicConnection::ValidateStopWaitingFrame( ...@@ -858,6 +898,11 @@ const char* QuicConnection::ValidateStopWaitingFrame(
bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a reset stream frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnRstStreamFrame(frame); debug_visitor_->OnRstStreamFrame(frame);
} }
...@@ -874,6 +919,11 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { ...@@ -874,6 +919,11 @@ bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
bool QuicConnection::OnConnectionCloseFrame( bool QuicConnection::OnConnectionCloseFrame(
const QuicConnectionCloseFrame& frame) { const QuicConnectionCloseFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a connection close frame was received, this is not a connectivity
// probe. A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnConnectionCloseFrame(frame); debug_visitor_->OnConnectionCloseFrame(frame);
} }
...@@ -893,6 +943,11 @@ bool QuicConnection::OnConnectionCloseFrame( ...@@ -893,6 +943,11 @@ bool QuicConnection::OnConnectionCloseFrame(
bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a go away frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnGoAwayFrame(frame); debug_visitor_->OnGoAwayFrame(frame);
} }
...@@ -910,6 +965,11 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { ...@@ -910,6 +965,11 @@ bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a window update frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_); debug_visitor_->OnWindowUpdateFrame(frame, time_of_last_received_packet_);
} }
...@@ -924,6 +984,11 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) { ...@@ -924,6 +984,11 @@ bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
DCHECK(connected_); DCHECK(connected_);
// Since a blocked frame was received, this is not a connectivity probe.
// A probe only contains a PING and full padding.
UpdatePacketContent(NOT_PADDED_PING);
if (debug_visitor_ != nullptr) { if (debug_visitor_ != nullptr) {
debug_visitor_->OnBlockedFrame(frame); debug_visitor_->OnBlockedFrame(frame);
} }
...@@ -946,6 +1011,30 @@ void QuicConnection::OnPacketComplete() { ...@@ -946,6 +1011,30 @@ void QuicConnection::OnPacketComplete() {
QUIC_DVLOG(1) << ENDPOINT << "Got packet " << last_header_.packet_number QUIC_DVLOG(1) << ENDPOINT << "Got packet " << last_header_.packet_number
<< " for " << last_header_.connection_id; << " for " << last_header_.connection_id;
if (server_reply_to_connectivity_probes_) {
QUIC_FLAG_COUNT_N(
quic_reloadable_flag_quic_server_reply_to_connectivity_probing, 3, 4);
if (current_packet_content_ == SECOND_FRAME_IS_PADDING) {
QUIC_DVLOG(1) << ENDPOINT << "Received a padded PING packet";
if (last_packet_source_address_ != peer_address_ ||
last_packet_destination_address_ != self_address_) {
// Padded PING packet associated with self/peer address change is a
// connectivity probing packet.
QUIC_DVLOG(1) << ENDPOINT
<< "Received a connectivity probing packet for "
<< last_header_.connection_id << " frome ip:port: "
<< last_packet_source_address_.ToString()
<< " to ip:port "
<< last_packet_destination_address_.ToString();
visitor_->OnConnectivityProbeReceived(last_packet_destination_address_,
last_packet_source_address_);
}
} else if (current_peer_migration_type_ != NO_CHANGE) {
StartPeerMigration(current_peer_migration_type_);
}
current_peer_migration_type_ = NO_CHANGE;
}
// An ack will be sent if a missing retransmittable packet was received; // An ack will be sent if a missing retransmittable packet was received;
const bool was_missing = const bool was_missing =
should_last_packet_instigate_acks_ && was_last_packet_missing_; should_last_packet_instigate_acks_ && was_last_packet_missing_;
...@@ -2557,6 +2646,48 @@ void QuicConnection::CheckIfApplicationLimited() { ...@@ -2557,6 +2646,48 @@ void QuicConnection::CheckIfApplicationLimited() {
} }
} }
void QuicConnection::UpdatePacketContent(PacketContent type) {
if (!server_reply_to_connectivity_probes_) {
return;
}
QUIC_FLAG_COUNT_N(
quic_reloadable_flag_quic_server_reply_to_connectivity_probing, 4, 4);
if (current_packet_content_ == NOT_PADDED_PING) {
// We have already learned the current packet is not a connectivity
// probing packet. Peer migration should have already been started earlier
// if needed.
return;
}
if (type == NO_FRAMES_RECEIVED) {
return;
}
if (type == FIRST_FRAME_IS_PING) {
if (current_packet_content_ == NO_FRAMES_RECEIVED) {
current_packet_content_ = FIRST_FRAME_IS_PING;
return;
}
}
if (type == SECOND_FRAME_IS_PADDING) {
if (current_packet_content_ == FIRST_FRAME_IS_PING) {
current_packet_content_ = SECOND_FRAME_IS_PADDING;
return;
}
}
current_packet_content_ = NOT_PADDED_PING;
if (current_peer_migration_type_ == NO_CHANGE) {
return;
}
// Start peer migration immediately when the current packet is confirmed not
// a connectivity probing packet.
StartPeerMigration(current_peer_migration_type_);
current_peer_migration_type_ = NO_CHANGE;
}
void QuicConnection::SetStreamNotifier( void QuicConnection::SetStreamNotifier(
StreamNotifierInterface* stream_notifier) { StreamNotifierInterface* stream_notifier) {
sent_packet_manager_.SetStreamNotifier(stream_notifier); sent_packet_manager_.SetStreamNotifier(stream_notifier);
......
...@@ -124,6 +124,11 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface { ...@@ -124,6 +124,11 @@ class QUIC_EXPORT_PRIVATE QuicConnectionVisitorInterface {
virtual void OnSuccessfulVersionNegotiation( virtual void OnSuccessfulVersionNegotiation(
const QuicTransportVersion& version) = 0; const QuicTransportVersion& version) = 0;
// Called when a connectivity probe has been received by the connection.
virtual void OnConnectivityProbeReceived(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address) = 0;
// Called when a blocked socket becomes writable. // Called when a blocked socket becomes writable.
virtual void OnCanWrite() = 0; virtual void OnCanWrite() = 0;
...@@ -669,8 +674,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection ...@@ -669,8 +674,9 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// Sends a connectivity probing packet to |peer_address| with // Sends a connectivity probing packet to |peer_address| with
// |probing_writer|. If |probing_writer| is nullptr, will use default // |probing_writer|. If |probing_writer| is nullptr, will use default
// packet writer to write the packet. // packet writer to write the packet.
void SendConnectivityProbingPacket(QuicPacketWriter* probing_writer, virtual void SendConnectivityProbingPacket(
const QuicSocketAddress& peer_address); QuicPacketWriter* probing_writer,
const QuicSocketAddress& peer_address);
// Sends an MTU discovery packet of size |mtu_discovery_target_| and updates // Sends an MTU discovery packet of size |mtu_discovery_target_| and updates
// the MTU discovery alarm. // the MTU discovery alarm.
...@@ -772,6 +778,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection ...@@ -772,6 +778,13 @@ class QUIC_EXPORT_PRIVATE QuicConnection
typedef std::list<SerializedPacket> QueuedPacketList; typedef std::list<SerializedPacket> QueuedPacketList;
enum PacketContent {
NO_FRAMES_RECEIVED,
FIRST_FRAME_IS_PING,
SECOND_FRAME_IS_PADDING,
NOT_PADDED_PING, // Set if the packet is not {PING, PADDING}.
};
// Notifies the visitor of the close and marks the connection as disconnected. // Notifies the visitor of the close and marks the connection as disconnected.
// Does not send a connection close frame to the peer. // Does not send a connection close frame to the peer.
void TearDownLocalConnectionState(QuicErrorCode error, void TearDownLocalConnectionState(QuicErrorCode error,
...@@ -869,7 +882,24 @@ class QUIC_EXPORT_PRIVATE QuicConnection ...@@ -869,7 +882,24 @@ class QUIC_EXPORT_PRIVATE QuicConnection
// congestion controller if it is the case. // congestion controller if it is the case.
void CheckIfApplicationLimited(); void CheckIfApplicationLimited();
// Sets |current_packet_content_| to |type| if applicable. And
// starts peer miration if current packet is confirmed not a connectivity
// probe and |current_peer_migration_type_| indicates peer address change.
void UpdatePacketContent(PacketContent type);
QuicFramer framer_; QuicFramer framer_;
// If true, server will respect client's connectivity probing packets and send
// connectivity probing to the source address of the received probing.
bool server_reply_to_connectivity_probes_;
// Contents received in the current packet, especially used to identify
// whether the current packet is a padded PING packet.
PacketContent current_packet_content_;
// Caches the current peer migration type if a peer migration might be
// initiated. As soon as the current packet is confirmed not a connectivity
// probe, peer migration will start.
PeerAddressChangeType current_peer_migration_type_;
QuicConnectionHelperInterface* helper_; // Not owned. QuicConnectionHelperInterface* helper_; // Not owned.
QuicAlarmFactory* alarm_factory_; // Not owned. QuicAlarmFactory* alarm_factory_; // Not owned.
PerPacketOptions* per_packet_options_; // Not owned. PerPacketOptions* per_packet_options_; // Not owned.
......
...@@ -809,6 +809,15 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> { ...@@ -809,6 +809,15 @@ class QuicConnectionTest : public QuicTestWithParam<TestParams> {
} }
} }
void ProcessReceivedPacket(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
const QuicReceivedPacket& packet) {
connection_.ProcessUdpPacket(self_address, peer_address, packet);
if (connection_.GetSendAlarm()->IsSet()) {
connection_.GetSendAlarm()->Fire();
}
}
void ProcessFramePacket(QuicFrame frame) { void ProcessFramePacket(QuicFrame frame) {
ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress); ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress);
} }
...@@ -1247,6 +1256,203 @@ TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) { ...@@ -1247,6 +1256,203 @@ TEST_P(QuicConnectionTest, PeerAddressChangeAtServer) {
EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
} }
TEST_P(QuicConnectionTest, ReceivePaddedPingAtServer) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_SERVER);
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
// Clear peer address.
QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress());
EXPECT_FALSE(connection_.peer_address().IsInitialized());
QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece());
EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0);
// Process a padded PING packet with no peer address change on server side
// will be ignored.
std::unique_ptr<QuicEncryptedPacket> probing_packet(
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
&peer_creator_));
std::unique_ptr<QuicReceivedPacket> received(
ConstructReceivedPacket(*probing_packet, clock_.Now()));
ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
}
TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtServer) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_SERVER);
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
// Clear peer address.
QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress());
EXPECT_FALSE(connection_.peer_address().IsInitialized());
QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece());
EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
} else {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0);
}
// Process a padded PING packet from a new peer address on server side
// is effectively receiving a connectivity probing.
const QuicSocketAddress kNewPeerAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
std::unique_ptr<QuicEncryptedPacket> probing_packet(
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
&peer_creator_));
std::unique_ptr<QuicReceivedPacket> received(
ConstructReceivedPacket(*probing_packet, clock_.Now()));
ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_EQ(kPeerAddress, connection_.peer_address());
} else {
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
}
// Process another packet with the old peer address on server side will not
// start peer migration.
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
}
TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_SERVER);
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
// Clear peer address.
QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress());
EXPECT_FALSE(connection_.peer_address().IsInitialized());
QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece());
EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
} else {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0);
}
// Process a padded PING packet from a new peer address on server side
// is effectively receiving a connectivity probing.
const QuicSocketAddress kNewPeerAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
std::unique_ptr<QuicEncryptedPacket> probing_packet(
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
&peer_creator_));
std::unique_ptr<QuicReceivedPacket> received(
ConstructReceivedPacket(*probing_packet, clock_.Now()));
ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received);
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_EQ(kPeerAddress, connection_.peer_address());
} else {
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
}
// Process another non-probing packet with the new peer address on server
// side will start peer migration.
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1);
} else {
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
}
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kNewPeerAddress);
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
}
TEST_P(QuicConnectionTest, ReceivePaddedPingAtClient) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_CLIENT);
EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
// Clear peer address.
QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress());
EXPECT_FALSE(connection_.peer_address().IsInitialized());
QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece());
EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
// Process a padded PING packet with same address on client side is ignored.
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(0);
std::unique_ptr<QuicEncryptedPacket> probing_packet(
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
&peer_creator_));
std::unique_ptr<QuicReceivedPacket> received(
ConstructReceivedPacket(*probing_packet, clock_.Now()));
ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
}
TEST_P(QuicConnectionTest, ReceiveConnectivityProbingAtClient) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_CLIENT);
EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective());
// Clear peer address.
QuicConnectionPeer::SetPeerAddress(&connection_, QuicSocketAddress());
EXPECT_FALSE(connection_.peer_address().IsInitialized());
QuicStreamFrame stream_frame(1u, false, 0u, QuicStringPiece());
EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber());
ProcessFramePacketWithAddresses(QuicFrame(&stream_frame), kSelfAddress,
kPeerAddress);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
// Process a padded PING packet with a different self address on client side
// is effectively receiving a connectivity probing.
EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0);
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_CALL(visitor_, OnConnectivityProbeReceived(_, _)).Times(1);
}
const QuicSocketAddress kNewSelfAddress =
QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
std::unique_ptr<QuicEncryptedPacket> probing_packet(
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
&peer_creator_));
std::unique_ptr<QuicReceivedPacket> received(
ConstructReceivedPacket(*probing_packet, clock_.Now()));
ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
}
TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) { TEST_P(QuicConnectionTest, PeerAddressChangeAtClient) {
EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
set_perspective(Perspective::IS_CLIENT); set_perspective(Perspective::IS_CLIENT);
......
...@@ -193,3 +193,10 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_explicit_close_after_tlp, false) ...@@ -193,3 +193,10 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_explicit_close_after_tlp, false)
// Enables 3 new connection options to make PROBE_RTT more aggressive // Enables 3 new connection options to make PROBE_RTT more aggressive
QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_less_probe_rtt, false) QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_less_probe_rtt, false)
// If true, server will send a connectivity probing to the source address of the
// received connectivity probing. Otherwise, server will treat connectivity
// probing packet as normal packet
QUIC_FLAG(bool,
FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing,
true)
...@@ -170,6 +170,16 @@ void QuicSession::OnWriteBlocked() { ...@@ -170,6 +170,16 @@ void QuicSession::OnWriteBlocked() {
void QuicSession::OnSuccessfulVersionNegotiation( void QuicSession::OnSuccessfulVersionNegotiation(
const QuicTransportVersion& /*version*/) {} const QuicTransportVersion& /*version*/) {}
void QuicSession::OnConnectivityProbeReceived(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address) {
if (perspective() == Perspective::IS_SERVER) {
// Server only sends back a connectivity probe after received a
// connectivity probe from a new peer address.
connection_->SendConnectivityProbingPacket(nullptr, peer_address);
}
}
void QuicSession::OnPathDegrading() {} void QuicSession::OnPathDegrading() {}
bool QuicSession::AllowSelfAddressChange() const { bool QuicSession::AllowSelfAddressChange() const {
......
...@@ -99,6 +99,9 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface, ...@@ -99,6 +99,9 @@ class QUIC_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface,
void OnWriteBlocked() override; void OnWriteBlocked() override;
void OnSuccessfulVersionNegotiation( void OnSuccessfulVersionNegotiation(
const QuicTransportVersion& version) override; const QuicTransportVersion& version) override;
void OnConnectivityProbeReceived(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address) override;
void OnCanWrite() override; void OnCanWrite() override;
void OnCongestionWindowChange(QuicTime /*now*/) override {} void OnCongestionWindowChange(QuicTime /*now*/) override {}
void OnConnectionMigration(PeerAddressChangeType type) override {} void OnConnectionMigration(PeerAddressChangeType type) override {}
......
...@@ -36,12 +36,12 @@ ...@@ -36,12 +36,12 @@
using std::string; using std::string;
using testing::CreateFunctor; using testing::CreateFunctor;
using testing::_;
using testing::AtLeast; using testing::AtLeast;
using testing::InSequence; using testing::InSequence;
using testing::Invoke; using testing::Invoke;
using testing::Return; using testing::Return;
using testing::StrictMock; using testing::StrictMock;
using testing::_;
namespace net { namespace net {
namespace test { namespace test {
...@@ -230,8 +230,8 @@ class TestSession : public QuicSpdySession { ...@@ -230,8 +230,8 @@ class TestSession : public QuicSpdySession {
return WritevData(stream, stream->id(), bytes, 0, FIN); return WritevData(stream, stream->id(), bytes, 0, FIN);
} }
using QuicSession::PostProcessAfterData;
using QuicSession::closed_streams; using QuicSession::closed_streams;
using QuicSession::PostProcessAfterData;
using QuicSession::zombie_streams; using QuicSession::zombie_streams;
private: private:
...@@ -817,6 +817,39 @@ TEST_P(QuicSessionTestServer, SendGoAway) { ...@@ -817,6 +817,39 @@ TEST_P(QuicSessionTestServer, SendGoAway) {
EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId)); EXPECT_TRUE(session_.GetOrCreateDynamicStream(kTestStreamId));
} }
// Test that server session will send a connectivity probe in response to a
// connectivity probe on the same path.
TEST_P(QuicSessionTestServer, ServerReplyToConnecitivityProbe) {
if (!FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
return;
}
QuicSocketAddress old_peer_address =
QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort);
EXPECT_EQ(old_peer_address, session_.peer_address());
QuicSocketAddress new_peer_address =
QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1);
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
MockPacketWriter* writer = static_cast<MockPacketWriter*>(
QuicConnectionPeer::GetWriter(session_.connection()));
EXPECT_CALL(*writer, WritePacket(_, _, _, new_peer_address, _))
.WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0)));
EXPECT_CALL(*connection_,
SendConnectivityProbingPacket(nullptr, new_peer_address))
.WillOnce(
Invoke(connection_,
&MockQuicConnection::ReallySendConnectivityProbingPacket));
}
session_.OnConnectivityProbeReceived(session_.self_address(),
new_peer_address);
if (FLAGS_quic_reloadable_flag_quic_server_reply_to_connectivity_probing) {
EXPECT_EQ(old_peer_address, session_.peer_address());
} else {
EXPECT_EQ(new_peer_address, session_.peer_address());
}
}
TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { TEST_P(QuicSessionTestServer, IncreasedTimeoutAfterCryptoHandshake) {
EXPECT_EQ(kInitialIdleTimeoutSecs + 3, EXPECT_EQ(kInitialIdleTimeoutSecs + 3,
QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
......
...@@ -76,6 +76,13 @@ SerializedPacket QuicPacketCreatorPeer::SerializeAllFrames( ...@@ -76,6 +76,13 @@ SerializedPacket QuicPacketCreatorPeer::SerializeAllFrames(
return packet; return packet;
} }
// static
std::unique_ptr<QuicEncryptedPacket>
QuicPacketCreatorPeer::SerializeConnectivityProbingPacket(
QuicPacketCreator* creator) {
return creator->SerializeConnectivityProbingPacket();
}
// static // static
EncryptionLevel QuicPacketCreatorPeer::GetEncryptionLevel( EncryptionLevel QuicPacketCreatorPeer::GetEncryptionLevel(
QuicPacketCreator* creator) { QuicPacketCreator* creator) {
......
...@@ -39,6 +39,8 @@ class QuicPacketCreatorPeer { ...@@ -39,6 +39,8 @@ class QuicPacketCreatorPeer {
const QuicFrames& frames, const QuicFrames& frames,
char* buffer, char* buffer,
size_t buffer_len); size_t buffer_len);
static std::unique_ptr<QuicEncryptedPacket>
SerializeConnectivityProbingPacket(QuicPacketCreator* creator);
static EncryptionLevel GetEncryptionLevel(QuicPacketCreator* creator); static EncryptionLevel GetEncryptionLevel(QuicPacketCreator* creator);
static QuicFramer* framer(QuicPacketCreator* creator); static QuicFramer* framer(QuicPacketCreator* creator);
......
...@@ -307,6 +307,9 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface { ...@@ -307,6 +307,9 @@ class MockQuicConnectionVisitor : public QuicConnectionVisitorInterface {
MOCK_CONST_METHOD0(HasOpenDynamicStreams, bool()); MOCK_CONST_METHOD0(HasOpenDynamicStreams, bool());
MOCK_METHOD1(OnSuccessfulVersionNegotiation, MOCK_METHOD1(OnSuccessfulVersionNegotiation,
void(const QuicTransportVersion& version)); void(const QuicTransportVersion& version));
MOCK_METHOD2(OnConnectivityProbeReceived,
void(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address));
MOCK_METHOD0(OnConfigNegotiated, void()); MOCK_METHOD0(OnConfigNegotiated, void());
MOCK_METHOD0(PostProcessAfterData, void()); MOCK_METHOD0(PostProcessAfterData, void());
MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void()); MOCK_METHOD0(OnAckNeedsRetransmittableFrame, void());
...@@ -421,6 +424,9 @@ class MockQuicConnection : public QuicConnection { ...@@ -421,6 +424,9 @@ class MockQuicConnection : public QuicConnection {
MOCK_METHOD2(SendWindowUpdate, MOCK_METHOD2(SendWindowUpdate,
void(QuicStreamId id, QuicStreamOffset byte_offset)); void(QuicStreamId id, QuicStreamOffset byte_offset));
MOCK_METHOD0(OnCanWrite, void()); MOCK_METHOD0(OnCanWrite, void());
MOCK_METHOD2(SendConnectivityProbingPacket,
void(QuicPacketWriter* probing_writer,
const QuicSocketAddress& peer_address));
MOCK_METHOD1(OnSendConnectionState, void(const CachedNetworkParameters&)); MOCK_METHOD1(OnSendConnectionState, void(const CachedNetworkParameters&));
MOCK_METHOD2(ResumeConnectionState, MOCK_METHOD2(ResumeConnectionState,
...@@ -446,6 +452,12 @@ class MockQuicConnection : public QuicConnection { ...@@ -446,6 +452,12 @@ class MockQuicConnection : public QuicConnection {
QuicConnection::SendGoAway(error, last_good_stream_id, reason); QuicConnection::SendGoAway(error, last_good_stream_id, reason);
} }
void ReallySendConnectivityProbingPacket(
QuicPacketWriter* probing_writer,
const QuicSocketAddress& peer_address) {
QuicConnection::SendConnectivityProbingPacket(probing_writer, peer_address);
}
private: private:
DISALLOW_COPY_AND_ASSIGN(MockQuicConnection); DISALLOW_COPY_AND_ASSIGN(MockQuicConnection);
}; };
......
...@@ -88,6 +88,9 @@ class QuicEndpoint : public Endpoint, ...@@ -88,6 +88,9 @@ class QuicEndpoint : public Endpoint,
void OnWriteBlocked() override {} void OnWriteBlocked() override {}
void OnSuccessfulVersionNegotiation( void OnSuccessfulVersionNegotiation(
const QuicTransportVersion& version) override {} const QuicTransportVersion& version) override {}
void OnConnectivityProbeReceived(
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address) override {}
void OnCongestionWindowChange(QuicTime now) override {} void OnCongestionWindowChange(QuicTime now) override {}
void OnConnectionMigration(PeerAddressChangeType type) override {} void OnConnectionMigration(PeerAddressChangeType type) override {}
void OnPathDegrading() override {} void OnPathDegrading() override {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment