846 lines
26 KiB
Python
846 lines
26 KiB
Python
|
# This file is part of Scapy
|
||
|
# See http://www.secdev.org/projects/scapy for more information
|
||
|
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
||
|
# This program is published under a GPLv2 license
|
||
|
|
||
|
"""LLTD Protocol
|
||
|
|
||
|
https://msdn.microsoft.com/en-us/library/cc233983.aspx
|
||
|
|
||
|
"""
|
||
|
|
||
|
from __future__ import absolute_import
|
||
|
from array import array
|
||
|
|
||
|
from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \
|
||
|
ShortField, ShortEnumField, ThreeBytesField, IntField, IntEnumField, \
|
||
|
LongField, MultiEnumField, FieldLenField, FieldListField, \
|
||
|
PacketListField, StrLenField, StrLenFieldUtf16, ConditionalField, MACField
|
||
|
from scapy.packet import Packet, Padding, bind_layers
|
||
|
from scapy.plist import PacketList
|
||
|
from scapy.layers.l2 import Ether
|
||
|
from scapy.layers.inet import IPField
|
||
|
from scapy.layers.inet6 import IP6Field
|
||
|
from scapy.data import ETHER_ANY
|
||
|
import scapy.modules.six as six
|
||
|
from scapy.compat import orb, chb
|
||
|
|
||
|
|
||
|
# Protocol layers
|
||
|
##################
|
||
|
|
||
|
|
||
|
class LLTD(Packet):
|
||
|
name = "LLTD"
|
||
|
answer_hashret = {
|
||
|
# (tos, function) tuple mapping (answer -> query), used by
|
||
|
# .hashret()
|
||
|
(1, 1): (0, 0),
|
||
|
(0, 12): (0, 11),
|
||
|
}
|
||
|
fields_desc = [
|
||
|
ByteField("version", 1),
|
||
|
ByteEnumField("tos", 0, {
|
||
|
0: "Topology discovery",
|
||
|
1: "Quick discovery",
|
||
|
2: "QoS diagnostics",
|
||
|
}),
|
||
|
ByteField("reserved", 0),
|
||
|
MultiEnumField("function", 0, {
|
||
|
0: {
|
||
|
0: "Discover",
|
||
|
1: "Hello",
|
||
|
2: "Emit",
|
||
|
3: "Train",
|
||
|
4: "Probe",
|
||
|
5: "Ack",
|
||
|
6: "Query",
|
||
|
7: "QueryResp",
|
||
|
8: "Reset",
|
||
|
9: "Charge",
|
||
|
10: "Flat",
|
||
|
11: "QueryLargeTlv",
|
||
|
12: "QueryLargeTlvResp",
|
||
|
},
|
||
|
1: {
|
||
|
0: "Discover",
|
||
|
1: "Hello",
|
||
|
8: "Reset",
|
||
|
},
|
||
|
2: {
|
||
|
0: "QosInitializeSink",
|
||
|
1: "QosReady",
|
||
|
2: "QosProbe",
|
||
|
3: "QosQuery",
|
||
|
4: "QosQueryResp",
|
||
|
5: "QosReset",
|
||
|
6: "QosError",
|
||
|
7: "QosAck",
|
||
|
8: "QosCounterSnapshot",
|
||
|
9: "QosCounterResult",
|
||
|
10: "QosCounterLease",
|
||
|
},
|
||
|
}, depends_on=lambda pkt: pkt.tos, fmt="B"),
|
||
|
MACField("real_dst", None),
|
||
|
MACField("real_src", None),
|
||
|
ConditionalField(ShortField("xid", 0),
|
||
|
lambda pkt: pkt.function in [0, 8]),
|
||
|
ConditionalField(ShortField("seq", 0),
|
||
|
lambda pkt: pkt.function not in [0, 8]),
|
||
|
]
|
||
|
|
||
|
def post_build(self, pkt, pay):
|
||
|
if (self.real_dst is None or self.real_src is None) and \
|
||
|
isinstance(self.underlayer, Ether):
|
||
|
eth = self.underlayer
|
||
|
if self.real_dst is None:
|
||
|
pkt = (pkt[:4] + eth.fields_desc[0].i2m(eth, eth.dst) +
|
||
|
pkt[10:])
|
||
|
if self.real_src is None:
|
||
|
pkt = (pkt[:10] + eth.fields_desc[1].i2m(eth, eth.src) +
|
||
|
pkt[16:])
|
||
|
return pkt + pay
|
||
|
|
||
|
def mysummary(self):
|
||
|
if isinstance(self.underlayer, Ether):
|
||
|
return self.underlayer.sprintf(
|
||
|
'LLTD %src% > %dst% %LLTD.tos% - %LLTD.function%'
|
||
|
)
|
||
|
else:
|
||
|
return self.sprintf('LLTD %tos% - %function%')
|
||
|
|
||
|
def hashret(self):
|
||
|
tos, function = self.tos, self.function
|
||
|
return "%c%c" % self.answer_hashret.get((tos, function),
|
||
|
(tos, function))
|
||
|
|
||
|
def answers(self, other):
|
||
|
if not isinstance(other, LLTD):
|
||
|
return False
|
||
|
if self.tos == 0:
|
||
|
if self.function == 0 and isinstance(self.payload, LLTDDiscover) \
|
||
|
and len(self[LLTDDiscover].stations_list) == 1:
|
||
|
# "Topology discovery - Discover" with one MAC address
|
||
|
# discovered answers a "Quick discovery - Hello"
|
||
|
return other.tos == 1 and \
|
||
|
other.function == 1 and \
|
||
|
LLTDAttributeHostID in other and \
|
||
|
other[LLTDAttributeHostID].mac == \
|
||
|
self[LLTDDiscover].stations_list[0]
|
||
|
elif self.function == 12:
|
||
|
# "Topology discovery - QueryLargeTlvResp" answers
|
||
|
# "Topology discovery - QueryLargeTlv" with same .seq
|
||
|
# value
|
||
|
return other.tos == 0 and other.function == 11 \
|
||
|
and other.seq == self.seq
|
||
|
elif self.tos == 1:
|
||
|
if self.function == 1 and isinstance(self.payload, LLTDHello):
|
||
|
# "Quick discovery - Hello" answers a "Topology
|
||
|
# discovery - Discover"
|
||
|
return other.tos == 0 and other.function == 0 and \
|
||
|
other.real_src == self.current_mapper_address
|
||
|
return False
|
||
|
|
||
|
|
||
|
class LLTDHello(Packet):
|
||
|
name = "LLTD - Hello"
|
||
|
show_summary = False
|
||
|
fields_desc = [
|
||
|
ShortField("gen_number", 0),
|
||
|
MACField("current_mapper_address", ETHER_ANY),
|
||
|
MACField("apparent_mapper_address", ETHER_ANY),
|
||
|
]
|
||
|
|
||
|
|
||
|
class LLTDDiscover(Packet):
|
||
|
name = "LLTD - Discover"
|
||
|
fields_desc = [
|
||
|
ShortField("gen_number", 0),
|
||
|
FieldLenField("stations_count", None, count_of="stations_list",
|
||
|
fmt="H"),
|
||
|
FieldListField("stations_list", [], MACField("", ETHER_ANY),
|
||
|
count_from=lambda pkt: pkt.stations_count)
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return (self.sprintf("Stations: %stations_list%")
|
||
|
if self.stations_list else "No station", [LLTD])
|
||
|
|
||
|
|
||
|
class LLTDEmiteeDesc(Packet):
|
||
|
name = "LLTD - Emitee Desc"
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 0, {0: "Train", 1: "Probe"}),
|
||
|
ByteField("pause", 0),
|
||
|
MACField("src", None),
|
||
|
MACField("dst", ETHER_ANY),
|
||
|
]
|
||
|
|
||
|
|
||
|
class LLTDEmit(Packet):
|
||
|
name = "LLTD - Emit"
|
||
|
fields_desc = [
|
||
|
FieldLenField("descs_count", None, count_of="descs_list",
|
||
|
fmt="H"),
|
||
|
PacketListField("descs_list", [], LLTDEmiteeDesc,
|
||
|
count_from=lambda pkt: pkt.descs_count),
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return ", ".join(desc.sprintf("%src% > %dst%")
|
||
|
for desc in self.descs_list), [LLTD]
|
||
|
|
||
|
|
||
|
class LLTDRecveeDesc(Packet):
|
||
|
name = "LLTD - Recvee Desc"
|
||
|
fields_desc = [
|
||
|
ShortEnumField("type", 0, {0: "Probe", 1: "ARP or ICMPv6"}),
|
||
|
MACField("real_src", ETHER_ANY),
|
||
|
MACField("ether_src", ETHER_ANY),
|
||
|
MACField("ether_dst", ETHER_ANY),
|
||
|
]
|
||
|
|
||
|
|
||
|
class LLTDQueryResp(Packet):
|
||
|
name = "LLTD - Query Response"
|
||
|
fields_desc = [
|
||
|
FlagsField("flags", 0, 2, "ME"),
|
||
|
BitField("descs_count", None, 14),
|
||
|
PacketListField("descs_list", [], LLTDRecveeDesc,
|
||
|
count_from=lambda pkt: pkt.descs_count),
|
||
|
]
|
||
|
|
||
|
def post_build(self, pkt, pay):
|
||
|
if self.descs_count is None:
|
||
|
# descs_count should be a FieldLenField but has an
|
||
|
# unsupported format (14 bits)
|
||
|
flags = orb(pkt[0]) & 0xc0
|
||
|
count = len(self.descs_list)
|
||
|
pkt = chb(flags + (count >> 8)) + chb(count % 256) + pkt[2:]
|
||
|
return pkt + pay
|
||
|
|
||
|
def mysummary(self):
|
||
|
return self.sprintf("%d response%s" % (
|
||
|
self.descs_count,
|
||
|
"s" if self.descs_count > 1 else "")), [LLTD]
|
||
|
|
||
|
|
||
|
class LLTDQueryLargeTlv(Packet):
|
||
|
name = "LLTD - Query Large Tlv"
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 14, {
|
||
|
14: "Icon image",
|
||
|
17: "Friendly Name",
|
||
|
19: "Hardware ID",
|
||
|
22: "AP Association Table",
|
||
|
24: "Detailed Icon Image",
|
||
|
26: "Component Table",
|
||
|
28: "Repeater AP Table",
|
||
|
}),
|
||
|
ThreeBytesField("offset", 0),
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return self.sprintf("%type% (offset %offset%)"), [LLTD]
|
||
|
|
||
|
|
||
|
class LLTDQueryLargeTlvResp(Packet):
|
||
|
name = "LLTD - Query Large Tlv Response"
|
||
|
fields_desc = [
|
||
|
FlagsField("flags", 0, 2, "RM"),
|
||
|
BitField("len", None, 14),
|
||
|
StrLenField("value", "", length_from=lambda pkt: pkt.len)
|
||
|
]
|
||
|
|
||
|
def post_build(self, pkt, pay):
|
||
|
if self.len is None:
|
||
|
# len should be a FieldLenField but has an unsupported
|
||
|
# format (14 bits)
|
||
|
flags = orb(pkt[0]) & 0xc0
|
||
|
length = len(self.value)
|
||
|
pkt = chb(flags + (length >> 8)) + chb(length % 256) + pkt[2:]
|
||
|
return pkt + pay
|
||
|
|
||
|
def mysummary(self):
|
||
|
return self.sprintf("%%len%% bytes%s" % (
|
||
|
" (last)" if not self.flags & 2 else ""
|
||
|
)), [LLTD]
|
||
|
|
||
|
|
||
|
class LLTDAttribute(Packet):
|
||
|
name = "LLTD Attribute"
|
||
|
show_indent = False
|
||
|
show_summary = False
|
||
|
# section 2.2.1.1
|
||
|
fields_desc = [
|
||
|
ByteEnumField("type", 0, {
|
||
|
0: "End Of Property",
|
||
|
1: "Host ID",
|
||
|
2: "Characteristics",
|
||
|
3: "Physical Medium",
|
||
|
7: "IPv4 Address",
|
||
|
9: "802.11 Max Rate",
|
||
|
10: "Performance Counter Frequency",
|
||
|
12: "Link Speed",
|
||
|
14: "Icon Image",
|
||
|
15: "Machine Name",
|
||
|
18: "Device UUID",
|
||
|
20: "QoS Characteristics",
|
||
|
21: "802.11 Physical Medium",
|
||
|
24: "Detailed Icon Image",
|
||
|
}),
|
||
|
FieldLenField("len", None, length_of="value", fmt="B"),
|
||
|
StrLenField("value", "", length_from=lambda pkt: pkt.len),
|
||
|
]
|
||
|
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *_, **kargs):
|
||
|
if _pkt:
|
||
|
cmd = orb(_pkt[0])
|
||
|
elif "type" in kargs:
|
||
|
cmd = kargs["type"]
|
||
|
if isinstance(cmd, six.string_types):
|
||
|
cmd = cls.fields_desc[0].s2i[cmd]
|
||
|
else:
|
||
|
return cls
|
||
|
return SPECIFIC_CLASSES.get(cmd, cls)
|
||
|
|
||
|
|
||
|
SPECIFIC_CLASSES = {}
|
||
|
|
||
|
|
||
|
def _register_lltd_specific_class(*attr_types):
|
||
|
"""This can be used as a class decorator; if we want to support Python
|
||
|
2.5, we have to replace
|
||
|
|
||
|
@_register_lltd_specific_class(x[, y[, ...]])
|
||
|
class LLTDAttributeSpecific(LLTDAttribute):
|
||
|
[...]
|
||
|
|
||
|
by
|
||
|
|
||
|
class LLTDAttributeSpecific(LLTDAttribute):
|
||
|
[...]
|
||
|
LLTDAttributeSpecific = _register_lltd_specific_class(x[, y[, ...]])(
|
||
|
LLTDAttributeSpecific
|
||
|
)
|
||
|
|
||
|
"""
|
||
|
def _register(cls):
|
||
|
for attr_type in attr_types:
|
||
|
SPECIFIC_CLASSES[attr_type] = cls
|
||
|
type_fld = LLTDAttribute.fields_desc[0].copy()
|
||
|
type_fld.default = attr_types[0]
|
||
|
cls.fields_desc = [type_fld] + cls.fields_desc
|
||
|
return cls
|
||
|
return _register
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(0)
|
||
|
class LLTDAttributeEOP(LLTDAttribute):
|
||
|
name = "LLTD Attribute - End Of Property"
|
||
|
fields_desc = []
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(1)
|
||
|
class LLTDAttributeHostID(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Host ID"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 6),
|
||
|
MACField("mac", ETHER_ANY),
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return "ID: %s" % self.mac, [LLTD, LLTDAttributeMachineName]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(2)
|
||
|
class LLTDAttributeCharacteristics(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Characteristics"
|
||
|
fields_desc = [
|
||
|
# According to MS doc, "this field MUST be set to 0x02". But
|
||
|
# according to MS implementation, that's wrong.
|
||
|
# ByteField("len", 2),
|
||
|
FieldLenField("len", None, length_of="reserved2", fmt="B",
|
||
|
adjust=lambda _, x: x + 2),
|
||
|
FlagsField("flags", 0, 5, "PXFML"),
|
||
|
BitField("reserved1", 0, 11),
|
||
|
StrLenField("reserved2", "", length_from=lambda x: x.len - 2)
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(3)
|
||
|
class LLTDAttributePhysicalMedium(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Physical Medium"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 4),
|
||
|
IntEnumField("medium", 6, {
|
||
|
# https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib
|
||
|
1: "other",
|
||
|
2: "regular1822",
|
||
|
3: "hdh1822",
|
||
|
4: "ddnX25",
|
||
|
5: "rfc877x25",
|
||
|
6: "ethernetCsmacd",
|
||
|
7: "iso88023Csmacd",
|
||
|
8: "iso88024TokenBus",
|
||
|
9: "iso88025TokenRing",
|
||
|
10: "iso88026Man",
|
||
|
11: "starLan",
|
||
|
12: "proteon10Mbit",
|
||
|
13: "proteon80Mbit",
|
||
|
14: "hyperchannel",
|
||
|
15: "fddi",
|
||
|
16: "lapb",
|
||
|
17: "sdlc",
|
||
|
18: "ds1",
|
||
|
19: "e1",
|
||
|
20: "basicISDN",
|
||
|
21: "primaryISDN",
|
||
|
22: "propPointToPointSerial",
|
||
|
23: "ppp",
|
||
|
24: "softwareLoopback",
|
||
|
25: "eon",
|
||
|
26: "ethernet3Mbit",
|
||
|
27: "nsip",
|
||
|
28: "slip",
|
||
|
29: "ultra",
|
||
|
30: "ds3",
|
||
|
31: "sip",
|
||
|
32: "frameRelay",
|
||
|
33: "rs232",
|
||
|
34: "para",
|
||
|
35: "arcnet",
|
||
|
36: "arcnetPlus",
|
||
|
37: "atm",
|
||
|
38: "miox25",
|
||
|
39: "sonet",
|
||
|
40: "x25ple",
|
||
|
41: "iso88022llc",
|
||
|
42: "localTalk",
|
||
|
43: "smdsDxi",
|
||
|
44: "frameRelayService",
|
||
|
45: "v35",
|
||
|
46: "hssi",
|
||
|
47: "hippi",
|
||
|
48: "modem",
|
||
|
49: "aal5",
|
||
|
50: "sonetPath",
|
||
|
51: "sonetVT",
|
||
|
52: "smdsIcip",
|
||
|
53: "propVirtual",
|
||
|
54: "propMultiplexor",
|
||
|
55: "ieee80212",
|
||
|
56: "fibreChannel",
|
||
|
57: "hippiInterface",
|
||
|
58: "frameRelayInterconnect",
|
||
|
59: "aflane8023",
|
||
|
60: "aflane8025",
|
||
|
61: "cctEmul",
|
||
|
62: "fastEther",
|
||
|
63: "isdn",
|
||
|
64: "v11",
|
||
|
65: "v36",
|
||
|
66: "g703at64k",
|
||
|
67: "g703at2mb",
|
||
|
68: "qllc",
|
||
|
69: "fastEtherFX",
|
||
|
70: "channel",
|
||
|
71: "ieee80211",
|
||
|
72: "ibm370parChan",
|
||
|
73: "escon",
|
||
|
74: "dlsw",
|
||
|
75: "isdns",
|
||
|
76: "isdnu",
|
||
|
77: "lapd",
|
||
|
78: "ipSwitch",
|
||
|
79: "rsrb",
|
||
|
80: "atmLogical",
|
||
|
81: "ds0",
|
||
|
82: "ds0Bundle",
|
||
|
83: "bsc",
|
||
|
84: "async",
|
||
|
85: "cnr",
|
||
|
86: "iso88025Dtr",
|
||
|
87: "eplrs",
|
||
|
88: "arap",
|
||
|
89: "propCnls",
|
||
|
90: "hostPad",
|
||
|
91: "termPad",
|
||
|
92: "frameRelayMPI",
|
||
|
93: "x213",
|
||
|
94: "adsl",
|
||
|
95: "radsl",
|
||
|
96: "sdsl",
|
||
|
97: "vdsl",
|
||
|
98: "iso88025CRFPInt",
|
||
|
99: "myrinet",
|
||
|
100: "voiceEM",
|
||
|
101: "voiceFXO",
|
||
|
102: "voiceFXS",
|
||
|
103: "voiceEncap",
|
||
|
104: "voiceOverIp",
|
||
|
105: "atmDxi",
|
||
|
106: "atmFuni",
|
||
|
107: "atmIma",
|
||
|
108: "pppMultilinkBundle",
|
||
|
109: "ipOverCdlc",
|
||
|
110: "ipOverClaw",
|
||
|
111: "stackToStack",
|
||
|
112: "virtualIpAddress",
|
||
|
113: "mpc",
|
||
|
114: "ipOverAtm",
|
||
|
115: "iso88025Fiber",
|
||
|
116: "tdlc",
|
||
|
117: "gigabitEthernet",
|
||
|
118: "hdlc",
|
||
|
119: "lapf",
|
||
|
120: "v37",
|
||
|
121: "x25mlp",
|
||
|
122: "x25huntGroup",
|
||
|
123: "transpHdlc",
|
||
|
124: "interleave",
|
||
|
125: "fast",
|
||
|
126: "ip",
|
||
|
127: "docsCableMaclayer",
|
||
|
128: "docsCableDownstream",
|
||
|
129: "docsCableUpstream",
|
||
|
130: "a12MppSwitch",
|
||
|
131: "tunnel",
|
||
|
132: "coffee",
|
||
|
133: "ces",
|
||
|
134: "atmSubInterface",
|
||
|
135: "l2vlan",
|
||
|
136: "l3ipvlan",
|
||
|
137: "l3ipxvlan",
|
||
|
138: "digitalPowerline",
|
||
|
139: "mediaMailOverIp",
|
||
|
140: "dtm",
|
||
|
141: "dcn",
|
||
|
142: "ipForward",
|
||
|
143: "msdsl",
|
||
|
144: "ieee1394",
|
||
|
145: "if-gsn",
|
||
|
146: "dvbRccMacLayer",
|
||
|
147: "dvbRccDownstream",
|
||
|
148: "dvbRccUpstream",
|
||
|
149: "atmVirtual",
|
||
|
150: "mplsTunnel",
|
||
|
151: "srp",
|
||
|
152: "voiceOverAtm",
|
||
|
153: "voiceOverFrameRelay",
|
||
|
154: "idsl",
|
||
|
155: "compositeLink",
|
||
|
156: "ss7SigLink",
|
||
|
157: "propWirelessP2P",
|
||
|
158: "frForward",
|
||
|
159: "rfc1483",
|
||
|
160: "usb",
|
||
|
161: "ieee8023adLag",
|
||
|
162: "bgppolicyaccounting",
|
||
|
163: "frf16MfrBundle",
|
||
|
164: "h323Gatekeeper",
|
||
|
165: "h323Proxy",
|
||
|
166: "mpls",
|
||
|
167: "mfSigLink",
|
||
|
168: "hdsl2",
|
||
|
169: "shdsl",
|
||
|
170: "ds1FDL",
|
||
|
171: "pos",
|
||
|
172: "dvbAsiIn",
|
||
|
173: "dvbAsiOut",
|
||
|
174: "plc",
|
||
|
175: "nfas",
|
||
|
176: "tr008",
|
||
|
177: "gr303RDT",
|
||
|
178: "gr303IDT",
|
||
|
179: "isup",
|
||
|
180: "propDocsWirelessMaclayer",
|
||
|
181: "propDocsWirelessDownstream",
|
||
|
182: "propDocsWirelessUpstream",
|
||
|
183: "hiperlan2",
|
||
|
184: "propBWAp2Mp",
|
||
|
185: "sonetOverheadChannel",
|
||
|
186: "digitalWrapperOverheadChannel",
|
||
|
187: "aal2",
|
||
|
188: "radioMAC",
|
||
|
189: "atmRadio",
|
||
|
190: "imt",
|
||
|
191: "mvl",
|
||
|
192: "reachDSL",
|
||
|
193: "frDlciEndPt",
|
||
|
194: "atmVciEndPt",
|
||
|
195: "opticalChannel",
|
||
|
196: "opticalTransport",
|
||
|
197: "propAtm",
|
||
|
198: "voiceOverCable",
|
||
|
199: "infiniband",
|
||
|
200: "teLink",
|
||
|
201: "q2931",
|
||
|
202: "virtualTg",
|
||
|
203: "sipTg",
|
||
|
204: "sipSig",
|
||
|
205: "docsCableUpstreamChannel",
|
||
|
206: "econet",
|
||
|
207: "pon155",
|
||
|
208: "pon622",
|
||
|
209: "bridge",
|
||
|
210: "linegroup",
|
||
|
211: "voiceEMFGD",
|
||
|
212: "voiceFGDEANA",
|
||
|
213: "voiceDID",
|
||
|
214: "mpegTransport",
|
||
|
215: "sixToFour",
|
||
|
216: "gtp",
|
||
|
217: "pdnEtherLoop1",
|
||
|
218: "pdnEtherLoop2",
|
||
|
219: "opticalChannelGroup",
|
||
|
220: "homepna",
|
||
|
221: "gfp",
|
||
|
222: "ciscoISLvlan",
|
||
|
223: "actelisMetaLOOP",
|
||
|
224: "fcipLink",
|
||
|
225: "rpr",
|
||
|
226: "qam",
|
||
|
227: "lmp",
|
||
|
228: "cblVectaStar",
|
||
|
229: "docsCableMCmtsDownstream",
|
||
|
230: "adsl2",
|
||
|
231: "macSecControlledIF",
|
||
|
232: "macSecUncontrolledIF",
|
||
|
233: "aviciOpticalEther",
|
||
|
234: "atmbond",
|
||
|
235: "voiceFGDOS",
|
||
|
236: "mocaVersion1",
|
||
|
237: "ieee80216WMAN",
|
||
|
238: "adsl2plus",
|
||
|
239: "dvbRcsMacLayer",
|
||
|
240: "dvbTdm",
|
||
|
241: "dvbRcsTdma",
|
||
|
242: "x86Laps",
|
||
|
243: "wwanPP",
|
||
|
244: "wwanPP2",
|
||
|
245: "voiceEBS",
|
||
|
246: "ifPwType",
|
||
|
247: "ilan",
|
||
|
248: "pip",
|
||
|
249: "aluELP",
|
||
|
250: "gpon",
|
||
|
251: "vdsl2",
|
||
|
252: "capwapDot11Profile",
|
||
|
253: "capwapDot11Bss",
|
||
|
254: "capwapWtpVirtualRadio",
|
||
|
255: "bits",
|
||
|
256: "docsCableUpstreamRfPort",
|
||
|
257: "cableDownstreamRfPort",
|
||
|
258: "vmwareVirtualNic",
|
||
|
259: "ieee802154",
|
||
|
260: "otnOdu",
|
||
|
261: "otnOtu",
|
||
|
262: "ifVfiType",
|
||
|
263: "g9981",
|
||
|
264: "g9982",
|
||
|
265: "g9983",
|
||
|
266: "aluEpon",
|
||
|
267: "aluEponOnu",
|
||
|
268: "aluEponPhysicalUni",
|
||
|
269: "aluEponLogicalLink",
|
||
|
271: "aluGponPhysicalUni",
|
||
|
272: "vmwareNicTeam",
|
||
|
277: "docsOfdmDownstream",
|
||
|
278: "docsOfdmaUpstream",
|
||
|
279: "gfast",
|
||
|
280: "sdci",
|
||
|
}),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(7)
|
||
|
class LLTDAttributeIPv4Address(LLTDAttribute):
|
||
|
name = "LLTD Attribute - IPv4 Address"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 4),
|
||
|
IPField("ipv4", "0.0.0.0"),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(8)
|
||
|
class LLTDAttributeIPv6Address(LLTDAttribute):
|
||
|
name = "LLTD Attribute - IPv6 Address"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 16),
|
||
|
IP6Field("ipv6", "::"),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(9)
|
||
|
class LLTDAttribute80211MaxRate(LLTDAttribute):
|
||
|
name = "LLTD Attribute - 802.11 Max Rate"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 2),
|
||
|
ShortField("rate", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(10)
|
||
|
class LLTDAttributePerformanceCounterFrequency(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Performance Counter Frequency"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 8),
|
||
|
LongField("freq", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(12)
|
||
|
class LLTDAttributeLinkSpeed(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Link Speed"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 4),
|
||
|
IntField("speed", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(14, 24, 26)
|
||
|
class LLTDAttributeLargeTLV(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Large TLV"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(15)
|
||
|
class LLTDAttributeMachineName(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Machine Name"
|
||
|
fields_desc = [
|
||
|
FieldLenField("len", None, length_of="hostname", fmt="B"),
|
||
|
StrLenFieldUtf16("hostname", "", length_from=lambda pkt: pkt.len),
|
||
|
]
|
||
|
|
||
|
def mysummary(self):
|
||
|
return (self.sprintf("Hostname: %r" % self.hostname),
|
||
|
[LLTD, LLTDAttributeHostID])
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(18)
|
||
|
class LLTDAttributeDeviceUUID(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Device UUID"
|
||
|
fields_desc = [
|
||
|
FieldLenField("len", None, length_of="uuid", fmt="B"),
|
||
|
StrLenField("uuid", b"\x00" * 16, length_from=lambda pkt: pkt.len),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(20)
|
||
|
class LLTDAttributeQOSCharacteristics(LLTDAttribute):
|
||
|
name = "LLTD Attribute - QoS Characteristics"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 4),
|
||
|
FlagsField("flags", 0, 3, "EQP"),
|
||
|
BitField("reserved1", 0, 13),
|
||
|
ShortField("reserved2", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(21)
|
||
|
class LLTDAttribute80211PhysicalMedium(LLTDAttribute):
|
||
|
name = "LLTD Attribute - 802.11 Physical Medium"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 1),
|
||
|
ByteEnumField("medium", 0, {
|
||
|
0: "Unknown",
|
||
|
1: "FHSS 2.4 GHz",
|
||
|
2: "DSSS 2.4 GHz",
|
||
|
3: "IR Baseband",
|
||
|
4: "OFDM 5 GHz",
|
||
|
5: "HRDSSS",
|
||
|
6: "ERP",
|
||
|
}),
|
||
|
]
|
||
|
|
||
|
|
||
|
@_register_lltd_specific_class(25)
|
||
|
class LLTDAttributeSeesList(LLTDAttribute):
|
||
|
name = "LLTD Attribute - Sees List Working Set"
|
||
|
fields_desc = [
|
||
|
ByteField("len", 2),
|
||
|
ShortField("max_entries", 0),
|
||
|
]
|
||
|
|
||
|
|
||
|
bind_layers(Ether, LLTD, type=0x88d9)
|
||
|
bind_layers(LLTD, LLTDDiscover, tos=0, function=0)
|
||
|
bind_layers(LLTD, LLTDDiscover, tos=1, function=0)
|
||
|
bind_layers(LLTD, LLTDHello, tos=0, function=1)
|
||
|
bind_layers(LLTD, LLTDHello, tos=1, function=1)
|
||
|
bind_layers(LLTD, LLTDEmit, tos=0, function=2)
|
||
|
bind_layers(LLTD, LLTDQueryResp, tos=0, function=7)
|
||
|
bind_layers(LLTD, LLTDQueryLargeTlv, tos=0, function=11)
|
||
|
bind_layers(LLTD, LLTDQueryLargeTlvResp, tos=0, function=12)
|
||
|
bind_layers(LLTDHello, LLTDAttribute)
|
||
|
bind_layers(LLTDAttribute, LLTDAttribute)
|
||
|
bind_layers(LLTDAttribute, Padding, type=0)
|
||
|
bind_layers(LLTDEmiteeDesc, Padding)
|
||
|
bind_layers(LLTDRecveeDesc, Padding)
|
||
|
|
||
|
|
||
|
# Utils
|
||
|
########
|
||
|
|
||
|
class LargeTlvBuilder(object):
|
||
|
"""An object to build content fetched through LLTDQueryLargeTlv /
|
||
|
LLTDQueryLargeTlvResp packets.
|
||
|
|
||
|
Usable with a PacketList() object:
|
||
|
>>> p = LargeTlvBuilder()
|
||
|
>>> p.parse(rdpcap('capture_file.cap'))
|
||
|
|
||
|
Or during a network capture:
|
||
|
>>> p = LargeTlvBuilder()
|
||
|
>>> sniff(filter="ether proto 0x88d9", prn=p.parse)
|
||
|
|
||
|
To get the result, use .get_data()
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self):
|
||
|
self.types_offsets = {}
|
||
|
self.data = {}
|
||
|
|
||
|
def parse(self, plist):
|
||
|
"""Update the builder using the provided `plist`. `plist` can
|
||
|
be either a Packet() or a PacketList().
|
||
|
|
||
|
"""
|
||
|
if not isinstance(plist, PacketList):
|
||
|
plist = PacketList(plist)
|
||
|
for pkt in plist[LLTD]:
|
||
|
if LLTDQueryLargeTlv in pkt:
|
||
|
key = "%s:%s:%d" % (pkt.real_dst, pkt.real_src, pkt.seq)
|
||
|
self.types_offsets[key] = (pkt[LLTDQueryLargeTlv].type,
|
||
|
pkt[LLTDQueryLargeTlv].offset)
|
||
|
elif LLTDQueryLargeTlvResp in pkt:
|
||
|
try:
|
||
|
key = "%s:%s:%d" % (pkt.real_src, pkt.real_dst, pkt.seq)
|
||
|
content, offset = self.types_offsets[key]
|
||
|
except KeyError:
|
||
|
continue
|
||
|
loc = slice(offset, offset + pkt[LLTDQueryLargeTlvResp].len)
|
||
|
key = "%s > %s [%s]" % (
|
||
|
pkt.real_src, pkt.real_dst,
|
||
|
LLTDQueryLargeTlv.fields_desc[0].i2s.get(content, content),
|
||
|
)
|
||
|
data = self.data.setdefault(key, array("B"))
|
||
|
datalen = len(data)
|
||
|
if datalen < loc.stop:
|
||
|
data.extend(array("B", b"\x00" * (loc.stop - datalen)))
|
||
|
data[loc] = array("B", pkt[LLTDQueryLargeTlvResp].value)
|
||
|
|
||
|
def get_data(self):
|
||
|
"""Returns a dictionary object, keys are strings "source >
|
||
|
destincation [content type]", and values are the content
|
||
|
fetched, also as a string.
|
||
|
|
||
|
"""
|
||
|
return {key: "".join(chr(byte) for byte in data)
|
||
|
for key, data in six.iteritems(self.data)}
|