-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Simple Two-Way Active Measurement Protocol (STAMP) (#…
…3742) * Add support for STAMP * Add regression test for STAMP This commit adds the support for Simple Two-Way Active Measurement Protocol (STAMP). Simple Two-Way Active Measurement Protocol: https://www.rfc-editor.org/rfc/rfc8762.html Simple Two-Way Active Measurement Protocol Optional Extensions: https://www.rfc-editor.org/rfc/rfc8972.html Signed-off-by: Carmine Scarpitta <carmine.scarpitta@uniroma2.it>
- Loading branch information
1 parent
83dba06
commit a1f999f
Showing
2 changed files
with
394 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,306 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
# This file is part of Scapy | ||
# See https://scapy.net/ for more information | ||
# Copyright (C) Carmine Scarpitta <carmine.scarpitta@uniroma2.it> | ||
|
||
# scapy.contrib.description = Simple Two-Way Active Measurement Protocol (STAMP) | ||
# scapy.contrib.status = loads | ||
|
||
""" | ||
STAMP (Simple Two-Way Active Measurement Protocol) - RFC 8762. | ||
References: | ||
* `Simple Two-Way Active Measurement Protocol [RFC 8762] | ||
<https://www.rfc-editor.org/rfc/rfc8762.html>`_ | ||
* `Simple Two-Way Active Measurement Protocol Optional Extensions [RFC 8972] | ||
<https://www.rfc-editor.org/rfc/rfc8972.html>`_ | ||
""" | ||
|
||
from scapy import config | ||
from scapy.base_classes import Packet_metaclass | ||
from scapy.layers.inet import UDP | ||
from scapy.layers.ntp import TimeStampField | ||
from scapy.packet import Packet, bind_layers | ||
from scapy.fields import ( | ||
BitEnumField, | ||
BitField, | ||
ByteEnumField, | ||
ByteField, | ||
FlagsField, | ||
IntField, | ||
MultipleTypeField, | ||
NBytesField, | ||
PacketField, | ||
PacketListField, | ||
ShortField, | ||
StrLenField, | ||
UTCTimeField | ||
) | ||
|
||
|
||
_sync_types = { | ||
0: 'No External Synchronization for the Time Source', | ||
1: 'Clock Synchronized to UTC using an External Source' | ||
} | ||
|
||
_timestamp_types = { | ||
0: 'NTP 64-bit Timestamp Format', | ||
1: 'PTPv2 Truncated Timestamp Format' | ||
} | ||
|
||
_stamp_tlvs = { | ||
|
||
} | ||
|
||
|
||
class ErrorEstimate(Packet): | ||
""" | ||
The Error Estimate specifies the estimate of the error and | ||
synchronization. The format of the Error Estimate field | ||
(defined in Section 4.1.2 of `RFC 4656 | ||
<https://www.rfc-editor.org/rfc/rfc4656.html>`_) is reported below:: | ||
0 1 | ||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|S|Z| Scale | Multiplier | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
``S`` is interpreted as follows: | ||
+-------+-------------------------------------------------------+ | ||
| Value | Description | | ||
+-------+-------------------------------------------------------+ | ||
| 0 | there is no notion of external synchronization for | | ||
| | the time source | | ||
+-------+-------------------------------------------------------+ | ||
| 1 | the party generating the timestamp has a clock that | | ||
| | is synchronized to UTC using an external source | | ||
+-------+-------------------------------------------------------+ | ||
``Z`` is interpreted as follows (defined in Section 2.3 of `RFC 8186 | ||
<https://www.rfc-editor.org/rfc/rfc8186.html>`_): | ||
+-------+---------------------------------------+ | ||
| Value | Description | | ||
+-------+---------------------------------------+ | ||
| 0 | NTP 64-bit format of a timestamp | | ||
+-------+---------------------------------------+ | ||
| 1 | PTPv2 truncated format of a timestamp | | ||
+-------+---------------------------------------+ | ||
``Scale`` and ``Multiplier`` are linked by the following relationship:: | ||
ErrorEstimate = Multiplier*2^(-32)*2^Scale (in seconds) | ||
References: | ||
* `A One-way Active Measurement Protocol (OWAMP) [RFC 4656] | ||
<https://www.rfc-editor.org/rfc/rfc4656.html>`_ | ||
* `Support of the IEEE 1588 Timestamp Format in a Two-Way Active | ||
Measurement Protocol (TWAMP) [RFC 8186] | ||
<https://www.rfc-editor.org/rfc/rfc8186.html>`_ | ||
""" | ||
|
||
name = 'Error Estimate' | ||
fields_desc = [ | ||
BitEnumField('S', 0, 1, _sync_types), | ||
BitEnumField('Z', 0, 1, _timestamp_types), | ||
BitField('scale', 0, 6), | ||
ByteField('multiplier', 1), | ||
] | ||
|
||
def guess_payload_class(self, payload): | ||
# type: (str) -> Packet_metaclass | ||
# Trick to tell scapy that the remaining bytes of the currently | ||
# dissected string is not a payload of this packet but of some other | ||
# underlayer packet | ||
return config.conf.padding_layer | ||
|
||
|
||
class STAMPTestTLV(Packet): | ||
""" | ||
The STAMP Test TLV defined in Section 4 of [RFC 8972] provides a flexible | ||
extension mechanism for optional informational elements. | ||
The TLV Format in a STAMP Test packet is reported below:: | ||
0 1 2 3 | ||
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 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|STAMP TLV Flags| Type | Length | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
~ Value ~ | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
+-------+---------+-------------------------------------------------+ | ||
| Field | Description | | ||
+-----------------+-------------------------------------------------+ | ||
| STAMP TLV Flags | 8-bit field; for the details about the STAMP | | ||
| | TLV Flags Format, see RFC 8972 | | ||
+-----------------+-------------------------------------------------+ | ||
| Type | characterizes the interpretation of the Value | | ||
| | field | | ||
+-----------------+-------------------------------------------------+ | ||
| Length | the length of the Value field in octets | | ||
+-----------------+-------------------------------------------------+ | ||
| Value | interpreted according to the value of the Type | | ||
| | field | | ||
+-----------------+-------------------------------------------------+ | ||
References: | ||
* `Simple Two-Way Active Measurement Protocol Optional Extensions | ||
[RFC 8972] <https://www.rfc-editor.org/rfc/rfc8972.html>`_ | ||
""" | ||
|
||
name = 'STAMP Test Packet - Generic TLV' | ||
fields_desc = [ | ||
FlagsField('flags', 0, 8, "UMIRRRRR"), | ||
ByteEnumField('type', None, _stamp_tlvs), | ||
ShortField('len', 0), | ||
StrLenField('value', '', length_from=lambda pkt: pkt.len), | ||
] | ||
|
||
def extract_padding(self, p): | ||
return b"", p | ||
|
||
registered_stamp_tlv = {} | ||
|
||
@classmethod | ||
def register_variant(cls): | ||
cls.registered_stamp_tlv[cls.type.default] = cls | ||
|
||
@classmethod | ||
def dispatch_hook(cls, pkt=None, *args, **kargs): | ||
if pkt: | ||
tmp_type = ord(pkt[1:2]) | ||
return cls.registered_stamp_tlv.get(tmp_type, cls) | ||
return cls | ||
|
||
|
||
class STAMPSessionSenderTestUnauthenticated(Packet): | ||
""" | ||
Extended STAMP Session-Sender Test Packet in Unauthenticated Mode. | ||
The format (defined in Section 3 of `RFC 8972 | ||
<https://www.rfc-editor.org/rfc/rfc8972.html>`_) is shown below:: | ||
0 1 2 3 | ||
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 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Sequence Number | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Timestamp | | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Error Estimate | SSID | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| | | ||
| | | ||
| MBZ (28 octets) | | ||
| | | ||
| | | ||
| | | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
~ TLVs ~ | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
References: | ||
* `Simple Two-Way Active Measurement Protocol Optional Extensions | ||
[RFC 8972] <https://www.rfc-editor.org/rfc/rfc8972.html>`_ | ||
""" | ||
name = 'STAMP Session-Sender Test' | ||
fields_desc = [ | ||
IntField('seq', 0), | ||
MultipleTypeField( | ||
[ | ||
(TimeStampField('ts', 0), | ||
lambda pkt:pkt.err_estimate.Z == 0) | ||
], | ||
UTCTimeField('ts', 0, fmt='Q') | ||
), | ||
PacketField('err_estimate', ErrorEstimate(), ErrorEstimate), | ||
ShortField('ssid', 1), | ||
NBytesField('mbz', 0, 28), # 28 bytes MBZ | ||
PacketListField('tlv_objects', [], STAMPTestTLV, | ||
length_from=lambda pkt: pkt.parent.len - 8 - 44), | ||
] | ||
|
||
|
||
class STAMPSessionReflectorTestUnauthenticated(Packet): | ||
""" | ||
Extended STAMP Session-Reflector Test Packet in Unauthenticated Mode. | ||
The format (defined in Section 3 of `RFC 8972 | ||
<https://www.rfc-editor.org/rfc/rfc8972.html>`_) is shown below:: | ||
0 1 2 3 | ||
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 | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Sequence Number | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Timestamp | | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Error Estimate | SSID | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Receive Timestamp | | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Session-Sender Sequence Number | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Session-Sender Timestamp | | ||
| | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
| Session-Sender Error Estimate | MBZ | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
|Ses-Sender TTL | MBZ | | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
~ TLVs ~ | ||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ||
References: | ||
* `Simple Two-Way Active Measurement Protocol Optional Extensions | ||
[RFC 8972] <https://www.rfc-editor.org/rfc/rfc8972.html>`_ | ||
""" | ||
name = 'STAMP Session-Reflector Test' | ||
fields_desc = [ | ||
IntField('seq', 0), | ||
MultipleTypeField( | ||
[ | ||
(TimeStampField('ts', 0), | ||
lambda pkt:pkt.err_estimate.Z == 0), | ||
], | ||
UTCTimeField('ts', 0, fmt='Q') | ||
), | ||
PacketField('err_estimate', ErrorEstimate(), ErrorEstimate), | ||
ShortField('ssid', 1), | ||
MultipleTypeField( | ||
[ | ||
(TimeStampField('ts_rx', 0), | ||
lambda pkt:pkt.err_estimate.Z == 0) | ||
], | ||
UTCTimeField('ts_rx', 0, fmt='Q') | ||
), | ||
IntField('seq_sender', 0), | ||
MultipleTypeField( | ||
[ | ||
(TimeStampField('ts_sender', 0), | ||
lambda pkt:pkt.err_estimate_sender.Z == 0) | ||
], | ||
UTCTimeField('ts_sender', 0, fmt='Q') | ||
), | ||
PacketField('err_estimate_sender', ErrorEstimate(), ErrorEstimate), | ||
ShortField('mbz1', 0), | ||
ByteField('ttl_sender', 255), | ||
NBytesField('mbz2', 0, 3), # 3 bytes MBZ | ||
PacketListField('tlv_objects', [], STAMPTestTLV, | ||
length_from=lambda pkt: pkt.parent.len - 8 - 44), | ||
] | ||
|
||
|
||
bind_layers(UDP, STAMPSessionSenderTestUnauthenticated, dport=862) | ||
bind_layers(UDP, STAMPSessionReflectorTestUnauthenticated, sport=862) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
% STAMP regression tests for Scapy | ||
|
||
# More information at http://www.secdev.org/projects/UTscapy/ | ||
|
||
# Type the following command to launch start the tests: | ||
# $ test/run_tests -t test/contrib/stamp.uts | ||
|
||
############ | ||
# STAMP | ||
############ | ||
|
||
+ STAMP tests | ||
|
||
= Load module | ||
|
||
load_contrib("stamp") | ||
|
||
= Test STAMP Session-Sender Test (Unauthenticated) | ||
~ stamp-session-sender-test | ||
|
||
created = STAMPSessionSenderTestUnauthenticated( | ||
seq=0x1234, | ||
ts=1234.5678, | ||
err_estimate=ErrorEstimate( | ||
S=1, | ||
Z=0, | ||
scale=0x12, | ||
multiplier=0x34 | ||
), | ||
ssid=1357 | ||
) | ||
assert raw(created) == b'\x00\x00\x12\x34\x00\x00\x04\xD2\x91\x5B\x57\x3E\x92\x34\x05\x4D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | ||
parsed = STAMPSessionSenderTestUnauthenticated(raw(created)) | ||
assert parsed.seq == 0x1234 | ||
assert parsed.ts == 1234.5678 | ||
assert parsed.err_estimate.S == 1 | ||
assert parsed.err_estimate.Z == 0 | ||
assert parsed.err_estimate.scale == 0x12 | ||
assert parsed.err_estimate.multiplier == 0x34 | ||
assert parsed.ssid == 1357 | ||
assert parsed.mbz == 0 | ||
assert not parsed.tlv_objects | ||
|
||
= Test STAMP Session-Reflector Test (Unauthenticated) | ||
~ stamp-session-reflector-test | ||
|
||
created = STAMPSessionReflectorTestUnauthenticated( | ||
seq=0x1234, | ||
ts=1234.5678, | ||
err_estimate=ErrorEstimate( | ||
S=1, | ||
Z=0, | ||
scale=0x12, | ||
multiplier=0x34 | ||
), | ||
ssid=1357, | ||
ts_rx=4321.8765, | ||
seq_sender=0x4321, | ||
ts_sender=2143.6587, | ||
err_estimate_sender=ErrorEstimate( | ||
S=0, | ||
Z=0, | ||
scale=0x21, | ||
multiplier=0x43 | ||
), | ||
ttl_sender=111 | ||
) | ||
assert raw(created) == b'\x00\x00\x12\x34\x00\x00\x04\xD2\x91\x5B\x57\x3E\x92\x34\x05\x4D\x00\x00\x10\xE1\xE0\x62\x4D\xD2\x00\x00\x43\x21\x00\x00\x08\x5F\xA8\xA0\x90\x2D\x21\x43\x00\x00\x6F\x00\x00\x00' | ||
parsed = STAMPSessionReflectorTestUnauthenticated(raw(created)) | ||
assert parsed.seq == 0x1234 | ||
assert parsed.ts == 1234.5678 | ||
assert parsed.err_estimate.S == 1 | ||
assert parsed.err_estimate.Z == 0 | ||
assert parsed.err_estimate.scale == 0x12 | ||
assert parsed.err_estimate.multiplier == 0x34 | ||
assert parsed.ssid == 1357 | ||
assert parsed.ts_rx == 4321.8765 | ||
assert parsed.seq_sender == 0x4321 | ||
assert parsed.ts_sender == 2143.6587 | ||
assert parsed.err_estimate_sender.S == 0 | ||
assert parsed.err_estimate_sender.Z == 0 | ||
assert parsed.err_estimate_sender.scale == 0x21 | ||
assert parsed.err_estimate_sender.multiplier == 0x43 | ||
assert parsed.mbz1 == 0 | ||
assert parsed.ttl_sender == 111 | ||
assert parsed.mbz2 == 0 | ||
assert not parsed.tlv_objects | ||
|