diff --git a/scapy/config.py b/scapy/config.py index e047aaf391a..4c2f0e2fd23 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -1057,6 +1057,7 @@ class Conf(ConfClass): 'ppi', 'ppp', 'pptp', + 'ptp_v2', 'radius', 'rip', 'rtp', diff --git a/scapy/layers/ptp_v2.py b/scapy/layers/ptp_v2.py new file mode 100644 index 00000000000..f7d1c72b5f7 --- /dev/null +++ b/scapy/layers/ptp_v2.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: GPL-2.0-only +# This file is part of Scapy +# See https://scapy.net/ for more information +# Copyright (C) Philippe Biondi +# Copyright (C) Satveer Brar + +""" +PTP (Precision Time Protocol). +References : IEEE 1588-2008 +""" + +import struct + +from scapy.packet import Packet, bind_layers +from scapy.fields import ( + BitEnumField, + BitField, + ByteField, + IntField, + LongField, + ShortField, + ByteEnumField, + FlagsField, + XLongField, + XByteField, + ConditionalField, +) +from scapy.layers.inet import UDP + + +############################################################################# +# PTPv2 +############################################################################# + +# IEEE 1588-2008 / Section 13.3.2.2 + +_message_type = { + 0x0: "Sync", + 0x1: "Delay_Req", + 0x2: "Pdelay_Req", + 0x3: "Pdelay_Resp", + 0x4: "Reserved", + 0x5: "Reserved", + 0x6: "Reserved", + 0x7: "Reserved", + 0x8: "Follow_Up", + 0x9: "Delay_Resp", + 0xA: "Pdelay_Resp_Follow", + 0xB: "Announce", + 0xC: "Signaling", + 0xD: "Management", + 0xE: "Reserved", + 0xF: "Reserved" +} + +_control_field = { + 0x00: "Sync", + 0x01: "Delay_Req", + 0x02: "Follow_Up", + 0x03: "Delay_Resp", + 0x04: "Management", + 0x05: "All others", +} + +_flags = { + 0x0001: "alternateMasterFlag", + 0x0002: "twoStepFlag", + 0x0004: "unicastFlag", + 0x0010: "ptpProfileSpecific1", + 0x0020: "ptpProfileSpecific2", + 0x0040: "reserved", + 0x0100: "leap61", + 0x0200: "leap59", + 0x0400: "currentUtcOffsetValid", + 0x0800: "ptpTimescale", + 0x1000: "timeTraceable", + 0x2000: "frequencyTraceable" +} + + +class PTP(Packet): + """ + PTP packet based on IEEE 1588-2008 / Section 13.3 + """ + + name = "PTP" + match_subclass = True + fields_desc = [ + BitField("transportSpecific", 0, 4), + BitEnumField("messageType", 0x0, 4, _message_type), + BitField("reserved1", 0, 4), + BitField("version", 2, 4), + ShortField("messageLength", None), + ByteField("domainNumber", 0), + ByteField("reserved2", 0), + FlagsField("flags", 0, 16, _flags), + LongField("correctionField", 0), + IntField("reserved3", 0), + XLongField("clockIdentity", 0), + ShortField("portNumber", 0), + ShortField("sequenceId", 0), + ByteEnumField("controlField", 0, _control_field), + ByteField("logMessageInterval", 0), + ConditionalField(BitField("originTimestamp_seconds", 0, 48), + lambda pkt: pkt.messageType in [0x0, 0x1, 0x2, 0xB]), + ConditionalField(IntField("originTimestamp_nanoseconds", 0), + lambda pkt: pkt.messageType in [0x0, 0x1, 0x2, 0xB]), + ConditionalField(BitField("preciseOriginTimestamp_seconds", 0, 48), + lambda pkt: pkt.messageType == 0x8), + ConditionalField(IntField("preciseOriginTimestamp_nanoseconds", 0), + lambda pkt: pkt.messageType == 0x8), + ConditionalField(BitField("requestReceiptTimestamp_seconds", 0, 48), + lambda pkt: pkt.messageType == 0x3), + ConditionalField(IntField("requestReceiptTimestamp_nanoseconds", 0), + lambda pkt: pkt.messageType == 0x3), + ConditionalField(BitField("receiveTimestamp_seconds", 0, 48), + lambda pkt: pkt.messageType == 0x9), + ConditionalField(IntField("receiveTimestamp_nanoseconds", 0), + lambda pkt: pkt.messageType == 0x9), + ConditionalField(BitField("responseOriginTimestamp_seconds", 0, 48), + lambda pkt: pkt.messageType == 0xA), + ConditionalField(IntField("responseOriginTimestamp_nanoseconds", 0), + lambda pkt: pkt.messageType == 0xA), + ConditionalField(ShortField("currentUtcOffset", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ByteField("reserved4", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ByteField("grandmasterPriority1", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ByteField("grandmasterClockClass", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(XByteField("grandmasterClockAccuracy", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ShortField("grandmasterClockVariance", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ByteField("grandmasterPriority2", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(XLongField("grandmasterIdentity", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(ShortField("stepsRemoved", 0), + lambda pkt: pkt.messageType == 0xB), + ConditionalField(XByteField("timeSource", 0), + lambda pkt: pkt.messageType == 0xB) + + ] + + def post_build(self, pkt, pay): # type: (bytes, bytes) -> bytes + """ + Update the messageLength field after building the packet + """ + if self.messageLength is None: + pkt = pkt[:2] + struct.pack("!H", len(pkt)) + pkt[4:] + + return pkt + pay + + +# Layer bindings + +bind_layers(UDP, PTP, sport=319, dport=319) +bind_layers(UDP, PTP, sport=320, dport=320) diff --git a/test/scapy/layers/ptp_v2.uts b/test/scapy/layers/ptp_v2.uts new file mode 100644 index 00000000000..c0d05e2361d --- /dev/null +++ b/test/scapy/layers/ptp_v2.uts @@ -0,0 +1,95 @@ +% PTP regression tests for Scapy + ++ Basic tests + += specific haslayer and getlayer implementations for PTP +~ haslayer getlayer PTP +pkt = IP() / UDP() / PTP() +assert PTP in pkt +assert pkt.haslayer(PTP) +assert isinstance(pkt[PTP], PTP) +assert isinstance(pkt.getlayer(PTP), PTP) + ++ Packet dissection tests + += Sync packet dissection +s = b'\x10\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x00\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.transportSpecific == 1 +assert pkt.messageType == 0 +assert pkt.reserved1 == 0 +assert pkt.version == 2 +assert pkt.messageLength == 44 +assert pkt.domainNumber == 123 +assert pkt.reserved2 == 0 +assert pkt.flags == None +assert pkt.correctionField == 0 +assert pkt.reserved3 == 0 +assert pkt.clockIdentity == 0x8063ffff0009ba +assert pkt.portNumber == 1 +assert pkt.sequenceId == 116 +assert pkt.controlField == 0 +assert pkt.logMessageInterval == 0 +assert pkt.originTimestamp_seconds == 1169232218 +assert pkt.originTimestamp_nanoseconds == 174389936 + += Delay_Req packet dissection +s= b'\x11\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x01\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0x1 +assert pkt.controlField == 0x1 + += Pdelay_Req packet dissection +s= b'\x12\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x05\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0x2 +assert pkt.controlField == 0x5 + += Pdelay_Resp packet dissection +s= b'\x13\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x05\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0x3 +assert pkt.controlField == 0x5 +assert pkt.requestReceiptTimestamp_seconds == 1169232218 +assert pkt.requestReceiptTimestamp_nanoseconds == 174389936 + += Follow_Up packet dissection +s= b'\x18\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x02\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0x8 +assert pkt.controlField == 0x2 +assert pkt.preciseOriginTimestamp_seconds == 1169232218 +assert pkt.preciseOriginTimestamp_nanoseconds == 174389936 + += Delay_Resp packet dissection +s= b'\x19\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x03\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0x9 +assert pkt.controlField == 0x3 +assert pkt.receiveTimestamp_seconds == 1169232218 +assert pkt.receiveTimestamp_nanoseconds == 174389936 + += Pdelay_Resp_Follow packet dissection +s= b'\x1A\x02\x00\x2c\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x05\x00\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0' +pkt = PTP(s) +assert pkt.messageType == 0xA +assert pkt.controlField == 0x5 +assert pkt.responseOriginTimestamp_seconds == 1169232218 +assert pkt.responseOriginTimestamp_nanoseconds == 174389936 + += Announce packet dissection +s= b'\x1b\x02\x00\x40\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\x00\x01\x00\x74\x05\x01\x00\x00\x45\xb1\x11\x5a\x0a\x64\xfa\xb0\x00\x00\x00\x60\x00\x00\x00\x80\x63\xff\xff\x00\x09\xba\xf8\x21\x00\x00\x80\x80' +pkt = PTP(s) +assert pkt.messageType == 0xB +assert pkt.messageLength == 64 +assert pkt.controlField == 0x5 +assert pkt.currentUtcOffset == 0 +assert pkt.reserved4 == 0 +assert pkt.grandmasterPriority1 == 96 +assert pkt.grandmasterClockClass == 0 +assert pkt.grandmasterClockAccuracy == 0x0 +assert pkt.grandmasterClockVariance == 128 +assert pkt.grandmasterPriority2 == 99 +assert pkt.grandmasterIdentity == 0xffff0009baf82100 +assert pkt.stepsRemoved == 128 +assert pkt.timeSource == 0x80 \ No newline at end of file