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