1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 #include <algorithm>
25
26 #include "tscore/ink_assert.h"
27 #include "tscore/ink_defs.h"
28 #include "QUICAltConnectionManager.h"
29 #include "QUICConnectionTable.h"
30 #include "QUICResetTokenTable.h"
31
32 static constexpr char V_DEBUG_TAG[] = "v_quic_alt_con";
33
34 #define QUICACMVDebug(fmt, ...) Debug(V_DEBUG_TAG, "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__)
35
QUICAltConnectionManager(QUICConnection * qc,QUICConnectionTable & ctable,QUICResetTokenTable & rtable,const QUICConnectionId & peer_initial_cid,uint32_t instance_id,uint8_t local_active_cid_limit,const IpEndpoint * preferred_endpoint_ipv4,const IpEndpoint * preferred_endpoint_ipv6)36 QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable, QUICResetTokenTable &rtable,
37 const QUICConnectionId &peer_initial_cid, uint32_t instance_id,
38 uint8_t local_active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4,
39 const IpEndpoint *preferred_endpoint_ipv6)
40 : _qc(qc), _ctable(ctable), _rtable(rtable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit)
41 {
42 // Sequence number of the initial CID is 0
43 this->_alt_quic_connection_ids_remote.push_back({0, peer_initial_cid, {}, {true}});
44 this->_alt_quic_connection_ids_local[0].seq_num = 0;
45 this->_alt_quic_connection_ids_local[0].advertised = true;
46
47 if ((preferred_endpoint_ipv4 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv4, qc->five_tuple().source())) ||
48 (preferred_endpoint_ipv6 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv6, qc->five_tuple().source()))) {
49 this->_alt_quic_connection_ids_local[1] = this->_generate_next_alt_con_info();
50 // This alt cid will be advertised via Transport Parameter, so no need to advertise it via NCID frame
51 this->_alt_quic_connection_ids_local[1].advertised = true;
52
53 IpEndpoint empty_endpoint_ipv4;
54 IpEndpoint empty_endpoint_ipv6;
55 empty_endpoint_ipv4.sa.sa_family = AF_UNSPEC;
56 empty_endpoint_ipv6.sa.sa_family = AF_UNSPEC;
57 if (preferred_endpoint_ipv4 == nullptr) {
58 preferred_endpoint_ipv4 = &empty_endpoint_ipv4;
59 }
60 if (preferred_endpoint_ipv6 == nullptr) {
61 preferred_endpoint_ipv6 = &empty_endpoint_ipv6;
62 }
63
64 // FIXME Check nullptr dereference
65 this->_local_preferred_address =
66 new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[1].id,
67 this->_alt_quic_connection_ids_local[1].token);
68 }
69 }
70
~QUICAltConnectionManager()71 QUICAltConnectionManager::~QUICAltConnectionManager()
72 {
73 ats_free(this->_alt_quic_connection_ids_local);
74 delete this->_local_preferred_address;
75 }
76
77 const QUICPreferredAddress *
preferred_address() const78 QUICAltConnectionManager::preferred_address() const
79 {
80 return this->_local_preferred_address;
81 }
82
83 std::vector<QUICFrameType>
interests()84 QUICAltConnectionManager::interests()
85 {
86 return {QUICFrameType::NEW_CONNECTION_ID, QUICFrameType::RETIRE_CONNECTION_ID};
87 }
88
89 QUICConnectionErrorUPtr
handle_frame(QUICEncryptionLevel level,const QUICFrame & frame)90 QUICAltConnectionManager::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame)
91 {
92 QUICConnectionErrorUPtr error = nullptr;
93
94 switch (frame.type()) {
95 case QUICFrameType::NEW_CONNECTION_ID:
96 error = this->_register_remote_connection_id(static_cast<const QUICNewConnectionIdFrame &>(frame));
97 break;
98 case QUICFrameType::RETIRE_CONNECTION_ID:
99 error = this->_retire_remote_connection_id(static_cast<const QUICRetireConnectionIdFrame &>(frame));
100 break;
101 default:
102 QUICACMVDebug("Unexpected frame type: %02x", static_cast<unsigned int>(frame.type()));
103 ink_assert(false);
104 break;
105 }
106
107 return error;
108 }
109
110 QUICAltConnectionManager::AltConnectionInfo
_generate_next_alt_con_info()111 QUICAltConnectionManager::_generate_next_alt_con_info()
112 {
113 QUICConnectionId conn_id;
114 conn_id.randomize();
115 QUICStatelessResetToken token(conn_id, this->_instance_id);
116 AltConnectionInfo aci = {++this->_alt_quic_connection_id_seq_num, conn_id, token, {false}};
117
118 if (this->_qc->direction() == NET_VCONNECTION_IN) {
119 this->_ctable.insert(conn_id, this->_qc);
120 }
121
122 if (is_debug_tag_set(V_DEBUG_TAG)) {
123 QUICACMVDebug("alt-cid=%s", conn_id.hex().c_str());
124 }
125
126 return aci;
127 }
128
129 void
_init_alt_connection_ids()130 QUICAltConnectionManager::_init_alt_connection_ids()
131 {
132 for (int i = this->_alt_quic_connection_id_seq_num + 1; i < this->_remote_active_cid_limit; ++i) {
133 this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info();
134 }
135 this->_need_advertise = true;
136 }
137
138 void
_update_alt_connection_id(uint64_t chosen_seq_num)139 QUICAltConnectionManager::_update_alt_connection_id(uint64_t chosen_seq_num)
140 {
141 for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
142 if (this->_alt_quic_connection_ids_local[i].seq_num == chosen_seq_num) {
143 this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info();
144 this->_need_advertise = true;
145 }
146 }
147 }
148
149 QUICConnectionErrorUPtr
_register_remote_connection_id(const QUICNewConnectionIdFrame & frame)150 QUICAltConnectionManager::_register_remote_connection_id(const QUICNewConnectionIdFrame &frame)
151 {
152 QUICConnectionErrorUPtr error = nullptr;
153
154 if (frame.connection_id() == QUICConnectionId::ZERO()) {
155 error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION, "received zero-length cid",
156 QUICFrameType::NEW_CONNECTION_ID);
157 } else {
158 int unused = 0;
159 for (auto &&x : this->_alt_quic_connection_ids_remote) {
160 if (x.seq_num == frame.sequence()) {
161 return error;
162 }
163 if (x.used == false && x.seq_num != 1) {
164 ++unused;
165 }
166 }
167 if (unused > this->_local_active_cid_limit) {
168 error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::CONNECTION_ID_LIMIT_ERROR, "received too many alt CIDs",
169 QUICFrameType::NEW_CONNECTION_ID);
170 } else {
171 this->_alt_quic_connection_ids_remote.push_back(
172 {frame.sequence(), frame.connection_id(), frame.stateless_reset_token(), {false}});
173 }
174 }
175
176 return error;
177 }
178
179 QUICConnectionErrorUPtr
_retire_remote_connection_id(const QUICRetireConnectionIdFrame & frame)180 QUICAltConnectionManager::_retire_remote_connection_id(const QUICRetireConnectionIdFrame &frame)
181 {
182 QUICConnectionErrorUPtr error = nullptr;
183
184 if (frame.seq_num() > this->_alt_quic_connection_id_seq_num) {
185 error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION, "received unused sequence number",
186 QUICFrameType::RETIRE_CONNECTION_ID);
187 } else {
188 this->_update_alt_connection_id(frame.seq_num());
189 }
190 return error;
191 }
192
193 bool
is_ready_to_migrate() const194 QUICAltConnectionManager::is_ready_to_migrate() const
195 {
196 if (this->_alt_quic_connection_ids_remote.empty()) {
197 return false;
198 }
199
200 for (auto &info : this->_alt_quic_connection_ids_remote) {
201 if (!info.used) {
202 return true;
203 }
204 }
205 return false;
206 }
207
208 QUICConnectionId
migrate_to_alt_cid()209 QUICAltConnectionManager::migrate_to_alt_cid()
210 {
211 for (auto &info : this->_alt_quic_connection_ids_remote) {
212 if (info.used) {
213 continue;
214 }
215 info.used = true;
216 this->_rtable.insert(info.token, this->_qc);
217 return info.id;
218 }
219
220 ink_assert(!"Could not find CID available");
221 return QUICConnectionId::ZERO();
222 }
223
224 bool
migrate_to(const QUICConnectionId & cid,QUICStatelessResetToken & new_reset_token)225 QUICAltConnectionManager::migrate_to(const QUICConnectionId &cid, QUICStatelessResetToken &new_reset_token)
226 {
227 if (this->_local_preferred_address) {
228 if (cid == this->_local_preferred_address->cid()) {
229 new_reset_token = this->_local_preferred_address->token();
230 return true;
231 }
232 }
233
234 for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
235 AltConnectionInfo &info = this->_alt_quic_connection_ids_local[i];
236 if (info.id == cid) {
237 // Migrate connection
238 new_reset_token = info.token;
239 return true;
240 }
241 }
242 return false;
243 }
244
245 void
drop_cid(const QUICConnectionId & cid)246 QUICAltConnectionManager::drop_cid(const QUICConnectionId &cid)
247 {
248 for (auto it = this->_alt_quic_connection_ids_remote.begin(); it != this->_alt_quic_connection_ids_remote.end(); ++it) {
249 if (it->id == cid) {
250 QUICACMVDebug("Dropping advertised CID %" PRIx32 " seq# %" PRIu64, it->id.h32(), it->seq_num);
251 this->_retired_seq_nums.push(it->seq_num);
252 this->_rtable.erase(it->token);
253 this->_alt_quic_connection_ids_remote.erase(it);
254 return;
255 }
256 }
257 }
258
259 void
invalidate_alt_connections()260 QUICAltConnectionManager::invalidate_alt_connections()
261 {
262 int n = this->_remote_active_cid_limit + ((this->_local_preferred_address == nullptr) ? 1 : 0);
263
264 for (int i = 0; i < n; ++i) {
265 this->_ctable.erase(this->_alt_quic_connection_ids_local[i].id, this->_qc);
266 }
267 }
268
269 void
set_remote_preferred_address(const QUICPreferredAddress & preferred_address)270 QUICAltConnectionManager::set_remote_preferred_address(const QUICPreferredAddress &preferred_address)
271 {
272 ink_assert(preferred_address.is_available());
273
274 // Sequence number of the preferred address is 1 if available
275 this->_alt_quic_connection_ids_remote.push_back({1, preferred_address.cid(), preferred_address.token(), {false}});
276 }
277
278 void
set_remote_active_cid_limit(uint8_t active_cid_limit)279 QUICAltConnectionManager::set_remote_active_cid_limit(uint8_t active_cid_limit)
280 {
281 this->_remote_active_cid_limit =
282 std::min(static_cast<unsigned int>(active_cid_limit), countof(this->_alt_quic_connection_ids_local));
283 this->_init_alt_connection_ids();
284 }
285
286 bool
will_generate_frame(QUICEncryptionLevel level,size_t current_packet_size,bool ack_eliciting,uint32_t seq_num)287 QUICAltConnectionManager::will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting,
288 uint32_t seq_num)
289 {
290 if (!this->_is_level_matched(level)) {
291 return false;
292 }
293
294 return this->_need_advertise || !this->_retired_seq_nums.empty();
295 }
296
297 /**
298 * @param connection_credit This is not used. Because NEW_CONNECTION_ID frame is not flow-controlled
299 */
300 QUICFrame *
generate_frame(uint8_t * buf,QUICEncryptionLevel level,uint64_t,uint16_t maximum_frame_size,size_t current_packet_size,uint32_t seq_num)301 QUICAltConnectionManager::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t /* connection_credit */,
302 uint16_t maximum_frame_size, size_t current_packet_size, uint32_t seq_num)
303 {
304 QUICFrame *frame = nullptr;
305 if (!this->_is_level_matched(level)) {
306 return frame;
307 }
308
309 if (this->_need_advertise) {
310 for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
311 if (!this->_alt_quic_connection_ids_local[i].advertised) {
312 // FIXME Should send a sequence number for retire_prior_to. Sending 0 for now.
313 frame = QUICFrameFactory::create_new_connection_id_frame(buf, this->_alt_quic_connection_ids_local[i].seq_num, 0,
314 this->_alt_quic_connection_ids_local[i].id,
315 this->_alt_quic_connection_ids_local[i].token);
316
317 if (frame && frame->size() > maximum_frame_size) {
318 // Cancel generating frame
319 frame = nullptr;
320 } else if (frame != nullptr) {
321 this->_records_new_connection_id_frame(level, static_cast<const QUICNewConnectionIdFrame &>(*frame));
322 this->_alt_quic_connection_ids_local[i].advertised = true;
323 }
324
325 return frame;
326 }
327 }
328 this->_need_advertise = false;
329 }
330
331 if (!this->_retired_seq_nums.empty()) {
332 auto s = this->_retired_seq_nums.front();
333 frame = QUICFrameFactory::create_retire_connection_id_frame(buf, s);
334 this->_records_retire_connection_id_frame(level, static_cast<const QUICRetireConnectionIdFrame &>(*frame));
335 this->_retired_seq_nums.pop();
336 return frame;
337 }
338
339 return frame;
340 }
341
342 void
_records_new_connection_id_frame(QUICEncryptionLevel level,const QUICNewConnectionIdFrame & frame)343 QUICAltConnectionManager::_records_new_connection_id_frame(QUICEncryptionLevel level, const QUICNewConnectionIdFrame &frame)
344 {
345 QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
346 info->type = frame.type();
347 info->level = level;
348
349 AltConnectionInfo *frame_info = reinterpret_cast<AltConnectionInfo *>(info->data);
350 frame_info->seq_num = frame.sequence();
351 frame_info->token = frame.stateless_reset_token();
352 frame_info->id = frame.connection_id();
353 this->_records_frame(frame.id(), std::move(info));
354 }
355
356 void
_records_retire_connection_id_frame(QUICEncryptionLevel level,const QUICRetireConnectionIdFrame & frame)357 QUICAltConnectionManager::_records_retire_connection_id_frame(QUICEncryptionLevel level, const QUICRetireConnectionIdFrame &frame)
358 {
359 QUICFrameInformationUPtr info = QUICFrameInformationUPtr(quicFrameInformationAllocator.alloc());
360 info->type = frame.type();
361 info->level = level;
362 *reinterpret_cast<int64_t *>(info->data) = frame.seq_num();
363 this->_records_frame(frame.id(), std::move(info));
364 }
365
366 void
_on_frame_lost(QUICFrameInformationUPtr & info)367 QUICAltConnectionManager::_on_frame_lost(QUICFrameInformationUPtr &info)
368 {
369 switch (info->type) {
370 case QUICFrameType::NEW_CONNECTION_ID: {
371 AltConnectionInfo *frame_info = reinterpret_cast<AltConnectionInfo *>(info->data);
372 for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
373 if (this->_alt_quic_connection_ids_local[i].seq_num == frame_info->seq_num) {
374 ink_assert(this->_alt_quic_connection_ids_local[i].advertised);
375 this->_alt_quic_connection_ids_local[i].advertised = false;
376 this->_need_advertise = true;
377 return;
378 }
379 }
380 break;
381 }
382 case QUICFrameType::RETIRE_CONNECTION_ID: {
383 this->_retired_seq_nums.push(*reinterpret_cast<int64_t *>(info->data));
384 break;
385 }
386 default:
387 ink_assert(0);
388 }
389 }
390