| /* |
| * 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/chunk_validators.h" |
| |
| #include <algorithm> |
| #include <utility> |
| #include <vector> |
| |
| #include "net/dcsctp/packet/chunk/sack_chunk.h" |
| #include "rtc_base/logging.h" |
| |
| namespace dcsctp { |
| |
| SackChunk ChunkValidators::Clean(SackChunk&& sack) { |
| if (Validate(sack)) { |
| return std::move(sack); |
| } |
| |
| RTC_DLOG(LS_WARNING) << "Received SACK is malformed; cleaning it"; |
| |
| std::vector<SackChunk::GapAckBlock> gap_ack_blocks; |
| gap_ack_blocks.reserve(sack.gap_ack_blocks().size()); |
| |
| // First: Only keep blocks that are sane |
| for (const SackChunk::GapAckBlock& gap_ack_block : sack.gap_ack_blocks()) { |
| if (gap_ack_block.end > gap_ack_block.start) { |
| gap_ack_blocks.emplace_back(gap_ack_block); |
| } |
| } |
| |
| // Not more than at most one remaining? Exit early. |
| if (gap_ack_blocks.size() <= 1) { |
| return SackChunk(sack.cumulative_tsn_ack(), sack.a_rwnd(), |
| std::move(gap_ack_blocks), sack.duplicate_tsns()); |
| } |
| |
| // Sort the intervals by their start value, to aid in the merging below. |
| absl::c_sort(gap_ack_blocks, [&](const SackChunk::GapAckBlock& a, |
| const SackChunk::GapAckBlock& b) { |
| return a.start < b.start; |
| }); |
| |
| // Merge overlapping ranges. |
| std::vector<SackChunk::GapAckBlock> merged; |
| merged.reserve(gap_ack_blocks.size()); |
| merged.push_back(gap_ack_blocks[0]); |
| |
| for (size_t i = 1; i < gap_ack_blocks.size(); ++i) { |
| if (merged.back().end + 1 >= gap_ack_blocks[i].start) { |
| merged.back().end = std::max(merged.back().end, gap_ack_blocks[i].end); |
| } else { |
| merged.push_back(gap_ack_blocks[i]); |
| } |
| } |
| |
| return SackChunk(sack.cumulative_tsn_ack(), sack.a_rwnd(), std::move(merged), |
| sack.duplicate_tsns()); |
| } |
| |
| bool ChunkValidators::Validate(const SackChunk& sack) { |
| if (sack.gap_ack_blocks().empty()) { |
| return true; |
| } |
| |
| // Ensure that gap-ack-blocks are sorted, has an "end" that is not before |
| // "start" and are non-overlapping and non-adjacent. |
| uint16_t prev_end = 0; |
| for (const SackChunk::GapAckBlock& gap_ack_block : sack.gap_ack_blocks()) { |
| if (gap_ack_block.end < gap_ack_block.start) { |
| return false; |
| } |
| if (gap_ack_block.start <= (prev_end + 1)) { |
| return false; |
| } |
| prev_end = gap_ack_block.end; |
| } |
| return true; |
| } |
| |
| } // namespace dcsctp |