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