| /** @file |
| * |
| * A brief file description |
| * |
| * @section license License |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "QUICPacket.h" |
| |
| #include <algorithm> |
| |
| #include <tscore/ink_assert.h> |
| #include <tscore/Diags.h> |
| |
| #include "QUICIntUtil.h" |
| #include "QUICDebugNames.h" |
| #include "QUICRetryIntegrityTag.h" |
| |
| using namespace std::literals; |
| static constexpr uint64_t aead_tag_len = 16; |
| static constexpr int LONG_HDR_OFFSET_CONNECTION_ID = 6; |
| static constexpr int LONG_HDR_OFFSET_VERSION = 1; |
| |
| #define QUICDebug(dcid, scid, fmt, ...) \ |
| Debug(tag.data(), "[%08" PRIx32 "-%08" PRIx32 "] " fmt, dcid.h32(), scid.h32(), ##__VA_ARGS__); |
| |
| // |
| // QUICPacket |
| // |
| QUICPacket::QUICPacket() {} |
| |
| QUICPacket::QUICPacket(bool ack_eliciting, bool probing) : _is_ack_eliciting(ack_eliciting), _is_probing_packet(probing) {} |
| |
| QUICPacket::~QUICPacket() {} |
| |
| QUICKeyPhase |
| QUICPacket::key_phase() const |
| { |
| ink_assert(!"This function should not be called"); |
| return QUICKeyPhase::INITIAL; |
| } |
| |
| bool |
| QUICPacket::is_ack_eliciting() const |
| { |
| return this->_is_ack_eliciting; |
| } |
| |
| bool |
| QUICPacket::is_probing_packet() const |
| { |
| return this->_is_probing_packet; |
| } |
| |
| uint16_t |
| QUICPacket::header_size() const |
| { |
| uint16_t size = 0; |
| |
| for (auto b = this->header_block(); b; b = b->next) { |
| size += b->size(); |
| } |
| |
| return size; |
| } |
| |
| uint16_t |
| QUICPacket::payload_length() const |
| { |
| uint16_t size = 0; |
| |
| for (auto b = this->payload_block(); b; b = b->next) { |
| size += b->size(); |
| } |
| |
| return size; |
| } |
| |
| uint16_t |
| QUICPacket::size() const |
| { |
| return this->header_size() + this->payload_length(); |
| } |
| |
| void |
| QUICPacket::store(uint8_t *buf, size_t *len) const |
| { |
| size_t written = 0; |
| Ptr<IOBufferBlock> block; |
| |
| block = this->header_block(); |
| while (block) { |
| memcpy(buf + written, block->start(), block->size()); |
| written += block->size(); |
| block = block->next; |
| } |
| |
| block = this->payload_block(); |
| while (block) { |
| memcpy(buf + written, block->start(), block->size()); |
| written += block->size(); |
| block = block->next; |
| } |
| |
| *len = written; |
| } |
| |
| uint8_t |
| QUICPacket::calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base) |
| { |
| uint64_t d = (num - base) * 2; |
| uint8_t len = 0; |
| |
| if (d > 0xFFFFFF) { |
| len = 4; |
| } else if (d > 0xFFFF) { |
| len = 3; |
| } else if (d > 0xFF) { |
| len = 2; |
| } else { |
| len = 1; |
| } |
| |
| return len; |
| } |
| |
| bool |
| QUICPacket::encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len) |
| { |
| uint64_t mask = 0; |
| switch (len) { |
| case 1: |
| mask = 0xFF; |
| break; |
| case 2: |
| mask = 0xFFFF; |
| break; |
| case 3: |
| mask = 0xFFFFFF; |
| break; |
| case 4: |
| mask = 0xFFFFFFFF; |
| break; |
| default: |
| ink_assert(!"len must be 1, 2, or 4"); |
| return false; |
| } |
| dst = src & mask; |
| |
| return true; |
| } |
| |
| bool |
| QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len, QUICPacketNumber largest_acked) |
| { |
| ink_assert(len == 1 || len == 2 || len == 3 || len == 4); |
| |
| uint64_t maximum_diff = 0; |
| switch (len) { |
| case 1: |
| maximum_diff = 0x100; |
| break; |
| case 2: |
| maximum_diff = 0x10000; |
| break; |
| case 3: |
| maximum_diff = 0x1000000; |
| break; |
| case 4: |
| maximum_diff = 0x100000000; |
| break; |
| default: |
| ink_assert(!"len must be 1, 2, 3 or 4"); |
| } |
| QUICPacketNumber base = largest_acked & (~(maximum_diff - 1)); |
| QUICPacketNumber candidate1 = base + src; |
| QUICPacketNumber candidate2 = base + src + maximum_diff; |
| QUICPacketNumber expected = largest_acked + 1; |
| |
| if (((candidate1 > expected) ? (candidate1 - expected) : (expected - candidate1)) < |
| ((candidate2 > expected) ? (candidate2 - expected) : (expected - candidate2))) { |
| dst = candidate1; |
| } else { |
| dst = candidate2; |
| } |
| |
| return true; |
| } |
| |
| // |
| // QUICPacketR |
| // |
| |
| QUICPacketR::QUICPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to) : _udp_con(udp_con), _from(from), _to(to) {} |
| |
| UDPConnection * |
| QUICPacketR::udp_con() const |
| { |
| return this->_udp_con; |
| } |
| |
| const IpEndpoint & |
| QUICPacketR::from() const |
| { |
| return this->_from; |
| } |
| |
| const IpEndpoint & |
| QUICPacketR::to() const |
| { |
| return this->_to; |
| } |
| |
| bool |
| QUICPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) |
| { |
| if (packet_len < 1) { |
| return false; |
| } |
| |
| if (QUICInvariants::is_long_header(packet)) { |
| return QUICLongHeaderPacketR::type(type, packet, packet_len); |
| } else { |
| type = QUICPacketType::PROTECTED; |
| return true; |
| } |
| } |
| |
| bool |
| QUICPacketR::read_essential_info(Ptr<IOBufferBlock> block, QUICPacketType &type, QUICVersion &version, QUICConnectionId &dcid, |
| QUICConnectionId &scid, QUICPacketNumber &packet_number, QUICPacketNumber base_packet_number, |
| QUICKeyPhase &key_phase) |
| { |
| uint8_t tmp[47 + 64]; |
| IOBufferReader reader; |
| reader.block = block; |
| int64_t len = std::min(static_cast<int64_t>(sizeof(tmp)), reader.read_avail()); |
| |
| if (len < 10) { |
| return false; |
| } |
| |
| reader.memcpy(tmp, 1, 0); |
| if (QUICInvariants::is_long_header(tmp)) { |
| reader.memcpy(tmp, len, 0); |
| type = static_cast<QUICPacketType>((0x30 & tmp[0]) >> 4); |
| QUICInvariants::version(version, tmp, len); |
| if (version == 0x00) { |
| type = QUICPacketType::VERSION_NEGOTIATION; |
| } |
| if (!QUICInvariants::dcid(dcid, tmp, len) || !QUICInvariants::scid(scid, tmp, len)) { |
| return false; |
| } |
| if (type != QUICPacketType::RETRY) { |
| int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); |
| size_t length_offset = 7 + dcid.length() + scid.length(); |
| if (length_offset >= static_cast<uint64_t>(len)) { |
| return false; |
| } |
| uint64_t value; |
| size_t field_len; |
| QUICVariableInt::decode(value, field_len, tmp + length_offset); |
| switch (type) { |
| case QUICPacketType::INITIAL: |
| length_offset += field_len + value; |
| if (length_offset >= static_cast<uint64_t>(len)) { |
| return false; |
| } |
| QUICVariableInt::decode(value, field_len, tmp + length_offset); |
| if (length_offset + field_len >= static_cast<uint64_t>(len)) { |
| return false; |
| } |
| if (length_offset + field_len + packet_number_len > static_cast<uint64_t>(len)) { |
| return false; |
| } |
| packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); |
| key_phase = QUICKeyPhase::INITIAL; |
| break; |
| case QUICPacketType::ZERO_RTT_PROTECTED: |
| if (length_offset + field_len + packet_number_len >= static_cast<uint64_t>(len)) { |
| return false; |
| } |
| packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); |
| key_phase = QUICKeyPhase::ZERO_RTT; |
| break; |
| case QUICPacketType::HANDSHAKE: |
| if (length_offset + field_len + packet_number_len >= static_cast<uint64_t>(len)) { |
| return false; |
| } |
| packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + length_offset + field_len, packet_number_len); |
| key_phase = QUICKeyPhase::INITIAL; |
| break; |
| case QUICPacketType::VERSION_NEGOTIATION: |
| break; |
| default: |
| break; |
| } |
| } else { |
| packet_number = 0; |
| } |
| } else { |
| len = std::min(static_cast<int64_t>(25), len); |
| reader.memcpy(tmp, len, 0); |
| type = QUICPacketType::PROTECTED; |
| QUICInvariants::dcid(dcid, tmp, len); |
| int packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(tmp); |
| if (tmp[0] & 0x04) { |
| key_phase = QUICKeyPhase::PHASE_1; |
| } else { |
| key_phase = QUICKeyPhase::PHASE_0; |
| } |
| packet_number = QUICTypeUtil::read_QUICPacketNumber(tmp + 1 + dcid.length(), packet_number_len); |
| } |
| return true; |
| } |
| |
| // |
| // QUICLongHeaderPacket |
| // |
| QUICLongHeaderPacket::QUICLongHeaderPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, bool ack_eliciting, |
| bool probing, bool crypto) |
| : QUICPacket(ack_eliciting, probing), _version(version), _dcid(dcid), _scid(scid), _is_crypto_packet(crypto) |
| { |
| } |
| |
| QUICConnectionId |
| QUICLongHeaderPacket::destination_cid() const |
| { |
| return this->_dcid; |
| } |
| |
| QUICConnectionId |
| QUICLongHeaderPacket::source_cid() const |
| { |
| return this->_scid; |
| } |
| |
| uint16_t |
| QUICLongHeaderPacket::payload_length() const |
| { |
| return this->_payload_length; |
| } |
| |
| QUICVersion |
| QUICLongHeaderPacket::version() const |
| { |
| return this->_version; |
| } |
| |
| size_t |
| QUICLongHeaderPacket::_write_common_header(uint8_t *buf) const |
| { |
| size_t n; |
| size_t len = 0; |
| |
| buf[0] = 0xC0; |
| buf[0] += static_cast<uint8_t>(this->type()) << 4; |
| len += 1; |
| |
| QUICTypeUtil::write_QUICVersion(this->_version, buf + len, &n); |
| len += n; |
| |
| // DICD |
| if (this->_dcid != QUICConnectionId::ZERO()) { |
| // Len |
| buf[len] = this->_dcid.length(); |
| len += 1; |
| |
| // ID |
| QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + len, &n); |
| len += n; |
| } else { |
| buf[len] = 0; |
| len += 1; |
| } |
| |
| // SCID |
| if (this->_scid != QUICConnectionId::ZERO()) { |
| // Len |
| buf[len] = this->_scid.length(); |
| len += 1; |
| |
| // ID |
| QUICTypeUtil::write_QUICConnectionId(this->_scid, buf + len, &n); |
| len += n; |
| } else { |
| buf[len] = 0; |
| len += 1; |
| } |
| |
| return len; |
| } |
| |
| bool |
| QUICLongHeaderPacket::is_crypto_packet() const |
| { |
| return this->_is_crypto_packet; |
| } |
| |
| // |
| // QUICLongHeaderPacketR |
| // |
| QUICLongHeaderPacketR::QUICLongHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks) |
| : QUICPacketR(udp_con, from, to) |
| { |
| IOBufferReader reader; |
| uint8_t data[47]; |
| |
| reader.block = blocks; |
| int64_t data_len = reader.read(data, sizeof(data)); |
| |
| QUICLongHeaderPacketR::version(this->_version, data, data_len); |
| } |
| |
| QUICVersion |
| QUICLongHeaderPacketR::version() const |
| { |
| return this->_version; |
| } |
| |
| QUICConnectionId |
| QUICLongHeaderPacketR::source_cid() const |
| { |
| return this->_scid; |
| } |
| |
| QUICConnectionId |
| QUICLongHeaderPacketR::destination_cid() const |
| { |
| return this->_dcid; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::type(QUICPacketType &type, const uint8_t *packet, size_t packet_len) |
| { |
| if (packet_len < 1) { |
| return false; |
| } |
| |
| QUICVersion version; |
| if (QUICLongHeaderPacketR::version(version, packet, packet_len) && version == 0x00) { |
| type = QUICPacketType::VERSION_NEGOTIATION; |
| } else { |
| uint8_t raw_type = (packet[0] & 0x30) >> 4; |
| type = static_cast<QUICPacketType>(raw_type); |
| } |
| return true; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::version(QUICVersion &version, const uint8_t *packet, size_t packet_len) |
| { |
| if (packet_len < 5) { |
| return false; |
| } |
| |
| version = QUICTypeUtil::read_QUICVersion(packet + LONG_HDR_OFFSET_VERSION); |
| return true; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::key_phase(QUICKeyPhase &phase, const uint8_t *packet, size_t packet_len) |
| { |
| QUICPacketType type = QUICPacketType::UNINITIALIZED; |
| QUICLongHeaderPacketR::type(type, packet, packet_len); |
| phase = QUICTypeUtil::key_phase(type); |
| return true; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::length(size_t &length, uint8_t &length_field_len, size_t &length_field_offset, const uint8_t *packet, |
| size_t packet_len) |
| { |
| // FIXME This is not great because each packet types have different formats. |
| // We should remove this function and have length() on each packet type classes instead. |
| |
| uint8_t dcil; |
| if (!QUICInvariants::dcil(dcil, packet, packet_len)) { |
| return false; |
| } |
| |
| uint8_t scil; |
| if (!QUICInvariants::scil(scil, packet, packet_len)) { |
| return false; |
| } |
| |
| length_field_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; |
| |
| QUICPacketType type = QUICPacketType::UNINITIALIZED; |
| QUICLongHeaderPacketR::type(type, packet, packet_len); |
| if (type == QUICPacketType::INITIAL) { |
| // Token Length (i) + Token (*) (for INITIAL packet) |
| size_t token_length = 0; |
| uint8_t token_length_field_len = 0; |
| size_t token_length_field_offset = 0; |
| if (!QUICInitialPacketR::token_length(token_length, token_length_field_len, token_length_field_offset, packet, packet_len)) { |
| return false; |
| } |
| length_field_offset += token_length_field_len + token_length; |
| } |
| |
| // Length (i) |
| if (length_field_offset >= packet_len) { |
| return false; |
| } |
| |
| length_field_len = QUICVariableInt::size(packet + length_field_offset); |
| length = QUICIntUtil::read_QUICVariableInt(packet + length_field_offset, packet_len - length_field_offset); |
| |
| return true; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::packet_length(size_t &packet_len, const uint8_t *buf, size_t buf_len) |
| { |
| size_t length; |
| uint8_t length_field_len; |
| size_t length_field_offset; |
| |
| if (!QUICLongHeaderPacketR::length(length, length_field_len, length_field_offset, buf, buf_len)) { |
| return false; |
| } |
| packet_len = length + length_field_offset + length_field_len; |
| |
| if (packet_len > buf_len) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool |
| QUICLongHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len) |
| { |
| size_t dummy; |
| uint8_t length_field_len; |
| size_t length_field_offset; |
| |
| if (!QUICLongHeaderPacketR::length(dummy, length_field_len, length_field_offset, packet, packet_len)) { |
| return false; |
| } |
| pn_offset = length_field_offset + length_field_len; |
| |
| if (pn_offset >= packet_len) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // |
| // QUICShortHeaderPacket |
| // |
| QUICShortHeaderPacket::QUICShortHeaderPacket(QUICConnectionId dcid, QUICPacketNumber packet_number, |
| QUICPacketNumber base_packet_number, QUICKeyPhase key_phase, bool ack_eliciting, |
| bool probing) |
| : QUICPacket(ack_eliciting, probing), _dcid(dcid), _packet_number(packet_number), _key_phase(key_phase) |
| { |
| this->_packet_number_len = QUICPacket::calc_packet_number_len(packet_number, base_packet_number); |
| } |
| |
| QUICPacketType |
| QUICShortHeaderPacket::type() const |
| { |
| return QUICPacketType::PROTECTED; |
| } |
| |
| QUICKeyPhase |
| QUICShortHeaderPacket::key_phase() const |
| { |
| return this->_key_phase; |
| } |
| |
| QUICConnectionId |
| QUICShortHeaderPacket::destination_cid() const |
| { |
| return this->_dcid; |
| } |
| |
| QUICPacketNumber |
| QUICShortHeaderPacket::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| uint16_t |
| QUICShortHeaderPacket::payload_length() const |
| { |
| return this->_payload_length; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICShortHeaderPacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(1 + QUICConnectionId::MAX_LENGTH + 4, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| size_t n; |
| buf[0] = 0x40; |
| |
| // Type |
| buf[0] = 0x40; |
| |
| // TODO Spin Bit |
| |
| // KeyPhase |
| if (this->_key_phase == QUICKeyPhase::PHASE_1) { |
| buf[0] |= 0x04; |
| } |
| |
| written_len += 1; |
| |
| // Destination Connection ID |
| if (this->_dcid != QUICConnectionId::ZERO()) { |
| QUICTypeUtil::write_QUICConnectionId(this->_dcid, buf + written_len, &n); |
| written_len += n; |
| } |
| |
| // Packet Number |
| QUICPacketNumber dst = 0; |
| size_t dst_len = this->_packet_number_len; |
| QUICPacket::encode_packet_number(dst, this->_packet_number, dst_len); |
| QUICTypeUtil::write_QUICPacketNumber(dst, dst_len, buf + written_len, &n); |
| written_len += n; |
| |
| // Packet Number Length |
| QUICTypeUtil::write_QUICPacketNumberLen(n, buf); |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICShortHeaderPacket::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICShortHeaderPacket::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| this->_payload_length = 0; |
| Ptr<IOBufferBlock> tmp = payload; |
| while (tmp) { |
| this->_payload_length += tmp->size(); |
| tmp = tmp->next; |
| } |
| if (unprotected) { |
| this->_payload_length += aead_tag_len; |
| } |
| } |
| |
| // |
| // QUICShortHeaderPacketR |
| // |
| QUICShortHeaderPacketR::QUICShortHeaderPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks, |
| QUICPacketNumber base_packet_number) |
| : QUICPacketR(udp_con, from, to) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| if (raw_buf[0] & 0x04) { |
| this->_key_phase = QUICKeyPhase::PHASE_1; |
| } else { |
| this->_key_phase = QUICKeyPhase::PHASE_0; |
| } |
| |
| QUICInvariants::dcid(this->_dcid, raw_buf, len); |
| |
| int offset = 1 + this->_dcid.length(); |
| this->_packet_number_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); |
| QUICPacketNumber src = QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, this->_packet_number_len); |
| QUICPacket::decode_packet_number(this->_packet_number, src, this->_packet_number_len, base_packet_number); |
| offset += this->_packet_number_len; |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| } |
| |
| QUICPacketType |
| QUICShortHeaderPacketR::type() const |
| { |
| return QUICPacketType::PROTECTED; |
| } |
| |
| QUICKeyPhase |
| QUICShortHeaderPacketR::key_phase() const |
| { |
| return this->_key_phase; |
| } |
| |
| QUICPacketNumber |
| QUICShortHeaderPacketR::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| QUICConnectionId |
| QUICShortHeaderPacketR::destination_cid() const |
| { |
| return this->_dcid; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICShortHeaderPacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICShortHeaderPacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICShortHeaderPacketR::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| } |
| |
| bool |
| QUICShortHeaderPacketR::packet_number_offset(size_t &pn_offset, const uint8_t *packet, size_t packet_len, int dcil) |
| { |
| pn_offset = 1 + dcil; |
| return true; |
| } |
| |
| // |
| // QUICStatelessResetPacket |
| // |
| QUICStatelessResetPacket::QUICStatelessResetPacket(QUICStatelessResetToken token, size_t maximum_size) |
| : QUICPacket(), _token(token), _maximum_size(maximum_size) |
| { |
| } |
| |
| QUICPacketType |
| QUICStatelessResetPacket::type() const |
| { |
| return QUICPacketType::STATELESS_RESET; |
| } |
| |
| QUICConnectionId |
| QUICStatelessResetPacket::destination_cid() const |
| { |
| ink_assert(!"You should not need DCID of Stateless Reset Packet"); |
| return QUICConnectionId::ZERO(); |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICStatelessResetPacket::header_block() const |
| { |
| // Required shortest length is 38 bits however less than 41 bytes in total indicates this is stateless reset. |
| constexpr uint8_t MIN_UNPREDICTABLE_FIELD_LEN = 5 + 20; |
| |
| std::random_device rnd; |
| |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| |
| size_t random_extra_length = rnd() & 0x07; // Extra 0 to 7 bytes |
| |
| if (MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length > this->_maximum_size) { |
| return block; |
| } |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(MIN_UNPREDICTABLE_FIELD_LEN + random_extra_length, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Generate random octets |
| for (int i = 0; i < MIN_UNPREDICTABLE_FIELD_LEN; ++i) { |
| buf[i] = static_cast<uint8_t>(rnd() & 0xFF); |
| } |
| buf[0] = (buf[0] | 0x40) & 0x7f; |
| written_len += MIN_UNPREDICTABLE_FIELD_LEN; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICStatelessResetPacket::payload_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(QUICStatelessResetToken::LEN, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| memcpy(buf, this->_token.buf(), QUICStatelessResetToken::LEN); |
| written_len += QUICStatelessResetToken::LEN; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| QUICPacketNumber |
| QUICStatelessResetPacket::packet_number() const |
| { |
| ink_assert(!"You should not need packet number of Stateless Reset Packet"); |
| return 0; |
| } |
| |
| QUICStatelessResetToken |
| QUICStatelessResetPacket::token() const |
| { |
| return this->_token; |
| } |
| |
| // |
| // QUICStatelessResetPacketR |
| // |
| QUICStatelessResetPacketR::QUICStatelessResetPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, |
| Ptr<IOBufferBlock> blocks) |
| : QUICPacketR(udp_con, from, to) |
| { |
| } |
| |
| QUICPacketType |
| QUICStatelessResetPacketR::type() const |
| { |
| return QUICPacketType::STATELESS_RESET; |
| } |
| |
| QUICPacketNumber |
| QUICStatelessResetPacketR::packet_number() const |
| { |
| ink_assert(!"You should not need packet number of Stateless Reset Packet"); |
| return 0; |
| } |
| |
| QUICConnectionId |
| QUICStatelessResetPacketR::destination_cid() const |
| { |
| ink_assert(!"You should not need DCID of Stateless Reset Packet"); |
| return QUICConnectionId::ZERO(); |
| } |
| |
| // |
| // QUICVersionNegotiationPacket |
| // |
| QUICVersionNegotiationPacket::QUICVersionNegotiationPacket(QUICConnectionId dcid, QUICConnectionId scid, |
| const QUICVersion versions[], int nversions) |
| : QUICLongHeaderPacket(0, dcid, scid, false, false, false), _versions(versions), _nversions(nversions) |
| { |
| } |
| |
| QUICPacketType |
| QUICVersionNegotiationPacket::type() const |
| { |
| return QUICPacketType::VERSION_NEGOTIATION; |
| } |
| |
| QUICVersion |
| QUICVersionNegotiationPacket::version() const |
| { |
| return 0; |
| } |
| |
| QUICPacketNumber |
| QUICVersionNegotiationPacket::packet_number() const |
| { |
| ink_assert(!"You should not need packet number of Version Negotiation Packet"); |
| return 0; |
| } |
| |
| uint16_t |
| QUICVersionNegotiationPacket::payload_length() const |
| { |
| uint16_t size = 0; |
| |
| for (auto b = this->payload_block(); b; b = b->next) { |
| size += b->size(); |
| } |
| |
| return size; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICVersionNegotiationPacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Common Long Header |
| written_len += this->_write_common_header(buf + written_len); |
| |
| // Overwrite the first byte |
| buf[0] = 0x80 | rand(); |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICVersionNegotiationPacket::payload_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| uint8_t *buf; |
| size_t written_len = 0; |
| size_t n; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(sizeof(QUICVersion) * (this->_nversions + 1), BUFFER_SIZE_INDEX_32K)); |
| buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| for (auto i = 0; i < this->_nversions; ++i) { |
| QUICTypeUtil::write_QUICVersion(*(this->_versions + i), buf + written_len, &n); |
| written_len += n; |
| } |
| |
| // [draft-18] 6.3. Using Reserved Versions |
| // To help ensure this, a server SHOULD include a reserved version (see Section 15) while generating a |
| // Version Negotiation packet. |
| QUICTypeUtil::write_QUICVersion(QUIC_EXERCISE_VERSION, buf + written_len, &n); |
| written_len += n; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| const QUICVersion * |
| QUICVersionNegotiationPacket::versions() const |
| { |
| return this->_versions; |
| } |
| |
| int |
| QUICVersionNegotiationPacket::nversions() const |
| { |
| return this->_nversions; |
| } |
| |
| // |
| // QUICVersionNegotiationPacketR |
| // |
| QUICVersionNegotiationPacketR::QUICVersionNegotiationPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, |
| Ptr<IOBufferBlock> blocks) |
| : QUICLongHeaderPacketR(udp_con, from, to, blocks) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| uint8_t dcil = 0; |
| uint8_t scil = 0; |
| QUICInvariants::dcil(dcil, raw_buf, len); |
| QUICInvariants::scil(scil, raw_buf, len); |
| |
| size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; |
| this->_dcid = {raw_buf + offset, dcil}; |
| offset += dcil + 1; |
| this->_scid = {raw_buf + offset, scil}; |
| offset += scil; |
| |
| this->_versions = raw_buf + offset; |
| this->_nversions = (len - offset) / sizeof(QUICVersion); |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| } |
| |
| QUICPacketType |
| QUICVersionNegotiationPacketR::type() const |
| { |
| return QUICPacketType::VERSION_NEGOTIATION; |
| } |
| |
| QUICPacketNumber |
| QUICVersionNegotiationPacketR::packet_number() const |
| { |
| ink_assert(!"You should not need packet number of Version Negotiation Packet"); |
| return 0; |
| } |
| |
| QUICConnectionId |
| QUICVersionNegotiationPacketR::destination_cid() const |
| { |
| return this->_dcid; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICVersionNegotiationPacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICVersionNegotiationPacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| const QUICVersion |
| QUICVersionNegotiationPacketR::supported_version(uint8_t index) const |
| { |
| return QUICTypeUtil::read_QUICVersion(this->_versions + sizeof(QUICVersion) * index); |
| } |
| |
| int |
| QUICVersionNegotiationPacketR::nversions() const |
| { |
| return this->_nversions; |
| } |
| |
| // |
| // QUICInitialPacket |
| // |
| QUICInitialPacket::QUICInitialPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t token_len, |
| ats_unique_buf token, size_t length, QUICPacketNumber packet_number, bool ack_eliciting, |
| bool probing, bool crypto) |
| : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), |
| _token_len(token_len), |
| _token(std::move(token)), |
| _packet_number(packet_number) |
| { |
| } |
| |
| QUICPacketType |
| QUICInitialPacket::type() const |
| { |
| return QUICPacketType::INITIAL; |
| } |
| |
| QUICKeyPhase |
| QUICInitialPacket::key_phase() const |
| { |
| return QUICKeyPhase::INITIAL; |
| } |
| |
| QUICPacketNumber |
| QUICInitialPacket::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICInitialPacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| size_t n; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Common Long Header |
| written_len += this->_write_common_header(buf + written_len); |
| |
| // Token Length |
| QUICIntUtil::write_QUICVariableInt(this->_token_len, buf + written_len, &n); |
| written_len += n; |
| |
| // Token |
| memcpy(buf + written_len, this->_token.get(), this->_token_len); |
| written_len += this->_token_len; |
| |
| QUICPacketNumber pn = 0; |
| size_t pn_len = 4; |
| QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); |
| |
| if (pn > 0x7FFFFF) { |
| pn_len = 4; |
| } else if (pn > 0x7FFF) { |
| pn_len = 3; |
| } else if (pn > 0x7F) { |
| pn_len = 2; |
| } else { |
| pn_len = 1; |
| } |
| |
| // PN Len |
| QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); |
| |
| // Length |
| QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); |
| written_len += n; |
| |
| // PN Field |
| QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); |
| written_len += n; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICInitialPacket::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICInitialPacket::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| this->_payload_length = 0; |
| Ptr<IOBufferBlock> tmp = payload; |
| while (tmp) { |
| this->_payload_length += tmp->size(); |
| tmp = tmp->next; |
| } |
| if (unprotected) { |
| this->_payload_length += aead_tag_len; |
| } |
| } |
| |
| // |
| // QUICInitialPacketR |
| // |
| QUICInitialPacketR::QUICInitialPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks, |
| QUICPacketNumber base_packet_number) |
| : QUICLongHeaderPacketR(udp_con, from, to, blocks) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| uint8_t dcil = 0; |
| uint8_t scil = 0; |
| QUICInvariants::dcil(dcil, raw_buf, len); |
| QUICInvariants::scil(scil, raw_buf, len); |
| |
| size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; |
| this->_dcid = {raw_buf + offset, dcil}; |
| offset += dcil + 1; |
| this->_scid = {raw_buf + offset, scil}; |
| offset += scil; |
| |
| // Token Length Field |
| uint64_t token_len = QUICIntUtil::read_QUICVariableInt(raw_buf + offset, len - offset); |
| offset += QUICVariableInt::size(raw_buf + offset); |
| |
| // Token Field |
| if (token_len) { |
| this->_token = new QUICAddressValidationToken(raw_buf + offset, token_len); |
| offset += token_len; |
| } else { |
| this->_token = new QUICAddressValidationToken(nullptr, 0); |
| } |
| |
| // Length Field |
| offset += QUICVariableInt::size(raw_buf + offset); |
| |
| // PN Field |
| int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); |
| QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, |
| base_packet_number); |
| offset += pn_len; |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| } |
| |
| QUICInitialPacketR::~QUICInitialPacketR() |
| { |
| delete this->_token; |
| } |
| |
| QUICPacketType |
| QUICInitialPacketR::type() const |
| { |
| return QUICPacketType::INITIAL; |
| } |
| |
| QUICPacketNumber |
| QUICInitialPacketR::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| QUICKeyPhase |
| QUICInitialPacketR::key_phase() const |
| { |
| return QUICKeyPhase::INITIAL; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICInitialPacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICInitialPacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICInitialPacketR::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| } |
| |
| const QUICAddressValidationToken & |
| QUICInitialPacketR::token() const |
| { |
| return *(this->_token); |
| } |
| |
| bool |
| QUICInitialPacketR::token_length(size_t &token_length, uint8_t &field_len, size_t &token_length_filed_offset, const uint8_t *packet, |
| size_t packet_len) |
| { |
| QUICPacketType type = QUICPacketType::UNINITIALIZED; |
| QUICPacketR::type(type, packet, packet_len); |
| |
| ink_assert(type == QUICPacketType::INITIAL); |
| |
| uint8_t dcil, scil; |
| QUICInvariants::dcil(dcil, packet, packet_len); |
| QUICInvariants::scil(scil, packet, packet_len); |
| |
| token_length_filed_offset = LONG_HDR_OFFSET_CONNECTION_ID + dcil + 1 + scil; |
| if (token_length_filed_offset >= packet_len) { |
| return false; |
| } |
| |
| token_length = QUICIntUtil::read_QUICVariableInt(packet + token_length_filed_offset, packet_len - token_length_filed_offset); |
| field_len = QUICVariableInt::size(packet + token_length_filed_offset); |
| |
| return true; |
| } |
| |
| // |
| // QUICZeroRttPacket |
| // |
| QUICZeroRttPacket::QUICZeroRttPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, |
| QUICPacketNumber packet_number, bool ack_eliciting, bool probing) |
| : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, false), _packet_number(packet_number) |
| { |
| } |
| |
| QUICPacketType |
| QUICZeroRttPacket::type() const |
| { |
| return QUICPacketType::ZERO_RTT_PROTECTED; |
| } |
| |
| QUICKeyPhase |
| QUICZeroRttPacket::key_phase() const |
| { |
| return QUICKeyPhase::ZERO_RTT; |
| } |
| |
| QUICPacketNumber |
| QUICZeroRttPacket::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICZeroRttPacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| size_t n; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Common Long Header |
| written_len += this->_write_common_header(buf + written_len); |
| |
| QUICPacketNumber pn = 0; |
| size_t pn_len = 4; |
| QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); |
| |
| if (pn > 0x7FFFFF) { |
| pn_len = 4; |
| } else if (pn > 0x7FFF) { |
| pn_len = 3; |
| } else if (pn > 0x7F) { |
| pn_len = 2; |
| } else { |
| pn_len = 1; |
| } |
| |
| // PN Len |
| QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); |
| |
| // Length |
| QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); |
| written_len += n; |
| |
| // PN Field |
| QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); |
| written_len += n; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICZeroRttPacket::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICZeroRttPacket::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| this->_payload_length = 0; |
| Ptr<IOBufferBlock> tmp = payload; |
| while (tmp) { |
| this->_payload_length += tmp->size(); |
| tmp = tmp->next; |
| } |
| if (unprotected) { |
| this->_payload_length += aead_tag_len; |
| } |
| } |
| |
| // |
| // QUICZeroRttPacketR |
| // |
| QUICZeroRttPacketR::QUICZeroRttPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks, |
| QUICPacketNumber base_packet_number) |
| : QUICLongHeaderPacketR(udp_con, from, to, blocks) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| uint8_t dcil = 0; |
| uint8_t scil = 0; |
| QUICInvariants::dcil(dcil, raw_buf, len); |
| QUICInvariants::scil(scil, raw_buf, len); |
| |
| size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; |
| this->_dcid = {raw_buf + offset, dcil}; |
| offset += dcil + 1; |
| this->_scid = {raw_buf + offset, scil}; |
| offset += scil; |
| |
| // Length Field |
| offset += QUICVariableInt::size(raw_buf + offset); |
| |
| // PN Field |
| int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); |
| QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, |
| base_packet_number); |
| offset += pn_len; |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| } |
| |
| QUICPacketType |
| QUICZeroRttPacketR::type() const |
| { |
| return QUICPacketType::ZERO_RTT_PROTECTED; |
| } |
| |
| QUICPacketNumber |
| QUICZeroRttPacketR::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| QUICKeyPhase |
| QUICZeroRttPacketR::key_phase() const |
| { |
| return QUICKeyPhase::ZERO_RTT; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICZeroRttPacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICZeroRttPacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICZeroRttPacketR::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| } |
| |
| // |
| // QUICHandshakePacket |
| // |
| QUICHandshakePacket::QUICHandshakePacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, size_t length, |
| QUICPacketNumber packet_number, bool ack_eliciting, bool probing, bool crypto) |
| : QUICLongHeaderPacket(version, dcid, scid, ack_eliciting, probing, crypto), _packet_number(packet_number) |
| { |
| } |
| |
| QUICPacketType |
| QUICHandshakePacket::type() const |
| { |
| return QUICPacketType::HANDSHAKE; |
| } |
| |
| QUICKeyPhase |
| QUICHandshakePacket::key_phase() const |
| { |
| return QUICKeyPhase::HANDSHAKE; |
| } |
| |
| QUICPacketNumber |
| QUICHandshakePacket::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICHandshakePacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| size_t n; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Common Long Header |
| written_len += this->_write_common_header(buf + written_len); |
| |
| QUICPacketNumber pn = 0; |
| size_t pn_len = 4; |
| QUICPacket::encode_packet_number(pn, this->_packet_number, pn_len); |
| |
| if (pn > 0x7FFFFF) { |
| pn_len = 4; |
| } else if (pn > 0x7FFF) { |
| pn_len = 3; |
| } else if (pn > 0x7F) { |
| pn_len = 2; |
| } else { |
| pn_len = 1; |
| } |
| |
| // PN Len |
| QUICTypeUtil::write_QUICPacketNumberLen(pn_len, buf); |
| |
| // Length |
| QUICIntUtil::write_QUICVariableInt(pn_len + this->_payload_length, buf + written_len, &n); |
| written_len += n; |
| |
| // PN Field |
| QUICTypeUtil::write_QUICPacketNumber(pn, pn_len, buf + written_len, &n); |
| written_len += n; |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICHandshakePacket::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICHandshakePacket::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| this->_payload_length = 0; |
| Ptr<IOBufferBlock> tmp = payload; |
| while (tmp) { |
| this->_payload_length += tmp->size(); |
| tmp = tmp->next; |
| } |
| if (unprotected) { |
| this->_payload_length += aead_tag_len; |
| } |
| } |
| |
| // |
| // QUICHandshakePacketR |
| // |
| QUICHandshakePacketR::QUICHandshakePacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks, |
| QUICPacketNumber base_packet_number) |
| : QUICLongHeaderPacketR(udp_con, from, to, blocks) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| uint8_t dcil = 0; |
| uint8_t scil = 0; |
| QUICInvariants::dcil(dcil, raw_buf, len); |
| QUICInvariants::scil(scil, raw_buf, len); |
| |
| size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; |
| this->_dcid = {raw_buf + offset, dcil}; |
| offset += dcil + 1; |
| this->_scid = {raw_buf + offset, scil}; |
| offset += scil; |
| |
| // Length Field |
| offset += QUICVariableInt::size(raw_buf + offset); |
| |
| // PN Field |
| int pn_len = QUICTypeUtil::read_QUICPacketNumberLen(raw_buf); |
| QUICPacket::decode_packet_number(this->_packet_number, QUICTypeUtil::read_QUICPacketNumber(raw_buf + offset, pn_len), pn_len, |
| base_packet_number); |
| offset += pn_len; |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| } |
| |
| QUICPacketType |
| QUICHandshakePacketR::type() const |
| { |
| return QUICPacketType::HANDSHAKE; |
| } |
| |
| QUICKeyPhase |
| QUICHandshakePacketR::key_phase() const |
| { |
| return QUICKeyPhase::HANDSHAKE; |
| } |
| |
| QUICPacketNumber |
| QUICHandshakePacketR::packet_number() const |
| { |
| return this->_packet_number; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICHandshakePacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICHandshakePacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| void |
| QUICHandshakePacketR::attach_payload(Ptr<IOBufferBlock> payload, bool unprotected) |
| { |
| this->_payload_block = payload; |
| } |
| |
| // |
| // QUICRetryPacket |
| // |
| QUICRetryPacket::QUICRetryPacket(QUICVersion version, QUICConnectionId dcid, QUICConnectionId scid, QUICRetryToken &token) |
| : QUICLongHeaderPacket(version, dcid, scid, false, false, false), _token(token) |
| { |
| } |
| |
| QUICPacketType |
| QUICRetryPacket::type() const |
| { |
| return QUICPacketType::RETRY; |
| } |
| |
| QUICPacketNumber |
| QUICRetryPacket::packet_number() const |
| { |
| ink_assert(!"You should not need packet number of Retry Packet"); |
| return 0; |
| } |
| |
| uint16_t |
| QUICRetryPacket::payload_length() const |
| { |
| uint16_t size = 0; |
| |
| for (auto b = this->payload_block(); b; b = b->next) { |
| size += b->size(); |
| } |
| |
| return size; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICRetryPacket::header_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| size_t written_len = 0; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(2048, BUFFER_SIZE_INDEX_32K)); |
| uint8_t *buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Common Long Header |
| written_len += this->_write_common_header(buf + written_len); |
| |
| block->fill(written_len); |
| |
| return block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICRetryPacket::payload_block() const |
| { |
| Ptr<IOBufferBlock> block; |
| uint8_t *buf; |
| size_t written_len = 0; |
| |
| block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| block->alloc(iobuffer_size_to_index(QUICConnectionId::MAX_LENGTH + this->_token.length() + QUICRetryIntegrityTag::LEN, |
| BUFFER_SIZE_INDEX_32K)); |
| buf = reinterpret_cast<uint8_t *>(block->start()); |
| |
| // Retry Token |
| memcpy(buf + written_len, this->_token.buf(), this->_token.length()); |
| written_len += this->_token.length(); |
| block->fill(written_len); |
| |
| // Retry Integrity Tag |
| QUICRetryIntegrityTag::compute(buf + written_len, this->_token.original_dcid(), this->header_block(), block); |
| written_len += QUICRetryIntegrityTag::LEN; |
| block->fill(QUICRetryIntegrityTag::LEN); |
| |
| return block; |
| } |
| |
| const QUICRetryToken & |
| QUICRetryPacket::token() const |
| { |
| return this->_token; |
| } |
| |
| // |
| // QUICRetryPacketR |
| // |
| QUICRetryPacketR::QUICRetryPacketR(UDPConnection *udp_con, IpEndpoint from, IpEndpoint to, Ptr<IOBufferBlock> blocks) |
| : QUICLongHeaderPacketR(udp_con, from, to, blocks) |
| { |
| size_t len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| len += b->size(); |
| } |
| |
| Ptr<IOBufferBlock> concatenated_block = make_ptr<IOBufferBlock>(new_IOBufferBlock()); |
| concatenated_block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); |
| concatenated_block->fill(len); |
| |
| uint8_t *raw_buf = reinterpret_cast<uint8_t *>(concatenated_block->start()); |
| |
| size_t copied_len = 0; |
| for (auto b = blocks; b; b = b->next) { |
| memcpy(raw_buf + copied_len, b->start(), b->size()); |
| copied_len += b->size(); |
| } |
| |
| uint8_t dcil = 0; |
| uint8_t scil = 0; |
| QUICInvariants::dcil(dcil, raw_buf, len); |
| QUICInvariants::scil(scil, raw_buf, len); |
| |
| size_t offset = LONG_HDR_OFFSET_CONNECTION_ID; |
| this->_dcid = {raw_buf + offset, dcil}; |
| offset += dcil + 1; |
| this->_scid = {raw_buf + offset, scil}; |
| offset += scil; |
| |
| // Retry Token field |
| this->_token = new QUICRetryToken(raw_buf + offset, len - offset - QUICRetryIntegrityTag::LEN); |
| offset += this->_token->length(); |
| |
| // Retry Integrity Tag |
| memcpy(this->_integrity_tag, raw_buf + offset, QUICRetryIntegrityTag::LEN); |
| |
| this->_header_block = concatenated_block->clone(); |
| this->_header_block->_end = this->_header_block->_start + offset; |
| this->_header_block->next = nullptr; |
| this->_payload_block = concatenated_block->clone(); |
| this->_payload_block->_start = this->_payload_block->_start + offset; |
| this->_payload_block_without_tag = this->_payload_block->clone(); |
| this->_payload_block_without_tag->_end = this->_payload_block_without_tag->_end - QUICRetryIntegrityTag::LEN; |
| } |
| |
| QUICRetryPacketR::~QUICRetryPacketR() |
| { |
| delete this->_token; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICRetryPacketR::header_block() const |
| { |
| return this->_header_block; |
| } |
| |
| Ptr<IOBufferBlock> |
| QUICRetryPacketR::payload_block() const |
| { |
| return this->_payload_block; |
| } |
| |
| QUICPacketType |
| QUICRetryPacketR::type() const |
| { |
| return QUICPacketType::RETRY; |
| } |
| |
| QUICPacketNumber |
| QUICRetryPacketR::packet_number() const |
| { |
| return 0; |
| } |
| |
| const QUICAddressValidationToken & |
| QUICRetryPacketR::token() const |
| { |
| return *(this->_token); |
| } |
| |
| bool |
| QUICRetryPacketR::has_valid_tag(QUICConnectionId &odcid) const |
| { |
| uint8_t tag_computed[QUICRetryIntegrityTag::LEN]; |
| QUICRetryIntegrityTag::compute(tag_computed, odcid, this->_header_block, this->_payload_block_without_tag); |
| |
| return memcmp(this->_integrity_tag, tag_computed, QUICRetryIntegrityTag::LEN) == 0; |
| } |