Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add-ptp-protocol #4640

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scapy/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ class Conf(ConfClass):
'ppi',
'ppp',
'pptp',
'ptp_v2',
'radius',
'rip',
'rtp',
Expand Down
160 changes: 160 additions & 0 deletions scapy/layers/ptp_v2.py
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
# 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)
95 changes: 95 additions & 0 deletions test/scapy/layers/ptp_v2.uts
Original file line number Diff line number Diff line change
@@ -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
Loading