xref: /aosp_15_r20/external/webrtc/net/dcsctp/packet/tlv_trait.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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