diff --git a/scapy/config.py b/scapy/config.py index ac94fc2f46f22af1daabf5184f9ac593a4dcc8e3..746f67b1f7f9fe03505ab5f3c3ca7424922a8abe 100755 --- a/scapy/config.py +++ b/scapy/config.py @@ -374,11 +374,13 @@ extensions_paths: path or list of paths where extensions are to be looked for stats_dot11_protocols = [] temp_files = [] netcache = NetCache() - load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", "hsrp", "inet6", "ir", "isakmp", "l2tp", - "mgcp", "mobileip", "netbios", "netflow", "ntp", "ppp", "radius", "rip", "rtp", - "skinny", "smb", "snmp", "tftp", "x509", "bluetooth", "dhcp6", "llmnr", "sctp", "vrrp", - "ipsec" ] - + load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs", + "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp", + "mobileip", "netbios", "netflow", "ntp", "ppp", + "radius", "rip", "rtp", "skinny", "smb", "snmp", + "tftp", "x509", "bluetooth", "dhcp6", "llmnr", + "sctp", "vrrp", "ipsec", "lltd" ] + if not Conf.ipv6_enabled: log_scapy.warning("IPv6 support disabled in Python. Cannot load scapy IPv6 layers.") diff --git a/scapy/fields.py b/scapy/fields.py index 17dae7a4f5a8a76cd749395daac4974887711c3f..2e938eb774036b477c0c4bb3436bcb5ce50fdf87 100644 --- a/scapy/fields.py +++ b/scapy/fields.py @@ -527,6 +527,12 @@ class StrLenField(StrField): l = self.length_from(pkt) return s[l:], self.m2i(pkt,s[:l]) +class StrLenFieldUtf16(StrLenField): + def h2i(self, pkt, x): + return x.encode('utf-16')[2:] + def i2h(self, pkt, x): + return x.decode('utf-16') + class BoundStrLenField(StrLenField): __slots__ = ["minlen", "maxlen"] def __init__(self,name, default, minlen= 0, maxlen= 255, fld=None, length_from=None): diff --git a/scapy/layers/lltd.py b/scapy/layers/lltd.py new file mode 100644 index 0000000000000000000000000000000000000000..c89c80a1d7b49ef5453c4de0279ad3943b8a269b --- /dev/null +++ b/scapy/layers/lltd.py @@ -0,0 +1,675 @@ +# This file is part of Scapy +# See http://www.secdev.org/projects/scapy for more informations +# 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 + +""" + +import struct + +from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \ + ShortField, ShortEnumField, IntField, IntEnumField, LongField, \ + MultiEnumField, FieldLenField, FieldListField, PacketListField, \ + StrLenField, StrLenFieldUtf16, ConditionalField, MACField +from scapy.packet import Packet, Padding, bind_layers +from scapy.layers.l2 import Ether +from scapy.layers.inet import IPField +from scapy.layers.inet6 import IP6Field +from scapy.config import conf +from scapy.data import ETHER_ANY + + +class LLTD(Packet): + name = "LLTD" + 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%') + + +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 = ord(pkt[0]) & 0xc0 + count = len(self.descs_list) + pkt = chr(flags + (count >> 8)) + chr(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 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 = struct.unpack("B", _pkt[0])[0] + elif "type" in kargs: + cmd = kargs["type"] + if isinstance(cmd, basestring): + cmd = cls.fields_desc[0].s2i[cmd] + return SPECIFIC_CLASSES.get(cmd, cls) + +SPECIFIC_CLASSES = {} + + +def register_lltd_specific_class(*attr_types): + 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="value", fmt="B"), + StrLenField("uuid", "\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(LLTDHello, LLTDAttribute) +bind_layers(LLTDAttribute, LLTDAttribute) +bind_layers(LLTDAttribute, Padding, type=0) +bind_layers(LLTDEmiteeDesc, Padding) +bind_layers(LLTDRecveeDesc, Padding) diff --git a/scapy/packet.py b/scapy/packet.py index d6f95189ced14b4658e6d7a8ee1b2df97eeb9189..2573eaa372325ecd7bc17d4aaf61f234282e78e6 100644 --- a/scapy/packet.py +++ b/scapy/packet.py @@ -51,6 +51,7 @@ class Packet(BasePacket): overload_fields = {} payload_guess = [] show_indent = 1 + show_summary = True @classmethod def from_hexcap(cls): @@ -984,9 +985,7 @@ A side effect is that, to obtain "{" and "}" characters, you must use return "" def _do_summary(self): - found,s,needed = self.payload._do_summary() - if s: - s = " / "+s + found, s, needed = self.payload._do_summary() ret = "" if not found or self.__class__ in needed: ret = self.mysummary() @@ -996,14 +995,17 @@ A side effect is that, to obtain "{" and "}" characters, you must use if ret or needed: found = 1 if not ret: - ret = self.__class__.__name__ + ret = self.__class__.__name__ if self.show_summary else "" if self.__class__ in conf.emph: impf = [] for f in self.fields_desc: if f in conf.emph: impf.append("%s=%s" % (f.name, f.i2repr(self, self.getfieldval(f.name)))) ret = "%s [%s]" % (ret," ".join(impf)) - ret = "%s%s" % (ret,s) + if ret and s: + ret = "%s / %s" % (ret, s) + else: + ret = "%s%s" % (ret,s) return found,ret,needed def summary(self, intern=0): diff --git a/test/regression.uts b/test/regression.uts index 7bf96e2a8216262a71209f6a738d1cae931c86b3..d1202359377dbb399064ed838b7f8340cddf9e34 100644 --- a/test/regression.uts +++ b/test/regression.uts @@ -4413,3 +4413,25 @@ assert all(any(proto in pkt for pkt in pktpcap) for proto in [ICMP, UDP, TCP]) = Check packets from pcap file assert all(IP in pkt for pkt in pktpcapng) assert all(any(proto in pkt for pkt in pktpcapng) for proto in [ICMP, UDP, TCP]) + + +############ +############ ++ LLTD protocol + += Simple packet dissection +pkt = Ether('\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x88\xd9\x01\x00\x00\x01\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x00\x00\xfe\xe9[\xa9\xaf\xc1\x0bS[\xa9\xaf\xc1\x0bS\x01\x06}[G\x8f\xec.\x02\x04p\x00\x00\x00\x03\x04\x00\x00\x00\x06\x07\x04\xac\x19\x88\xe4\t\x02\x00l\n\x08\x00\x00\x00\x00\x00\x0fB@\x0c\x04\x00\x08=`\x0e\x00\x0f\x0eT\x00E\x00S\x00T\x00-\x00A\x00P\x00\x12\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x04\x00\x00\x00\x00\x15\x01\x02\x18\x00\x19\x02\x04\x00\x1a\x00\x00') +assert pkt.dst == pkt.real_dst +assert pkt.src == pkt.real_src +assert pkt.current_mapper_address == pkt.apparent_mapper_address +assert pkt.mac == '7d:5b:47:8f:ec:2e' +assert pkt.hostname == "TEST-AP" +assert isinstance(pkt[LLTDAttributeEOP].payload, NoPayload) + += Packet build / dissection +pkt = Ether(str(Ether(dst=ETHER_BROADCAST, src=RandMAC()) / LLTD(tos=0, function=0))) +assert LLTD in pkt +assert pkt.dst == pkt.real_dst +assert pkt.src == pkt.real_src +assert pkt.tos == 0 +assert pkt.function == 0