Skip to content

Commit 3375e7f

Browse files
committed
[wip] SCTP heartbeat chunk
1 parent a0793fd commit 3375e7f

File tree

2 files changed

+118
-2
lines changed

2 files changed

+118
-2
lines changed

microschc/protocol/sctp.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,14 @@ def _parse_chunk(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
194194

195195
if chunk_type_value == SCTPChunkTypes.DATA:
196196
chunk_fields: List[FieldDescriptor] = self._parse_chunk_data(chunk_value)
197-
198197
elif chunk_type_value == SCTPChunkTypes.INIT:
199198
chunk_fields: List[FieldDescriptor] = self._parse_chunk_init(chunk_value)
200199
elif chunk_type_value == SCTPChunkTypes.INIT_ACK:
201200
chunk_fields: List[FieldDescriptor] = self._parse_chunk_init_ack(chunk_value)
202201
elif chunk_type_value == SCTPChunkTypes.SACK:
203202
chunk_fields: List[FieldDescriptor] = self._parse_chunk_selective_ack(chunk_value)
203+
elif chunk_type_value == SCTPChunkTypes.HEARTBEAT:
204+
chunk_fields: List[FieldDescriptor] = self._parse_chunk_heartbeat(chunk_value)
204205
else:
205206
chunk_fields: List[FieldDescriptor] = [FieldDescriptor(id=SCTPFields.CHUNK_VALUE, position=0, value=chunk_value)]
206207
fields.extend(chunk_fields)
@@ -405,6 +406,28 @@ def _parse_chunk_selective_ack(self, buffer: Buffer) -> List[FieldDescriptor]:
405406
remainer = remainer[32:]
406407

407408
return fields
409+
410+
def _parse_chunk_heartbeat(self, buffer: Buffer) -> List[FieldDescriptor]:
411+
"""
412+
0 1 2 3
413+
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
414+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
415+
| Type = 4 | Chunk Flags | Heartbeat Length |
416+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
417+
\ \
418+
/ Heartbeat Information TLV (Variable-Length) /
419+
\ \
420+
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
421+
"""
422+
fields: List[FieldDescriptor] = []
423+
424+
parameters = buffer
425+
while parameters.length > 0:
426+
parameter_fields, bits_consumed = self._parse_parameter(parameters)
427+
fields.extend(parameter_fields)
428+
parameters = parameters[bits_consumed:]
429+
430+
return fields
408431

409432
def _parse_parameter(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
410433
"""
@@ -440,5 +463,7 @@ def _parse_parameter(self, buffer: Buffer) -> Tuple[List[FieldDescriptor], int]:
440463
FieldDescriptor(id=SCTPFields.PARAMETER_PADDING, value=parameter_padding, position=0)
441464
)
442465
return fields, parameter_length_value + parameter_padding_length
466+
467+
443468

444469

tests/protocol/test_sctp.py

+92-1
Original file line numberDiff line numberDiff line change
@@ -520,4 +520,95 @@ def test_sctp_parser_selective_ack():
520520
assert number_inbound_streams_fd.position == 0
521521
assert number_inbound_streams_fd.value == Buffer(content=b'\x00\x00', length=16)
522522

523-
523+
524+
def test_sctp_parser_parse_heartbeat():
525+
"""test: SCTP header parser parses SCTP Header with HEARTBEAT chunk
526+
527+
The packet is made of a SCTP header with the following fields:
528+
- id='Source Port Number' length=16 position=0 value=b'\x0b\x59'
529+
- id='Destination Port Number' length=16 position=0 value=b'\x0b\x59'
530+
- id='Verification Tag' length=32 position=0 value=b'\x00\x00\x0e\x50'
531+
- id='Checksum' length=32 position=0 value=b'\x53\xc3\x05\x5f'
532+
- id='Chunk Type' length=8 position=0 value=b'\x04'
533+
- id='Chunk Flags' length=8 position=0 value=b'\x00'
534+
- id='Chunk Length' length=16 position=0 value=b'\x00\x18'
535+
- id='Parameter Type' length=16 position=0 value=b'\x00\x01'
536+
- id='Parameter Length' length=16 position=0 value=b'\x00\x14'
537+
- id='Parameter Value' length=16 position=0 value=b'\x40\xe4\x4b\x92\x0a\x1c\x06\x2c\x1b\x66\xaf\x7e\x00\x00\x00\x00'
538+
539+
"""
540+
541+
valid_sctp_packet:bytes = bytes(b'\x0b\x59\x0b\x59\x00\x00\x0e\x50\x53\xc3\x05\x5f\x04\x00\x00\x18'
542+
b'\x00\x01\x00\x14\x40\xe4\x4b\x92\x0a\x1c\x06\x2c\x1b\x66\xaf\x7e'
543+
b'\x00\x00\x00\x00'
544+
545+
)
546+
547+
valid_sctp_packet_buffer: Buffer = Buffer(content=valid_sctp_packet, length=len(valid_sctp_packet)*8)
548+
parser:SCTPParser = SCTPParser()
549+
550+
sctp_header_descriptor: HeaderDescriptor = parser.parse(buffer=valid_sctp_packet_buffer)
551+
552+
# test sctp_header_descriptor type
553+
assert isinstance(sctp_header_descriptor, HeaderDescriptor)
554+
555+
# test for sctp_header_descriptor.fields length
556+
assert len(sctp_header_descriptor.fields) == 10
557+
558+
# test for sctp_header_descriptor.fields types
559+
for field in sctp_header_descriptor.fields:
560+
assert isinstance(field, FieldDescriptor)
561+
562+
# assert field descriptors match SCTP header content
563+
# - common header fields
564+
source_port_fd:FieldDescriptor = sctp_header_descriptor.fields[0]
565+
assert source_port_fd.id == SCTPFields.SOURCE_PORT
566+
assert source_port_fd.position == 0
567+
assert source_port_fd.value == Buffer(content=b'\x0b\x59', length=16)
568+
569+
destination_port_fd:FieldDescriptor = sctp_header_descriptor.fields[1]
570+
assert destination_port_fd.id == SCTPFields.DESTINATION_PORT
571+
assert destination_port_fd.position == 0
572+
assert destination_port_fd.value == Buffer(content=b'\x0b\x59', length=16)
573+
574+
verification_tag_fd:FieldDescriptor = sctp_header_descriptor.fields[2]
575+
assert verification_tag_fd.id == SCTPFields.VERIFICATION_TAG
576+
assert verification_tag_fd.position == 0
577+
assert verification_tag_fd.value == Buffer(content=b'\x00\x00\x0e\x50', length=32)
578+
579+
checksum_fd:FieldDescriptor = sctp_header_descriptor.fields[3]
580+
assert checksum_fd.id == SCTPFields.CHECKSUM
581+
assert checksum_fd.position == 0
582+
assert checksum_fd.value == Buffer(content=b'\x53\xc3\x05\x5f', length=32)
583+
584+
585+
# - chunk common header fields
586+
chunk_type_fd:FieldDescriptor = sctp_header_descriptor.fields[4]
587+
assert chunk_type_fd.id == SCTPFields.CHUNK_TYPE
588+
assert chunk_type_fd.position == 0
589+
assert chunk_type_fd.value == Buffer(content=b'\x04', length=8)
590+
591+
chunk_flags_fd:FieldDescriptor = sctp_header_descriptor.fields[5]
592+
assert chunk_flags_fd.id == SCTPFields.CHUNK_FLAGS
593+
assert chunk_flags_fd.position == 0
594+
assert chunk_flags_fd.value == Buffer(content=b'\x00', length=8)
595+
596+
chunk_length_fd:FieldDescriptor = sctp_header_descriptor.fields[6]
597+
assert chunk_length_fd.id == SCTPFields.CHUNK_LENGTH
598+
assert chunk_length_fd.position == 0
599+
assert chunk_length_fd.value == Buffer(content=b'\x00\x18', length=16)
600+
601+
parameter_type_fd:FieldDescriptor = sctp_header_descriptor.fields[7]
602+
assert parameter_type_fd.id == SCTPFields.PARAMETER_TYPE
603+
assert parameter_type_fd.position == 0
604+
assert parameter_type_fd.value == Buffer(content=b'\x00\x01', length=16)
605+
606+
parameter_length_fd:FieldDescriptor = sctp_header_descriptor.fields[8]
607+
assert parameter_length_fd.id == SCTPFields.PARAMETER_LENGTH
608+
assert parameter_length_fd.position == 0
609+
assert parameter_length_fd.value == Buffer(content=b'\x00\x14', length=16)
610+
611+
parameter_value_fd:FieldDescriptor = sctp_header_descriptor.fields[9]
612+
assert parameter_value_fd.id == SCTPFields.PARAMETER_VALUE
613+
assert parameter_value_fd.position == 0
614+
assert parameter_value_fd.value == Buffer(content=b'\x40\xe4\x4b\x92\x0a\x1c\x06\x2c\x1b\x66\xaf\x7e\x00\x00\x00\x00', length=128)

0 commit comments

Comments
 (0)