/* * 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 #include #include #include "api/array_view.h" #include "net/dcsctp/common/internal_types.h" #include "net/dcsctp/common/math.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/public/dcsctp_options.h" #include "net/dcsctp/testing/testing_macros.h" #include "rtc_base/gunit.h" #include "test/gmock.h" namespace dcsctp { namespace { using ::testing::ElementsAre; using ::testing::SizeIs; constexpr VerificationTag kVerificationTag = VerificationTag(0x12345678); constexpr DcSctpOptions kVerifyChecksumOptions = DcSctpOptions{.disable_checksum_verification = false, .enable_zero_checksum = false}; 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, kVerifyChecksumOptions)); 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, kVerifyChecksumOptions)); 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, kVerifyChecksumOptions).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, .enable_zero_checksum = false})); 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 serialized = b.Build(); ASSERT_HAS_VALUE_AND_ASSIGN( SctpPacket packet, SctpPacket::Parse(serialized, kVerifyChecksumOptions)); 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 serialized = b.Build(); ASSERT_HAS_VALUE_AND_ASSIGN( SctpPacket packet, SctpPacket::Parse(serialized, kVerifyChecksumOptions)); 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(), kVerifyChecksumOptions)); 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()); EXPECT_EQ(cause.upper_layer_abort_reason(), ""); } TEST(SctpPacketTest, DetectPacketWithZeroSizeChunk) { uint8_t data[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0a, 0x0a, 0x5c, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00}; EXPECT_FALSE(SctpPacket::Parse(data, kVerifyChecksumOptions).has_value()); } TEST(SctpPacketTest, ReturnsCorrectSpaceAvailableToStayWithinMTU) { DcSctpOptions options; options.mtu = 1191; SctpPacket::Builder builder(VerificationTag(123), options); // Chunks will be padded to an even 4 bytes, so the maximum packet size should // be rounded down. const size_t kMaxPacketSize = RoundDownTo4(options.mtu); EXPECT_EQ(kMaxPacketSize, 1188u); const size_t kSctpHeaderSize = 12; EXPECT_EQ(builder.bytes_remaining(), kMaxPacketSize - kSctpHeaderSize); EXPECT_EQ(builder.bytes_remaining(), 1176u); // Add a smaller packet first. DataChunk::Options data_options; std::vector payload1(183); builder.Add( DataChunk(TSN(1), StreamID(1), SSN(0), PPID(53), payload1, data_options)); size_t chunk1_size = RoundUpTo4(DataChunk::kHeaderSize + payload1.size()); EXPECT_EQ(builder.bytes_remaining(), kMaxPacketSize - kSctpHeaderSize - chunk1_size); EXPECT_EQ(builder.bytes_remaining(), 976u); // Hand-calculated. std::vector payload2(957); builder.Add( DataChunk(TSN(1), StreamID(1), SSN(0), PPID(53), payload2, data_options)); size_t chunk2_size = RoundUpTo4(DataChunk::kHeaderSize + payload2.size()); EXPECT_EQ(builder.bytes_remaining(), kMaxPacketSize - kSctpHeaderSize - chunk1_size - chunk2_size); EXPECT_EQ(builder.bytes_remaining(), 0u); // Hand-calculated. } TEST(SctpPacketTest, AcceptsZeroSetZeroChecksum) { /* 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: 0x00000000 [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, 0x00, 0x00, 0x00, 0x00, 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 = false, .enable_zero_checksum = 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, 0x00000000u); } TEST(SctpPacketTest, RejectsNonZeroIncorrectChecksumWhenZeroChecksumIsActive) { /* 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: 0x00000001 [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, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x55, 0x08, 0x36, 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; EXPECT_FALSE(SctpPacket::Parse(data, {.disable_checksum_verification = false, .enable_zero_checksum = true}) .has_value()); } TEST(SctpPacketTest, WritePacketWithCalculatedChecksum) { SctpPacket::Builder b(kVerificationTag, {}); b.Add(SackChunk(/*cumulative_tsn_ack=*/TSN(999), /*a_rwnd=*/456, /*gap_ack_blocks=*/{}, /*duplicate_tsns=*/{})); EXPECT_THAT(b.Build(), ElementsAre(0x13, 0x88, 0x13, 0x88, 0x12, 0x34, 0x56, 0x78, // 0x07, 0xe8, 0x38, 0x77, // checksum 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00)); } TEST(SctpPacketTest, WritePacketWithZeroChecksum) { SctpPacket::Builder b(kVerificationTag, {}); b.Add(SackChunk(/*cumulative_tsn_ack=*/TSN(999), /*a_rwnd=*/456, /*gap_ack_blocks=*/{}, /*duplicate_tsns=*/{})); EXPECT_THAT(b.Build(/*write_checksum=*/false), ElementsAre(0x13, 0x88, 0x13, 0x88, 0x12, 0x34, 0x56, 0x78, // 0x00, 0x00, 0x00, 0x00, // checksum 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0xe7, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00)); } } // namespace } // namespace dcsctp