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