Skip to content

Commit a98e2c4

Browse files
committed
[wip] SCTP init ack chunk parsing
1 parent 6827423 commit a98e2c4

File tree

2 files changed

+238
-29
lines changed

2 files changed

+238
-29
lines changed

microschc/protocol/sctp.py

+78-26
Original file line numberDiff line numberDiff line change
@@ -55,32 +55,38 @@ class SCTPFields(str, Enum):
5555
SCTP_HEADER_ID = 'SCTP'
5656

5757
class SCTPFields(str, Enum):
58-
SOURCE_PORT = f'{SCTP_HEADER_ID}:Source Port'
59-
DESTINATION_PORT = f'{SCTP_HEADER_ID}:Destination Port'
60-
VERIFICATION_TAG = f'{SCTP_HEADER_ID}:Verification Tag'
61-
CHECKSUM = f'{SCTP_HEADER_ID}:Checksum'
62-
CHUNK_TYPE = f'{SCTP_HEADER_ID}:Chunk Type'
63-
CHUNK_FLAGS = f'{SCTP_HEADER_ID}:Chunk Flags'
64-
CHUNK_LENGTH = f'{SCTP_HEADER_ID}:Chunk Length'
65-
CHUNK_VALUE = f'{SCTP_HEADER_ID}:Chunk Value'
66-
CHUNK_PADDING = f'{SCTP_HEADER_ID}:Chunk Padding'
67-
68-
CHUNK_DATA_TSN = f'{SCTP_HEADER_ID}:Data TSN'
69-
CHUNK_DATA_STREAM_IDENTIFIER = f'{SCTP_HEADER_ID}:Data Stream Identifier S'
70-
CHUNK_DATA_STREAM_SEQUENCE_NUMBER = f'{SCTP_HEADER_ID}:Data Stream Sequence Number n'
71-
CHUNK_DATA_PAYLOAD_PROTOCOL_IDENTIFIER = f'{SCTP_HEADER_ID}:Data Payload Protocol Identifier'
72-
CHUNK_DATA_PAYLOAD = f'{SCTP_HEADER_ID}:Data Payload'
73-
74-
CHUNK_INIT_INITIATE_TAG = f'{SCTP_HEADER_ID}:Init Initiate Tag'
75-
CHUNK_INIT_ADVERTISED_RECEIVER_WINDOW_CREDIT = f'{SCTP_HEADER_ID}:Init Advertised Receiver Window Credit'
76-
CHUNK_INIT_NUMBER_OF_OUTBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Number of Outbound Streams'
77-
CHUNK_INIT_NUMBER_OF_INBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Number of Inbound Streams'
78-
CHUNK_INIT_INITIAL_TSN = f'{SCTP_HEADER_ID}:Init Initial TSN'
79-
80-
PARAMETER_TYPE = f'{SCTP_HEADER_ID}:Parameter Type'
81-
PARAMETER_LENGTH = f'{SCTP_HEADER_ID}:Parameter Length'
82-
PARAMETER_VALUE = f'{SCTP_HEADER_ID}:Parameter Value'
83-
PARAMETER_PADDING = f'{SCTP_HEADER_ID}:Parameter Padding'
58+
SOURCE_PORT = f'{SCTP_HEADER_ID}:Source Port'
59+
DESTINATION_PORT = f'{SCTP_HEADER_ID}:Destination Port'
60+
VERIFICATION_TAG = f'{SCTP_HEADER_ID}:Verification Tag'
61+
CHECKSUM = f'{SCTP_HEADER_ID}:Checksum'
62+
CHUNK_TYPE = f'{SCTP_HEADER_ID}:Chunk Type'
63+
CHUNK_FLAGS = f'{SCTP_HEADER_ID}:Chunk Flags'
64+
CHUNK_LENGTH = f'{SCTP_HEADER_ID}:Chunk Length'
65+
CHUNK_VALUE = f'{SCTP_HEADER_ID}:Chunk Value'
66+
CHUNK_PADDING = f'{SCTP_HEADER_ID}:Chunk Padding'
67+
68+
CHUNK_DATA_TSN = f'{SCTP_HEADER_ID}:Data TSN'
69+
CHUNK_DATA_STREAM_IDENTIFIER = f'{SCTP_HEADER_ID}:Data Stream Identifier S'
70+
CHUNK_DATA_STREAM_SEQUENCE_NUMBER = f'{SCTP_HEADER_ID}:Data Stream Sequence Number n'
71+
CHUNK_DATA_PAYLOAD_PROTOCOL_IDENTIFIER = f'{SCTP_HEADER_ID}:Data Payload Protocol Identifier'
72+
CHUNK_DATA_PAYLOAD = f'{SCTP_HEADER_ID}:Data Payload'
73+
74+
CHUNK_INIT_INITIATE_TAG = f'{SCTP_HEADER_ID}:Init Initiate Tag'
75+
CHUNK_INIT_ADVERTISED_RECEIVER_WINDOW_CREDIT = f'{SCTP_HEADER_ID}:Init Advertised Receiver Window Credit'
76+
CHUNK_INIT_NUMBER_OF_OUTBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Number of Outbound Streams'
77+
CHUNK_INIT_NUMBER_OF_INBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Number of Inbound Streams'
78+
CHUNK_INIT_INITIAL_TSN = f'{SCTP_HEADER_ID}:Init Initial TSN'
79+
80+
CHUNK_INIT_ACK_INITIATE_TAG = f'{SCTP_HEADER_ID}:Init Ack Initiate Tag'
81+
CHUNK_INIT_ACK_ADVERTISED_RECEIVER_WINDOW_CREDIT = f'{SCTP_HEADER_ID}:Init Ack Advertised Receiver Window Credit'
82+
CHUNK_INIT_ACK_NUMBER_OF_OUTBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Ack Number of Outbound Streams'
83+
CHUNK_INIT_ACK_NUMBER_OF_INBOUND_STREAMS = f'{SCTP_HEADER_ID}:Init Ack Number of Inbound Streams'
84+
CHUNK_INIT_ACK_INITIAL_TSN = f'{SCTP_HEADER_ID}:Init Ack Initial TSN'
85+
86+
PARAMETER_TYPE = f'{SCTP_HEADER_ID}:Parameter Type'
87+
PARAMETER_LENGTH = f'{SCTP_HEADER_ID}:Parameter Length'
88+
PARAMETER_VALUE = f'{SCTP_HEADER_ID}:Parameter Value'
89+
PARAMETER_PADDING = f'{SCTP_HEADER_ID}:Parameter Padding'
8490

8591

8692

@@ -219,6 +225,9 @@ def _parse_chunk(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
219225
elif chunk_type_value == SCTPChunkTypes.INIT:
220226
chunk_fields: List[FieldDescriptor] = self._parse_chunk_init(chunk_value)
221227
fields.extend(chunk_fields)
228+
elif chunk_type_value == SCTPChunkTypes.INIT_ACK:
229+
chunk_fields: List[FieldDescriptor] = self._parse_chunk_init_ack(chunk_value)
230+
fields.extend(chunk_fields)
222231
else:
223232
fields.append(FieldDescriptor(id=SCTPFields.CHUNK_VALUE, position=0, value=chunk_value))
224233

@@ -311,6 +320,49 @@ def _parse_chunk_init(self, buffer: Buffer) -> List[FieldDescriptor]:
311320
parameters = parameters[bits_consumed:]
312321

313322
return fields
323+
324+
def _parse_chunk_init_ack(self, buffer: Buffer) -> List[FieldDescriptor]:
325+
"""
326+
0 1 2 3
327+
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
328+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
329+
| Type = 2 | Chunk Flags | Chunk Length |
330+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
331+
| Initiate Tag |
332+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
333+
| Advertised Receiver Window Credit (a_rwnd) |
334+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
335+
| Number of Outbound Streams | Number of Inbound Streams |
336+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
337+
| Initial TSN |
338+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
339+
\ \
340+
/ Optional/Variable-Length Parameters /
341+
\ \
342+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
343+
"""
344+
fields: List[FieldDescriptor] = []
345+
initiate_tag: Buffer = buffer[0:32]
346+
advertised_receiver_window_credit: Buffer = buffer[32:64]
347+
number_outbound_streams: Buffer = buffer[64:80]
348+
number_inbound_streams: Buffer = buffer[80:96]
349+
initial_tsn: Buffer = buffer[96:128]
350+
351+
fields.extend([
352+
FieldDescriptor(id=SCTPFields.CHUNK_INIT_ACK_INITIATE_TAG, value=initiate_tag, position=0),
353+
FieldDescriptor(id=SCTPFields.CHUNK_INIT_ACK_ADVERTISED_RECEIVER_WINDOW_CREDIT, value=advertised_receiver_window_credit, position=0),
354+
FieldDescriptor(id=SCTPFields.CHUNK_INIT_ACK_NUMBER_OF_OUTBOUND_STREAMS, value=number_outbound_streams, position=0),
355+
FieldDescriptor(id=SCTPFields.CHUNK_INIT_ACK_NUMBER_OF_INBOUND_STREAMS, value=number_inbound_streams, position=0),
356+
FieldDescriptor(id=SCTPFields.CHUNK_INIT_ACK_INITIAL_TSN, value=initial_tsn, position=0)
357+
])
358+
359+
parameters = buffer[128:]
360+
while parameters.length > 0:
361+
parameter_fields, bits_consumed = self._parse_parameter(parameters)
362+
fields.extend(parameter_fields)
363+
parameters = parameters[bits_consumed:]
364+
365+
return fields
314366

315367
def _parse_parameter(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
316368
"""

tests/protocol/test_sctp.py

+160-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_sctp_parser_import():
1212
assert( isinstance(parser, SCTPParser) )
1313

1414
def test_sctp_parser_parse_data():
15-
"""test: SCTP header parser parses SCTP Header
15+
"""test: SCTP header parser parses SCTP Header with DATA chunk
1616
1717
The packet is made of a SCTP header with the following fields:
1818
- id='Source Port Number' length=16 position=0 value=b'\x25\x0f'
@@ -133,7 +133,7 @@ def test_sctp_parser_parse_data():
133133

134134

135135
def test_sctp_parser_parse_init():
136-
"""test: SCTP header parser parses SCTP Header
136+
"""test: SCTP header parser parses SCTP Header with INIT chunk
137137
138138
The packet is made of a SCTP header with the following fields:
139139
- id='Source Port Number' length=16 position=0 value=b'\x00\x07'
@@ -268,4 +268,161 @@ def test_sctp_parser_parse_init():
268268
parameter_padding_fd:FieldDescriptor = sctp_header_descriptor.fields[17]
269269
assert parameter_padding_fd.id == SCTPFields.PARAMETER_PADDING
270270
assert parameter_padding_fd.position == 0
271-
assert parameter_padding_fd.value == Buffer(content=b'\x00\x00', length=16)
271+
assert parameter_padding_fd.value == Buffer(content=b'\x00\x00', length=16)
272+
273+
274+
275+
276+
277+
def test_sctp_parser_parse_init_ack():
278+
"""test: SCTP header parser parses SCTP Header with INIT_ACK chunk
279+
280+
The packet is made of a SCTP header with the following fields:
281+
- id='Source Port Number' length=16 position=0 value=b'\x00\x07'
282+
- id='Destination Port Number' length=16 position=0 value=b'\x00\x07'
283+
- id='Verification Tag' length=32 position=0 value=b'\x43\x23\x25\x44'
284+
- id='Checksum' length=32 position=0 value=b'\xc9\x01\x85\x24'
285+
- id='Chunk Type' length=8 position=0 value=b'\x02'
286+
- id='Chunk Flags' length=8 position=0 value=b'\x00'
287+
- id='Chunk Length' length=16 position=0 value=b'\x00\x80'
288+
- id='Initiate Tag length=32 position=0 value=b'\x00\x00\x0e\xb0'
289+
- id='Advertised Receiver Window Credit' length=32 position=0 value=b'\x00\x00\x10\x00'
290+
- id='Number of Outbound Streams' length=16 position=0 value=b'\x00\x11'
291+
- id='Number of Inbound Streams' length=16 position=0 value=b'\x00\x11'
292+
- id='Initial TSN length=32 position=0 value=b'\x00\x00\x36\x14'
293+
- id='Parameter Type' length=16 position=0 value=b'\x00\x07'
294+
- id='Parameter Length' length=16 position=0 value=b'\x00\x68'
295+
- id='Parameter Value' length=800 position=0 value=b'\x00\x00\x0e\xb0\x00\x00\x10\x00\x00\x11\x00\x11'
296+
b'\x00\x00\x36\x14\x43\x23\x25\x44\x00\x00\xff\xff\x00\x11\x00\x11'
297+
b'\x5c\xfe\x37\x9f\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'
298+
b'\xa2\x85\xb1\x3f\x10\x27\x00\x00\x17\xcd\x8f\x1c\x11\x76\x9b\x04'
299+
b'\x55\xc0\xd0\xf2\x2c\x3e\x7c\x35\x00\x01\x00\x01\x00\x00\x00\x00'
300+
b'\x00\x00\x00\x00\x00\x05\x00\x08\xc0\xa8\xaa\x38\x00\x05\x00\x08'
301+
b'\xc0\xa8\xaa\x08\xc0\x00\x00\x04'
302+
- id='Parameter Type' length=16 position=0 value=b'\xc0\x00'
303+
- id='Parameter Length' length=16 position=0 value=b'\x00\x04'
304+
305+
"""
306+
307+
valid_sctp_packet:bytes = bytes(b'\x00\x07\x00\x07\x43\x23\x25\x44\xc9\x01\x85\x24\x02\x00\x00\x80'
308+
b'\x00\x00\x0e\xb0\x00\x00\x10\x00\x00\x11\x00\x11\x00\x00\x36\x14'
309+
b'\x00\x07\x00\x68\x00\x00\x0e\xb0\x00\x00\x10\x00\x00\x11\x00\x11'
310+
b'\x00\x00\x36\x14\x43\x23\x25\x44\x00\x00\xff\xff\x00\x11\x00\x11'
311+
b'\x5c\xfe\x37\x9f\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'
312+
b'\xa2\x85\xb1\x3f\x10\x27\x00\x00\x17\xcd\x8f\x1c\x11\x76\x9b\x04'
313+
b'\x55\xc0\xd0\xf2\x2c\x3e\x7c\x35\x00\x01\x00\x01\x00\x00\x00\x00'
314+
b'\x00\x00\x00\x00\x00\x05\x00\x08\xc0\xa8\xaa\x38\x00\x05\x00\x08'
315+
b'\xc0\xa8\xaa\x08\xc0\x00\x00\x04\xc0\x00\x00\x04'
316+
)
317+
valid_sctp_packet_buffer: Buffer = Buffer(content=valid_sctp_packet, length=len(valid_sctp_packet)*8)
318+
parser:SCTPParser = SCTPParser()
319+
320+
sctp_header_descriptor: HeaderDescriptor = parser.parse(buffer=valid_sctp_packet_buffer)
321+
322+
# test sctp_header_descriptor type
323+
assert isinstance(sctp_header_descriptor, HeaderDescriptor)
324+
325+
# test for sctp_header_descriptor.fields length
326+
assert len(sctp_header_descriptor.fields) == 17
327+
328+
# test for sctp_header_descriptor.fields types
329+
for field in sctp_header_descriptor.fields:
330+
assert isinstance(field, FieldDescriptor)
331+
332+
# assert field descriptors match SCTP header content
333+
# - common header fields
334+
source_port_fd:FieldDescriptor = sctp_header_descriptor.fields[0]
335+
assert source_port_fd.id == SCTPFields.SOURCE_PORT
336+
assert source_port_fd.position == 0
337+
assert source_port_fd.value == Buffer(content=b'\x00\x07', length=16)
338+
339+
destination_port_fd:FieldDescriptor = sctp_header_descriptor.fields[1]
340+
assert destination_port_fd.id == SCTPFields.DESTINATION_PORT
341+
assert destination_port_fd.position == 0
342+
assert destination_port_fd.value == Buffer(content=b'\x00\x07', length=16)
343+
344+
verification_tag_fd:FieldDescriptor = sctp_header_descriptor.fields[2]
345+
assert verification_tag_fd.id == SCTPFields.VERIFICATION_TAG
346+
assert verification_tag_fd.position == 0
347+
assert verification_tag_fd.value == Buffer(content=b'\x43\x23\x25\x44', length=32)
348+
349+
checksum_fd:FieldDescriptor = sctp_header_descriptor.fields[3]
350+
assert checksum_fd.id == SCTPFields.CHECKSUM
351+
assert checksum_fd.position == 0
352+
assert checksum_fd.value == Buffer(content=b'\xc9\x01\x85\x24', length=32)
353+
354+
355+
# - chunk common header fields
356+
chunk_type_fd:FieldDescriptor = sctp_header_descriptor.fields[4]
357+
assert chunk_type_fd.id == SCTPFields.CHUNK_TYPE
358+
assert chunk_type_fd.position == 0
359+
assert chunk_type_fd.value == Buffer(content=b'\x02', length=8)
360+
361+
chunk_flags_fd:FieldDescriptor = sctp_header_descriptor.fields[5]
362+
assert chunk_flags_fd.id == SCTPFields.CHUNK_FLAGS
363+
assert chunk_flags_fd.position == 0
364+
assert chunk_flags_fd.value == Buffer(content=b'\x00', length=8)
365+
366+
chunk_length_fd:FieldDescriptor = sctp_header_descriptor.fields[6]
367+
assert chunk_length_fd.id == SCTPFields.CHUNK_LENGTH
368+
assert chunk_length_fd.position == 0
369+
assert chunk_length_fd.value == Buffer(content=b'\x00\x80', length=16)
370+
371+
initiate_tag_fd:FieldDescriptor = sctp_header_descriptor.fields[7]
372+
assert initiate_tag_fd.id == SCTPFields.CHUNK_INIT_ACK_INITIATE_TAG
373+
assert initiate_tag_fd.position == 0
374+
assert initiate_tag_fd.value == Buffer(content=b'\x00\x00\x0e\xb0', length=32)
375+
376+
advertised_receiver_window_credit_fd:FieldDescriptor = sctp_header_descriptor.fields[8]
377+
assert advertised_receiver_window_credit_fd.id == SCTPFields.CHUNK_INIT_ACK_ADVERTISED_RECEIVER_WINDOW_CREDIT
378+
assert advertised_receiver_window_credit_fd.position == 0
379+
assert advertised_receiver_window_credit_fd.value == Buffer(content=b'\x00\x00\x10\x00', length=32)
380+
381+
number_outbound_streams_fd:FieldDescriptor = sctp_header_descriptor.fields[9]
382+
assert number_outbound_streams_fd.id == SCTPFields.CHUNK_INIT_ACK_NUMBER_OF_OUTBOUND_STREAMS
383+
assert number_outbound_streams_fd.position == 0
384+
assert number_outbound_streams_fd.value == Buffer(content=b'\x00\x11', length=16)
385+
386+
number_inbound_streams_fd:FieldDescriptor = sctp_header_descriptor.fields[10]
387+
assert number_inbound_streams_fd.id == SCTPFields.CHUNK_INIT_ACK_NUMBER_OF_INBOUND_STREAMS
388+
assert number_inbound_streams_fd.position == 0
389+
assert number_inbound_streams_fd.value == Buffer(content=b'\x00\x11', length=16)
390+
391+
initial_tsn_fd:FieldDescriptor = sctp_header_descriptor.fields[11]
392+
assert initial_tsn_fd.id == SCTPFields.CHUNK_INIT_ACK_INITIAL_TSN
393+
assert initial_tsn_fd.position == 0
394+
assert initial_tsn_fd.value == Buffer(content=b'\x00\x00\x36\x14', length=32)
395+
396+
parameter_type_fd:FieldDescriptor = sctp_header_descriptor.fields[12]
397+
assert parameter_type_fd.id == SCTPFields.PARAMETER_TYPE
398+
assert parameter_type_fd.position == 0
399+
assert parameter_type_fd.value == Buffer(content=b'\x00\x07', length=16)
400+
401+
parameter_length_fd:FieldDescriptor = sctp_header_descriptor.fields[13]
402+
assert parameter_length_fd.id == SCTPFields.PARAMETER_LENGTH
403+
assert parameter_length_fd.position == 0
404+
assert parameter_length_fd.value == Buffer(content=b'\x00\x68', length=16)
405+
406+
parameter_value_fd:FieldDescriptor = sctp_header_descriptor.fields[14]
407+
assert parameter_value_fd.id == SCTPFields.PARAMETER_VALUE
408+
assert parameter_value_fd.position == 0
409+
assert parameter_value_fd.value == Buffer(content=b'\x00\x00\x0e\xb0\x00\x00\x10\x00\x00\x11\x00\x11'
410+
b'\x00\x00\x36\x14\x43\x23\x25\x44\x00\x00\xff\xff\x00\x11\x00\x11'
411+
b'\x5c\xfe\x37\x9f\x07\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00'
412+
b'\xa2\x85\xb1\x3f\x10\x27\x00\x00\x17\xcd\x8f\x1c\x11\x76\x9b\x04'
413+
b'\x55\xc0\xd0\xf2\x2c\x3e\x7c\x35\x00\x01\x00\x01\x00\x00\x00\x00'
414+
b'\x00\x00\x00\x00\x00\x05\x00\x08\xc0\xa8\xaa\x38\x00\x05\x00\x08'
415+
b'\xc0\xa8\xaa\x08\xc0\x00\x00\x04',
416+
length=800)
417+
418+
parameter_type_fd:FieldDescriptor = sctp_header_descriptor.fields[15]
419+
assert parameter_type_fd.id == SCTPFields.PARAMETER_TYPE
420+
assert parameter_type_fd.position == 0
421+
assert parameter_type_fd.value == Buffer(content=b'\xc0\x00', length=16)
422+
423+
parameter_length_fd:FieldDescriptor = sctp_header_descriptor.fields[16]
424+
assert parameter_length_fd.id == SCTPFields.PARAMETER_LENGTH
425+
assert parameter_length_fd.position == 0
426+
assert parameter_length_fd.value == Buffer(content=b'\x00\x04', length=16)
427+
428+

0 commit comments

Comments
 (0)