LLDP: support complex ID types

This commit is contained in:
gpotter2 2018-03-09 20:36:47 +01:00
parent 2c5bf915ee
commit 3935d91f73
3 changed files with 78 additions and 66 deletions

View File

@ -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):

View File

@ -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

View File

@ -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):