1 /*
2 * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10 #include "net/dcsctp/packet/sctp_packet.h"
11
12 #include <stddef.h>
13
14 #include <cstdint>
15 #include <string>
16 #include <utility>
17 #include <vector>
18
19 #include "absl/memory/memory.h"
20 #include "absl/types/optional.h"
21 #include "api/array_view.h"
22 #include "net/dcsctp/common/math.h"
23 #include "net/dcsctp/packet/bounded_byte_reader.h"
24 #include "net/dcsctp/packet/bounded_byte_writer.h"
25 #include "net/dcsctp/packet/chunk/chunk.h"
26 #include "net/dcsctp/packet/crc32c.h"
27 #include "net/dcsctp/public/dcsctp_options.h"
28 #include "rtc_base/logging.h"
29 #include "rtc_base/strings/string_format.h"
30
31 namespace dcsctp {
32 namespace {
33 constexpr size_t kMaxUdpPacketSize = 65535;
34 constexpr size_t kChunkTlvHeaderSize = 4;
35 constexpr size_t kExpectedDescriptorCount = 4;
36 } // namespace
37
38 /*
39 0 1 2 3
40 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
41 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42 | Source Port Number | Destination Port Number |
43 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 | Verification Tag |
45 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 | Checksum |
47 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 */
49
Builder(VerificationTag verification_tag,const DcSctpOptions & options)50 SctpPacket::Builder::Builder(VerificationTag verification_tag,
51 const DcSctpOptions& options)
52 : verification_tag_(verification_tag),
53 source_port_(options.local_port),
54 dest_port_(options.remote_port),
55 max_packet_size_(RoundDownTo4(options.mtu)) {}
56
Add(const Chunk & chunk)57 SctpPacket::Builder& SctpPacket::Builder::Add(const Chunk& chunk) {
58 if (out_.empty()) {
59 out_.reserve(max_packet_size_);
60 out_.resize(SctpPacket::kHeaderSize);
61 BoundedByteWriter<kHeaderSize> buffer(out_);
62 buffer.Store16<0>(source_port_);
63 buffer.Store16<2>(dest_port_);
64 buffer.Store32<4>(*verification_tag_);
65 // Checksum is at offset 8 - written when calling Build();
66 }
67 RTC_DCHECK(IsDivisibleBy4(out_.size()));
68
69 chunk.SerializeTo(out_);
70 if (out_.size() % 4 != 0) {
71 out_.resize(RoundUpTo4(out_.size()));
72 }
73
74 RTC_DCHECK(out_.size() <= max_packet_size_)
75 << "Exceeded max size, data=" << out_.size()
76 << ", max_size=" << max_packet_size_;
77 return *this;
78 }
79
bytes_remaining() const80 size_t SctpPacket::Builder::bytes_remaining() const {
81 if (out_.empty()) {
82 // The packet header (CommonHeader) hasn't been written yet:
83 return max_packet_size_ - kHeaderSize;
84 } else if (out_.size() > max_packet_size_) {
85 RTC_DCHECK_NOTREACHED() << "Exceeded max size, data=" << out_.size()
86 << ", max_size=" << max_packet_size_;
87 return 0;
88 }
89 return max_packet_size_ - out_.size();
90 }
91
Build()92 std::vector<uint8_t> SctpPacket::Builder::Build() {
93 std::vector<uint8_t> out;
94 out_.swap(out);
95
96 if (!out.empty()) {
97 uint32_t crc = GenerateCrc32C(out);
98 BoundedByteWriter<kHeaderSize>(out).Store32<8>(crc);
99 }
100
101 RTC_DCHECK(out.size() <= max_packet_size_)
102 << "Exceeded max size, data=" << out.size()
103 << ", max_size=" << max_packet_size_;
104
105 return out;
106 }
107
Parse(rtc::ArrayView<const uint8_t> data,bool disable_checksum_verification)108 absl::optional<SctpPacket> SctpPacket::Parse(
109 rtc::ArrayView<const uint8_t> data,
110 bool disable_checksum_verification) {
111 if (data.size() < kHeaderSize + kChunkTlvHeaderSize ||
112 data.size() > kMaxUdpPacketSize) {
113 RTC_DLOG(LS_WARNING) << "Invalid packet size";
114 return absl::nullopt;
115 }
116
117 BoundedByteReader<kHeaderSize> reader(data);
118
119 CommonHeader common_header;
120 common_header.source_port = reader.Load16<0>();
121 common_header.destination_port = reader.Load16<2>();
122 common_header.verification_tag = VerificationTag(reader.Load32<4>());
123 common_header.checksum = reader.Load32<8>();
124
125 // Create a copy of the packet, which will be held by this object.
126 std::vector<uint8_t> data_copy =
127 std::vector<uint8_t>(data.begin(), data.end());
128
129 // Verify the checksum. The checksum field must be zero when that's done.
130 BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>(0);
131 uint32_t calculated_checksum = GenerateCrc32C(data_copy);
132 if (!disable_checksum_verification &&
133 calculated_checksum != common_header.checksum) {
134 RTC_DLOG(LS_WARNING) << rtc::StringFormat(
135 "Invalid packet checksum, packet_checksum=0x%08x, "
136 "calculated_checksum=0x%08x",
137 common_header.checksum, calculated_checksum);
138 return absl::nullopt;
139 }
140 // Restore the checksum in the header.
141 BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>(common_header.checksum);
142
143 // Validate and parse the chunk headers in the message.
144 /*
145 0 1 2 3
146 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
147 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
148 | Chunk Type | Chunk Flags | Chunk Length |
149 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
150 */
151
152 std::vector<ChunkDescriptor> descriptors;
153 descriptors.reserve(kExpectedDescriptorCount);
154 rtc::ArrayView<const uint8_t> descriptor_data =
155 rtc::ArrayView<const uint8_t>(data_copy).subview(kHeaderSize);
156 while (!descriptor_data.empty()) {
157 if (descriptor_data.size() < kChunkTlvHeaderSize) {
158 RTC_DLOG(LS_WARNING) << "Too small chunk";
159 return absl::nullopt;
160 }
161 BoundedByteReader<kChunkTlvHeaderSize> chunk_header(descriptor_data);
162 uint8_t type = chunk_header.Load8<0>();
163 uint8_t flags = chunk_header.Load8<1>();
164 uint16_t length = chunk_header.Load16<2>();
165 uint16_t padded_length = RoundUpTo4(length);
166 if (padded_length > descriptor_data.size()) {
167 RTC_DLOG(LS_WARNING) << "Too large chunk. length=" << length
168 << ", remaining=" << descriptor_data.size();
169 return absl::nullopt;
170 } else if (padded_length < kChunkTlvHeaderSize) {
171 RTC_DLOG(LS_WARNING) << "Too small chunk. length=" << length;
172 return absl::nullopt;
173 }
174 descriptors.emplace_back(type, flags,
175 descriptor_data.subview(0, padded_length));
176 descriptor_data = descriptor_data.subview(padded_length);
177 }
178
179 // Note that iterators (and pointer) are guaranteed to be stable when moving a
180 // std::vector, and `descriptors` have pointers to within `data_copy`.
181 return SctpPacket(common_header, std::move(data_copy),
182 std::move(descriptors));
183 }
184 } // namespace dcsctp
185