Home | History | Annotate | Download | only in layers
      1 # This file is part of Scapy
      2 # See http://www.secdev.org/projects/scapy for more informations
      3 # Copyright (C) Philippe Biondi <phil (at] secdev.org>
      4 # This program is published under a GPLv2 license
      5 
      6 """LLTD Protocol
      7 
      8 https://msdn.microsoft.com/en-us/library/cc233983.aspx
      9 
     10 """
     11 
     12 from __future__ import absolute_import
     13 from array import array
     14 
     15 from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \
     16     ShortField, ShortEnumField, ThreeBytesField, IntField, IntEnumField, \
     17     LongField, MultiEnumField, FieldLenField, FieldListField, \
     18     PacketListField, StrLenField, StrLenFieldUtf16, ConditionalField, MACField
     19 from scapy.packet import Packet, Padding, bind_layers
     20 from scapy.plist import PacketList
     21 from scapy.layers.l2 import Ether
     22 from scapy.layers.inet import IPField
     23 from scapy.layers.inet6 import IP6Field
     24 from scapy.data import ETHER_ANY
     25 import scapy.modules.six as six
     26 from scapy.compat import *
     27 
     28 
     29 # Protocol layers
     30 ##################
     31 
     32 
     33 class LLTD(Packet):
     34     name = "LLTD"
     35     answer_hashret = {
     36         # (tos, function) tuple mapping (answer -> query), used by
     37         # .hashret()
     38         (1, 1): (0, 0),
     39         (0, 12): (0, 11),
     40     }
     41     fields_desc = [
     42         ByteField("version", 1),
     43         ByteEnumField("tos", 0, {
     44             0: "Topology discovery",
     45             1: "Quick discovery",
     46             2: "QoS diagnostics",
     47         }),
     48         ByteField("reserved", 0),
     49         MultiEnumField("function", 0, {
     50             0: {
     51                 0: "Discover",
     52                 1: "Hello",
     53                 2: "Emit",
     54                 3: "Train",
     55                 4: "Probe",
     56                 5: "Ack",
     57                 6: "Query",
     58                 7: "QueryResp",
     59                 8: "Reset",
     60                 9: "Charge",
     61                 10: "Flat",
     62                 11: "QueryLargeTlv",
     63                 12: "QueryLargeTlvResp",
     64             },
     65             1: {
     66                 0: "Discover",
     67                 1: "Hello",
     68                 8: "Reset",
     69             },
     70             2: {
     71                 0: "QosInitializeSink",
     72                 1: "QosReady",
     73                 2: "QosProbe",
     74                 3: "QosQuery",
     75                 4: "QosQueryResp",
     76                 5: "QosReset",
     77                 6: "QosError",
     78                 7: "QosAck",
     79                 8: "QosCounterSnapshot",
     80                 9: "QosCounterResult",
     81                 10: "QosCounterLease",
     82             },
     83         }, depends_on=lambda pkt: pkt.tos, fmt="B"),
     84         MACField("real_dst", None),
     85         MACField("real_src", None),
     86         ConditionalField(ShortField("xid", 0),
     87                          lambda pkt: pkt.function in [0, 8]),
     88         ConditionalField(ShortField("seq", 0),
     89                          lambda pkt: pkt.function not in [0, 8]),
     90     ]
     91 
     92     def post_build(self, pkt, pay):
     93         if (self.real_dst is None or self.real_src is None) and \
     94            isinstance(self.underlayer, Ether):
     95             eth = self.underlayer
     96             if self.real_dst is None:
     97                 pkt = (pkt[:4] + eth.fields_desc[0].i2m(eth, eth.dst) +
     98                        pkt[10:])
     99             if self.real_src is None:
    100                 pkt = (pkt[:10] + eth.fields_desc[1].i2m(eth, eth.src) +
    101                        pkt[16:])
    102         return pkt + pay
    103 
    104     def mysummary(self):
    105         if isinstance(self.underlayer, Ether):
    106             return self.underlayer.sprintf(
    107                 'LLTD %src% > %dst% %LLTD.tos% - %LLTD.function%'
    108             )
    109         else:
    110             return self.sprintf('LLTD %tos% - %function%')
    111 
    112     def hashret(self):
    113         tos, function = self.tos, self.function
    114         return "%c%c" % self.answer_hashret.get((tos, function),
    115                                                 (tos, function))
    116 
    117     def answers(self, other):
    118         if not isinstance(other, LLTD):
    119             return False
    120         if self.tos == 0:
    121             if self.function == 0 and isinstance(self.payload, LLTDDiscover) \
    122                and len(self[LLTDDiscover].stations_list) == 1:
    123                 # "Topology discovery - Discover" with one MAC address
    124                 # discovered answers a "Quick discovery - Hello"
    125                 return other.tos == 1 and \
    126                     other.function == 1 and \
    127                     LLTDAttributeHostID in other and \
    128                     other[LLTDAttributeHostID].mac == \
    129                     self[LLTDDiscover].stations_list[0]
    130             elif self.function == 12:
    131                 # "Topology discovery - QueryLargeTlvResp" answers
    132                 # "Topology discovery - QueryLargeTlv" with same .seq
    133                 # value
    134                 return other.tos == 0 and other.function == 11 \
    135                     and other.seq == self.seq
    136         elif self.tos == 1:
    137             if self.function == 1 and isinstance(self.payload, LLTDHello):
    138                 # "Quick discovery - Hello" answers a "Topology
    139                 # discovery - Discover"
    140                 return other.tos == 0 and other.function == 0 and \
    141                     other.real_src == self.current_mapper_address
    142         return False
    143 
    144 
    145 class LLTDHello(Packet):
    146     name = "LLTD - Hello"
    147     show_summary = False
    148     fields_desc = [
    149         ShortField("gen_number", 0),
    150         MACField("current_mapper_address", ETHER_ANY),
    151         MACField("apparent_mapper_address", ETHER_ANY),
    152     ]
    153 
    154 
    155 class LLTDDiscover(Packet):
    156     name = "LLTD - Discover"
    157     fields_desc = [
    158         ShortField("gen_number", 0),
    159         FieldLenField("stations_count", None, count_of="stations_list",
    160                       fmt="H"),
    161         FieldListField("stations_list", [], MACField("", ETHER_ANY),
    162                        count_from=lambda pkt: pkt.stations_count)
    163     ]
    164 
    165     def mysummary(self):
    166         return (self.sprintf("Stations: %stations_list%")
    167                 if self.stations_list else "No station", [LLTD])
    168 
    169 
    170 class LLTDEmiteeDesc(Packet):
    171     name = "LLTD - Emitee Desc"
    172     fields_desc = [
    173         ByteEnumField("type", 0, {0: "Train", 1: "Probe"}),
    174         ByteField("pause", 0),
    175         MACField("src", None),
    176         MACField("dst", ETHER_ANY),
    177     ]
    178 
    179 
    180 class LLTDEmit(Packet):
    181     name = "LLTD - Emit"
    182     fields_desc = [
    183         FieldLenField("descs_count", None, count_of="descs_list",
    184                       fmt="H"),
    185         PacketListField("descs_list", [], LLTDEmiteeDesc,
    186                         count_from=lambda pkt: pkt.descs_count),
    187     ]
    188 
    189     def mysummary(self):
    190         return ", ".join(desc.sprintf("%src% > %dst%")
    191                          for desc in self.descs_list), [LLTD]
    192 
    193 
    194 class LLTDRecveeDesc(Packet):
    195     name = "LLTD - Recvee Desc"
    196     fields_desc = [
    197         ShortEnumField("type", 0, {0: "Probe", 1: "ARP or ICMPv6"}),
    198         MACField("real_src", ETHER_ANY),
    199         MACField("ether_src", ETHER_ANY),
    200         MACField("ether_dst", ETHER_ANY),
    201     ]
    202 
    203 
    204 class LLTDQueryResp(Packet):
    205     name = "LLTD - Query Response"
    206     fields_desc = [
    207         FlagsField("flags", 0, 2, "ME"),
    208         BitField("descs_count", None, 14),
    209         PacketListField("descs_list", [], LLTDRecveeDesc,
    210                         count_from=lambda pkt: pkt.descs_count),
    211     ]
    212 
    213     def post_build(self, pkt, pay):
    214         if self.descs_count is None:
    215             # descs_count should be a FieldLenField but has an
    216             # unsupported format (14 bits)
    217             flags = orb(pkt[0]) & 0xc0
    218             count = len(self.descs_list)
    219             pkt = chb(flags + (count >> 8)) + chb(count % 256) + pkt[2:]
    220         return pkt + pay
    221 
    222     def mysummary(self):
    223         return self.sprintf("%d response%s" % (
    224             self.descs_count,
    225             "s" if self.descs_count > 1 else "")), [LLTD]
    226 
    227 
    228 class LLTDQueryLargeTlv(Packet):
    229     name = "LLTD - Query Large Tlv"
    230     fields_desc = [
    231         ByteEnumField("type", 14, {
    232             14: "Icon image",
    233             17: "Friendly Name",
    234             19: "Hardware ID",
    235             22: "AP Association Table",
    236             24: "Detailed Icon Image",
    237             26: "Component Table",
    238             28: "Repeater AP Table",
    239         }),
    240         ThreeBytesField("offset", 0),
    241     ]
    242 
    243     def mysummary(self):
    244         return self.sprintf("%type% (offset %offset%)"), [LLTD]
    245 
    246 
    247 class LLTDQueryLargeTlvResp(Packet):
    248     name = "LLTD - Query Large Tlv Response"
    249     fields_desc = [
    250         FlagsField("flags", 0, 2, "RM"),
    251         BitField("len", None, 14),
    252         StrLenField("value", "", length_from=lambda pkt: pkt.len)
    253     ]
    254 
    255     def post_build(self, pkt, pay):
    256         if self.len is None:
    257             # len should be a FieldLenField but has an unsupported
    258             # format (14 bits)
    259             flags = orb(pkt[0]) & 0xc0
    260             length = len(self.value)
    261             pkt = chb(flags + (length >> 8)) + chb(length % 256) + pkt[2:]
    262         return pkt + pay
    263 
    264     def mysummary(self):
    265         return self.sprintf("%%len%% bytes%s" % (
    266             " (last)" if not self.flags & 2 else ""
    267         )), [LLTD]
    268 
    269 
    270 class LLTDAttribute(Packet):
    271     name = "LLTD Attribute"
    272     show_indent = False
    273     show_summary = False
    274     # section 2.2.1.1
    275     fields_desc = [
    276         ByteEnumField("type", 0, {
    277             0: "End Of Property",
    278             1: "Host ID",
    279             2: "Characteristics",
    280             3: "Physical Medium",
    281             7: "IPv4 Address",
    282             9: "802.11 Max Rate",
    283             10: "Performance Counter Frequency",
    284             12: "Link Speed",
    285             14: "Icon Image",
    286             15: "Machine Name",
    287             18: "Device UUID",
    288             20: "QoS Characteristics",
    289             21: "802.11 Physical Medium",
    290             24: "Detailed Icon Image",
    291         }),
    292         FieldLenField("len", None, length_of="value", fmt="B"),
    293         StrLenField("value", "", length_from=lambda pkt: pkt.len),
    294     ]
    295 
    296     @classmethod
    297     def dispatch_hook(cls, _pkt=None, *_, **kargs):
    298         if _pkt:
    299             cmd = orb(_pkt[0])
    300         elif "type" in kargs:
    301             cmd = kargs["type"]
    302             if isinstance(cmd, six.string_types):
    303                 cmd = cls.fields_desc[0].s2i[cmd]
    304         else:
    305             return cls
    306         return SPECIFIC_CLASSES.get(cmd, cls)
    307 
    308 SPECIFIC_CLASSES = {}
    309 
    310 
    311 def _register_lltd_specific_class(*attr_types):
    312     """This can be used as a class decorator; if we want to support Python
    313     2.5, we have to replace
    314 
    315 @_register_lltd_specific_class(x[, y[, ...]])
    316 class LLTDAttributeSpecific(LLTDAttribute):
    317 [...]
    318 
    319 by
    320 
    321 class LLTDAttributeSpecific(LLTDAttribute):
    322 [...]
    323 LLTDAttributeSpecific = _register_lltd_specific_class(x[, y[, ...]])(
    324     LLTDAttributeSpecific
    325 )
    326 
    327     """
    328     def _register(cls):
    329         for attr_type in attr_types:
    330             SPECIFIC_CLASSES[attr_type] = cls
    331         type_fld = LLTDAttribute.fields_desc[0].copy()
    332         type_fld.default = attr_types[0]
    333         cls.fields_desc = [type_fld] + cls.fields_desc
    334         return cls
    335     return _register
    336 
    337 
    338 @_register_lltd_specific_class(0)
    339 class LLTDAttributeEOP(LLTDAttribute):
    340     name = "LLTD Attribute - End Of Property"
    341     fields_desc = []
    342 
    343 
    344 @_register_lltd_specific_class(1)
    345 class LLTDAttributeHostID(LLTDAttribute):
    346     name = "LLTD Attribute - Host ID"
    347     fields_desc = [
    348         ByteField("len", 6),
    349         MACField("mac", ETHER_ANY),
    350     ]
    351 
    352     def mysummary(self):
    353         return "ID: %s" % self.mac, [LLTD, LLTDAttributeMachineName]
    354 
    355 
    356 @_register_lltd_specific_class(2)
    357 class LLTDAttributeCharacteristics(LLTDAttribute):
    358     name = "LLTD Attribute - Characteristics"
    359     fields_desc = [
    360         # According to MS doc, "this field MUST be set to 0x02". But
    361         # according to MS implementation, that's wrong.
    362         # ByteField("len", 2),
    363         FieldLenField("len", None, length_of="reserved2", fmt="B",
    364                       adjust=lambda _, x: x + 2),
    365         FlagsField("flags", 0, 5, "PXFML"),
    366         BitField("reserved1", 0, 11),
    367         StrLenField("reserved2", "", length_from=lambda x: x.len - 2)
    368     ]
    369 
    370 
    371 @_register_lltd_specific_class(3)
    372 class LLTDAttributePhysicalMedium(LLTDAttribute):
    373     name = "LLTD Attribute - Physical Medium"
    374     fields_desc = [
    375         ByteField("len", 4),
    376         IntEnumField("medium", 6, {
    377             # https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib
    378             1: "other",
    379             2: "regular1822",
    380             3: "hdh1822",
    381             4: "ddnX25",
    382             5: "rfc877x25",
    383             6: "ethernetCsmacd",
    384             7: "iso88023Csmacd",
    385             8: "iso88024TokenBus",
    386             9: "iso88025TokenRing",
    387             10: "iso88026Man",
    388             11: "starLan",
    389             12: "proteon10Mbit",
    390             13: "proteon80Mbit",
    391             14: "hyperchannel",
    392             15: "fddi",
    393             16: "lapb",
    394             17: "sdlc",
    395             18: "ds1",
    396             19: "e1",
    397             20: "basicISDN",
    398             21: "primaryISDN",
    399             22: "propPointToPointSerial",
    400             23: "ppp",
    401             24: "softwareLoopback",
    402             25: "eon",
    403             26: "ethernet3Mbit",
    404             27: "nsip",
    405             28: "slip",
    406             29: "ultra",
    407             30: "ds3",
    408             31: "sip",
    409             32: "frameRelay",
    410             33: "rs232",
    411             34: "para",
    412             35: "arcnet",
    413             36: "arcnetPlus",
    414             37: "atm",
    415             38: "miox25",
    416             39: "sonet",
    417             40: "x25ple",
    418             41: "iso88022llc",
    419             42: "localTalk",
    420             43: "smdsDxi",
    421             44: "frameRelayService",
    422             45: "v35",
    423             46: "hssi",
    424             47: "hippi",
    425             48: "modem",
    426             49: "aal5",
    427             50: "sonetPath",
    428             51: "sonetVT",
    429             52: "smdsIcip",
    430             53: "propVirtual",
    431             54: "propMultiplexor",
    432             55: "ieee80212",
    433             56: "fibreChannel",
    434             57: "hippiInterface",
    435             58: "frameRelayInterconnect",
    436             59: "aflane8023",
    437             60: "aflane8025",
    438             61: "cctEmul",
    439             62: "fastEther",
    440             63: "isdn",
    441             64: "v11",
    442             65: "v36",
    443             66: "g703at64k",
    444             67: "g703at2mb",
    445             68: "qllc",
    446             69: "fastEtherFX",
    447             70: "channel",
    448             71: "ieee80211",
    449             72: "ibm370parChan",
    450             73: "escon",
    451             74: "dlsw",
    452             75: "isdns",
    453             76: "isdnu",
    454             77: "lapd",
    455             78: "ipSwitch",
    456             79: "rsrb",
    457             80: "atmLogical",
    458             81: "ds0",
    459             82: "ds0Bundle",
    460             83: "bsc",
    461             84: "async",
    462             85: "cnr",
    463             86: "iso88025Dtr",
    464             87: "eplrs",
    465             88: "arap",
    466             89: "propCnls",
    467             90: "hostPad",
    468             91: "termPad",
    469             92: "frameRelayMPI",
    470             93: "x213",
    471             94: "adsl",
    472             95: "radsl",
    473             96: "sdsl",
    474             97: "vdsl",
    475             98: "iso88025CRFPInt",
    476             99: "myrinet",
    477             100: "voiceEM",
    478             101: "voiceFXO",
    479             102: "voiceFXS",
    480             103: "voiceEncap",
    481             104: "voiceOverIp",
    482             105: "atmDxi",
    483             106: "atmFuni",
    484             107: "atmIma",
    485             108: "pppMultilinkBundle",
    486             109: "ipOverCdlc",
    487             110: "ipOverClaw",
    488             111: "stackToStack",
    489             112: "virtualIpAddress",
    490             113: "mpc",
    491             114: "ipOverAtm",
    492             115: "iso88025Fiber",
    493             116: "tdlc",
    494             117: "gigabitEthernet",
    495             118: "hdlc",
    496             119: "lapf",
    497             120: "v37",
    498             121: "x25mlp",
    499             122: "x25huntGroup",
    500             123: "transpHdlc",
    501             124: "interleave",
    502             125: "fast",
    503             126: "ip",
    504             127: "docsCableMaclayer",
    505             128: "docsCableDownstream",
    506             129: "docsCableUpstream",
    507             130: "a12MppSwitch",
    508             131: "tunnel",
    509             132: "coffee",
    510             133: "ces",
    511             134: "atmSubInterface",
    512             135: "l2vlan",
    513             136: "l3ipvlan",
    514             137: "l3ipxvlan",
    515             138: "digitalPowerline",
    516             139: "mediaMailOverIp",
    517             140: "dtm",
    518             141: "dcn",
    519             142: "ipForward",
    520             143: "msdsl",
    521             144: "ieee1394",
    522             145: "if-gsn",
    523             146: "dvbRccMacLayer",
    524             147: "dvbRccDownstream",
    525             148: "dvbRccUpstream",
    526             149: "atmVirtual",
    527             150: "mplsTunnel",
    528             151: "srp",
    529             152: "voiceOverAtm",
    530             153: "voiceOverFrameRelay",
    531             154: "idsl",
    532             155: "compositeLink",
    533             156: "ss7SigLink",
    534             157: "propWirelessP2P",
    535             158: "frForward",
    536             159: "rfc1483",
    537             160: "usb",
    538             161: "ieee8023adLag",
    539             162: "bgppolicyaccounting",
    540             163: "frf16MfrBundle",
    541             164: "h323Gatekeeper",
    542             165: "h323Proxy",
    543             166: "mpls",
    544             167: "mfSigLink",
    545             168: "hdsl2",
    546             169: "shdsl",
    547             170: "ds1FDL",
    548             171: "pos",
    549             172: "dvbAsiIn",
    550             173: "dvbAsiOut",
    551             174: "plc",
    552             175: "nfas",
    553             176: "tr008",
    554             177: "gr303RDT",
    555             178: "gr303IDT",
    556             179: "isup",
    557             180: "propDocsWirelessMaclayer",
    558             181: "propDocsWirelessDownstream",
    559             182: "propDocsWirelessUpstream",
    560             183: "hiperlan2",
    561             184: "propBWAp2Mp",
    562             185: "sonetOverheadChannel",
    563             186: "digitalWrapperOverheadChannel",
    564             187: "aal2",
    565             188: "radioMAC",
    566             189: "atmRadio",
    567             190: "imt",
    568             191: "mvl",
    569             192: "reachDSL",
    570             193: "frDlciEndPt",
    571             194: "atmVciEndPt",
    572             195: "opticalChannel",
    573             196: "opticalTransport",
    574             197: "propAtm",
    575             198: "voiceOverCable",
    576             199: "infiniband",
    577             200: "teLink",
    578             201: "q2931",
    579             202: "virtualTg",
    580             203: "sipTg",
    581             204: "sipSig",
    582             205: "docsCableUpstreamChannel",
    583             206: "econet",
    584             207: "pon155",
    585             208: "pon622",
    586             209: "bridge",
    587             210: "linegroup",
    588             211: "voiceEMFGD",
    589             212: "voiceFGDEANA",
    590             213: "voiceDID",
    591             214: "mpegTransport",
    592             215: "sixToFour",
    593             216: "gtp",
    594             217: "pdnEtherLoop1",
    595             218: "pdnEtherLoop2",
    596             219: "opticalChannelGroup",
    597             220: "homepna",
    598             221: "gfp",
    599             222: "ciscoISLvlan",
    600             223: "actelisMetaLOOP",
    601             224: "fcipLink",
    602             225: "rpr",
    603             226: "qam",
    604             227: "lmp",
    605             228: "cblVectaStar",
    606             229: "docsCableMCmtsDownstream",
    607             230: "adsl2",
    608             231: "macSecControlledIF",
    609             232: "macSecUncontrolledIF",
    610             233: "aviciOpticalEther",
    611             234: "atmbond",
    612             235: "voiceFGDOS",
    613             236: "mocaVersion1",
    614             237: "ieee80216WMAN",
    615             238: "adsl2plus",
    616             239: "dvbRcsMacLayer",
    617             240: "dvbTdm",
    618             241: "dvbRcsTdma",
    619             242: "x86Laps",
    620             243: "wwanPP",
    621             244: "wwanPP2",
    622             245: "voiceEBS",
    623             246: "ifPwType",
    624             247: "ilan",
    625             248: "pip",
    626             249: "aluELP",
    627             250: "gpon",
    628             251: "vdsl2",
    629             252: "capwapDot11Profile",
    630             253: "capwapDot11Bss",
    631             254: "capwapWtpVirtualRadio",
    632             255: "bits",
    633             256: "docsCableUpstreamRfPort",
    634             257: "cableDownstreamRfPort",
    635             258: "vmwareVirtualNic",
    636             259: "ieee802154",
    637             260: "otnOdu",
    638             261: "otnOtu",
    639             262: "ifVfiType",
    640             263: "g9981",
    641             264: "g9982",
    642             265: "g9983",
    643             266: "aluEpon",
    644             267: "aluEponOnu",
    645             268: "aluEponPhysicalUni",
    646             269: "aluEponLogicalLink",
    647             271: "aluGponPhysicalUni",
    648             272: "vmwareNicTeam",
    649             277: "docsOfdmDownstream",
    650             278: "docsOfdmaUpstream",
    651             279: "gfast",
    652             280: "sdci",
    653         }),
    654     ]
    655 
    656 
    657 @_register_lltd_specific_class(7)
    658 class LLTDAttributeIPv4Address(LLTDAttribute):
    659     name = "LLTD Attribute - IPv4 Address"
    660     fields_desc = [
    661         ByteField("len", 4),
    662         IPField("ipv4", "0.0.0.0"),
    663     ]
    664 
    665 
    666 @_register_lltd_specific_class(8)
    667 class LLTDAttributeIPv6Address(LLTDAttribute):
    668     name = "LLTD Attribute - IPv6 Address"
    669     fields_desc = [
    670         ByteField("len", 16),
    671         IP6Field("ipv6", "::"),
    672     ]
    673 
    674 
    675 @_register_lltd_specific_class(9)
    676 class LLTDAttribute80211MaxRate(LLTDAttribute):
    677     name = "LLTD Attribute - 802.11 Max Rate"
    678     fields_desc = [
    679         ByteField("len", 2),
    680         ShortField("rate", 0),
    681     ]
    682 
    683 
    684 @_register_lltd_specific_class(10)
    685 class LLTDAttributePerformanceCounterFrequency(LLTDAttribute):
    686     name = "LLTD Attribute - Performance Counter Frequency"
    687     fields_desc = [
    688         ByteField("len", 8),
    689         LongField("freq", 0),
    690     ]
    691 
    692 
    693 @_register_lltd_specific_class(12)
    694 class LLTDAttributeLinkSpeed(LLTDAttribute):
    695     name = "LLTD Attribute - Link Speed"
    696     fields_desc = [
    697         ByteField("len", 4),
    698         IntField("speed", 0),
    699     ]
    700 
    701 
    702 @_register_lltd_specific_class(14, 24, 26)
    703 class LLTDAttributeLargeTLV(LLTDAttribute):
    704     name = "LLTD Attribute - Large TLV"
    705     fields_desc = [
    706         ByteField("len", 0),
    707     ]
    708 
    709 
    710 @_register_lltd_specific_class(15)
    711 class LLTDAttributeMachineName(LLTDAttribute):
    712     name = "LLTD Attribute - Machine Name"
    713     fields_desc = [
    714         FieldLenField("len", None, length_of="hostname", fmt="B"),
    715         StrLenFieldUtf16("hostname", "", length_from=lambda pkt: pkt.len),
    716     ]
    717 
    718     def mysummary(self):
    719         return (self.sprintf("Hostname: %r" % self.hostname),
    720                 [LLTD, LLTDAttributeHostID])
    721 
    722 
    723 @_register_lltd_specific_class(18)
    724 class LLTDAttributeDeviceUUID(LLTDAttribute):
    725     name = "LLTD Attribute - Device UUID"
    726     fields_desc = [
    727         FieldLenField("len", None, length_of="uuid", fmt="B"),
    728         StrLenField("uuid", b"\x00" * 16, length_from=lambda pkt: pkt.len),
    729     ]
    730 
    731 
    732 @_register_lltd_specific_class(20)
    733 class LLTDAttributeQOSCharacteristics(LLTDAttribute):
    734     name = "LLTD Attribute - QoS Characteristics"
    735     fields_desc = [
    736         ByteField("len", 4),
    737         FlagsField("flags", 0, 3, "EQP"),
    738         BitField("reserved1", 0, 13),
    739         ShortField("reserved2", 0),
    740     ]
    741 
    742 
    743 @_register_lltd_specific_class(21)
    744 class LLTDAttribute80211PhysicalMedium(LLTDAttribute):
    745     name = "LLTD Attribute - 802.11 Physical Medium"
    746     fields_desc = [
    747         ByteField("len", 1),
    748         ByteEnumField("medium", 0, {
    749             0: "Unknown",
    750             1: "FHSS 2.4 GHz",
    751             2: "DSSS 2.4 GHz",
    752             3: "IR Baseband",
    753             4: "OFDM 5 GHz",
    754             5: "HRDSSS",
    755             6: "ERP",
    756         }),
    757     ]
    758 
    759 
    760 @_register_lltd_specific_class(25)
    761 class LLTDAttributeSeesList(LLTDAttribute):
    762     name = "LLTD Attribute - Sees List Working Set"
    763     fields_desc = [
    764         ByteField("len", 2),
    765         ShortField("max_entries", 0),
    766     ]
    767 
    768 
    769 bind_layers(Ether, LLTD, type=0x88d9)
    770 bind_layers(LLTD, LLTDDiscover, tos=0, function=0)
    771 bind_layers(LLTD, LLTDDiscover, tos=1, function=0)
    772 bind_layers(LLTD, LLTDHello, tos=0, function=1)
    773 bind_layers(LLTD, LLTDHello, tos=1, function=1)
    774 bind_layers(LLTD, LLTDEmit, tos=0, function=2)
    775 bind_layers(LLTD, LLTDQueryResp, tos=0, function=7)
    776 bind_layers(LLTD, LLTDQueryLargeTlv, tos=0, function=11)
    777 bind_layers(LLTD, LLTDQueryLargeTlvResp, tos=0, function=12)
    778 bind_layers(LLTDHello, LLTDAttribute)
    779 bind_layers(LLTDAttribute, LLTDAttribute)
    780 bind_layers(LLTDAttribute, Padding, type=0)
    781 bind_layers(LLTDEmiteeDesc, Padding)
    782 bind_layers(LLTDRecveeDesc, Padding)
    783 
    784 
    785 # Utils
    786 ########
    787 
    788 class LargeTlvBuilder(object):
    789     """An object to build content fetched through LLTDQueryLargeTlv /
    790     LLTDQueryLargeTlvResp packets.
    791 
    792     Usable with a PacketList() object:
    793     >>> p = LargeTlvBuilder()
    794     >>> p.parse(rdpcap('capture_file.cap'))
    795 
    796     Or during a network capture:
    797     >>> p = LargeTlvBuilder()
    798     >>> sniff(filter="ether proto 0x88d9", prn=p.parse)
    799 
    800     To get the result, use .get_data()
    801 
    802     """
    803     def __init__(self):
    804         self.types_offsets = {}
    805         self.data = {}
    806 
    807     def parse(self, plist):
    808         """Update the builder using the provided `plist`. `plist` can
    809         be either a Packet() or a PacketList().
    810 
    811         """
    812         if not isinstance(plist, PacketList):
    813             plist = PacketList(plist)
    814         for pkt in plist[LLTD]:
    815             if LLTDQueryLargeTlv in pkt:
    816                 key = "%s:%s:%d" % (pkt.real_dst, pkt.real_src, pkt.seq)
    817                 self.types_offsets[key] = (pkt[LLTDQueryLargeTlv].type,
    818                                            pkt[LLTDQueryLargeTlv].offset)
    819             elif LLTDQueryLargeTlvResp in pkt:
    820                 try:
    821                     key = "%s:%s:%d" % (pkt.real_src, pkt.real_dst, pkt.seq)
    822                     content, offset = self.types_offsets[key]
    823                 except KeyError:
    824                     continue
    825                 loc = slice(offset, offset + pkt[LLTDQueryLargeTlvResp].len)
    826                 key = "%s > %s [%s]" % (
    827                     pkt.real_src, pkt.real_dst,
    828                     LLTDQueryLargeTlv.fields_desc[0].i2s.get(content, content),
    829                 )
    830                 data = self.data.setdefault(key, array("B"))
    831                 datalen = len(data)
    832                 if datalen < loc.stop:
    833                     data.extend(array("B", b"\x00" * (loc.stop - datalen)))
    834                 data[loc] = array("B", pkt[LLTDQueryLargeTlvResp].value)
    835 
    836     def get_data(self):
    837         """Returns a dictionary object, keys are strings "source >
    838         destincation [content type]", and values are the content
    839         fetched, also as a string.
    840 
    841         """
    842         return {key: "".join(chr(byte) for byte in data)
    843                 for key, data in six.iteritems(self.data)}
    844