/* * 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 #include #include #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 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 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