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 #include "tscore/ink_assert.h"
26 #include "tscore/ink_defs.h"
27 #include "QUICAltConnectionManager.h"
28 #include "QUICConnectionTable.h"
29 
30 static constexpr char V_DEBUG_TAG[] = "v_quic_alt_con";
31 
32 #define QUICACMVDebug(fmt, ...) Debug(V_DEBUG_TAG, "[%s] " fmt, this->_qc->cids().data(), ##__VA_ARGS__)
33 
QUICAltConnectionManager(QUICConnection * qc,QUICConnectionTable & ctable,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)34 QUICAltConnectionManager::QUICAltConnectionManager(QUICConnection *qc, QUICConnectionTable &ctable,
35                                                    const QUICConnectionId &peer_initial_cid, uint32_t instance_id,
36                                                    uint8_t local_active_cid_limit, const IpEndpoint *preferred_endpoint_ipv4,
37                                                    const IpEndpoint *preferred_endpoint_ipv6)
38   : _qc(qc), _ctable(ctable), _instance_id(instance_id), _local_active_cid_limit(local_active_cid_limit)
39 {
40   // Sequence number of the initial CID is 0
41   this->_alt_quic_connection_ids_remote.push_back({0, peer_initial_cid, {}, {true}});
42 
43   if ((preferred_endpoint_ipv4 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv4, qc->five_tuple().source())) ||
44       (preferred_endpoint_ipv6 && !ats_ip_addr_port_eq(*preferred_endpoint_ipv6, qc->five_tuple().source()))) {
45     this->_alt_quic_connection_ids_local[0] = this->_generate_next_alt_con_info();
46     // This alt cid will be advertised via Transport Parameter, so no need to advertise it via NCID frame
47     this->_alt_quic_connection_ids_local[0].advertised = true;
48 
49     IpEndpoint empty_endpoint_ipv4;
50     IpEndpoint empty_endpoint_ipv6;
51     empty_endpoint_ipv4.sa.sa_family = AF_UNSPEC;
52     empty_endpoint_ipv6.sa.sa_family = AF_UNSPEC;
53     if (preferred_endpoint_ipv4 == nullptr) {
54       preferred_endpoint_ipv4 = &empty_endpoint_ipv4;
55     }
56     if (preferred_endpoint_ipv6 == nullptr) {
57       preferred_endpoint_ipv6 = &empty_endpoint_ipv6;
58     }
59 
60     // FIXME Check nullptr dereference
61     this->_local_preferred_address =
62       new QUICPreferredAddress(*preferred_endpoint_ipv4, *preferred_endpoint_ipv6, this->_alt_quic_connection_ids_local[0].id,
63                                this->_alt_quic_connection_ids_local[0].token);
64   }
65 }
66 
~QUICAltConnectionManager()67 QUICAltConnectionManager::~QUICAltConnectionManager()
68 {
69   ats_free(this->_alt_quic_connection_ids_local);
70   delete this->_local_preferred_address;
71 }
72 
73 const QUICPreferredAddress *
preferred_address() const74 QUICAltConnectionManager::preferred_address() const
75 {
76   return this->_local_preferred_address;
77 }
78 
79 std::vector<QUICFrameType>
interests()80 QUICAltConnectionManager::interests()
81 {
82   return {QUICFrameType::NEW_CONNECTION_ID, QUICFrameType::RETIRE_CONNECTION_ID};
83 }
84 
85 QUICConnectionErrorUPtr
handle_frame(QUICEncryptionLevel level,const QUICFrame & frame)86 QUICAltConnectionManager::handle_frame(QUICEncryptionLevel level, const QUICFrame &frame)
87 {
88   QUICConnectionErrorUPtr error = nullptr;
89 
90   switch (frame.type()) {
91   case QUICFrameType::NEW_CONNECTION_ID:
92     error = this->_register_remote_connection_id(static_cast<const QUICNewConnectionIdFrame &>(frame));
93     break;
94   case QUICFrameType::RETIRE_CONNECTION_ID:
95     error = this->_retire_remote_connection_id(static_cast<const QUICRetireConnectionIdFrame &>(frame));
96     break;
97   default:
98     QUICACMVDebug("Unexpected frame type: %02x", static_cast<unsigned int>(frame.type()));
99     ink_assert(false);
100     break;
101   }
102 
103   return error;
104 }
105 
106 QUICAltConnectionManager::AltConnectionInfo
_generate_next_alt_con_info()107 QUICAltConnectionManager::_generate_next_alt_con_info()
108 {
109   QUICConnectionId conn_id;
110   conn_id.randomize();
111   QUICStatelessResetToken token(conn_id, this->_instance_id);
112   AltConnectionInfo aci = {++this->_alt_quic_connection_id_seq_num, conn_id, token, {false}};
113 
114   if (this->_qc->direction() == NET_VCONNECTION_IN) {
115     this->_ctable.insert(conn_id, this->_qc);
116   }
117 
118   if (is_debug_tag_set(V_DEBUG_TAG)) {
119     char new_cid_str[QUICConnectionId::MAX_HEX_STR_LENGTH];
120     conn_id.hex(new_cid_str, QUICConnectionId::MAX_HEX_STR_LENGTH);
121     QUICACMVDebug("alt-cid=%s", new_cid_str);
122   }
123 
124   return aci;
125 }
126 
127 void
_init_alt_connection_ids()128 QUICAltConnectionManager::_init_alt_connection_ids()
129 {
130   for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
131     this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info();
132   }
133   this->_need_advertise = true;
134 }
135 
136 bool
_update_alt_connection_id(uint64_t chosen_seq_num)137 QUICAltConnectionManager::_update_alt_connection_id(uint64_t chosen_seq_num)
138 {
139   // Seq 0 is special so it's not in the array
140   if (chosen_seq_num == 0) {
141     return true;
142   }
143 
144   // Seq 1 is for Preferred Address
145   if (chosen_seq_num == 1) {
146     return true;
147   }
148 
149   for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
150     if (this->_alt_quic_connection_ids_local[i].seq_num == chosen_seq_num) {
151       this->_alt_quic_connection_ids_local[i] = this->_generate_next_alt_con_info();
152       this->_need_advertise                   = true;
153       return true;
154     }
155   }
156 
157   return false;
158 }
159 
160 QUICConnectionErrorUPtr
_register_remote_connection_id(const QUICNewConnectionIdFrame & frame)161 QUICAltConnectionManager::_register_remote_connection_id(const QUICNewConnectionIdFrame &frame)
162 {
163   QUICConnectionErrorUPtr error = nullptr;
164 
165   if (frame.connection_id() == QUICConnectionId::ZERO()) {
166     error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION, "received zero-length cid",
167                                                   QUICFrameType::NEW_CONNECTION_ID);
168   } else {
169     int unused = std::count_if(this->_alt_quic_connection_ids_remote.begin(), this->_alt_quic_connection_ids_remote.end(),
170                                [](AltConnectionInfo info) { return info.used == false && info.seq_num != 1; });
171     if (unused > this->_local_active_cid_limit) {
172       error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION, "received too many alt CIDs",
173                                                     QUICFrameType::NEW_CONNECTION_ID);
174     } else {
175       this->_alt_quic_connection_ids_remote.push_back(
176         {frame.sequence(), frame.connection_id(), frame.stateless_reset_token(), {false}});
177     }
178   }
179 
180   return error;
181 }
182 
183 QUICConnectionErrorUPtr
_retire_remote_connection_id(const QUICRetireConnectionIdFrame & frame)184 QUICAltConnectionManager::_retire_remote_connection_id(const QUICRetireConnectionIdFrame &frame)
185 {
186   QUICConnectionErrorUPtr error = nullptr;
187 
188   if (!this->_update_alt_connection_id(frame.seq_num())) {
189     error = std::make_unique<QUICConnectionError>(QUICTransErrorCode::PROTOCOL_VIOLATION, "received unused sequence number",
190                                                   QUICFrameType::RETIRE_CONNECTION_ID);
191   }
192   return error;
193 }
194 
195 bool
is_ready_to_migrate() const196 QUICAltConnectionManager::is_ready_to_migrate() const
197 {
198   if (this->_alt_quic_connection_ids_remote.empty()) {
199     return false;
200   }
201 
202   for (auto &info : this->_alt_quic_connection_ids_remote) {
203     if (!info.used) {
204       return true;
205     }
206   }
207   return false;
208 }
209 
210 QUICConnectionId
migrate_to_alt_cid()211 QUICAltConnectionManager::migrate_to_alt_cid()
212 {
213   for (auto &info : this->_alt_quic_connection_ids_remote) {
214     if (info.used) {
215       continue;
216     }
217     info.used = true;
218     return info.id;
219   }
220 
221   ink_assert(!"Could not find CID available");
222   return QUICConnectionId::ZERO();
223 }
224 
225 bool
migrate_to(const QUICConnectionId & cid,QUICStatelessResetToken & new_reset_token)226 QUICAltConnectionManager::migrate_to(const QUICConnectionId &cid, QUICStatelessResetToken &new_reset_token)
227 {
228   if (this->_local_preferred_address) {
229     if (cid == this->_local_preferred_address->cid()) {
230       new_reset_token = this->_local_preferred_address->token();
231       return true;
232     }
233   }
234 
235   for (int i = 0; i < this->_remote_active_cid_limit; ++i) {
236     AltConnectionInfo &info = this->_alt_quic_connection_ids_local[i];
237     if (info.id == cid) {
238       // Migrate connection
239       new_reset_token = info.token;
240       return true;
241     }
242   }
243   return false;
244 }
245 
246 void
drop_cid(const QUICConnectionId & cid)247 QUICAltConnectionManager::drop_cid(const QUICConnectionId &cid)
248 {
249   for (auto it = this->_alt_quic_connection_ids_remote.begin(); it != this->_alt_quic_connection_ids_remote.end(); ++it) {
250     if (it->id == cid) {
251       QUICACMVDebug("Dropping advertized CID %" PRIx32 " seq# %" PRIu64, it->id.h32(), it->seq_num);
252       this->_retired_seq_nums.push(it->seq_num);
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