File
Blob: firmware/vendor/sctp-proto/src/chunk/chunk_selective_ack.rs
| 1 | use super::{chunk_header::*, chunk_type::*, *}; |
| 2 | |
| 3 | ///chunkSelectiveAck represents an SCTP Chunk of type SACK |
| 4 | /// |
| 5 | ///This chunk is sent to the peer endpoint to acknowledge received DATA |
| 6 | ///chunks and to inform the peer endpoint of gaps in the received |
| 7 | ///subsequences of DATA chunks as represented by their TSNs. |
| 8 | ///0 1 2 3 |
| 9 | ///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 |
| 10 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 11 | ///| Type = 3 |Chunk Flags | Chunk Length | |
| 12 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 13 | ///| Cumulative TSN Ack | |
| 14 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 15 | ///| Advertised Receiver Window Credit (a_rwnd) | |
| 16 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 17 | ///| Number of Gap Ack Blocks = N | Number of Duplicate TSNs = X | |
| 18 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 19 | ///| Gap Ack Block #1 Start | Gap Ack Block #1 End | |
| 20 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 21 | ///| | |
| 22 | ///| ... | |
| 23 | ///| | |
| 24 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 25 | ///| Gap Ack Block #N Start | Gap Ack Block #N End | |
| 26 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 27 | ///| Duplicate TSN 1 | |
| 28 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 29 | ///| | |
| 30 | ///| ... | |
| 31 | ///| | |
| 32 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 33 | ///| Duplicate TSN X | |
| 34 | ///+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| 35 | #[derive(Debug, Default, Copy, Clone)] |
| 36 | pub(crate) struct GapAckBlock { |
| 37 | pub(crate) start: u16, |
| 38 | pub(crate) end: u16, |
| 39 | } |
| 40 | |
| 41 | /// makes gapAckBlock printable |
| 42 | impl fmt::Display for GapAckBlock { |
| 43 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 44 | write!(f, "{} - {}", self.start, self.end) |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | #[derive(Default, Debug)] |
| 49 | pub(crate) struct ChunkSelectiveAck { |
| 50 | pub(crate) cumulative_tsn_ack: u32, |
| 51 | pub(crate) advertised_receiver_window_credit: u32, |
| 52 | pub(crate) gap_ack_blocks: Vec<GapAckBlock>, |
| 53 | pub(crate) duplicate_tsn: Vec<u32>, |
| 54 | } |
| 55 | |
| 56 | /// makes chunkSelectiveAck printable |
| 57 | impl fmt::Display for ChunkSelectiveAck { |
| 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 59 | let mut res = format!( |
| 60 | "SACK cumTsnAck={} arwnd={} dupTsn={:?}", |
| 61 | self.cumulative_tsn_ack, self.advertised_receiver_window_credit, self.duplicate_tsn |
| 62 | ); |
| 63 | |
| 64 | for gap in &self.gap_ack_blocks { |
| 65 | res += format!("\n gap ack: {}", gap).as_str(); |
| 66 | } |
| 67 | |
| 68 | write!(f, "{}", res) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | pub(crate) const SELECTIVE_ACK_HEADER_SIZE: usize = 12; |
| 73 | |
| 74 | impl Chunk for ChunkSelectiveAck { |
| 75 | fn header(&self) -> ChunkHeader { |
| 76 | ChunkHeader { |
| 77 | typ: CT_SACK, |
| 78 | flags: 0, |
| 79 | value_length: self.value_length() as u16, |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | fn unmarshal(raw: &Bytes) -> Result<Self> { |
| 84 | let header = ChunkHeader::unmarshal(raw)?; |
| 85 | |
| 86 | if header.typ != CT_SACK { |
| 87 | return Err(Error::ErrChunkTypeNotSack); |
| 88 | } |
| 89 | |
| 90 | if header.value_length() < SELECTIVE_ACK_HEADER_SIZE { |
| 91 | return Err(Error::ErrSackSizeNotLargeEnoughInfo); |
| 92 | } |
| 93 | |
| 94 | let reader = &mut raw.slice(CHUNK_HEADER_SIZE..CHUNK_HEADER_SIZE + header.value_length()); |
| 95 | |
| 96 | let cumulative_tsn_ack = reader.get_u32(); |
| 97 | let advertised_receiver_window_credit = reader.get_u32(); |
| 98 | let gap_ack_blocks_len = reader.get_u16() as usize; |
| 99 | let duplicate_tsn_len = reader.get_u16() as usize; |
| 100 | |
| 101 | // The reader is bounded by header.value_length(), so we |
| 102 | // must check against that, not raw.len() which may include |
| 103 | // subsequent chunks. |
| 104 | if header.value_length() |
| 105 | < SELECTIVE_ACK_HEADER_SIZE + (4 * gap_ack_blocks_len + 4 * duplicate_tsn_len) |
| 106 | { |
| 107 | return Err(Error::ErrSackSizeNotLargeEnoughInfo); |
| 108 | } |
| 109 | |
| 110 | let mut gap_ack_blocks = vec![]; |
| 111 | let mut duplicate_tsn = vec![]; |
| 112 | for _ in 0..gap_ack_blocks_len { |
| 113 | let start = reader.get_u16(); |
| 114 | let end = reader.get_u16(); |
| 115 | gap_ack_blocks.push(GapAckBlock { start, end }); |
| 116 | } |
| 117 | for _ in 0..duplicate_tsn_len { |
| 118 | duplicate_tsn.push(reader.get_u32()); |
| 119 | } |
| 120 | |
| 121 | Ok(ChunkSelectiveAck { |
| 122 | cumulative_tsn_ack, |
| 123 | advertised_receiver_window_credit, |
| 124 | gap_ack_blocks, |
| 125 | duplicate_tsn, |
| 126 | }) |
| 127 | } |
| 128 | |
| 129 | fn marshal_to(&self, writer: &mut BytesMut) -> Result<usize> { |
| 130 | self.header().marshal_to(writer)?; |
| 131 | |
| 132 | writer.put_u32(self.cumulative_tsn_ack); |
| 133 | writer.put_u32(self.advertised_receiver_window_credit); |
| 134 | writer.put_u16(self.gap_ack_blocks.len() as u16); |
| 135 | writer.put_u16(self.duplicate_tsn.len() as u16); |
| 136 | for g in &self.gap_ack_blocks { |
| 137 | writer.put_u16(g.start); |
| 138 | writer.put_u16(g.end); |
| 139 | } |
| 140 | for t in &self.duplicate_tsn { |
| 141 | writer.put_u32(*t); |
| 142 | } |
| 143 | |
| 144 | Ok(writer.len()) |
| 145 | } |
| 146 | |
| 147 | fn check(&self) -> Result<()> { |
| 148 | Ok(()) |
| 149 | } |
| 150 | |
| 151 | fn value_length(&self) -> usize { |
| 152 | SELECTIVE_ACK_HEADER_SIZE + self.gap_ack_blocks.len() * 4 + self.duplicate_tsn.len() * 4 |
| 153 | } |
| 154 | |
| 155 | fn as_any(&self) -> &(dyn Any + Send + Sync) { |
| 156 | self |
| 157 | } |
| 158 | } |