xref: /aosp_15_r20/external/webrtc/net/dcsctp/packet/chunk_validators.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/chunk_validators.h"
11 
12 #include <algorithm>
13 #include <utility>
14 #include <vector>
15 
16 #include "net/dcsctp/packet/chunk/sack_chunk.h"
17 #include "rtc_base/logging.h"
18 
19 namespace dcsctp {
20 
Clean(SackChunk && sack)21 SackChunk ChunkValidators::Clean(SackChunk&& sack) {
22   if (Validate(sack)) {
23     return std::move(sack);
24   }
25 
26   RTC_DLOG(LS_WARNING) << "Received SACK is malformed; cleaning it";
27 
28   std::vector<SackChunk::GapAckBlock> gap_ack_blocks;
29   gap_ack_blocks.reserve(sack.gap_ack_blocks().size());
30 
31   // First: Only keep blocks that are sane
32   for (const SackChunk::GapAckBlock& gap_ack_block : sack.gap_ack_blocks()) {
33     if (gap_ack_block.end > gap_ack_block.start) {
34       gap_ack_blocks.emplace_back(gap_ack_block);
35     }
36   }
37 
38   // Not more than at most one remaining? Exit early.
39   if (gap_ack_blocks.size() <= 1) {
40     return SackChunk(sack.cumulative_tsn_ack(), sack.a_rwnd(),
41                      std::move(gap_ack_blocks), sack.duplicate_tsns());
42   }
43 
44   // Sort the intervals by their start value, to aid in the merging below.
45   absl::c_sort(gap_ack_blocks, [&](const SackChunk::GapAckBlock& a,
46                                    const SackChunk::GapAckBlock& b) {
47     return a.start < b.start;
48   });
49 
50   // Merge overlapping ranges.
51   std::vector<SackChunk::GapAckBlock> merged;
52   merged.reserve(gap_ack_blocks.size());
53   merged.push_back(gap_ack_blocks[0]);
54 
55   for (size_t i = 1; i < gap_ack_blocks.size(); ++i) {
56     if (merged.back().end + 1 >= gap_ack_blocks[i].start) {
57       merged.back().end = std::max(merged.back().end, gap_ack_blocks[i].end);
58     } else {
59       merged.push_back(gap_ack_blocks[i]);
60     }
61   }
62 
63   return SackChunk(sack.cumulative_tsn_ack(), sack.a_rwnd(), std::move(merged),
64                    sack.duplicate_tsns());
65 }
66 
Validate(const SackChunk & sack)67 bool ChunkValidators::Validate(const SackChunk& sack) {
68   if (sack.gap_ack_blocks().empty()) {
69     return true;
70   }
71 
72   // Ensure that gap-ack-blocks are sorted, has an "end" that is not before
73   // "start" and are non-overlapping and non-adjacent.
74   uint16_t prev_end = 0;
75   for (const SackChunk::GapAckBlock& gap_ack_block : sack.gap_ack_blocks()) {
76     if (gap_ack_block.end < gap_ack_block.start) {
77       return false;
78     }
79     if (gap_ack_block.start <= (prev_end + 1)) {
80       return false;
81     }
82     prev_end = gap_ack_block.end;
83   }
84   return true;
85 }
86 
87 }  // namespace dcsctp
88