diff --git a/scapy/contrib/diameter.py b/scapy/contrib/diameter.py index b5110709e08..7bd74e28245 100644 --- a/scapy/contrib/diameter.py +++ b/scapy/contrib/diameter.py @@ -4828,7 +4828,5 @@ def DiamAns(cmd, **fields): bind_layers(TCP, DiamG, dport=3868) bind_layers(TCP, DiamG, sport=3868) -bind_layers(SCTPChunkData, DiamG, dport=3868) -bind_layers(SCTPChunkData, DiamG, sport=3868) bind_layers(SCTPChunkData, DiamG, proto_id=46) bind_layers(SCTPChunkData, DiamG, proto_id=47) diff --git a/scapy/layers/sctp.py b/scapy/layers/sctp.py index b69d6c193ed..c3ec1ad45ab 100644 --- a/scapy/layers/sctp.py +++ b/scapy/layers/sctp.py @@ -24,6 +24,7 @@ IntEnumField, IntField, MultipleTypeField, + PacketLenField, PacketListField, PadField, ShortEnumField, @@ -624,6 +625,26 @@ class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet): } +class _SCTPChunkDataField(PacketLenField): + """PacketLenField that dispatches using bind_layers bindings.""" + + def m2i(self, pkt, m): + # Only dissect complete messages + if pkt.beginning != 1 or pkt.ending != 1: + return m + # Check bind_layers bindings + for fval, cls in pkt.payload_guess: + if all( + hasattr(pkt, k) and v == pkt.getfieldval(k) + for k, v in fval.items() + ): + try: + return cls(m) + except Exception: + pass + return m + + class SCTPChunkData(_SCTPChunkGuessPayload, Packet): # TODO : add a padding function in post build if this layer is used to generate SCTP chunk data # noqa: E501 fields_desc = [ByteEnumField("type", 0, sctpchunktypes), @@ -637,7 +658,8 @@ class SCTPChunkData(_SCTPChunkGuessPayload, Packet): XShortField("stream_id", None), XShortField("stream_seq", None), IntEnumField("proto_id", None, SCTP_PAYLOAD_PROTOCOL_INDENTIFIERS), # noqa: E501 - PadField(StrLenField("data", None, length_from=lambda pkt: pkt.len - 16), # noqa: E501 + PadField(_SCTPChunkDataField("data", None, conf.raw_layer, + length_from=lambda pkt: pkt.len - 16), # noqa: E501 4, padwith=b"\x00"), ] diff --git a/test/contrib/diameter.uts b/test/contrib/diameter.uts index e06a055a16d..6a7f1b14ec1 100644 --- a/test/contrib/diameter.uts +++ b/test/contrib/diameter.uts @@ -253,3 +253,33 @@ r3b = DiamReq ('Multimedia-Auth', drHbHId=0x5478, drEtEId=0x1234, raw(r3b) == raw(r3) + +####################################################################### ++ Diameter over SCTP +####################################################################### + += Diameter decoded from SCTPChunkData via proto_id binding + +from scapy.layers.sctp import SCTP, SCTPChunkData + +diam_pkt = DiamAns('Capabilities-Exchange', drHbHId=0x1234, drEtEId=0x5678, + avpList=[AVP('Origin-Host', val='host.example.com'), + AVP('Origin-Realm', val='example.com')]) + +pkt = SCTP(raw(SCTP() / SCTPChunkData(proto_id=46, beginning=1, ending=1, data=raw(diam_pkt)))) +chunk = pkt[SCTPChunkData] +assert isinstance(chunk.data, DiamG) +assert chunk.proto_id == 46 +assert chunk.data.drHbHId == 0x1234 +assert chunk.data.avpList[0].avpCode == 264 + += SCTPChunkData with unknown proto_id keeps raw bytes + +pkt = SCTP(raw(SCTP() / SCTPChunkData(proto_id=0, data=b"test"))) +assert pkt[SCTPChunkData].data == b"test" + += SCTPChunkData fragment is not decoded + +pkt = SCTP(raw(SCTP() / SCTPChunkData(proto_id=46, beginning=1, ending=0, data=raw(diam_pkt)))) +assert not isinstance(pkt[SCTPChunkData].data, DiamG) +