Skip to content
Open
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
110 changes: 91 additions & 19 deletions scapy/contrib/rsvp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from scapy.compat import chb
from scapy.packet import Packet, bind_layers
from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \
IPField, ShortField, StrLenField, XByteField, XShortField
IPField, IntField, ShortField, StrLenField, XByteField, XShortField
from scapy.layers.inet import IP, checksum

rsvpmsgtypes = {0x01: "Path",
Expand All @@ -26,6 +26,9 @@


class RSVP(Packet):
"""Common RSVP message header. The header is followed by this message's objects,
which chains to further RSVP_Object instances, forming the sequence of RSVP objects.
"""
name = "RSVP"
fields_desc = [BitField("Version", 1, 4),
BitField("Flags", 1, 4),
Expand All @@ -36,6 +39,9 @@ class RSVP(Packet):
ShortField("Length", None)]

def post_build(self, p, pay):
"""Append payload bytes to the header, and patch in Length/checksum if unset.
Comment thread
Dizarc marked this conversation as resolved.
Returns the fully packet message bytes.
"""
p += pay
if self.Length is None:
tmp_len = len(p)
Expand All @@ -47,7 +53,7 @@ def post_build(self, p, pay):
return p


rsvptypes = {0x01: "Session",
rsvptypes = {0x01: "SESSION",
0x03: "HOP",
0x04: "INTEGRITY",
0x05: "TIME_VALUES",
Expand Down Expand Up @@ -126,13 +132,22 @@ def post_build(self, p, pay):


class RSVP_Object(Packet):
"""Common RSVP object header. Followed by exactly one RSVP object data structure,
Dissection naturally chains into the next object if more bytes remain.
"""
name = "RSVP_Object"
fields_desc = [ShortField("Length", 4),
ByteEnumField("Class", 0x01, rsvptypes),
ByteField("C_Type", 1)]

def guess_payload_class(self, payload):
if self.Class == 0x03:
"""Pick the data class to dissect based on Class.
Falls back to a generic container.
"""
if self.Class == 0x01:
if self.C_Type == 0x07:
return RSVP_SESSION
elif self.Class == 0x03:
return RSVP_HOP
elif self.Class == 0x05:
return RSVP_Time
Expand All @@ -147,49 +162,95 @@ def guess_payload_class(self, payload):


class RSVP_Data(Packet):
"""Defines a generic/unknown RSVP object data structure for any Class value
that doesn't have a dedicated class implemented yet.
"""
name = "Data"
overload_fields = {RSVP_Object: {"Class": 0x01}}
fields_desc = [StrLenField("Data", "", length_from=lambda pkt:pkt.underlayer.Length - 4)] # noqa: E501
fields_desc = [
StrLenField(
"Data",
"",
length_from=lambda pkt: pkt.underlayer.Length - 4
),
]

def default_payload_class(self, payload):
return RSVP_Object


class RSVP_SESSION(Packet):
"""SESSION LSP_TUNNEL_IPV4 object data structure, RFC 3209 section 4.6.1.1.
Identifies the session for which this message is sent.
"""
name = "SESSION"
overload_fields = {RSVP_Object: {"Class": 0x01, "C_Type": 0x07}}
fields_desc = [IPField("dest_addr", "0.0.0.0"),
ShortField("reserved", 0),
ShortField("tunnel_id", 0),
IPField("ext_tunnel_id", "0.0.0.0")]

def default_payload_class(self, payload):
return RSVP_Object


class RSVP_HOP(Packet):
"""RSVP_HOP object data structure, RFC 2205 section A.2.
Identifies the IP address and the Logical Interface Handle (LIH)
of the interface this message was sent from.
"""
name = "HOP"
overload_fields = {RSVP_Object: {"Class": 0x03}}
fields_desc = [IPField("neighbor", "0.0.0.0"),
BitField("inface", 1, 32)]
IntField("inface", 1)]

def default_payload_class(self, payload):
return RSVP_Object


class RSVP_Time(Packet):
"""TIME_VALUES object RFC 2205 section A.4.
Carries the refresh period a sender will use to resend PATH/RESV messages,
which the receiver can use to determine when to tear down
the reservation if no further messages are received.
"""
name = "Time Val"
overload_fields = {RSVP_Object: {"Class": 0x05}}
fields_desc = [BitField("refresh", 1, 32)]
fields_desc = [IntField("refresh", 1)]

def default_payload_class(self, payload):
return RSVP_Object


class RSVP_SenderTSPEC(Packet):
"""SENDER_TSPEC object RFC 2210 section 3.1.
Carries the sender's traffic specification for the reservation.
"""
name = "Sender_TSPEC"
overload_fields = {RSVP_Object: {"Class": 0x0c}}
fields_desc = [ByteField("Msg_Format", 0),
ByteField("reserve", 0),
ShortField("Data_Length", 4),
ByteField("Srv_hdr", 1),
ByteField("reserve2", 0),
ShortField("Srv_Length", 4),
StrLenField("Tokens", "", length_from=lambda pkt:pkt.underlayer.Length - 12)] # noqa: E501
fields_desc = [
ByteField("Msg_Format", 0),
ByteField("reserve", 0),
ShortField("Data_Length", 4),
ByteField("Srv_hdr", 1),
ByteField("reserve2", 0),
ShortField("Srv_Length", 4),
StrLenField(
"Tokens",
"",
length_from=lambda pkt: pkt.underlayer.Length - 12
),
]

def default_payload_class(self, payload):
return RSVP_Object


class RSVP_LabelReq(Packet):
"""LABEL_REQUEST object RFC 3209 section 4.2.
Requests that a label be allocated for this LSP, and indicates
which layer 3 protocol the label will carry.
"""
name = "Label Req"
overload_fields = {RSVP_Object: {"Class": 0x13}}
fields_desc = [ShortField("reserve", 1),
Expand All @@ -200,18 +261,29 @@ def default_payload_class(self, payload):


class RSVP_SessionAttrb(Packet):
"""SESSION_ATTRIBUTE object RFC 3209 section 4.7.
Carries session data, mainly used for setup/recovery
priority and human readable session name.
"""
name = "Session_Attribute"
overload_fields = {RSVP_Object: {"Class": 0xCF}}
fields_desc = [ByteField("Setup_priority", 1),
ByteField("Hold_priority", 1),
ByteField("flags", 1),
FieldLenField("Name_length", None, length_of="Name"),
StrLenField("Name", "", length_from=lambda pkt:pkt.Name_length), # noqa: E501
]
fields_desc = [
ByteField("Setup_priority", 1),
ByteField("Hold_priority", 1),
ByteField("flags", 1),
FieldLenField("Name_length", None, length_of="Name"),
StrLenField(
"Name",
"",
length_from=lambda pkt: pkt.Name_length
),
]

def default_payload_class(self, payload):
return RSVP_Object


# Decode IP packets with protocol number 46 as RSVP packets,
# and RSVP packets as RSVP_Object packets.
bind_layers(IP, RSVP, {"proto": 46})
bind_layers(RSVP, RSVP_Object)
43 changes: 43 additions & 0 deletions test/contrib/rsvp.uts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,51 @@ pkt = Ether(raw(pkt))
assert RSVP_SessionAttrb in pkt
assert pkt.Name == b"test"

# TODO: Add more objects to default build

= Master dissection

pkt = Ether(b"\x00\x90\x92\x9d\x94\x01\x00\xd0c\xc3\xb8G\x08\x00E\x00\x00\x80\x8ad\x00\x00\xff.\x8c\xe7\xd2\x00\x00\x02\xd2\x00\x00\x01\x10\x02\xeb\xfa\xff\x00\x00l\x00\x10\x01\x07\x10\x02\x02\x02\x00\x00\x00\x01\x11\x03\x03\x03\x00\x0c\x03\x01\xd2\x00\x00\x02\x00\x00\x00\x00\x00\x08\x05\x01\x00\x00u0\x00\x08\x08\x01\x00\x00\x00\x12\x00$\t\x02\x00\x00\x00\x07\x05\x00\x00\x06\x7f\x00\x00\x05I\x18\x96\x80Dz\x00\x00\x7f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\n\x07\x11\x03\x03\x03\x00\x00'\x11\x00\x08\x10\x01\x00\x00\x00\x10\x03\x06-\xad")
# Header
assert RSVP in pkt
header = pkt[RSVP]
assert header.Version == 1
assert header.Class == 2
assert header.TTL == 255
assert header.Length == 108

# Session (C-type 7) object
assert RSVP_SESSION in pkt
session = pkt[RSVP_SESSION]
assert session.dest_addr == "16.2.2.2"
assert session.reserved == 0
assert session.tunnel_id == 1
assert session.ext_tunnel_id == "17.3.3.3"

# HOP object
assert RSVP_HOP in pkt
hop = pkt[RSVP_HOP]
assert hop.neighbor == "210.0.0.2"
assert hop.inface == 0

# TIME_VALUES object
assert RSVP_Time in pkt
assert pkt[RSVP_Time].refresh == 30000

# TODO: add the rest of the objects to master dissector once they are no longer generic

+ RSVP_SESSION

= RSVP_SESSION, build and dissect round-trip standalone

pkt = RSVP_Object() / RSVP_SESSION(dest_addr="10.0.0.1", reserved=6, tunnel_id=0, ext_tunnel_id="1.1.1.1")
raw_pkt = raw(pkt)
pkt2 = RSVP_Object(raw_pkt)
assert RSVP_SESSION in pkt2
assert pkt2.Class == 0x01
assert pkt2.C_Type == 0x07
assert pkt2.dest_addr == "10.0.0.1"
assert pkt2.reserved == 6
assert pkt2.tunnel_id == 0
assert pkt2.ext_tunnel_id == "1.1.1.1"
assert bytes(pkt2) == raw_pkt
Loading