@@ -47,6 +47,14 @@ class SCTPFields(str, Enum):
47
47
CHUNK_INIT_ACK_NUMBER_OF_OUTBOUND_STREAMS = f'{ SCTP_HEADER_ID } :Init Ack Number of Outbound Streams'
48
48
CHUNK_INIT_ACK_NUMBER_OF_INBOUND_STREAMS = f'{ SCTP_HEADER_ID } :Init Ack Number of Inbound Streams'
49
49
CHUNK_INIT_ACK_INITIAL_TSN = f'{ SCTP_HEADER_ID } :Init Ack Initial TSN'
50
+
51
+ CHUNK_SACK_CUMULATIVE_TSN_ACK = f'{ SCTP_HEADER_ID } :Selective Ack Cumulative TSN Ack'
52
+ CHUNK_SACK_ADVERTISED_RECEIVER_WINDOW_CREDIT = f'{ SCTP_HEADER_ID } :Selective Ack Advertised Receiver Window Credit'
53
+ CHUNK_SACK_NUMBER_GAP_ACK_BLOCKS = f'{ SCTP_HEADER_ID } :Selective Ack Number Gap Ack Blocks'
54
+ CHUNK_SACK_NUMBER_DUPLICATE_TSNS = f'{ SCTP_HEADER_ID } :Selective Ack Number Duplicate TSNs'
55
+ CHUNK_SACK_GAP_ACK_BLOCK_START = f'{ SCTP_HEADER_ID } :Selective Ack Gap Ack BLock Start'
56
+ CHUNK_SACK_GAP_ACK_BLOCK_END = f'{ SCTP_HEADER_ID } :Selective Ack Gap Ack BLock End'
57
+ CHUNK_SACK_DUPLICATE_TSN = f'{ SCTP_HEADER_ID } :Selective Ack Duplicate TSN'
50
58
51
59
PARAMETER_TYPE = f'{ SCTP_HEADER_ID } :Parameter Type'
52
60
PARAMETER_LENGTH = f'{ SCTP_HEADER_ID } :Parameter Length'
@@ -186,16 +194,17 @@ def _parse_chunk(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
186
194
187
195
if chunk_type_value == SCTPChunkTypes .DATA :
188
196
chunk_fields : List [FieldDescriptor ] = self ._parse_chunk_data (chunk_value )
189
- fields . extend ( chunk_fields )
197
+
190
198
elif chunk_type_value == SCTPChunkTypes .INIT :
191
199
chunk_fields : List [FieldDescriptor ] = self ._parse_chunk_init (chunk_value )
192
- fields .extend (chunk_fields )
193
200
elif chunk_type_value == SCTPChunkTypes .INIT_ACK :
194
201
chunk_fields : List [FieldDescriptor ] = self ._parse_chunk_init_ack (chunk_value )
195
- fields .extend (chunk_fields )
202
+ elif chunk_type_value == SCTPChunkTypes .SACK :
203
+ chunk_fields : List [FieldDescriptor ] = self ._parse_chunk_selective_ack (chunk_value )
196
204
else :
197
- fields .append (FieldDescriptor (id = SCTPFields .CHUNK_VALUE , position = 0 , value = chunk_value ))
198
-
205
+ chunk_fields : List [FieldDescriptor ] = [FieldDescriptor (id = SCTPFields .CHUNK_VALUE , position = 0 , value = chunk_value )]
206
+ fields .extend (chunk_fields )
207
+
199
208
chunk_padding_length : int = (32 - chunk_length_value % 32 )% 32
200
209
if chunk_padding_length > 0 :
201
210
chunk_padding : Buffer = buffer [chunk_length_value : chunk_length_value + chunk_padding_length ]
@@ -328,6 +337,74 @@ def _parse_chunk_init_ack(self, buffer: Buffer) -> List[FieldDescriptor]:
328
337
parameters = parameters [bits_consumed :]
329
338
330
339
return fields
340
+
341
+
342
+ def _parse_chunk_selective_ack (self , buffer : Buffer ) -> List [FieldDescriptor ]:
343
+ """
344
+ 0 1 2 3
345
+ 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
346
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347
+ | Type = 3 | Chunk Flags | Chunk Length |
348
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
349
+ | Cumulative TSN Ack |
350
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
351
+ | Advertised Receiver Window Credit (a_rwnd) |
352
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
353
+ | Number of Gap Ack Blocks = N | Number of Duplicate TSNs = M |
354
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355
+ | Gap Ack Block #1 Start | Gap Ack Block #1 End |
356
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
357
+ / /
358
+ \ ... \
359
+ / /
360
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
361
+ | Gap Ack Block #N Start | Gap Ack Block #N End |
362
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363
+ | Duplicate TSN 1 |
364
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365
+ / /
366
+ \ ... \
367
+ / /
368
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369
+ | Duplicate TSN M |
370
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371
+ """
372
+ fields : List [FieldDescriptor ] = []
373
+ cumulative_tsn_ack : Buffer = buffer [0 :32 ]
374
+ advertised_receiver_window_credit : Buffer = buffer [32 :64 ]
375
+ number_gap_ack_blocks : Buffer = buffer [64 :80 ]
376
+ number_duplicate_tsns : Buffer = buffer [80 :96 ]
377
+
378
+ fields .extend ([
379
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_CUMULATIVE_TSN_ACK , value = cumulative_tsn_ack , position = 0 ),
380
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_ADVERTISED_RECEIVER_WINDOW_CREDIT , value = advertised_receiver_window_credit , position = 0 ),
381
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_NUMBER_GAP_ACK_BLOCKS , value = number_gap_ack_blocks , position = 0 ),
382
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_NUMBER_DUPLICATE_TSNS , value = number_duplicate_tsns , position = 0 )
383
+ ])
384
+
385
+ remainer : Buffer = buffer [96 :]
386
+ # Gap ack Blocks
387
+ number_gap_ack_blocks_value : int = number_gap_ack_blocks .value ()
388
+
389
+ for _ in range (number_gap_ack_blocks_value ):
390
+ gap_ack_block_start : Buffer = remainer [0 :16 ]
391
+ gap_ack_block_end : Buffer = remainer [16 :32 ]
392
+ fields .extend ([
393
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_GAP_ACK_BLOCK_START , value = gap_ack_block_start ),
394
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_GAP_ACK_BLOCK_END , value = gap_ack_block_end )
395
+ ])
396
+ remainer = remainer [32 :]
397
+
398
+ # Duplicate TSNs
399
+ number_duplicate_tsns_value : int = number_duplicate_tsns .value ()
400
+ for _ in range (number_duplicate_tsns_value ):
401
+ duplicate_tsn : Buffer = remainer [0 :32 ]
402
+ fields .extend ([
403
+ FieldDescriptor (id = SCTPFields .CHUNK_SACK_DUPLICATE_TSN , value = duplicate_tsn ),
404
+ ])
405
+ remainer = remainer [32 :]
406
+
407
+ return fields
331
408
332
409
def _parse_parameter (self , buffer : Buffer ) -> Tuple [List [FieldDescriptor ], int ]:
333
410
"""
0 commit comments