Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
connection_id_manager.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
7#include <algorithm>
8
10{
11
12// Static member definition
13const connection_id connection_id_manager::empty_cid_{};
14
15// ============================================================================
16// Constructor
17// ============================================================================
18
20 : active_cid_limit_(active_cid_limit)
21{
22}
23
24// ============================================================================
25// Peer Connection ID Management
26// ============================================================================
27
29{
30 // Initial CID has sequence number 0 and no stateless reset token
32 entry.cid = cid;
33 entry.sequence_number = 0;
34 entry.stateless_reset_token.fill(0);
35 entry.retired = false;
36
37 peer_cids_.clear();
38 peer_cids_.push_back(entry);
39 active_index_ = 0;
40}
41
43 uint64_t sequence,
44 uint64_t retire_prior_to,
45 const std::array<uint8_t, 16>& reset_token)
46 -> VoidResult
47{
48 // RFC 9000 Section 19.15: retire_prior_to must be <= sequence
49 if (retire_prior_to > sequence)
50 {
52 "retire_prior_to exceeds sequence number",
53 "connection_id_manager");
54 }
55
56 // Check for duplicate sequence number
57 auto it = find_by_sequence(sequence);
58 if (it != peer_cids_.end())
59 {
60 // RFC 9000: If a NEW_CONNECTION_ID frame is received with a sequence
61 // number already seen, it must be ignored if identical, or a protocol
62 // error if different.
63 if (it->cid == cid && it->stateless_reset_token == reset_token)
64 {
65 // Identical frame, ignore
66 return ok();
67 }
69 "Duplicate sequence number with different CID",
70 "connection_id_manager");
71 }
72
73 // Check active CID limit (count non-retired CIDs)
74 auto active_count = count_active_cids();
75 if (active_count >= active_cid_limit_)
76 {
78 "Active connection ID limit exceeded",
79 "connection_id_manager");
80 }
81
82 // Add the new CID
84 entry.cid = cid;
85 entry.sequence_number = sequence;
86 entry.stateless_reset_token = reset_token;
87 entry.retired = false;
88
89 peer_cids_.push_back(entry);
90
91 // Process retire_prior_to
92 if (retire_prior_to > largest_retire_prior_to_)
93 {
94 retire_cids_prior_to(retire_prior_to);
95 largest_retire_prior_to_ = retire_prior_to;
96 }
97
98 return ok();
99}
100
102{
103 if (peer_cids_.empty() || active_index_ >= peer_cids_.size())
104 {
105 return empty_cid_;
106 }
107
108 return peer_cids_[active_index_].cid;
109}
110
112{
113 // Find an unused, non-retired CID
114 for (size_t i = 0; i < peer_cids_.size(); ++i)
115 {
116 if (i != active_index_ && !peer_cids_[i].retired)
117 {
118 // Mark the old CID for retirement
119 if (active_index_ < peer_cids_.size())
120 {
121 auto old_seq = peer_cids_[active_index_].sequence_number;
122 (void)retire_peer_cid(old_seq);
123 }
124
125 active_index_ = i;
126 return ok();
127 }
128 }
129
131 "No available connection ID for rotation",
132 "connection_id_manager");
133}
134
136{
137 size_t count = 0;
138 for (size_t i = 0; i < peer_cids_.size(); ++i)
139 {
140 if (i != active_index_ && !peer_cids_[i].retired)
141 {
142 ++count;
143 }
144 }
145 return count;
146}
147
149 const std::array<uint8_t, 16>& token) const -> bool
150{
151 for (const auto& entry : peer_cids_)
152 {
153 if (entry.stateless_reset_token == token)
154 {
155 return true;
156 }
157 }
158 return false;
159}
160
161// ============================================================================
162// Retirement Management
163// ============================================================================
164
166{
167 for (auto& entry : peer_cids_)
168 {
169 if (!entry.retired && entry.sequence_number < prior_to)
170 {
171 entry.retired = true;
172
173 // Queue RETIRE_CONNECTION_ID frame
175 frame.sequence_number = entry.sequence_number;
176 pending_retire_frames_.push_back(frame);
177
178 // If this was the active CID, we need to switch
179 if (&entry == &peer_cids_[active_index_])
180 {
181 // Find a new active CID
182 for (size_t i = 0; i < peer_cids_.size(); ++i)
183 {
184 if (!peer_cids_[i].retired)
185 {
186 active_index_ = i;
187 break;
188 }
189 }
190 }
191 }
192 }
193}
194
196 -> std::vector<retire_connection_id_frame>
197{
198 return pending_retire_frames_;
199}
200
205
207{
208 auto it = find_by_sequence(sequence);
209 if (it == peer_cids_.end())
210 {
212 "Connection ID sequence not found",
213 "connection_id_manager");
214 }
215
216 if (!it->retired)
217 {
218 it->retired = true;
219
220 // Queue RETIRE_CONNECTION_ID frame
222 frame.sequence_number = sequence;
223 pending_retire_frames_.push_back(frame);
224 }
225
226 return ok();
227}
228
229// ============================================================================
230// State Queries
231// ============================================================================
232
234{
235 return std::any_of(peer_cids_.begin(), peer_cids_.end(),
236 [&cid](const connection_id_entry& entry)
237 { return entry.cid == cid && !entry.retired; });
238}
239
240// ============================================================================
241// Private Methods
242// ============================================================================
243
245 -> std::vector<connection_id_entry>::iterator
246{
247 return std::find_if(peer_cids_.begin(), peer_cids_.end(),
248 [sequence](const connection_id_entry& entry)
249 { return entry.sequence_number == sequence; });
250}
251
253{
254 return static_cast<size_t>(std::count_if(
255 peer_cids_.begin(), peer_cids_.end(),
256 [](const connection_id_entry& entry) { return !entry.retired; }));
257}
258
259} // namespace kcenon::network::protocols::quic
auto get_pending_retire_frames() -> std::vector< retire_connection_id_frame >
Get pending RETIRE_CONNECTION_ID frames to send.
auto add_peer_cid(const connection_id &cid, uint64_t sequence, uint64_t retire_prior_to, const std::array< uint8_t, 16 > &reset_token) -> VoidResult
Add a new peer connection ID from NEW_CONNECTION_ID frame.
auto retire_peer_cid(uint64_t sequence) -> VoidResult
Mark a specific CID sequence as retired.
auto has_peer_cid(const connection_id &cid) const -> bool
Check if a given CID matches any stored peer CID.
auto rotate_peer_cid() -> VoidResult
Rotate to a new peer connection ID.
auto count_active_cids() const -> size_t
Count non-retired CIDs.
std::vector< connection_id_entry > peer_cids_
Peer connection IDs.
auto is_stateless_reset_token(const std::array< uint8_t, 16 > &token) const -> bool
Check if a stateless reset token matches any stored CID.
auto get_active_peer_cid() const -> const connection_id &
Get the currently active peer connection ID.
std::vector< retire_connection_id_frame > pending_retire_frames_
Pending RETIRE_CONNECTION_ID frames to send.
static const connection_id empty_cid_
Empty CID for error cases.
void set_initial_peer_cid(const connection_id &cid)
Set the initial peer connection ID (from Initial packet)
auto find_by_sequence(uint64_t sequence) -> std::vector< connection_id_entry >::iterator
Find an entry by sequence number.
auto available_peer_cids() const -> size_t
Get the number of available (non-retired, non-active) peer CIDs.
connection_id_manager(uint64_t active_cid_limit=8)
Construct a connection ID manager.
void retire_cids_prior_to(uint64_t prior_to)
Retire CIDs with sequence numbers less than the given value.
void clear_pending_retire_frames()
Clear the pending retire frames queue.
size_t active_index_
Index of the currently active peer CID.
QUIC Connection ID (RFC 9000 Section 5.1)
std::variant< padding_frame, ping_frame, ack_frame, reset_stream_frame, stop_sending_frame, crypto_frame, new_token_frame, stream_frame, max_data_frame, max_stream_data_frame, max_streams_frame, data_blocked_frame, stream_data_blocked_frame, streams_blocked_frame, new_connection_id_frame, retire_connection_id_frame, path_challenge_frame, path_response_frame, connection_close_frame, handshake_done_frame > frame
Variant type holding any QUIC frame.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
Entry for storing a peer's connection ID with metadata.
bool retired
uint64_t sequence_number
std::array< uint8_t, 16 > stateless_reset_token
connection_id cid
RETIRE_CONNECTION_ID frame (RFC 9000 Section 19.16)
uint64_t sequence_number
Sequence number of CID to retire.