|
| 1 | +# SPDX-License-Identifier: GPL-2.0-only |
| 2 | +# This file is part of Scapy |
| 3 | +# See https://scapy.net/ for more information |
| 4 | +# Copyright (C) Philippe Biondi <phil@secdev.org> |
| 5 | +# Copyright (C) Satveer Brar |
| 6 | + |
| 7 | +""" |
| 8 | +PTP (Precision Time Protocol). |
| 9 | +References : IEEE 1588-2008 |
| 10 | +""" |
| 11 | + |
| 12 | +import struct |
| 13 | + |
| 14 | +from scapy.packet import Packet, bind_layers |
| 15 | +from scapy.fields import ( |
| 16 | + BitEnumField, |
| 17 | + BitField, |
| 18 | + ByteField, |
| 19 | + IntField, |
| 20 | + LongField, |
| 21 | + ShortField, |
| 22 | + ByteEnumField, |
| 23 | + FlagsField, |
| 24 | + XLongField, |
| 25 | + XByteField, |
| 26 | + ConditionalField, |
| 27 | +) |
| 28 | +from scapy.layers.inet import UDP |
| 29 | + |
| 30 | + |
| 31 | +############################################################################# |
| 32 | +# PTPv2 |
| 33 | +############################################################################# |
| 34 | + |
| 35 | +# IEEE 1588-2008 / Section 13.3.2.2 |
| 36 | + |
| 37 | +_message_type = { |
| 38 | + 0x0: "Sync", |
| 39 | + 0x1: "Delay_Req", |
| 40 | + 0x2: "Pdelay_Req", |
| 41 | + 0x3: "Pdelay_Resp", |
| 42 | + 0x4: "Reserved", |
| 43 | + 0x5: "Reserved", |
| 44 | + 0x6: "Reserved", |
| 45 | + 0x7: "Reserved", |
| 46 | + 0x8: "Follow_Up", |
| 47 | + 0x9: "Delay_Resp", |
| 48 | + 0xA: "Pdelay_Resp_Follow", |
| 49 | + 0xB: "Announce", |
| 50 | + 0xC: "Signaling", |
| 51 | + 0xD: "Management", |
| 52 | + 0xE: "Reserved", |
| 53 | + 0xF: "Reserved" |
| 54 | +} |
| 55 | + |
| 56 | +_control_field = { |
| 57 | + 0x00: "Sync", |
| 58 | + 0x01: "Delay_Req", |
| 59 | + 0x02: "Follow_Up", |
| 60 | + 0x03: "Delay_Resp", |
| 61 | + 0x04: "Management", |
| 62 | + 0x05: "All others", |
| 63 | +} |
| 64 | + |
| 65 | +_flags = { |
| 66 | + 0x0001: "alternateMasterFlag", |
| 67 | + 0x0002: "twoStepFlag", |
| 68 | + 0x0004: "unicastFlag", |
| 69 | + 0x0010: "ptpProfileSpecific1", |
| 70 | + 0x0020: "ptpProfileSpecific2", |
| 71 | + 0x0040: "reserved", |
| 72 | + 0x0100: "leap61", |
| 73 | + 0x0200: "leap59", |
| 74 | + 0x0400: "currentUtcOffsetValid", |
| 75 | + 0x0800: "ptpTimescale", |
| 76 | + 0x1000: "timeTraceable", |
| 77 | + 0x2000: "frequencyTraceable" |
| 78 | +} |
| 79 | + |
| 80 | + |
| 81 | +class PTP(Packet): |
| 82 | + """ |
| 83 | + PTP packet based on IEEE 1588-2008 / Section 13.3 |
| 84 | + """ |
| 85 | + |
| 86 | + name = "PTP" |
| 87 | + match_subclass = True |
| 88 | + fields_desc = [ |
| 89 | + BitField("transportSpecific", 0, 4), |
| 90 | + BitEnumField("messageType", 0x0, 4, _message_type), |
| 91 | + BitField("reserved1", 0, 4), |
| 92 | + BitField("version", 2, 4), |
| 93 | + ShortField("messageLength", None), |
| 94 | + ByteField("domainNumber", 0), |
| 95 | + ByteField("reserved2", 0), |
| 96 | + FlagsField("flags", 0, 16, _flags), |
| 97 | + LongField("correctionField", 0), |
| 98 | + IntField("reserved3", 0), |
| 99 | + XLongField("clockIdentity", 0), |
| 100 | + ShortField("portNumber", 0), |
| 101 | + ShortField("sequenceId", 0), |
| 102 | + ByteEnumField("controlField", 0, _control_field), |
| 103 | + ByteField("logMessageInterval", 0), |
| 104 | + ConditionalField(BitField("originTimestamp_seconds", 0, 48), |
| 105 | + lambda pkt: pkt.messageType in [0x0, 0x1, 0x2, 0xB]), |
| 106 | + ConditionalField(IntField("originTimestamp_nanoseconds", 0), |
| 107 | + lambda pkt: pkt.messageType in [0x0, 0x1, 0x2, 0xB]), |
| 108 | + ConditionalField(BitField("preciseOriginTimestamp_seconds", 0, 48), |
| 109 | + lambda pkt: pkt.messageType == 0x8), |
| 110 | + ConditionalField(IntField("preciseOriginTimestamp_nanoseconds", 0), |
| 111 | + lambda pkt: pkt.messageType == 0x8), |
| 112 | + ConditionalField(BitField("requestReceiptTimestamp_seconds", 0, 48), |
| 113 | + lambda pkt: pkt.messageType == 0x3), |
| 114 | + ConditionalField(IntField("requestReceiptTimestamp_nanoseconds", 0), |
| 115 | + lambda pkt: pkt.messageType == 0x3), |
| 116 | + ConditionalField(BitField("receiveTimestamp_seconds", 0, 48), |
| 117 | + lambda pkt: pkt.messageType == 0x9), |
| 118 | + ConditionalField(IntField("receiveTimestamp_nanoseconds", 0), |
| 119 | + lambda pkt: pkt.messageType == 0x9), |
| 120 | + ConditionalField(BitField("responseOriginTimestamp_seconds", 0, 48), |
| 121 | + lambda pkt: pkt.messageType == 0xA), |
| 122 | + ConditionalField(IntField("responseOriginTimestamp_nanoseconds", 0), |
| 123 | + lambda pkt: pkt.messageType == 0xA), |
| 124 | + ConditionalField(ShortField("currentUtcOffset", 0), |
| 125 | + lambda pkt: pkt.messageType == 0xB), |
| 126 | + ConditionalField(ByteField("reserved4", 0), |
| 127 | + lambda pkt: pkt.messageType == 0xB), |
| 128 | + ConditionalField(ByteField("grandmasterPriority1", 0), |
| 129 | + lambda pkt: pkt.messageType == 0xB), |
| 130 | + ConditionalField(ByteField("grandmasterClockClass", 0), |
| 131 | + lambda pkt: pkt.messageType == 0xB), |
| 132 | + ConditionalField(XByteField("grandmasterClockAccuracy", 0), |
| 133 | + lambda pkt: pkt.messageType == 0xB), |
| 134 | + ConditionalField(ShortField("grandmasterClockVariance", 0), |
| 135 | + lambda pkt: pkt.messageType == 0xB), |
| 136 | + ConditionalField(ByteField("grandmasterPriority2", 0), |
| 137 | + lambda pkt: pkt.messageType == 0xB), |
| 138 | + ConditionalField(XLongField("grandmasterIdentity", 0), |
| 139 | + lambda pkt: pkt.messageType == 0xB), |
| 140 | + ConditionalField(ShortField("stepsRemoved", 0), |
| 141 | + lambda pkt: pkt.messageType == 0xB), |
| 142 | + ConditionalField(XByteField("timeSource", 0), |
| 143 | + lambda pkt: pkt.messageType == 0xB) |
| 144 | + |
| 145 | + ] |
| 146 | + |
| 147 | + def post_build(self, pkt, pay): # type: (bytes, bytes) -> bytes |
| 148 | + """ |
| 149 | + Update the messageLength field after building the packet |
| 150 | + """ |
| 151 | + if self.messageLength is None: |
| 152 | + pkt = pkt[:2] + struct.pack("!H", len(pkt)) + pkt[4:] |
| 153 | + |
| 154 | + return pkt + pay |
| 155 | + |
| 156 | + |
| 157 | +# Layer bindings |
| 158 | + |
| 159 | +bind_layers(UDP, PTP, sport=319, dport=319) |
| 160 | +bind_layers(UDP, PTP, sport=320, dport=320) |
0 commit comments