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