1*d9f75844SAndroid Build Coastguard Worker /* 2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker #ifndef NET_DCSCTP_PACKET_TLV_TRAIT_H_ 11*d9f75844SAndroid Build Coastguard Worker #define NET_DCSCTP_PACKET_TLV_TRAIT_H_ 12*d9f75844SAndroid Build Coastguard Worker 13*d9f75844SAndroid Build Coastguard Worker #include <stdint.h> 14*d9f75844SAndroid Build Coastguard Worker #include <string.h> 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard Worker #include <algorithm> 17*d9f75844SAndroid Build Coastguard Worker #include <cstddef> 18*d9f75844SAndroid Build Coastguard Worker #include <cstdint> 19*d9f75844SAndroid Build Coastguard Worker #include <string> 20*d9f75844SAndroid Build Coastguard Worker #include <vector> 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h" 23*d9f75844SAndroid Build Coastguard Worker #include "api/array_view.h" 24*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/packet/bounded_byte_reader.h" 25*d9f75844SAndroid Build Coastguard Worker #include "net/dcsctp/packet/bounded_byte_writer.h" 26*d9f75844SAndroid Build Coastguard Worker 27*d9f75844SAndroid Build Coastguard Worker namespace dcsctp { 28*d9f75844SAndroid Build Coastguard Worker namespace tlv_trait_impl { 29*d9f75844SAndroid Build Coastguard Worker // Logging functions, only to be used by TLVTrait, which is a templated class. 30*d9f75844SAndroid Build Coastguard Worker void ReportInvalidSize(size_t actual_size, size_t expected_size); 31*d9f75844SAndroid Build Coastguard Worker void ReportInvalidType(int actual_type, int expected_type); 32*d9f75844SAndroid Build Coastguard Worker void ReportInvalidFixedLengthField(size_t value, size_t expected); 33*d9f75844SAndroid Build Coastguard Worker void ReportInvalidVariableLengthField(size_t value, size_t available); 34*d9f75844SAndroid Build Coastguard Worker void ReportInvalidPadding(size_t padding_bytes); 35*d9f75844SAndroid Build Coastguard Worker void ReportInvalidLengthMultiple(size_t length, size_t alignment); 36*d9f75844SAndroid Build Coastguard Worker } // namespace tlv_trait_impl 37*d9f75844SAndroid Build Coastguard Worker 38*d9f75844SAndroid Build Coastguard Worker // Various entities in SCTP are padded data blocks, with a type and length 39*d9f75844SAndroid Build Coastguard Worker // field at fixed offsets, all stored in a 4-byte header. 40*d9f75844SAndroid Build Coastguard Worker // 41*d9f75844SAndroid Build Coastguard Worker // See e.g. https://tools.ietf.org/html/rfc4960#section-3.2 and 42*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc4960#section-3.2.1 43*d9f75844SAndroid Build Coastguard Worker // 44*d9f75844SAndroid Build Coastguard Worker // These are helper classes for writing and parsing that data, which in SCTP is 45*d9f75844SAndroid Build Coastguard Worker // called Type-Length-Value, or TLV. 46*d9f75844SAndroid Build Coastguard Worker // 47*d9f75844SAndroid Build Coastguard Worker // This templated class is configurable - a struct passed in as template 48*d9f75844SAndroid Build Coastguard Worker // parameter with the following expected members: 49*d9f75844SAndroid Build Coastguard Worker // * kType - The type field's value 50*d9f75844SAndroid Build Coastguard Worker // * kTypeSizeInBytes - The type field's width in bytes. 51*d9f75844SAndroid Build Coastguard Worker // Either 1 or 2. 52*d9f75844SAndroid Build Coastguard Worker // * kHeaderSize - The fixed size header 53*d9f75844SAndroid Build Coastguard Worker // * kVariableLengthAlignment - The size alignment on the variable data. Set 54*d9f75844SAndroid Build Coastguard Worker // to zero (0) if no variable data is used. 55*d9f75844SAndroid Build Coastguard Worker // 56*d9f75844SAndroid Build Coastguard Worker // This class is to be used as a trait 57*d9f75844SAndroid Build Coastguard Worker // (https://en.wikipedia.org/wiki/Trait_(computer_programming)) that adds a few 58*d9f75844SAndroid Build Coastguard Worker // public and protected members and which a class inherits from when it 59*d9f75844SAndroid Build Coastguard Worker // represents a type-length-value object. 60*d9f75844SAndroid Build Coastguard Worker template <typename Config> 61*d9f75844SAndroid Build Coastguard Worker class TLVTrait { 62*d9f75844SAndroid Build Coastguard Worker private: 63*d9f75844SAndroid Build Coastguard Worker static constexpr size_t kTlvHeaderSize = 4; 64*d9f75844SAndroid Build Coastguard Worker 65*d9f75844SAndroid Build Coastguard Worker protected: 66*d9f75844SAndroid Build Coastguard Worker static constexpr size_t kHeaderSize = Config::kHeaderSize; 67*d9f75844SAndroid Build Coastguard Worker 68*d9f75844SAndroid Build Coastguard Worker static_assert(Config::kTypeSizeInBytes == 1 || Config::kTypeSizeInBytes == 2, 69*d9f75844SAndroid Build Coastguard Worker "kTypeSizeInBytes must be 1 or 2"); 70*d9f75844SAndroid Build Coastguard Worker static_assert(Config::kHeaderSize >= kTlvHeaderSize, 71*d9f75844SAndroid Build Coastguard Worker "HeaderSize must be >= 4 bytes"); 72*d9f75844SAndroid Build Coastguard Worker static_assert((Config::kHeaderSize % 4 == 0), 73*d9f75844SAndroid Build Coastguard Worker "kHeaderSize must be an even multiple of 4 bytes"); 74*d9f75844SAndroid Build Coastguard Worker static_assert((Config::kVariableLengthAlignment == 0 || 75*d9f75844SAndroid Build Coastguard Worker Config::kVariableLengthAlignment == 1 || 76*d9f75844SAndroid Build Coastguard Worker Config::kVariableLengthAlignment == 2 || 77*d9f75844SAndroid Build Coastguard Worker Config::kVariableLengthAlignment == 4 || 78*d9f75844SAndroid Build Coastguard Worker Config::kVariableLengthAlignment == 8), 79*d9f75844SAndroid Build Coastguard Worker "kVariableLengthAlignment must be an allowed value"); 80*d9f75844SAndroid Build Coastguard Worker 81*d9f75844SAndroid Build Coastguard Worker // Validates the data with regards to size, alignment and type. 82*d9f75844SAndroid Build Coastguard Worker // If valid, returns a bounded buffer. ParseTLV(rtc::ArrayView<const uint8_t> data)83*d9f75844SAndroid Build Coastguard Worker static absl::optional<BoundedByteReader<Config::kHeaderSize>> ParseTLV( 84*d9f75844SAndroid Build Coastguard Worker rtc::ArrayView<const uint8_t> data) { 85*d9f75844SAndroid Build Coastguard Worker if (data.size() < Config::kHeaderSize) { 86*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidSize(data.size(), Config::kHeaderSize); 87*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 88*d9f75844SAndroid Build Coastguard Worker } 89*d9f75844SAndroid Build Coastguard Worker BoundedByteReader<kTlvHeaderSize> tlv_header(data); 90*d9f75844SAndroid Build Coastguard Worker 91*d9f75844SAndroid Build Coastguard Worker const int type = (Config::kTypeSizeInBytes == 1) 92*d9f75844SAndroid Build Coastguard Worker ? tlv_header.template Load8<0>() 93*d9f75844SAndroid Build Coastguard Worker : tlv_header.template Load16<0>(); 94*d9f75844SAndroid Build Coastguard Worker 95*d9f75844SAndroid Build Coastguard Worker if (type != Config::kType) { 96*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidType(type, Config::kType); 97*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 98*d9f75844SAndroid Build Coastguard Worker } 99*d9f75844SAndroid Build Coastguard Worker const uint16_t length = tlv_header.template Load16<2>(); 100*d9f75844SAndroid Build Coastguard Worker if (Config::kVariableLengthAlignment == 0) { 101*d9f75844SAndroid Build Coastguard Worker // Don't expect any variable length data at all. 102*d9f75844SAndroid Build Coastguard Worker if (length != Config::kHeaderSize || data.size() != Config::kHeaderSize) { 103*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidFixedLengthField(length, 104*d9f75844SAndroid Build Coastguard Worker Config::kHeaderSize); 105*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 106*d9f75844SAndroid Build Coastguard Worker } 107*d9f75844SAndroid Build Coastguard Worker } else { 108*d9f75844SAndroid Build Coastguard Worker // Expect variable length data - verify its size alignment. 109*d9f75844SAndroid Build Coastguard Worker if (length > data.size() || length < Config::kHeaderSize) { 110*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidVariableLengthField(length, data.size()); 111*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 112*d9f75844SAndroid Build Coastguard Worker } 113*d9f75844SAndroid Build Coastguard Worker const size_t padding = data.size() - length; 114*d9f75844SAndroid Build Coastguard Worker if (padding > 3) { 115*d9f75844SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc4960#section-3.2 116*d9f75844SAndroid Build Coastguard Worker // "This padding MUST NOT be more than 3 bytes in total" 117*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidPadding(padding); 118*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 119*d9f75844SAndroid Build Coastguard Worker } 120*d9f75844SAndroid Build Coastguard Worker if (!ValidateLengthAlignment(length, Config::kVariableLengthAlignment)) { 121*d9f75844SAndroid Build Coastguard Worker tlv_trait_impl::ReportInvalidLengthMultiple( 122*d9f75844SAndroid Build Coastguard Worker length, Config::kVariableLengthAlignment); 123*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 124*d9f75844SAndroid Build Coastguard Worker } 125*d9f75844SAndroid Build Coastguard Worker } 126*d9f75844SAndroid Build Coastguard Worker return BoundedByteReader<Config::kHeaderSize>(data.subview(0, length)); 127*d9f75844SAndroid Build Coastguard Worker } 128*d9f75844SAndroid Build Coastguard Worker 129*d9f75844SAndroid Build Coastguard Worker // Allocates space for data with a static header size, as defined by 130*d9f75844SAndroid Build Coastguard Worker // `Config::kHeaderSize` and a variable footer, as defined by `variable_size` 131*d9f75844SAndroid Build Coastguard Worker // (which may be 0) and writes the type and length in the header. 132*d9f75844SAndroid Build Coastguard Worker static BoundedByteWriter<Config::kHeaderSize> AllocateTLV( 133*d9f75844SAndroid Build Coastguard Worker std::vector<uint8_t>& out, 134*d9f75844SAndroid Build Coastguard Worker size_t variable_size = 0) { 135*d9f75844SAndroid Build Coastguard Worker const size_t offset = out.size(); 136*d9f75844SAndroid Build Coastguard Worker const size_t size = Config::kHeaderSize + variable_size; 137*d9f75844SAndroid Build Coastguard Worker out.resize(offset + size); 138*d9f75844SAndroid Build Coastguard Worker 139*d9f75844SAndroid Build Coastguard Worker BoundedByteWriter<kTlvHeaderSize> tlv_header( 140*d9f75844SAndroid Build Coastguard Worker rtc::ArrayView<uint8_t>(out.data() + offset, kTlvHeaderSize)); 141*d9f75844SAndroid Build Coastguard Worker if (Config::kTypeSizeInBytes == 1) { 142*d9f75844SAndroid Build Coastguard Worker tlv_header.template Store8<0>(static_cast<uint8_t>(Config::kType)); 143*d9f75844SAndroid Build Coastguard Worker } else { 144*d9f75844SAndroid Build Coastguard Worker tlv_header.template Store16<0>(Config::kType); 145*d9f75844SAndroid Build Coastguard Worker } 146*d9f75844SAndroid Build Coastguard Worker tlv_header.template Store16<2>(size); 147*d9f75844SAndroid Build Coastguard Worker 148*d9f75844SAndroid Build Coastguard Worker return BoundedByteWriter<Config::kHeaderSize>( 149*d9f75844SAndroid Build Coastguard Worker rtc::ArrayView<uint8_t>(out.data() + offset, size)); 150*d9f75844SAndroid Build Coastguard Worker } 151*d9f75844SAndroid Build Coastguard Worker 152*d9f75844SAndroid Build Coastguard Worker private: ValidateLengthAlignment(uint16_t length,size_t alignment)153*d9f75844SAndroid Build Coastguard Worker static bool ValidateLengthAlignment(uint16_t length, size_t alignment) { 154*d9f75844SAndroid Build Coastguard Worker // This is to avoid MSVC believing there could be a "mod by zero", when it 155*d9f75844SAndroid Build Coastguard Worker // certainly can't. 156*d9f75844SAndroid Build Coastguard Worker if (alignment == 0) { 157*d9f75844SAndroid Build Coastguard Worker return true; 158*d9f75844SAndroid Build Coastguard Worker } 159*d9f75844SAndroid Build Coastguard Worker return (length % alignment) == 0; 160*d9f75844SAndroid Build Coastguard Worker } 161*d9f75844SAndroid Build Coastguard Worker }; 162*d9f75844SAndroid Build Coastguard Worker 163*d9f75844SAndroid Build Coastguard Worker } // namespace dcsctp 164*d9f75844SAndroid Build Coastguard Worker 165*d9f75844SAndroid Build Coastguard Worker #endif // NET_DCSCTP_PACKET_TLV_TRAIT_H_ 166