Add PeerIssuedConnectionIdManager that handles NewConnectionId Frame.

PiperOrigin-RevId: 353019787
Change-Id: I6b9e38c357165759503e17be03599d696b4e44f5
diff --git a/quic/core/quic_connection_id_manager.cc b/quic/core/quic_connection_id_manager.cc
new file mode 100644
index 0000000..d4380b7
--- /dev/null
+++ b/quic/core/quic_connection_id_manager.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/core/quic_connection_id_manager.h"
+#include <cstdio>
+
+#include "quic/core/quic_clock.h"
+#include "quic/core/quic_connection_id.h"
+#include "quic/core/quic_error_codes.h"
+#include "quic/platform/api/quic_uint128.h"
+
+namespace quic {
+
+QuicConnectionIdData::QuicConnectionIdData(
+    const QuicConnectionId& connection_id,
+    uint64_t sequence_number,
+    QuicUint128 stateless_reset_token)
+    : connection_id(connection_id),
+      sequence_number(sequence_number),
+      stateless_reset_token(stateless_reset_token) {}
+
+namespace {
+
+class RetirePeerIssuedConnectionIdAlarm : public QuicAlarm::Delegate {
+ public:
+  explicit RetirePeerIssuedConnectionIdAlarm(
+      QuicConnectionIdManagerVisitorInterface* visitor)
+      : visitor_(visitor) {}
+  RetirePeerIssuedConnectionIdAlarm(const RetirePeerIssuedConnectionIdAlarm&) =
+      delete;
+  RetirePeerIssuedConnectionIdAlarm& operator=(
+      const RetirePeerIssuedConnectionIdAlarm&) = delete;
+
+  void OnAlarm() override { visitor_->OnPeerIssuedConnectionIdRetired(); }
+
+ private:
+  QuicConnectionIdManagerVisitorInterface* visitor_;
+};
+
+std::vector<QuicConnectionIdData>::const_iterator FindConnectionIdData(
+    const std::vector<QuicConnectionIdData>& cid_data_vector,
+    const QuicConnectionId& cid) {
+  return std::find_if(cid_data_vector.begin(), cid_data_vector.end(),
+                      [&cid](const QuicConnectionIdData& cid_data) {
+                        return cid == cid_data.connection_id;
+                      });
+}
+
+}  // namespace
+
+QuicPeerIssuedConnectionIdManager::QuicPeerIssuedConnectionIdManager(
+    size_t active_connection_id_limit,
+    const QuicConnectionId& initial_peer_issued_connection_id,
+    const QuicClock* clock,
+    QuicAlarmFactory* alarm_factory,
+    QuicConnectionIdManagerVisitorInterface* visitor)
+    : active_connection_id_limit_(active_connection_id_limit),
+      clock_(clock),
+      retire_connection_id_alarm_(alarm_factory->CreateAlarm(
+          new RetirePeerIssuedConnectionIdAlarm(visitor))) {
+  DCHECK(!initial_peer_issued_connection_id.IsEmpty());
+  active_connection_id_data_.emplace_back(initial_peer_issued_connection_id,
+                                          /*sequence_number=*/0u,
+                                          QuicUint128());
+  recent_new_connection_id_sequence_numbers_.Add(0u, 1u);
+}
+
+QuicPeerIssuedConnectionIdManager::~QuicPeerIssuedConnectionIdManager() {
+  retire_connection_id_alarm_->Cancel();
+}
+
+bool QuicPeerIssuedConnectionIdManager::IsConnectionIdNew(
+    const QuicNewConnectionIdFrame& frame) {
+  auto is_old_connection_id = [&frame](const QuicConnectionIdData& cid_data) {
+    return cid_data.connection_id == frame.connection_id;
+  };
+  if (std::any_of(active_connection_id_data_.begin(),
+                  active_connection_id_data_.end(), is_old_connection_id)) {
+    return false;
+  }
+  if (std::any_of(unused_connection_id_data_.begin(),
+                  unused_connection_id_data_.end(), is_old_connection_id)) {
+    return false;
+  }
+  if (std::any_of(to_be_retired_connection_id_data_.begin(),
+                  to_be_retired_connection_id_data_.end(),
+                  is_old_connection_id)) {
+    return false;
+  }
+  return true;
+}
+
+void QuicPeerIssuedConnectionIdManager::PrepareToRetireConnectionIdPriorTo(
+    uint64_t retire_prior_to,
+    std::vector<QuicConnectionIdData>* cid_data_vector) {
+  auto it2 = cid_data_vector->begin();
+  for (auto it = cid_data_vector->begin(); it != cid_data_vector->end(); ++it) {
+    if (it->sequence_number >= retire_prior_to) {
+      *it2++ = *it;
+    } else {
+      to_be_retired_connection_id_data_.push_back(*it);
+      if (!retire_connection_id_alarm_->IsSet()) {
+        retire_connection_id_alarm_->Set(clock_->ApproximateNow());
+      }
+    }
+  }
+  cid_data_vector->erase(it2, cid_data_vector->end());
+}
+
+QuicErrorCode QuicPeerIssuedConnectionIdManager::OnNewConnectionIdFrame(
+    const QuicNewConnectionIdFrame& frame,
+    std::string* error_detail) {
+  if (recent_new_connection_id_sequence_numbers_.Contains(
+          frame.sequence_number)) {
+    // This frame has a recently seen sequence number. Ignore.
+    return QUIC_NO_ERROR;
+  }
+  if (!IsConnectionIdNew(frame)) {
+    *error_detail =
+        "Received a NEW_CONNECTION_ID frame that reuses a previously seen Id.";
+    return IETF_QUIC_PROTOCOL_VIOLATION;
+  }
+
+  recent_new_connection_id_sequence_numbers_.AddOptimizedForAppend(
+      frame.sequence_number, frame.sequence_number + 1);
+
+  if (recent_new_connection_id_sequence_numbers_.Size() >
+      kMaxNumConnectionIdSequenceNumberIntervals) {
+    *error_detail =
+        "Too many disjoint connection Id sequence number intervals.";
+    return IETF_QUIC_PROTOCOL_VIOLATION;
+  }
+
+  // QuicFramer::ProcessNewConnectionIdFrame guarantees that
+  // frame.sequence_number >= frame.retire_prior_to, and hence there is no need
+  // to check that.
+  if (frame.sequence_number < max_new_connection_id_frame_retire_prior_to_) {
+    // Later frames have asked for retirement of the current frame.
+    to_be_retired_connection_id_data_.emplace_back(frame.connection_id,
+                                                   frame.sequence_number,
+                                                   frame.stateless_reset_token);
+    if (!retire_connection_id_alarm_->IsSet()) {
+      retire_connection_id_alarm_->Set(clock_->ApproximateNow());
+    }
+    return QUIC_NO_ERROR;
+  }
+  if (frame.retire_prior_to > max_new_connection_id_frame_retire_prior_to_) {
+    max_new_connection_id_frame_retire_prior_to_ = frame.retire_prior_to;
+    PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
+                                       &active_connection_id_data_);
+    PrepareToRetireConnectionIdPriorTo(frame.retire_prior_to,
+                                       &unused_connection_id_data_);
+  }
+
+  if (active_connection_id_data_.size() + unused_connection_id_data_.size() >=
+      active_connection_id_limit_) {
+    *error_detail = "Peer provides more connection IDs than the limit.";
+    return QUIC_CONNECTION_ID_LIMIT_ERROR;
+  }
+
+  unused_connection_id_data_.emplace_back(
+      frame.connection_id, frame.sequence_number, frame.stateless_reset_token);
+  return QUIC_NO_ERROR;
+}
+
+const QuicConnectionIdData*
+QuicPeerIssuedConnectionIdManager::ConsumeOneUnusedConnectionId() {
+  if (unused_connection_id_data_.empty()) {
+    return nullptr;
+  }
+  active_connection_id_data_.push_back(unused_connection_id_data_.back());
+  unused_connection_id_data_.pop_back();
+  return &active_connection_id_data_.back();
+}
+
+void QuicPeerIssuedConnectionIdManager::PrepareToRetireActiveConnectionId(
+    const QuicConnectionId& cid) {
+  auto it = FindConnectionIdData(active_connection_id_data_, cid);
+  if (it == active_connection_id_data_.end()) {
+    // The cid has already been retired.
+    return;
+  }
+  to_be_retired_connection_id_data_.push_back(*it);
+  active_connection_id_data_.erase(it);
+  if (!retire_connection_id_alarm_->IsSet()) {
+    retire_connection_id_alarm_->Set(clock_->ApproximateNow());
+  }
+}
+
+bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
+    const QuicConnectionId& cid) const {
+  return FindConnectionIdData(active_connection_id_data_, cid) !=
+         active_connection_id_data_.end();
+}
+
+std::vector<uint64_t> QuicPeerIssuedConnectionIdManager::
+    ConsumeToBeRetiredConnectionIdSequenceNumbers() {
+  std::vector<uint64_t> result;
+  for (auto const& cid_data : to_be_retired_connection_id_data_) {
+    result.push_back(cid_data.sequence_number);
+  }
+  to_be_retired_connection_id_data_.clear();
+  return result;
+}
+
+}  // namespace quic
diff --git a/quic/core/quic_connection_id_manager.h b/quic/core/quic_connection_id_manager.h
new file mode 100644
index 0000000..1977740
--- /dev/null
+++ b/quic/core/quic_connection_id_manager.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// QuicPeerIssuedConnectionIdManager handles the states associated with receving
+// and retiring peer issued connection Ids.
+// TODO(haoyuewang) Implements QuicSelfIssuedConnectionIdManager.
+// QuicConnectionIdManager handles the states associated with connection Ids
+// issued by the current end point.
+
+#ifndef QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_MANAGER_H_
+#define QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_MANAGER_H_
+
+#include <memory>
+#include "quic/core/frames/quic_new_connection_id_frame.h"
+#include "quic/core/quic_alarm.h"
+#include "quic/core/quic_alarm_factory.h"
+#include "quic/core/quic_connection_id.h"
+#include "quic/core/quic_interval_set.h"
+#include "quic/platform/api/quic_uint128.h"
+#include "net/quic/platform/impl/quic_export_impl.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicConnectionIdData {
+  QuicConnectionIdData(const QuicConnectionId& connection_id,
+                       uint64_t sequence_number,
+                       QuicUint128 stateless_reset_token);
+
+  QuicConnectionId connection_id;
+  uint64_t sequence_number;
+  QuicUint128 stateless_reset_token;
+};
+
+// Used by QuicSelfIssuedConnectionIdManager
+// and QuicPeerIssuedConnectionIdManager.
+class QUIC_EXPORT_PRIVATE QuicConnectionIdManagerVisitorInterface {
+ public:
+  virtual ~QuicConnectionIdManagerVisitorInterface() = default;
+  virtual void OnPeerIssuedConnectionIdRetired() = 0;
+};
+
+class QUIC_EXPORT_PRIVATE QuicPeerIssuedConnectionIdManager {
+ public:
+  // QuicPeerIssuedConnectionIdManager should be instantiated only when a peer
+  // issued-non empty connection ID is received.
+  QuicPeerIssuedConnectionIdManager(
+      size_t active_connection_id_limit,
+      const QuicConnectionId& initial_peer_issued_connection_id,
+      const QuicClock* clock,
+      QuicAlarmFactory* alarm_factory,
+      QuicConnectionIdManagerVisitorInterface* visitor);
+
+  ~QuicPeerIssuedConnectionIdManager();
+
+  QuicErrorCode OnNewConnectionIdFrame(const QuicNewConnectionIdFrame& frame,
+                                       std::string* error_detail);
+
+  // Returns the data associated with an unused connection Id. After the call,
+  // the Id is marked as used. Returns nullptr if there is no unused connection
+  // Id.
+  const QuicConnectionIdData* ConsumeOneUnusedConnectionId();
+
+  // Add the connection Id to the pending retirement connection Id list.
+  void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid);
+
+  bool IsConnectionIdActive(const QuicConnectionId& cid) const;
+
+  // Get the sequence numbers of all the connection Ids pending retirement when
+  // it is safe to retires these Ids.
+  std::vector<uint64_t> ConsumeToBeRetiredConnectionIdSequenceNumbers();
+
+ private:
+  friend class QuicConnectionIdManagerPeer;
+
+  bool IsConnectionIdNew(const QuicNewConnectionIdFrame& frame);
+
+  void PrepareToRetireConnectionIdPriorTo(
+      uint64_t retire_prior_to,
+      std::vector<QuicConnectionIdData>* cid_data_vector);
+
+  size_t active_connection_id_limit_;
+  const QuicClock* clock_;
+  std::unique_ptr<QuicAlarm> retire_connection_id_alarm_;
+  std::vector<QuicConnectionIdData> active_connection_id_data_;
+  std::vector<QuicConnectionIdData> unused_connection_id_data_;
+  std::vector<QuicConnectionIdData> to_be_retired_connection_id_data_;
+  // Track sequence numbers of recent NEW_CONNECTION_ID frames received from
+  // the peer.
+  QuicIntervalSet<uint64_t> recent_new_connection_id_sequence_numbers_;
+  uint64_t max_new_connection_id_frame_retire_prior_to_ = 0u;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_QUIC_CONNECTION_ID_MANAGER_H_
diff --git a/quic/core/quic_connection_id_manager_test.cc b/quic/core/quic_connection_id_manager_test.cc
new file mode 100644
index 0000000..47d3dea
--- /dev/null
+++ b/quic/core/quic_connection_id_manager_test.cc
@@ -0,0 +1,505 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quic/core/quic_connection_id_manager.h"
+#include <cstddef>
+
+#include "quic/core/quic_connection_id.h"
+#include "quic/core/quic_error_codes.h"
+#include "quic/platform/api/quic_test.h"
+#include "quic/test_tools/mock_clock.h"
+#include "quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+
+class QuicConnectionIdManagerPeer {
+ public:
+  static QuicAlarm* GetRetirePeerIssuedConnectionIdAlarm(
+      QuicPeerIssuedConnectionIdManager* manager) {
+    return manager->retire_connection_id_alarm_.get();
+  }
+};
+
+namespace {
+
+using ::quic::test::IsError;
+using ::quic::test::IsQuicNoError;
+using ::quic::test::TestConnectionId;
+using ::testing::ElementsAre;
+using ::testing::IsNull;
+
+class TestPeerIssuedConnectionIdManagerVisitor
+    : public QuicConnectionIdManagerVisitorInterface {
+ public:
+  void SetPeerIssuedConnectionIdManager(
+      QuicPeerIssuedConnectionIdManager* peer_issued_connection_id_manager) {
+    peer_issued_connection_id_manager_ = peer_issued_connection_id_manager;
+  }
+
+  void OnPeerIssuedConnectionIdRetired() override {
+    // Replace current connection Id if it has been retired.
+    if (!peer_issued_connection_id_manager_->IsConnectionIdActive(
+            current_peer_issued_connection_id_)) {
+      current_peer_issued_connection_id_ =
+          peer_issued_connection_id_manager_->ConsumeOneUnusedConnectionId()
+              ->connection_id;
+    }
+    // Retire all the to-be-retired connection Ids.
+    most_recent_retired_connection_id_sequence_numbers_ =
+        peer_issued_connection_id_manager_
+            ->ConsumeToBeRetiredConnectionIdSequenceNumbers();
+  }
+
+  const std::vector<uint64_t>&
+  most_recent_retired_connection_id_sequence_numbers() {
+    return most_recent_retired_connection_id_sequence_numbers_;
+  }
+
+  void SetCurrentPeerConnectionId(QuicConnectionId cid) {
+    current_peer_issued_connection_id_ = cid;
+  }
+
+  const QuicConnectionId& GetCurrentPeerConnectionId() {
+    return current_peer_issued_connection_id_;
+  }
+
+ private:
+  QuicPeerIssuedConnectionIdManager* peer_issued_connection_id_manager_ =
+      nullptr;
+  QuicConnectionId current_peer_issued_connection_id_;
+  std::vector<uint64_t> most_recent_retired_connection_id_sequence_numbers_;
+};
+
+class QuicPeerIssuedConnectionIdManagerTest : public QuicTest {
+ public:
+  QuicPeerIssuedConnectionIdManagerTest()
+      : peer_issued_cid_manager_(/*active_connection_id_limit=*/2,
+                                 initial_connection_id,
+                                 &clock_,
+                                 &alarm_factory_,
+                                 &cid_manager_visitor_) {
+    clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+    cid_manager_visitor_.SetPeerIssuedConnectionIdManager(
+        &peer_issued_cid_manager_);
+    cid_manager_visitor_.SetCurrentPeerConnectionId(initial_connection_id);
+    retire_peer_issued_cid_alarm_ =
+        QuicConnectionIdManagerPeer::GetRetirePeerIssuedConnectionIdAlarm(
+            &peer_issued_cid_manager_);
+  }
+
+ protected:
+  MockClock clock_;
+  test::MockAlarmFactory alarm_factory_;
+  TestPeerIssuedConnectionIdManagerVisitor cid_manager_visitor_;
+  QuicConnectionId initial_connection_id = TestConnectionId(0);
+  QuicPeerIssuedConnectionIdManager peer_issued_cid_manager_;
+  QuicAlarm* retire_peer_issued_cid_alarm_ = nullptr;
+  std::string error_details_;
+};
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ConnectionIdSequenceWhenMigrationSucceed) {
+  {
+    // Receives CID #1 from peer.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+
+    // Start to use CID #1 for alternative path.
+    const QuicConnectionIdData* aternative_connection_id_data =
+        peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    ASSERT_THAT(aternative_connection_id_data, testing::NotNull());
+    EXPECT_EQ(aternative_connection_id_data->connection_id,
+              TestConnectionId(1));
+    EXPECT_EQ(aternative_connection_id_data->stateless_reset_token,
+              frame.stateless_reset_token);
+
+    // Connection migration succeed. Prepares to retire CID #0.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(0));
+    cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(1));
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(0u));
+  }
+
+  {
+    // Receives CID #2 from peer since CID #0 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(2);
+    frame.sequence_number = 2u;
+    frame.retire_prior_to = 1u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #2 for alternative path.
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    // Connection migration succeed. Prepares to retire CID #1.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(1));
+    cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(2));
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(1u));
+  }
+
+  {
+    // Receives CID #3 from peer since CID #1 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(3);
+    frame.sequence_number = 3u;
+    frame.retire_prior_to = 2u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #3 for alternative path.
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    // Connection migration succeed. Prepares to retire CID #2.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(2));
+    cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(2u));
+  }
+
+  {
+    // Receives CID #4 from peer since CID #2 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(4);
+    frame.sequence_number = 4u;
+    frame.retire_prior_to = 3u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ConnectionIdSequenceWhenMigrationFail) {
+  {
+    // Receives CID #1 from peer.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #1 for alternative path.
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    // Connection migration fails. Prepares to retire CID #1.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(1));
+    // Actually retires CID #1.
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(1u));
+  }
+
+  {
+    // Receives CID #2 from peer since CID #1 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(2);
+    frame.sequence_number = 2u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #2 for alternative path.
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    // Connection migration fails again. Prepares to retire CID #2.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(2));
+    // Actually retires CID #2.
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(2u));
+  }
+
+  {
+    // Receives CID #3 from peer since CID #2 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(3);
+    frame.sequence_number = 3u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #3 for alternative path.
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+    // Connection migration succeed. Prepares to retire CID #0.
+    peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+        TestConnectionId(0));
+    // After CID #3 is default (i.e., when there is no pending frame to write
+    // associated with CID #0), #0 can actually be retired.
+    cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(0u));
+  }
+
+  {
+    // Receives CID #4 from peer since CID #0 is retired.
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(4);
+    frame.sequence_number = 4u;
+    frame.retire_prior_to = 3u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    EXPECT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    EXPECT_FALSE(retire_peer_issued_cid_alarm_->IsSet());
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ReceivesNewConnectionIdOutOfOrder) {
+  {
+    // Receives new CID #1 that retires prior to #0.
+    // Outcome: (active: #0 unused: #1)
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    // Start to use CID #1 for alternative path.
+    // Outcome: (active: #0 #1 unused: None)
+    peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+  }
+
+  {
+    // Receives new CID #3 that retires prior to #2.
+    // Outcome: (active: None unused: #3)
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(3);
+    frame.sequence_number = 3u;
+    frame.retire_prior_to = 2u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  {
+    // Receives new CID #2 that retires prior to #1.
+    // Outcome: (active: None unused: #3, #2)
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(2);
+    frame.sequence_number = 2u;
+    frame.retire_prior_to = 1u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  {
+    EXPECT_FALSE(
+        peer_issued_cid_manager_.IsConnectionIdActive(TestConnectionId(0)));
+    EXPECT_FALSE(
+        peer_issued_cid_manager_.IsConnectionIdActive(TestConnectionId(1)));
+    // When there is no frame associated with #0 and #1 to write, replace the
+    // in-use CID with an unused CID (#2) and retires #0 & #1.
+    ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+    alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+    EXPECT_THAT(cid_manager_visitor_
+                    .most_recent_retired_connection_id_sequence_numbers(),
+                ElementsAre(0u, 1u));
+    EXPECT_EQ(cid_manager_visitor_.GetCurrentPeerConnectionId(),
+              TestConnectionId(2));
+    // Get another unused CID for path validation.
+    EXPECT_EQ(
+        peer_issued_cid_manager_.ConsumeOneUnusedConnectionId()->connection_id,
+        TestConnectionId(3));
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       VisitedNewConnectionIdFrameIsIgnored) {
+  // Receives new CID #1 that retires prior to #0.
+  // Outcome: (active: #0 unused: #1)
+  QuicNewConnectionIdFrame frame;
+  frame.connection_id = TestConnectionId(1);
+  frame.sequence_number = 1u;
+  frame.retire_prior_to = 0u;
+  frame.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+  ASSERT_THAT(
+      peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+      IsQuicNoError());
+  // Start to use CID #1 for alternative path.
+  // Outcome: (active: #0 #1 unused: None)
+  peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
+  // Prepare to retire CID #1 as path validation fails.
+  peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
+      TestConnectionId(1));
+  // Actually retires CID #1.
+  ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
+  alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
+  EXPECT_THAT(
+      cid_manager_visitor_.most_recent_retired_connection_id_sequence_numbers(),
+      ElementsAre(1u));
+  // Receives the same frame again. Should be a no-op.
+  ASSERT_THAT(
+      peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+      IsQuicNoError());
+  EXPECT_THAT(peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(),
+              testing::IsNull());
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ErrorWhenActiveConnectionIdLimitExceeded) {
+  {
+    // Receives new CID #1 that retires prior to #0.
+    // Outcome: (active: #0 unused: #1)
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(2);
+    frame.sequence_number = 2u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsError(QUIC_CONNECTION_ID_LIMIT_ERROR));
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ErrorWhenTheSameConnectionIdIsSeenWithDifferentSequenceNumbers) {
+  {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 2u;
+    frame.retire_prior_to = 1u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(TestConnectionId(2));
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsError(IETF_QUIC_PROTOCOL_VIOLATION));
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       NewConnectionIdFrameWithTheSameSequenceNumberIsIgnored) {
+  {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(1);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(2);
+    frame.sequence_number = 1u;
+    frame.retire_prior_to = 0u;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(TestConnectionId(2));
+    EXPECT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+    EXPECT_EQ(
+        peer_issued_cid_manager_.ConsumeOneUnusedConnectionId()->connection_id,
+        TestConnectionId(1));
+    EXPECT_THAT(peer_issued_cid_manager_.ConsumeOneUnusedConnectionId(),
+                IsNull());
+  }
+}
+
+TEST_F(QuicPeerIssuedConnectionIdManagerTest,
+       ErrorWhenThereAreTooManyGapsInIssuedConnectionIdSequenceNumbers) {
+  // Add 20 intervals: [0, 1), [2, 3), ..., [38,39)
+  for (int i = 2; i <= 38; i += 2) {
+    QuicNewConnectionIdFrame frame;
+    frame.connection_id = TestConnectionId(i);
+    frame.sequence_number = i;
+    frame.retire_prior_to = i;
+    frame.stateless_reset_token =
+        QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+    ASSERT_THAT(
+        peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+        IsQuicNoError());
+  }
+
+  // Interval [40, 41) goes over the limit.
+  QuicNewConnectionIdFrame frame;
+  frame.connection_id = TestConnectionId(40);
+  frame.sequence_number = 40u;
+  frame.retire_prior_to = 40u;
+  frame.stateless_reset_token =
+      QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+  ASSERT_THAT(
+      peer_issued_cid_manager_.OnNewConnectionIdFrame(frame, &error_details_),
+      IsError(IETF_QUIC_PROTOCOL_VIOLATION));
+}
+
+}  // namespace
+}  // namespace quic
diff --git a/quic/core/quic_constants.h b/quic/core/quic_constants.h
index a2ead36..619ed32 100644
--- a/quic/core/quic_constants.h
+++ b/quic/core/quic_constants.h
@@ -209,6 +209,10 @@
 // This will likely have to be tuned.
 const QuicPacketCount kMaxPacketGap = 5000;
 
+// The max number of sequence number intervals that
+// QuicPeerIssuedConnetionIdManager can maintain.
+const size_t kMaxNumConnectionIdSequenceNumberIntervals = 20;
+
 // The maximum number of random padding bytes to add.
 const QuicByteCount kMaxNumRandomPaddingBytes = 256;
 
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 8363dc1..9d7d610 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -167,6 +167,7 @@
     RETURN_STRING_LITERAL(QUIC_STREAMS_BLOCKED_DATA);
     RETURN_STRING_LITERAL(QUIC_INVALID_NEW_CONNECTION_ID_DATA);
     RETURN_STRING_LITERAL(QUIC_INVALID_RETIRE_CONNECTION_ID_DATA);
+    RETURN_STRING_LITERAL(QUIC_CONNECTION_ID_LIMIT_ERROR);
     RETURN_STRING_LITERAL(QUIC_INVALID_STOP_SENDING_FRAME_DATA);
     RETURN_STRING_LITERAL(QUIC_INVALID_PATH_CHALLENGE_DATA);
     RETURN_STRING_LITERAL(QUIC_INVALID_PATH_RESPONSE_DATA);
@@ -758,7 +759,8 @@
     case QUIC_TLS_CERTIFICATE_REQUIRED:
       return {true, static_cast<uint64_t>(CRYPTO_ERROR_FIRST +
                                           SSL_AD_CERTIFICATE_REQUIRED)};
-
+    case QUIC_CONNECTION_ID_LIMIT_ERROR:
+      return {true, static_cast<uint64_t>(CONNECTION_ID_LIMIT_ERROR)};
     case QUIC_LAST_ERROR:
       return {false, static_cast<uint64_t>(QUIC_LAST_ERROR)};
   }
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 885e088..737c1ef 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -348,6 +348,8 @@
   QUIC_INVALID_STREAM_BLOCKED_DATA = 106,
   // NEW CONNECTION ID frame data is malformed.
   QUIC_INVALID_NEW_CONNECTION_ID_DATA = 107,
+  // More connection IDs than allowed are issued.
+  QUIC_CONNECTION_ID_LIMIT_ERROR = 203,
   // Received a MAX STREAM DATA frame with errors.
   QUIC_INVALID_STOP_SENDING_FRAME_DATA = 108,
   // Error deframing PATH CHALLENGE or PATH RESPONSE frames.
@@ -584,7 +586,7 @@
   QUIC_TLS_CERTIFICATE_REQUIRED = 202,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 203,
+  QUIC_LAST_ERROR = 204,
 };
 // QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
 // or a varint62 when doing IETF QUIC. Ensure that its value does not exceed