mirror of https://github.com/secdev/scapy.git
LLDP: support complex ID types
This commit is contained in:
parent
2c5bf915ee
commit
3935d91f73
|
@ -42,11 +42,13 @@
|
|||
|
||||
"""
|
||||
from scapy.config import conf
|
||||
from scapy.layers.dot11 import Packet
|
||||
from scapy.layers.l2 import Ether, Dot1Q, bind_layers, \
|
||||
BitField, StrLenField, ByteEnumField, BitEnumField, \
|
||||
BitFieldLenField, ShortField, Scapy_Exception, \
|
||||
XStrLenField, ByteField, EnumField, ThreeBytesField
|
||||
from scapy.error import log_runtime, Scapy_Exception
|
||||
from scapy.layers.l2 import Ether, Dot1Q
|
||||
from scapy.fields import MACField, IPField, BitField, \
|
||||
StrLenField, ByteEnumField, BitEnumField, \
|
||||
EnumField, ThreeBytesField, BitFieldLenField, \
|
||||
ShortField, XStrLenField
|
||||
from scapy.packet import Packet, Padding, bind_layers
|
||||
from scapy.modules.six.moves import range
|
||||
from scapy.data import ETHER_TYPES
|
||||
from scapy.compat import orb
|
||||
|
@ -282,6 +284,48 @@ class LLDPDU(Packet):
|
|||
|
||||
super(LLDPDU, self).dissection_done(pkt)
|
||||
|
||||
def _check(self):
|
||||
"""Overwrited by LLDPU objects"""
|
||||
pass
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDU, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDU, self).do_build()
|
||||
|
||||
class _LLDPidField(StrLenField):
|
||||
"""Class that selects the type of the ID field depending
|
||||
on the type of `subtype`"""
|
||||
__slots__ = StrLenField.__slots__ + ["subtypes_dict"]
|
||||
|
||||
def __init__(self, name, default, subtypes_dict, *args, **kargs):
|
||||
self.subtypes_dict = subtypes_dict
|
||||
super(_LLDPidField, self).__init__(name, default, *args, **kargs)
|
||||
|
||||
def m2i(self, pkt, x):
|
||||
cls = self.subtypes_dict.get(pkt.subtype, StrLenField)
|
||||
try:
|
||||
return (cls.m2i.__func__ if six.PY2 else cls.m2i)(self, pkt, x)
|
||||
except:
|
||||
log_runtime.exception("Failed to dissect " + self.name + " ! ")
|
||||
return StrLenField.m2i(self, pkt, x)
|
||||
|
||||
def i2m(self, pkt, x):
|
||||
cls = self.subtypes_dict.get(pkt.subtype, StrLenField)
|
||||
try:
|
||||
return (cls.i2m.__func__ if six.PY2 else cls.i2m)(self, pkt, x)
|
||||
except:
|
||||
log_runtime.exception("Failed to build " + self.name + " ! ")
|
||||
return StrLenField.i2m(self, pkt, x)
|
||||
|
||||
def _ldp_id_adjustlen(pkt, x):
|
||||
"""Return the length of the `id` field,
|
||||
according to its real encoded type"""
|
||||
f, v = pkt.getfield_and_val('id')
|
||||
return len(_LLDPidField.i2m(f, pkt, v)) + 1
|
||||
|
||||
class LLDPDUChassisID(LLDPDU):
|
||||
"""
|
||||
|
@ -299,6 +343,11 @@ class LLDPDUChassisID(LLDPDU):
|
|||
range(0x08, 0xff): 'reserved'
|
||||
}
|
||||
|
||||
LLDP_CHASSIS_ID_TLV_SUBTYPES_FIELDS = {
|
||||
0x04: MACField,
|
||||
0x05: IPField,
|
||||
}
|
||||
|
||||
SUBTYPE_RESERVED = 0x00
|
||||
SUBTYPE_CHASSIS_COMPONENT = 0x01
|
||||
SUBTYPE_INTERFACE_ALIAS = 0x02
|
||||
|
@ -311,9 +360,9 @@ class LLDPDUChassisID(LLDPDU):
|
|||
fields_desc = [
|
||||
BitEnumField('_type', 0x01, 7, LLDPDU.TYPES),
|
||||
BitFieldLenField('_length', None, 9, length_of='id',
|
||||
adjust=lambda pkt, x: len(pkt.id) + 1),
|
||||
adjust=lambda pkt, x: _ldp_id_adjustlen(pkt, x)),
|
||||
ByteEnumField('subtype', 0x00, LLDP_CHASSIS_ID_TLV_SUBTYPES),
|
||||
XStrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
|
||||
_LLDPidField('id', '', LLDP_CHASSIS_ID_TLV_SUBTYPES_FIELDS, length_from=lambda pkt: pkt._length - 1)
|
||||
]
|
||||
|
||||
def _check(self):
|
||||
|
@ -323,14 +372,6 @@ class LLDPDUChassisID(LLDPDU):
|
|||
if conf.contribs['LLDP'].strict_mode() and not self.id:
|
||||
raise LLDPInvalidLengthField('id must be >= 1 characters long')
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUChassisID, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUChassisID, self).do_build()
|
||||
|
||||
|
||||
class LLDPDUPortID(LLDPDU):
|
||||
"""
|
||||
|
@ -348,6 +389,11 @@ class LLDPDUPortID(LLDPDU):
|
|||
range(0x08, 0xff): 'reserved'
|
||||
}
|
||||
|
||||
LLDP_PORT_ID_TLV_SUBTYPES = {
|
||||
0x03: MACField,
|
||||
0x04: IPField,
|
||||
}
|
||||
|
||||
SUBTYPE_RESERVED = 0x00
|
||||
SUBTYPE_INTERFACE_ALIAS = 0x01
|
||||
SUBTYPE_PORT_COMPONENT = 0x02
|
||||
|
@ -360,9 +406,9 @@ class LLDPDUPortID(LLDPDU):
|
|||
fields_desc = [
|
||||
BitEnumField('_type', 0x02, 7, LLDPDU.TYPES),
|
||||
BitFieldLenField('_length', None, 9, length_of='id',
|
||||
adjust=lambda pkt, x: len(pkt.id) + 1),
|
||||
adjust=lambda pkt, x: _ldp_id_adjustlen(pkt, x)),
|
||||
ByteEnumField('subtype', 0x00, LLDP_PORT_ID_TLV_SUBTYPES),
|
||||
StrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
|
||||
_LLDPidField('id', '', LLDP_PORT_ID_TLV_SUBTYPES, length_from=lambda pkt: pkt._length - 1)
|
||||
]
|
||||
|
||||
def _check(self):
|
||||
|
@ -372,14 +418,6 @@ class LLDPDUPortID(LLDPDU):
|
|||
if conf.contribs['LLDP'].strict_mode() and not self.id:
|
||||
raise LLDPInvalidLengthField('id must be >= 1 characters long')
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUPortID, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUPortID, self).do_build()
|
||||
|
||||
|
||||
class LLDPDUTimeToLive(LLDPDU):
|
||||
"""
|
||||
|
@ -399,15 +437,6 @@ class LLDPDUTimeToLive(LLDPDU):
|
|||
raise LLDPInvalidLengthField('length must be 2 - got '
|
||||
'{}'.format(self._length))
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUTimeToLive, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUTimeToLive, self).do_build()
|
||||
|
||||
|
||||
class LLDPDUEndOfLLDPDU(LLDPDU):
|
||||
"""
|
||||
ieee 802.1ab-2016 - sec. 8.5.1 / p. 26
|
||||
|
@ -428,14 +457,6 @@ class LLDPDUEndOfLLDPDU(LLDPDU):
|
|||
raise LLDPInvalidLengthField('length must be 0 - got '
|
||||
'{}'.format(self._length))
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUEndOfLLDPDU, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUEndOfLLDPDU, self).do_build()
|
||||
|
||||
|
||||
class LLDPDUPortDescription(LLDPDU):
|
||||
"""
|
||||
|
@ -519,14 +540,6 @@ class LLDPDUSystemCapabilities(LLDPDU):
|
|||
raise LLDPInvalidLengthField('length must be 4 - got '
|
||||
'{}'.format(self._length))
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUSystemCapabilities, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUSystemCapabilities, self).do_build()
|
||||
|
||||
|
||||
class LLDPDUManagementAddress(LLDPDU):
|
||||
"""
|
||||
|
@ -659,14 +672,6 @@ class LLDPDUManagementAddress(LLDPDU):
|
|||
'management address must be 1..31 characters long - '
|
||||
'got string of size {}'.format(management_address_len))
|
||||
|
||||
def post_dissect(self, s):
|
||||
self._check()
|
||||
return super(LLDPDUManagementAddress, self).post_dissect(s)
|
||||
|
||||
def do_build(self):
|
||||
self._check()
|
||||
return super(LLDPDUManagementAddress, self).do_build()
|
||||
|
||||
|
||||
class ThreeBytesEnumField(EnumField, ThreeBytesField):
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ frm = Ether(frm)
|
|||
conf.contribs['LLDP'].strict_mode_disable()
|
||||
frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
|
||||
LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \
|
||||
LLDPDUTimeToLive() / \
|
||||
LLDPDUEndOfLLDPDU()
|
||||
assert(len(raw(frm)) == 60)
|
||||
|
@ -39,7 +39,7 @@ assert(len(raw(Ether(raw(frm))[Padding])) == 24)
|
|||
|
||||
frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
|
||||
LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth012345678901234567890123') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \
|
||||
LLDPDUTimeToLive() / \
|
||||
LLDPDUEndOfLLDPDU()
|
||||
assert (len(raw(frm)) == 60)
|
||||
|
@ -47,7 +47,7 @@ assert (len(raw(Ether(raw(frm))[Padding])) == 1)
|
|||
|
||||
frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
|
||||
LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0123456789012345678901234') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \
|
||||
LLDPDUTimeToLive() / \
|
||||
LLDPDUEndOfLLDPDU()
|
||||
assert (len(raw(frm)) == 60)
|
||||
|
@ -58,6 +58,13 @@ except IndexError:
|
|||
pass
|
||||
|
||||
|
||||
= Advanced test: check length of complex IDs
|
||||
|
||||
pkt = Ether()/LLDPDUChassisID(id="ff:dd:ee:bb:aa:99", subtype=0x04)/LLDPDUPortID(subtype=0x03, id="aa:bb:cc:dd:ee:ff")/LLDPDUTimeToLive(ttl=120)/LLDPDUEndOfLLDPDU()
|
||||
pkt = Ether(raw(pkt))
|
||||
assert pkt[LLDPDUChassisID]._length == 7
|
||||
assert pkt[LLDPDUPortID]._length == 7
|
||||
|
||||
+ strict mode handling - build
|
||||
= basic frame structure
|
||||
|
||||
|
@ -168,8 +175,8 @@ frm = Ether(frm.build())
|
|||
conf.contribs['LLDP'].strict_mode_enable()
|
||||
|
||||
frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
|
||||
LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id=b'\x01\x02\x03\x04\x05\x06')/\
|
||||
LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id='06:05:04:03:02:01') / \
|
||||
LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id='01:02:03:04:05:06')/\
|
||||
LLDPDUTimeToLive()/\
|
||||
LLDPDUSystemName(system_name='things will')/\
|
||||
LLDPDUSystemCapabilities(telephone_available=1,
|
||||
|
@ -188,8 +195,8 @@ frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \
|
|||
|
||||
frm = Ether(frm.build())
|
||||
|
||||
assert str2mac(frm[LLDPDUChassisID].id) == '06:05:04:03:02:01'
|
||||
assert str2mac(frm[LLDPDUPortID].id) == '01:02:03:04:05:06'
|
||||
assert frm[LLDPDUChassisID].id == '06:05:04:03:02:01'
|
||||
assert frm[LLDPDUPortID].id == '01:02:03:04:05:06'
|
||||
sys_capabilities = frm[LLDPDUSystemCapabilities]
|
||||
assert sys_capabilities.reserved_5_available == 0
|
||||
assert sys_capabilities.reserved_4_available == 0
|
||||
|
|
|
@ -368,7 +368,7 @@ def fletcher16_checkbytes(binbuf, offset):
|
|||
|
||||
|
||||
def mac2str(mac):
|
||||
return b"".join(chb(int(x, 16)) for x in mac.split(':'))
|
||||
return b"".join(chb(int(x, 16)) for x in plain_str(mac).split(':'))
|
||||
|
||||
def str2mac(s):
|
||||
if isinstance(s, str):
|
||||
|
|
Loading…
Reference in New Issue