dcsctp: Add SCTP packet
This represents the wire encoding/decoding of SCTP packets. Bug: webrtc:12614 Change-Id: Id7a4e4654f29eea126ea3058333f5c625606446b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/213349 Commit-Queue: Victor Boivie <boivie@webrtc.org> Reviewed-by: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#33650}
This commit is contained in:
parent
943ad970f4
commit
471fc8c329
@ -198,6 +198,21 @@ rtc_library("chunk") {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("sctp_packet") {
|
||||
deps = [
|
||||
":crc32c",
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base",
|
||||
"../../../rtc_base:checks",
|
||||
"../../../rtc_base:rtc_base_approved",
|
||||
"../public:types",
|
||||
]
|
||||
sources = [
|
||||
"sctp_packet.cc",
|
||||
"sctp_packet.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_library("dcsctp_packet_unittests") {
|
||||
testonly = true
|
||||
@ -208,6 +223,7 @@ if (rtc_include_tests) {
|
||||
":crc32c",
|
||||
":error_cause",
|
||||
":parameter",
|
||||
":sctp_packet",
|
||||
":tlv_trait",
|
||||
"../../../api:array_view",
|
||||
"../../../rtc_base:checks",
|
||||
@ -258,6 +274,7 @@ if (rtc_include_tests) {
|
||||
"parameter/ssn_tsn_reset_request_parameter_test.cc",
|
||||
"parameter/state_cookie_parameter_test.cc",
|
||||
"parameter/supported_extensions_parameter_test.cc",
|
||||
"sctp_packet_test.cc",
|
||||
"tlv_trait_test.cc",
|
||||
]
|
||||
}
|
||||
|
||||
159
net/dcsctp/packet/sctp_packet.cc
Normal file
159
net/dcsctp/packet/sctp_packet.cc
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "net/dcsctp/packet/sctp_packet.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/array_view.h"
|
||||
#include "net/dcsctp/common/math.h"
|
||||
#include "net/dcsctp/packet/bounded_byte_reader.h"
|
||||
#include "net/dcsctp/packet/bounded_byte_writer.h"
|
||||
#include "net/dcsctp/packet/chunk/chunk.h"
|
||||
#include "net/dcsctp/packet/crc32c.h"
|
||||
#include "net/dcsctp/public/dcsctp_options.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/strings/string_format.h"
|
||||
|
||||
namespace dcsctp {
|
||||
namespace {
|
||||
constexpr size_t kMaxUdpPacketSize = 65535;
|
||||
constexpr size_t kChunkTlvHeaderSize = 4;
|
||||
constexpr size_t kExpectedDescriptorCount = 4;
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Port Number | Destination Port Number |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Verification Tag |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
SctpPacket::Builder::Builder(VerificationTag verification_tag,
|
||||
const DcSctpOptions& options)
|
||||
: verification_tag_(verification_tag),
|
||||
source_port_(options.local_port),
|
||||
dest_port_(options.remote_port),
|
||||
max_mtu_(options.mtu) {}
|
||||
|
||||
SctpPacket::Builder& SctpPacket::Builder::Add(const Chunk& chunk) {
|
||||
if (out_.empty()) {
|
||||
out_.reserve(max_mtu_);
|
||||
out_.resize(SctpPacket::kHeaderSize);
|
||||
BoundedByteWriter<kHeaderSize> buffer(out_);
|
||||
buffer.Store16<0>(source_port_);
|
||||
buffer.Store16<2>(dest_port_);
|
||||
buffer.Store32<4>(*verification_tag_);
|
||||
// Checksum is at offset 8 - written when calling Build();
|
||||
}
|
||||
chunk.SerializeTo(out_);
|
||||
if (out_.size() % 4 != 0) {
|
||||
out_.resize(RoundUpTo4(out_.size()));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SctpPacket::Builder::Build() {
|
||||
std::vector<uint8_t> out;
|
||||
out_.swap(out);
|
||||
|
||||
if (!out.empty()) {
|
||||
uint32_t crc = GenerateCrc32C(out);
|
||||
BoundedByteWriter<kHeaderSize>(out).Store32<8>(crc);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
absl::optional<SctpPacket> SctpPacket::Parse(
|
||||
rtc::ArrayView<const uint8_t> data,
|
||||
bool disable_checksum_verification) {
|
||||
if (data.size() < kHeaderSize + kChunkTlvHeaderSize ||
|
||||
data.size() > kMaxUdpPacketSize) {
|
||||
RTC_DLOG(LS_WARNING) << "Invalid packet size";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
BoundedByteReader<kHeaderSize> reader(data);
|
||||
|
||||
CommonHeader common_header;
|
||||
common_header.source_port = reader.Load16<0>();
|
||||
common_header.destination_port = reader.Load16<2>();
|
||||
common_header.verification_tag = VerificationTag(reader.Load32<4>());
|
||||
common_header.checksum = reader.Load32<8>();
|
||||
|
||||
// Create a copy of the packet, which will be held by this object.
|
||||
std::vector<uint8_t> data_copy =
|
||||
std::vector<uint8_t>(data.begin(), data.end());
|
||||
|
||||
// Verify the checksum. The checksum field must be zero when that's done.
|
||||
BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>(0);
|
||||
uint32_t calculated_checksum = GenerateCrc32C(data_copy);
|
||||
if (!disable_checksum_verification &&
|
||||
calculated_checksum != common_header.checksum) {
|
||||
RTC_DLOG(LS_WARNING) << rtc::StringFormat(
|
||||
"Invalid packet checksum, packet_checksum=0x%08x, "
|
||||
"calculated_checksum=0x%08x",
|
||||
common_header.checksum, calculated_checksum);
|
||||
return absl::nullopt;
|
||||
}
|
||||
// Restore the checksum in the header.
|
||||
BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>(common_header.checksum);
|
||||
|
||||
// Validate and parse the chunk headers in the message.
|
||||
/*
|
||||
0 1 2 3
|
||||
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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Chunk Type | Chunk Flags | Chunk Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
std::vector<ChunkDescriptor> descriptors;
|
||||
descriptors.reserve(kExpectedDescriptorCount);
|
||||
rtc::ArrayView<const uint8_t> descriptor_data =
|
||||
rtc::ArrayView<const uint8_t>(data_copy).subview(kHeaderSize);
|
||||
while (!descriptor_data.empty()) {
|
||||
if (descriptor_data.size() < kChunkTlvHeaderSize) {
|
||||
RTC_DLOG(LS_WARNING) << "Too small chunk";
|
||||
return absl::nullopt;
|
||||
}
|
||||
BoundedByteReader<kChunkTlvHeaderSize> chunk_header(descriptor_data);
|
||||
uint8_t type = chunk_header.Load8<0>();
|
||||
uint8_t flags = chunk_header.Load8<1>();
|
||||
uint16_t length = chunk_header.Load16<2>();
|
||||
uint16_t padded_length = RoundUpTo4(length);
|
||||
if (padded_length > descriptor_data.size()) {
|
||||
RTC_DLOG(LS_WARNING) << "Too large chunk. length=" << length
|
||||
<< ", remaining=" << descriptor_data.size();
|
||||
return absl::nullopt;
|
||||
}
|
||||
descriptors.emplace_back(type, flags,
|
||||
descriptor_data.subview(0, padded_length));
|
||||
descriptor_data = descriptor_data.subview(padded_length);
|
||||
}
|
||||
|
||||
// Note that iterators (and pointer) are guaranteed to be stable when moving a
|
||||
// std::vector, and `descriptors` have pointers to within `data_copy`.
|
||||
return SctpPacket(common_header, std::move(data_copy),
|
||||
std::move(descriptors));
|
||||
}
|
||||
} // namespace dcsctp
|
||||
121
net/dcsctp/packet/sctp_packet.h
Normal file
121
net/dcsctp/packet/sctp_packet.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#ifndef NET_DCSCTP_PACKET_SCTP_PACKET_H_
|
||||
#define NET_DCSCTP_PACKET_SCTP_PACKET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "net/dcsctp/common/internal_types.h"
|
||||
#include "net/dcsctp/packet/chunk/chunk.h"
|
||||
#include "net/dcsctp/public/dcsctp_options.h"
|
||||
|
||||
namespace dcsctp {
|
||||
|
||||
// The "Common Header", which every SCTP packet starts with, and is described in
|
||||
// https://tools.ietf.org/html/rfc4960#section-3.1.
|
||||
struct CommonHeader {
|
||||
uint16_t source_port;
|
||||
uint16_t destination_port;
|
||||
VerificationTag verification_tag;
|
||||
uint32_t checksum;
|
||||
};
|
||||
|
||||
// Represents an immutable (received or to-be-sent) SCTP packet.
|
||||
class SctpPacket {
|
||||
public:
|
||||
static constexpr size_t kHeaderSize = 12;
|
||||
|
||||
struct ChunkDescriptor {
|
||||
ChunkDescriptor(uint8_t type,
|
||||
uint8_t flags,
|
||||
rtc::ArrayView<const uint8_t> data)
|
||||
: type(type), flags(flags), data(data) {}
|
||||
uint8_t type;
|
||||
uint8_t flags;
|
||||
rtc::ArrayView<const uint8_t> data;
|
||||
};
|
||||
|
||||
SctpPacket(SctpPacket&& other) = default;
|
||||
SctpPacket& operator=(SctpPacket&& other) = default;
|
||||
SctpPacket(const SctpPacket&) = delete;
|
||||
SctpPacket& operator=(const SctpPacket&) = delete;
|
||||
|
||||
// Used for building SctpPacket, as those are immutable.
|
||||
class Builder {
|
||||
public:
|
||||
Builder(VerificationTag verification_tag, const DcSctpOptions& options);
|
||||
|
||||
Builder(Builder&& other) = default;
|
||||
Builder& operator=(Builder&& other) = default;
|
||||
|
||||
// Adds a chunk to the to-be-built SCTP packet.
|
||||
Builder& Add(const Chunk& chunk);
|
||||
|
||||
// The number of bytes remaining in the packet, until the MTU is reached.
|
||||
size_t bytes_remaining() const {
|
||||
return out_.size() >= max_mtu_ ? 0 : max_mtu_ - out_.size();
|
||||
}
|
||||
|
||||
// Indicates if any packets have been added to the builder.
|
||||
bool empty() const { return out_.empty(); }
|
||||
|
||||
// Returns the payload of the build SCTP packet. The Builder will be cleared
|
||||
// after having called this function, and can be used to build a new packet.
|
||||
std::vector<uint8_t> Build();
|
||||
|
||||
private:
|
||||
void WritePacketHeader();
|
||||
VerificationTag verification_tag_;
|
||||
uint16_t source_port_;
|
||||
uint16_t dest_port_;
|
||||
size_t max_mtu_;
|
||||
std::vector<uint8_t> out_;
|
||||
};
|
||||
|
||||
// Parses `data` as an SCTP packet and returns it if it validates.
|
||||
static absl::optional<SctpPacket> Parse(
|
||||
rtc::ArrayView<const uint8_t> data,
|
||||
bool disable_checksum_verification = false);
|
||||
|
||||
// Returns the SCTP common header.
|
||||
const CommonHeader& common_header() const { return common_header_; }
|
||||
|
||||
// Returns the chunks (types and offsets) within the packet.
|
||||
rtc::ArrayView<const ChunkDescriptor> descriptors() const {
|
||||
return descriptors_;
|
||||
}
|
||||
|
||||
private:
|
||||
SctpPacket(const CommonHeader& common_header,
|
||||
std::vector<uint8_t> data,
|
||||
std::vector<ChunkDescriptor> descriptors)
|
||||
: common_header_(common_header),
|
||||
data_(std::move(data)),
|
||||
descriptors_(std::move(descriptors)) {}
|
||||
|
||||
CommonHeader common_header_;
|
||||
|
||||
// As the `descriptors_` refer to offset within data, and since SctpPacket is
|
||||
// movable, `data` needs to be pointer stable, which it is according to
|
||||
// http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#2321
|
||||
std::vector<uint8_t> data_;
|
||||
// The chunks and their offsets within `data_ `.
|
||||
std::vector<ChunkDescriptor> descriptors_;
|
||||
};
|
||||
} // namespace dcsctp
|
||||
|
||||
#endif // NET_DCSCTP_PACKET_SCTP_PACKET_H_
|
||||
296
net/dcsctp/packet/sctp_packet_test.cc
Normal file
296
net/dcsctp/packet/sctp_packet_test.cc
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "net/dcsctp/packet/sctp_packet.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "net/dcsctp/common/internal_types.h"
|
||||
#include "net/dcsctp/packet/chunk/abort_chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/cookie_ack_chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/data_chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/init_chunk.h"
|
||||
#include "net/dcsctp/packet/chunk/sack_chunk.h"
|
||||
#include "net/dcsctp/packet/error_cause/error_cause.h"
|
||||
#include "net/dcsctp/packet/error_cause/user_initiated_abort_cause.h"
|
||||
#include "net/dcsctp/packet/parameter/parameter.h"
|
||||
#include "net/dcsctp/packet/tlv_trait.h"
|
||||
#include "net/dcsctp/testing/testing_macros.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace dcsctp {
|
||||
namespace {
|
||||
using ::testing::SizeIs;
|
||||
|
||||
constexpr VerificationTag kVerificationTag = VerificationTag(0x12345678);
|
||||
|
||||
TEST(SctpPacketTest, DeserializeSimplePacketFromCapture) {
|
||||
/*
|
||||
Stream Control Transmission Protocol, Src Port: 5000 (5000), Dst Port: 5000
|
||||
(5000) Source port: 5000 Destination port: 5000 Verification tag: 0x00000000
|
||||
[Association index: 1]
|
||||
Checksum: 0xaa019d33 [unverified]
|
||||
[Checksum Status: Unverified]
|
||||
INIT chunk (Outbound streams: 1000, inbound streams: 1000)
|
||||
Chunk type: INIT (1)
|
||||
Chunk flags: 0x00
|
||||
Chunk length: 90
|
||||
Initiate tag: 0x0eddca08
|
||||
Advertised receiver window credit (a_rwnd): 131072
|
||||
Number of outbound streams: 1000
|
||||
Number of inbound streams: 1000
|
||||
Initial TSN: 1426601527
|
||||
ECN parameter
|
||||
Parameter type: ECN (0x8000)
|
||||
Parameter length: 4
|
||||
Forward TSN supported parameter
|
||||
Parameter type: Forward TSN supported (0xc000)
|
||||
Parameter length: 4
|
||||
Supported Extensions parameter (Supported types: FORWARD_TSN, AUTH,
|
||||
ASCONF, ASCONF_ACK, RE_CONFIG) Parameter type: Supported Extensions
|
||||
(0x8008) Parameter length: 9 Supported chunk type: FORWARD_TSN (192) Supported
|
||||
chunk type: AUTH (15) Supported chunk type: ASCONF (193) Supported chunk type:
|
||||
ASCONF_ACK (128) Supported chunk type: RE_CONFIG (130) Parameter padding:
|
||||
000000 Random parameter Parameter type: Random (0x8002) Parameter length: 36
|
||||
Random number: c5a86155090e6f420050634cc8d6b908dfd53e17c99cb143…
|
||||
Requested HMAC Algorithm parameter (Supported HMACs: SHA-1)
|
||||
Parameter type: Requested HMAC Algorithm (0x8004)
|
||||
Parameter length: 6
|
||||
HMAC identifier: SHA-1 (1)
|
||||
Parameter padding: 0000
|
||||
Authenticated Chunk list parameter (Chunk types to be authenticated:
|
||||
ASCONF_ACK, ASCONF) Parameter type: Authenticated Chunk list
|
||||
(0x8003) Parameter length: 6 Chunk type: ASCONF_ACK (128) Chunk type: ASCONF
|
||||
(193) Chunk padding: 0000
|
||||
*/
|
||||
|
||||
uint8_t data[] = {
|
||||
0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x01, 0x9d, 0x33,
|
||||
0x01, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0xca, 0x08, 0x00, 0x02, 0x00, 0x00,
|
||||
0x03, 0xe8, 0x03, 0xe8, 0x55, 0x08, 0x36, 0x37, 0x80, 0x00, 0x00, 0x04,
|
||||
0xc0, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x09, 0xc0, 0x0f, 0xc1, 0x80,
|
||||
0x82, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0xc5, 0xa8, 0x61, 0x55,
|
||||
0x09, 0x0e, 0x6f, 0x42, 0x00, 0x50, 0x63, 0x4c, 0xc8, 0xd6, 0xb9, 0x08,
|
||||
0xdf, 0xd5, 0x3e, 0x17, 0xc9, 0x9c, 0xb1, 0x43, 0x28, 0x4e, 0xaf, 0x64,
|
||||
0x68, 0x2a, 0xc2, 0x97, 0x80, 0x04, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00,
|
||||
0x80, 0x03, 0x00, 0x06, 0x80, 0xc1, 0x00, 0x00};
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(data));
|
||||
EXPECT_EQ(packet.common_header().source_port, 5000);
|
||||
EXPECT_EQ(packet.common_header().destination_port, 5000);
|
||||
EXPECT_EQ(packet.common_header().verification_tag, VerificationTag(0));
|
||||
EXPECT_EQ(packet.common_header().checksum, 0xaa019d33);
|
||||
|
||||
EXPECT_THAT(packet.descriptors(), SizeIs(1));
|
||||
EXPECT_EQ(packet.descriptors()[0].type, InitChunk::kType);
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(InitChunk init,
|
||||
InitChunk::Parse(packet.descriptors()[0].data));
|
||||
EXPECT_EQ(init.initial_tsn(), TSN(1426601527));
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, DeserializePacketWithTwoChunks) {
|
||||
/*
|
||||
Stream Control Transmission Protocol, Src Port: 1234 (1234),
|
||||
Dst Port: 4321 (4321)
|
||||
Source port: 1234
|
||||
Destination port: 4321
|
||||
Verification tag: 0x697e3a4e
|
||||
[Association index: 3]
|
||||
Checksum: 0xc06e8b36 [unverified]
|
||||
[Checksum Status: Unverified]
|
||||
COOKIE_ACK chunk
|
||||
Chunk type: COOKIE_ACK (11)
|
||||
Chunk flags: 0x00
|
||||
Chunk length: 4
|
||||
SACK chunk (Cumulative TSN: 2930332242, a_rwnd: 131072,
|
||||
gaps: 0, duplicate TSNs: 0)
|
||||
Chunk type: SACK (3)
|
||||
Chunk flags: 0x00
|
||||
Chunk length: 16
|
||||
Cumulative TSN ACK: 2930332242
|
||||
Advertised receiver window credit (a_rwnd): 131072
|
||||
Number of gap acknowledgement blocks: 0
|
||||
Number of duplicated TSNs: 0
|
||||
*/
|
||||
|
||||
uint8_t data[] = {0x04, 0xd2, 0x10, 0xe1, 0x69, 0x7e, 0x3a, 0x4e,
|
||||
0xc0, 0x6e, 0x8b, 0x36, 0x0b, 0x00, 0x00, 0x04,
|
||||
0x03, 0x00, 0x00, 0x10, 0xae, 0xa9, 0x52, 0x52,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(data));
|
||||
EXPECT_EQ(packet.common_header().source_port, 1234);
|
||||
EXPECT_EQ(packet.common_header().destination_port, 4321);
|
||||
EXPECT_EQ(packet.common_header().verification_tag,
|
||||
VerificationTag(0x697e3a4eu));
|
||||
EXPECT_EQ(packet.common_header().checksum, 0xc06e8b36u);
|
||||
|
||||
EXPECT_THAT(packet.descriptors(), SizeIs(2));
|
||||
EXPECT_EQ(packet.descriptors()[0].type, CookieAckChunk::kType);
|
||||
EXPECT_EQ(packet.descriptors()[1].type, SackChunk::kType);
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(
|
||||
CookieAckChunk cookie_ack,
|
||||
CookieAckChunk::Parse(packet.descriptors()[0].data));
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SackChunk sack,
|
||||
SackChunk::Parse(packet.descriptors()[1].data));
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, DeserializePacketWithWrongChecksum) {
|
||||
/*
|
||||
Stream Control Transmission Protocol, Src Port: 5000 (5000),
|
||||
Dst Port: 5000 (5000)
|
||||
Source port: 5000
|
||||
Destination port: 5000
|
||||
Verification tag: 0x0eddca08
|
||||
[Association index: 1]
|
||||
Checksum: 0x2a81f531 [unverified]
|
||||
[Checksum Status: Unverified]
|
||||
SACK chunk (Cumulative TSN: 1426601536, a_rwnd: 131072,
|
||||
gaps: 0, duplicate TSNs: 0)
|
||||
Chunk type: SACK (3)
|
||||
Chunk flags: 0x00
|
||||
Chunk length: 16
|
||||
Cumulative TSN ACK: 1426601536
|
||||
Advertised receiver window credit (a_rwnd): 131072
|
||||
Number of gap acknowledgement blocks: 0
|
||||
Number of duplicated TSNs: 0
|
||||
*/
|
||||
|
||||
uint8_t data[] = {0x13, 0x88, 0x13, 0x88, 0x0e, 0xdd, 0xca, 0x08, 0x2a, 0x81,
|
||||
0xf5, 0x31, 0x03, 0x00, 0x00, 0x10, 0x55, 0x08, 0x36, 0x40,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
EXPECT_FALSE(SctpPacket::Parse(data).has_value());
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, DeserializePacketDontValidateChecksum) {
|
||||
/*
|
||||
Stream Control Transmission Protocol, Src Port: 5000 (5000),
|
||||
Dst Port: 5000 (5000)
|
||||
Source port: 5000
|
||||
Destination port: 5000
|
||||
Verification tag: 0x0eddca08
|
||||
[Association index: 1]
|
||||
Checksum: 0x2a81f531 [unverified]
|
||||
[Checksum Status: Unverified]
|
||||
SACK chunk (Cumulative TSN: 1426601536, a_rwnd: 131072,
|
||||
gaps: 0, duplicate TSNs: 0)
|
||||
Chunk type: SACK (3)
|
||||
Chunk flags: 0x00
|
||||
Chunk length: 16
|
||||
Cumulative TSN ACK: 1426601536
|
||||
Advertised receiver window credit (a_rwnd): 131072
|
||||
Number of gap acknowledgement blocks: 0
|
||||
Number of duplicated TSNs: 0
|
||||
*/
|
||||
|
||||
uint8_t data[] = {0x13, 0x88, 0x13, 0x88, 0x0e, 0xdd, 0xca, 0x08, 0x2a, 0x81,
|
||||
0xf5, 0x31, 0x03, 0x00, 0x00, 0x10, 0x55, 0x08, 0x36, 0x40,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(
|
||||
SctpPacket packet,
|
||||
SctpPacket::Parse(data, /*disable_checksum_verification=*/true));
|
||||
EXPECT_EQ(packet.common_header().source_port, 5000);
|
||||
EXPECT_EQ(packet.common_header().destination_port, 5000);
|
||||
EXPECT_EQ(packet.common_header().verification_tag,
|
||||
VerificationTag(0x0eddca08u));
|
||||
EXPECT_EQ(packet.common_header().checksum, 0x2a81f531u);
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, SerializeAndDeserializeSingleChunk) {
|
||||
SctpPacket::Builder b(kVerificationTag, {});
|
||||
InitChunk init(/*initiate_tag=*/VerificationTag(123), /*a_rwnd=*/456,
|
||||
/*nbr_outbound_streams=*/65535,
|
||||
/*nbr_inbound_streams=*/65534, /*initial_tsn=*/TSN(789),
|
||||
/*parameters=*/Parameters());
|
||||
|
||||
b.Add(init);
|
||||
std::vector<uint8_t> serialized = b.Build();
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(serialized));
|
||||
|
||||
EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag);
|
||||
|
||||
ASSERT_THAT(packet.descriptors(), SizeIs(1));
|
||||
EXPECT_EQ(packet.descriptors()[0].type, InitChunk::kType);
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(InitChunk deserialized,
|
||||
InitChunk::Parse(packet.descriptors()[0].data));
|
||||
EXPECT_EQ(deserialized.initiate_tag(), VerificationTag(123));
|
||||
EXPECT_EQ(deserialized.a_rwnd(), 456u);
|
||||
EXPECT_EQ(deserialized.nbr_outbound_streams(), 65535u);
|
||||
EXPECT_EQ(deserialized.nbr_inbound_streams(), 65534u);
|
||||
EXPECT_EQ(deserialized.initial_tsn(), TSN(789));
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, SerializeAndDeserializeThreeChunks) {
|
||||
SctpPacket::Builder b(kVerificationTag, {});
|
||||
b.Add(SackChunk(/*cumulative_tsn_ack=*/TSN(999), /*a_rwnd=*/456,
|
||||
{SackChunk::GapAckBlock(2, 3)},
|
||||
/*duplicate_tsns=*/{TSN(1), TSN(2), TSN(3)}));
|
||||
b.Add(DataChunk(TSN(123), StreamID(456), SSN(789), PPID(9090),
|
||||
/*payload=*/{1, 2, 3, 4, 5},
|
||||
/*options=*/{}));
|
||||
b.Add(DataChunk(TSN(124), StreamID(654), SSN(987), PPID(909),
|
||||
/*payload=*/{5, 4, 3, 3, 1},
|
||||
/*options=*/{}));
|
||||
|
||||
std::vector<uint8_t> serialized = b.Build();
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(serialized));
|
||||
|
||||
EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag);
|
||||
|
||||
ASSERT_THAT(packet.descriptors(), SizeIs(3));
|
||||
EXPECT_EQ(packet.descriptors()[0].type, SackChunk::kType);
|
||||
EXPECT_EQ(packet.descriptors()[1].type, DataChunk::kType);
|
||||
EXPECT_EQ(packet.descriptors()[2].type, DataChunk::kType);
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SackChunk sack,
|
||||
SackChunk::Parse(packet.descriptors()[0].data));
|
||||
EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(999));
|
||||
EXPECT_EQ(sack.a_rwnd(), 456u);
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(DataChunk data1,
|
||||
DataChunk::Parse(packet.descriptors()[1].data));
|
||||
EXPECT_EQ(data1.tsn(), TSN(123));
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(DataChunk data2,
|
||||
DataChunk::Parse(packet.descriptors()[2].data));
|
||||
EXPECT_EQ(data2.tsn(), TSN(124));
|
||||
}
|
||||
|
||||
TEST(SctpPacketTest, ParseAbortWithEmptyCause) {
|
||||
SctpPacket::Builder b(kVerificationTag, {});
|
||||
b.Add(AbortChunk(
|
||||
/*filled_in_verification_tag=*/true,
|
||||
Parameters::Builder().Add(UserInitiatedAbortCause("")).Build()));
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(b.Build()));
|
||||
|
||||
EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag);
|
||||
|
||||
ASSERT_THAT(packet.descriptors(), SizeIs(1));
|
||||
EXPECT_EQ(packet.descriptors()[0].type, AbortChunk::kType);
|
||||
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(AbortChunk abort,
|
||||
AbortChunk::Parse(packet.descriptors()[0].data));
|
||||
ASSERT_HAS_VALUE_AND_ASSIGN(
|
||||
UserInitiatedAbortCause cause,
|
||||
abort.error_causes().get<UserInitiatedAbortCause>());
|
||||
EXPECT_EQ(cause.upper_layer_abort_reason(), "");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace dcsctp
|
||||
Loading…
x
Reference in New Issue
Block a user