diff --git a/psl-cli.py b/psl-cli.py index 27e8e61..1b14a79 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- "Main Program executed by the user" @@ -13,12 +13,15 @@ def discover(args, switch): "Search for Switches" print("Searching for ProSafe Plus Switches ...\n") - data = switch.discover() - if data != False: + found = False + + for data in switch.discover(): + found = True for entry in data.keys(): - print entry.get_name() + ': ' + data[entry] - print "" - else: + print(entry.get_name() + ': ' + data[entry]) + print("") + + if not found: print("No result received...") print("did you try to adjust your timeout?") @@ -27,9 +30,15 @@ def discover(args, switch): def exploit(args, switch): "exploit in current (2012) fw, can set a new password" switch.passwd_exploit(args.mac[0], args.new_password[0]) - + def set_switch(args, switch): "Set values on switch" + results = switch.query(switch.CMD_NUMBER_OF_PORTS, args.mac[0]) + if switch.CMD_NUMBER_OF_PORTS in results: + switch.CMD_PORT_MIRROR.set_total_ports(results[switch.CMD_NUMBER_OF_PORTS]) + switch.CMD_VLAN_ID.set_total_ports(results[switch.CMD_NUMBER_OF_PORTS]) + switch.CMD_VLAN802_ID.set_total_ports(results[switch.CMD_NUMBER_OF_PORTS]) + cmds = {ProSafeLinux.CMD_PASSWORD: args.passwd[0]} for scmd in switch.get_setable_cmds(): if vars(args)[scmd.get_name()] is not None: @@ -40,7 +49,7 @@ def set_switch(args, switch): if isinstance(scmd, psl_typ.PslTypBoolean): cmds[scmd] = (vars(args)[scmd.get_name()][0] == "on") else: - if len(vars(args)[scmd.get_name()])==1: + if len(vars(args)[scmd.get_name()])==1 and scmd.get_num_args()==1: cmds[scmd] = vars(args)[scmd.get_name()][0] else: cmds[scmd] = vars(args)[scmd.get_name()] @@ -48,12 +57,12 @@ def set_switch(args, switch): valid, errors = switch.verify_data(cmds) if not valid: for error in errors: - print error + print(error) else: print("Changing Values..\n") result = switch.transmit(cmds, args.mac[0]) if 'error' in result: - print "FAILED: Error with " + str(result['error']) + print("FAILED: Error with " + str(result['error'])) def query(args, switch, querycommand = None): @@ -148,7 +157,7 @@ def main(): subparsers = parser.add_subparsers(help='operation', dest="operation") subparsers.add_parser('discover', help='Find all switches in all subnets') - + exploit_parser = subparsers.add_parser("exploit", help="set a password without knowing the old one") exploit_parser.add_argument("--mac", nargs=1, @@ -165,7 +174,7 @@ def main(): for cmd in switch.get_query_cmds(): choices.append(cmd.get_name()) choices.append("all") - + query_parser.add_argument("query", nargs="+", help="What to query for", choices=choices) @@ -187,12 +196,16 @@ def main(): dest=cmd.get_name(), action='store_true') else: - set_parser.add_argument("--" + cmd.get_name(), + action = 'store' + if cmd.allow_multiple(): + action = 'append' + set_parser.add_argument("--" + cmd.get_name(), nargs=cmd.get_num_args(), type=cmd.get_set_type(), help=cmd.get_set_help(), metavar=cmd.get_metavar(), - choices=cmd.get_choices()) + choices=cmd.get_choices(), + action=action) args = parser.parse_args() interface = args.interface[0] diff --git a/psl-cmd.py b/psl-cmd.py index c02b2ee..5a15c66 100755 --- a/psl-cmd.py +++ b/psl-cmd.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # diff --git a/psl_class.py b/psl_class.py index 1ab0762..4017b4b 100644 --- a/psl_class.py +++ b/psl_class.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- "Main Class to communicate with gs108e and gs105e netgear switches" @@ -8,6 +8,7 @@ import random import struct import socket +import select import fcntl import psl_typ import inspect @@ -68,7 +69,7 @@ class ProSafeLinux: CMD_IP = psl_typ.PslTypIpv4(0x0006, "ip") CMD_NETMASK = psl_typ.PslTypIpv4(0x0007, "netmask") CMD_GATEWAY = psl_typ.PslTypIpv4(0x0008, "gateway") - CMD_NEW_PASSWORD = psl_typ.PslTypPassword(0x0009, "new_password", True) + CMD_NEW_PASSWORD = psl_typ.PslTypHexNoQuery(0x0009, "new_password") CMD_PASSWORD = psl_typ.PslTypPassword(0x000a, "password", False) CMD_DHCP = psl_typ.PslTypDHCP(0x000b, "dhcp") CMD_FIXMEC = psl_typ.PslTypHex(0x000c, "fixmeC") @@ -76,40 +77,71 @@ class ProSafeLinux: CMD_FIRMWARE2V = psl_typ.PslTypStringQueryOnly(0x000e, "firmware2ver") CMD_FIRMWAREACTIVE = psl_typ.PslTypHex(0x000f, "firmware_active") CMD_REBOOT = psl_typ.PslTypAction(0x0013, "reboot") + CMD_ENHANCEDENCRYPTION = psl_typ.PslTypHex(0x0014, "enhanced_encryption") + CMD_PASSWORD_NONCE = psl_typ.PslTypHexNoQuery(0x0017, "password_nonce") + CMD_PASSWORD_HASH = psl_typ.PslTypHexNoQuery(0x001a, "password_hash") CMD_FACTORY_RESET = psl_typ.PslTypAction(0x0400, "factory_reset") - CMD_SPEED_STAT = psl_typ.PslTypSpeedStat(0x0c00, "speed_stat") + CMD_PORT_STATUS = psl_typ.PslTypPortStatus(0x0c00, "port_status") CMD_PORT_STAT = psl_typ.PslTypPortStat(0x1000, "port_stat") CMD_RESET_PORT_STAT = psl_typ.PslTypAction(0x1400, "reset_port_stat") - CMD_TEST_CABLE = psl_typ.PslTypHexNoQuery(0x1800, "test_cable") - CMD_TEST_CABLE_RESP = psl_typ.PslTypHexNoQuery(0x1c00, "test_cable_resp") + CMD_TEST_CABLE = psl_typ.PslTypCableTest(0x1800, "test_cable") + CMD_TEST_CABLE_RESULT = psl_typ.PslTypCableTestResult(0x1c00, "test_cable_result") CMD_VLAN_SUPPORT = psl_typ.PslTypVlanSupport(0x2000, "vlan_support") CMD_VLAN_ID = psl_typ.PslTypVlanId(0x2400, "vlan_id") CMD_VLAN802_ID = psl_typ.PslTypVlan802Id(0x2800, "vlan802_id") + CMD_DEL_VLAN = psl_typ.PslTypDeleteVlan(0x2c00, "delete_vlan") CMD_VLANPVID = psl_typ.PslTypVlanPVID(0x3000, "vlan_pvid") - CMD_QUALITY_OF_SERVICE = psl_typ.PslTypQos(0x3400, "qos") + CMD_QUALITY_OF_SERVICE = psl_typ.PslTypQosMode(0x3400, "qos_mode") CMD_PORT_BASED_QOS = psl_typ.PslTypPortBasedQOS(0x3800, "port_based_qos") CMD_BANDWIDTH_INCOMING_LIMIT = psl_typ.PslTypBandwidth( 0x4c00, "bandwidth_in") CMD_BANDWIDTH_OUTGOING_LIMIT = psl_typ.PslTypBandwidth( 0x5000, "bandwidth_out") - CMD_FIXME5400 = psl_typ.PslTypHex(0x5400, "fixme5400") + CMD_BROADCAST_FILTERING = psl_typ.PslTypFiltering(0x5400, "broadcast_filtering") CMD_BROADCAST_BANDWIDTH = psl_typ.PslTypBandwidth(0x5800, "broadcast_bandwidth") CMD_PORT_MIRROR = psl_typ.PslTypPortMirror(0x5c00, "port_mirror") - CMD_NUMBER_OF_PORTS = psl_typ.PslTypHex(0x6000, "number_of_ports") + CMD_NUMBER_OF_PORTS = psl_typ.PslTypDec(0x6000, "number_of_ports") CMD_IGMP_SNOOPING = psl_typ.PslTypIGMPSnooping(0x6800, "igmp_snooping") CMD_BLOCK_UNKNOWN_MULTICAST = psl_typ.PslTypBoolean( 0x6c00, "block_unknown_multicast") CMD_IGMP_HEADER_VALIDATION = psl_typ.PslTypBoolean(0x7000, "igmp_header_validation") - CMD_FIXME7400 = psl_typ.PslTypHex(0x7400, "fixme7400") + CMD_SUPPORTED_TLVS = psl_typ.PslTypHex(0x7400, "supported_tlvs") + CMD_SERIAL_NUMBER = psl_typ.PslTypSerialNum(0x7800, "serial_number") + CMD_LOOP_DETECTION = psl_typ.PslTypBoolean(0x9000, "loop_detection") + CMD_PORT_ADMIN = psl_typ.PslTypAdminPortStatus(0x9400, "port_admin") CMD_END = psl_typ.PslTypEnd(0xffff, "END") + ERR_SUCCESS = psl_typ.PslError(0x00, "Success") + ERR_PROTO_NOT_SUPPORTED = psl_typ.PslError(0x01, "Protocol version not supported") + ERR_CMD_NOT_SUPPORTED = psl_typ.PslError(0x02, "Command not supported") + ERR_TLV_NOT_SUPPORTED = psl_typ.PslError(0x03, "TLV type not supported") + ERR_BAD_TLV_LENGTH = psl_typ.PslError(0x04, "Invalid TLV length") + ERR_BAD_TLV_VALUE = psl_typ.PslError(0x05, "Invalid TLV value") + ERR_BLOCKED_BY_ACL = psl_typ.PslError(0x06, "Manager IP is blocked by ACL") + ERR_BAD_PASSWORD = psl_typ.PslError(0x07, "Invalid password") + ERR_FIRMWARE_DOWNLOAD_REQUESTED = psl_typ.PslError(0x08, "Firmware download requested") + ERR_BAD_USERNAME = psl_typ.PslError(0x09, "Invalid username") + ERR_MANAGE_BY_BROWSER = psl_typ.PslError(0x0a, "Switch only supports management by browser") + ERR_INVALID_PASSWORD = psl_typ.PslError(0x0d, "Invalid password") + ERR_LOCKED_30_MINS = psl_typ.PslError(0x0e, "3 failed attempts. Switch is locked for 30 minutes") + ERR_MANAGE_DISABLED = psl_typ.PslError(0x0f, "Switch management disabled. Use browser to enable") + ERR_TFTP_CALL = psl_typ.PslError(0x81, "TFTP call error") + ERR_TFTP_OOM = psl_typ.PslError(0x82, "TFTP Out of memory") + ERR_FIRMWARE_UPDATE_FAILED = psl_typ.PslError(0x83, "Firmware update failed") + ERR_TFTP_TIMED_OUT = psl_typ.PslError(0x84, "TFTP timed out") + CTYPE_QUERY_REQUEST = 0x0101 # CTYPE_QUERY_RESPONSE = 0x0102 CTYPE_TRANSMIT_REQUEST = 0x103 # CTYPE_TRANSMIT_RESPONSE = 0x104 + ENCTYPE_NONE = 0x00 + ENCTYPE_SIMPLE = 0x01 + ENCTYPE_HASH32 = 0x08 + ENCTYPE_HASH64 = 0x10 + RECPORT = 63321 SENDPORT = 63322 @@ -118,8 +150,9 @@ def __init__(self): self.myhost = None self.srcmac = None self.ssocket = None - self.rsocket = None - self.timeout=0.1 + self.brsocket = None + self.ursocket = None + self.timeout=2 # i still see no win in randomizing the starting sequence... self.seq = random.randint(100, 2000) @@ -127,11 +160,15 @@ def __init__(self): self.mac_cache = {} self.cmd_by_id = {} self.cmd_by_name = {} + self.errmsgs = {} for key, value in inspect.getmembers(ProSafeLinux): if key.startswith("CMD_"): self.cmd_by_name[value.get_name()] = value self.cmd_by_id[value.get_id()] = value + if key.startswith("ERR_"): + self.errmsgs[value.get_code()] = value + def set_timeout(self, timeout): self.timeout=timeout @@ -148,12 +185,18 @@ def bind(self, interface): self.ssocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) self.ssocket.bind((self.myhost, self.RECPORT)) - # receive socket - self.rsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - self.rsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - # self.rsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - self.rsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - self.rsocket.bind(("255.255.255.255", self.RECPORT)) + # broadcast receive socket + self.brsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.brsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + # self.brsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.brsocket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + self.brsocket.bind(("255.255.255.255", self.RECPORT)) + + # unicast receive socket + self.ursocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.ursocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + # self.ursocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.ursocket.bind((self.myhost, self.RECPORT)) return True @@ -192,17 +235,25 @@ def set_debug_output(self): "set debugging" self.debug = True - def recv(self, maxlen=8192): + def recv(self, maxlen=8192, sock=None): "receive a packet from the switch" - self.rsocket.settimeout(self.timeout) + socks = [sock]; + if sock is None: + socks = [self.brsocket, self.ursocket] + try: - message, address = self.rsocket.recvfrom(maxlen) + rsocks,_,_ = select.select(socks, [], [], self.timeout) + if rsocks == []: + return (None, None) + + message, address = rsocks[0].recvfrom(maxlen) + except socket.timeout: return (None, None) except socket.error as error: # according to the Python documentation this error # is system-specifc; this works on Linux - if error.errno == errno.EAGAIN: + if error.errno == errno.EAGAIN: return (None, None) raise if self.debug: @@ -215,56 +266,65 @@ def recv_all(self): while True: (message, address) = self.recv() if message is None: - return (None, address) - return (message, address) + return + yield (message, address) def parse_data(self, pack): "unpack packet send by the switch" if pack == None: return False - if self.debug: - pprint.pprint(len(pack[2:4])) data = {} - if struct.unpack(">H", pack[2:4])[0] != 0x0000: + status = struct.unpack(">B", pack[2:3])[0] + if status != 0x00: errorcmd = self.get_cmd_by_hex(struct.unpack(">H", pack[4:6])[0]) + + if status in self.errmsgs: + errorobj = self.errmsgs[status] + else: + errorobj = psl_typ.PslError(status, "Unknown error - 0x{:02x}".format(status)) + if errorcmd: data["error"] = errorcmd.get_name() else: data["error"] = struct.unpack(">H", pack[4:6])[0] -# data["seq"] = struct.unpack(">H", pack[22:24])[0] -# data["ctype"] = struct.unpack(">H", pack[0:2])[0] -# data["mymac"] = binascii.hexlify(pack[8:14]) -# data["theirmac"] = binascii.hexlify(pack[14:20]).decode() - pos = 32 - cmd_id = 0 - while (posH", pack[pos:(pos + 2)])[0] - if self.get_cmd_by_hex(cmd_id): - cmd = self.get_cmd_by_hex(cmd_id) - else: - # we don't need a switch for "unknown_warn" here...let the client handle unknown responses -# print("Unknown Response %d" % cmd_id) - cmd = psl_typ.PslTypUnknown(cmd_id, "UNKNOWN %d" % cmd_id) - pos = pos + 2 - cmdlen = struct.unpack(">H", pack[pos:(pos + 2)])[0] - pos = pos + 2 - if cmdlen > 0: - value = cmd.unpack_cmd(pack[pos:(pos + cmdlen)]) - else: - value = None - if cmd in data and value != None: - if type(data[cmd]) != type(list()): - data[cmd] = [data[cmd]] - data[cmd].append(value) - elif value != None: - data[cmd] = value - if self.debug: - print("cmd_id %d of length %d :" % (cmd_id, cmdlen)) - data_hex = binascii.hexlify(pack[pos:(pos + cmdlen)]).decode() - print("data=" + data_hex) - pos = pos + cmdlen + + data["error"] = "{} - {}".format(data["error"], errorobj.get_desc()) + data["error_obj"] = errorobj + else: +# data["seq"] = struct.unpack(">H", pack[22:24])[0] +# data["ctype"] = struct.unpack(">H", pack[0:2])[0] +# data["mymac"] = binascii.hexlify(pack[8:14]) +# data["theirmac"] = binascii.hexlify(pack[14:20]).decode() + pos = 32 + cmd_id = 0 + while (posH", pack[pos:(pos + 2)])[0] + if self.get_cmd_by_hex(cmd_id): + cmd = self.get_cmd_by_hex(cmd_id) + else: + # we don't need a switch for "unknown_warn" here...let the client handle unknown responses +# print("Unknown Response %d" % cmd_id) + cmd = psl_typ.PslTypUnknown(cmd_id, "UNKNOWN %d" % cmd_id) + pos = pos + 2 + cmdlen = struct.unpack(">H", pack[pos:(pos + 2)])[0] + pos = pos + 2 + if cmdlen > 0: + value = cmd.unpack_cmd(pack[pos:(pos + cmdlen)]) + else: + value = None + if cmd in data and value != None: + if type(data[cmd]) != type(list()): + data[cmd] = [data[cmd]] + data[cmd].append(value) + elif value != None: + data[cmd] = value + if self.debug: + print("cmd_id %d of length %d :" % (cmd_id, cmdlen)) + data_hex = binascii.hexlify(pack[pos:(pos + cmdlen)]).decode() + print("data=" + data_hex) + pos = pos + cmdlen return data def send(self, host, port, data): @@ -319,6 +379,8 @@ def ip_from_mac(self, mac): if message != None and message != False: if self.CMD_MAC in message: if message[self.CMD_MAC].capitalize() == mac.capitalize(): + if self.CMD_IP in message: + self.mac_cache[message[self.CMD_MAC]] = message[self.CMD_IP] return address[0] return "255.255.255.255" @@ -340,60 +402,241 @@ def query(self, cmd_arr, mac, with_address=False, use_ip_func=True): if type(cmd_arr).__name__ != 'tupe' and type(cmd_arr).__name__ != 'list': cmd_arr = (cmd_arr, ) self.send_query(cmd_arr, mac, use_ip_func) - message, address = self.recv_all() + message, address = self.recv() if with_address: return (self.parse_data(message), address) else: return self.parse_data(message) + def queryall(self, cmd_arr, mac, with_address=False, use_ip_func=True): + "get some values from the switch, but do not change them" + # translate non-list to list + if type(cmd_arr).__name__ != 'tupe' and type(cmd_arr).__name__ != 'list': + cmd_arr = (cmd_arr, ) + self.send_query(cmd_arr, mac, use_ip_func) + for message, address in self.recv_all(): + if with_address: + yield (self.parse_data(message), address) + else: + yield self.parse_data(message) + def transmit(self, cmddict, mac): "change something in the switch, like name, mac ..." transmit_counter = 0 ipadr = self.ip_from_mac(mac) - data = self.baseudp(destmac=mac, ctype=self.CTYPE_TRANSMIT_REQUEST) - firmwarevers = self.query(self.get_cmd_by_name("firmwarever"), mac) - firmwarevers = firmwarevers.values()[0].translate({ord("."):None}) - # New firmwares put capital leter V in front ... - if "V" == firmwarevers[0]: - firmwarevers = firmwarevers[1:] + data = b'' if type(cmddict).__name__ == 'dict': if self.CMD_PASSWORD in cmddict: - if int(firmwarevers) > 10004: - print("using password hack on firmware: %s" % - (firmwarevers)) - _hashkey = "NtgrSmartSwitchRock" - _plainpass = cmddict[self.CMD_PASSWORD] - _password = "" - for i in range(len(_plainpass)): - _password += chr(ord(_plainpass[i]) ^ ord(_hashkey[i])) - else: - _password = cmddict[self.CMD_PASSWORD] - data += self.addudp(self.CMD_PASSWORD, _password) + result = self.add_password(mac, cmddict[self.CMD_PASSWORD]) + + if type(result).__name__ == 'dict': + return result + + data += result; + del cmddict[self.CMD_PASSWORD] + + if self.CMD_NEW_PASSWORD in cmddict: + result = self.add_new_password(mac, cmddict[self.CMD_NEW_PASSWORD]) + + if type(result).__name__ == 'dict': + return result + + data += result; + del cmddict[self.CMD_NEW_PASSWORD] + for cmd, pdata in list(cmddict.items()): - if cmd != self.CMD_PASSWORD: + if type(pdata).__name__ == 'list' and cmd.allow_multiple(): + for entry in pdata: + if cmd.get_num_args() == 1: + # Get single arguments out of a list (of one) + data += self.addudp(cmd, entry[0]) + else: + data += self.addudp(cmd, entry) + else: data += self.addudp(cmd, pdata) elif type(cmddict).__name__ == 'string': - print 'got string!' + print('got string!') data += cmddict data += self.addudp(self.CMD_END) + + header = self.baseudp(destmac=mac, ctype=self.CTYPE_TRANSMIT_REQUEST) + data = header + data + self.send(ipadr, self.SENDPORT, data) - message, address = self.recv_all() + message, address = self.recv() while message == None and transmit_counter < 3: time.sleep(1) - message, address = self.recv_all() + message, address = self.recv() transmit_counter += 1 if message == None: return { 'error' : 'no result received within 3 seconds' } return self.parse_data(message) + def add_password(self, mac, password): + "Add password to UDP data sent to the switch" + + data = None; + + # Find out what the switch supports + enc = self.query(self.CMD_ENHANCEDENCRYPTION, mac) + + if enc == False: + enc = self.ENCTYPE_NONE + else: + enc = int(enc[self.CMD_ENHANCEDENCRYPTION], 16) + + if enc == self.ENCTYPE_NONE: + # No encryption - just plaintext + data = self.addudp(self.CMD_PASSWORD, password) + elif enc == self.ENCTYPE_SIMPLE: + # Simple fixed XOR + _hashkey = "NtgrSmartSwitchRock" + _hashpass = "" + for i in range(len(password)): + _hashpass += chr(ord(password[i]) ^ ord(_hashkey[i])) + data = self.addudp(self.CMD_PASSWORD, _hashpass) + elif enc == self.ENCTYPE_HASH32 or enc == self.ENCTYPE_HASH64: + nonce = self.query(self.CMD_PASSWORD_NONCE, mac) + if nonce == False: + return { 'error' : 'Could not get nonce from switch' } + + # Jump through hoops to convert a hex string to an indexable + # group of bytes that works on Python2 and Python3 + nonce = bytearray(binascii.unhexlify(nonce[self.CMD_PASSWORD_NONCE])); + + _mac = mac + if len(_mac) > 6: + _mac = bytearray(pack_mac(_mac)) + + _hashpass = [_mac[1] ^ _mac[5], + _mac[0] ^ _mac[4], + _mac[2] ^ _mac[3], + _mac[4] ^ _mac[5]] + + _hashpass[0] ^= nonce[3] ^ nonce[2] + _hashpass[1] ^= nonce[3] ^ nonce[1] + _hashpass[2] ^= nonce[0] ^ nonce[2] + _hashpass[3] ^= nonce[0] ^ nonce[1] + + if enc == self.ENCTYPE_HASH32: + for i in range(min(len(password),16)): + if (i < 4) or (i > 7): + idx = ((i + 3) % 4) + idx = ((i + 3) % 4) + idx ^= (idx // 2) + else: + idx = 3 - (i % 4) + + _hashpass[idx] ^= ord(password[i]) + + _hashpass = struct.pack(">BBBB", *_hashpass) + data = self.addudp(self.CMD_PASSWORD_HASH, binascii.hexlify(_hashpass)) + else: + _hashpass += _hashpass; + + _hashpass[6] ^= ord(password[0]) + + for i in range(len(password)): + _hashpass[i // 3] ^= ord(password[i]) + + if (i < 6) and (i % 2): + _hashpass[7] ^= ord(password[i]) + + _hashpass = struct.pack(">BBBBBBBB", *_hashpass) + data = self.addudp(self.CMD_PASSWORD_HASH, binascii.hexlify(_hashpass)) + else: + return { 'error' : 'Unknown encryption type 0x%02x' % enc } + + return data + + def add_new_password(self, mac, password): + "Add new password to UDP data sent to the switch" + + data = None; + + # Find out what the switch supports + enc = self.query(self.CMD_ENHANCEDENCRYPTION, mac) + + if enc == False: + enc = self.ENCTYPE_NONE + else: + enc = int(enc[self.CMD_ENHANCEDENCRYPTION], 16) + + if enc == self.ENCTYPE_NONE or enc == self.ENCTYPE_SIMPLE: + # No encryption - just plaintext + data = self.addudp(self.CMD_PASSWORD, password) + elif enc == self.ENCTYPE_SIMPLE: + # Simple fixed XOR + _hashkey = "NtgrSmartSwitchRock" + _hashpass = "" + for i in range(len(password)): + _hashpass += chr(ord(password[i]) ^ ord(_hashkey[i])) + data = self.addudp(self.CMD_PASSWORD, _hashpass) + elif enc == self.ENCTYPE_HASH32: + return { 'error' : 'Unsupported encryption type 0x%02x' % enc } + elif enc == self.ENCTYPE_HASH64: + nonce = self.query(self.CMD_PASSWORD_NONCE, mac) + if nonce == False: + return { 'error' : 'Could not get nonce from switch' } + + # Jump through hoops to convert a hex string to an indexable + # group of bytes that works on Python2 and Python3 + nonce = bytearray(binascii.unhexlify(nonce[self.CMD_PASSWORD_NONCE])); + + _mac = mac + if len(_mac) > 6: + _mac = bytearray(pack_mac(_mac)) + + _hashpass = [_mac[1] ^ _mac[5], + _mac[0] ^ _mac[4], + _mac[2] ^ _mac[3], + _mac[4] ^ _mac[5]] + + _hashpass[0] ^= nonce[3] ^ nonce[2] + _hashpass[1] ^= nonce[3] ^ nonce[1] + _hashpass[2] ^= nonce[0] ^ nonce[2] + _hashpass[3] ^= nonce[0] ^ nonce[1] + + if enc == self.ENCTYPE_HASH32: + for i in range(min(len(password),16)): + if (i < 4) or (i > 7): + idx = ((i + 3) % 4) + idx = ((i + 3) % 4) + idx ^= (idx // 2) + else: + idx = 3 - (i % 4) + + _hashpass[idx] ^= ord(password[i]) + + _hashpass = struct.pack(">BBBB", *_hashpass) + data = self.addudp(self.CMD_HASH, binascii.hexlify(_hashpass)) + else: + + _hashpass += _hashpass; + + _hashpass[6] ^= ord(password[0]) + + for i in range(len(password)): + _hashpass[i // 3] ^= ord(password[i]) + + if (i < 6) and (i % 2): + _hashpass[7] ^= ord(password[i]) + + _hashpass = struct.pack(">BBBBBBBB", *_hashpass) + data = self.addudp(self.CMD_PASSWORD_HASH, binascii.hexlify(_hashpass)) + else: + return { 'error' : 'Unknown encryption type 0x%02x' % enc } + + return data + def passwd_exploit(self, mac, new): "exploit in current (2012) firmware version, set a new password" # The order of the CMD_PASSWORD and CMD_NEW_PASSWORD is important data = self.addudp(self.CMD_NEW_PASSWORD, new) data += self.addudp(self.CMD_PASSWORD, new) return self.transmit(data, mac) - + def discover(self): "find any switch in the network" query_arr = [self.CMD_MODEL, @@ -401,10 +644,10 @@ def discover(self): self.CMD_MAC, self.CMD_DHCP, self.CMD_IP] - message = self.query(query_arr, None) - if message != False: - self.mac_cache[message[self.CMD_MAC]] = message[self.CMD_IP] - return message + for message in self.queryall(query_arr, None): + if message != False: + self.mac_cache[message[self.CMD_MAC]] = message[self.CMD_IP] + yield message def verify_data(self, datadict): "Verify the data we want to set on the switch" diff --git a/psl_typ.py b/psl_typ.py index cbad2ab..726df49 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -4,6 +4,22 @@ import binascii import struct +class PslError: + "Error class to map error codes to descriptions" + def __init__(self, code, desc): + "constructor" + self.code = code + self.desc = desc + + def get_code(self): + "error code" + return self.code + + def get_desc(self): + "human-readable error description" + return self.desc + + class PslTyp: "Base type every other type is inherited by this" def __init__(self, cmd_id, name): @@ -58,15 +74,19 @@ def get_num_args(self): def get_metavar(self): "argparse metavar to set" return None - + def get_set_type(self): "argparse type to set" return None - + def get_set_help(self): "argparse help argument for set operation" return None + def allow_multiple(self): + "can this command be set multiple times in one transaction" + return self.get_num_args() > 1 + ############################################################################### @@ -82,7 +102,6 @@ def pack_cmd(self, value): return value.encode() def unpack_cmd(self, value): - value = value.split("\0", 1)[0] return value.decode() def is_setable(self): @@ -148,6 +167,35 @@ def get_choices(self): ############################################################################### +class PslTypFiltering(PslTyp): + " Broadcast filtering, like a boolean but 0x00 and 0x03" + def pack_py(self, value): + if (value): + return struct.pack(">b", 0x03) + else: + return struct.pack(">b", 0x00) + + def unpack_py(self, value): + numval = struct.unpack(">b", value)[0] + return (numval == 0x03) + + def pack_cmd(self, value): + return self.pack_py(value.lowercase == "on") + + def unpack_cmd(self, value): + if (self.unpack_py(value)): + return "on" + else: + return "off" + + def is_setable(self): + return True + + def get_choices(self): + return ["on", "off"] + + +############################################################################### class PslTypDHCP(PslTypBoolean): "DHCP" # we already have that in base PslTypBoolean class, haven't we ? @@ -250,6 +298,24 @@ def unpack_cmd(self, value): ################################################################################ +class PslTypDec(PslTyp): + "just decode to decimal" + def pack_py(self, value): + return binascii.unhexlify(value) + + def unpack_py(self, value): + # Convert bytes to a hex string then to a decimal integer + # This allows us to convert a big-endian value of unknown length + return int(binascii.hexlify(value).decode(), 16) + + def pack_cmd(self, value): + return self.pack_py(value) + + def unpack_cmd(self, value): + return self.unpack_py(value) + +################################################################################ + class PslTypUnknown(PslTypHex): "Unknown Data" def unpack_cmd(self, value): @@ -281,28 +347,37 @@ def print_result(self, value): ################################################################################ -class PslTypSpeedStat(PslTyp): - "Speed statistic 10/100/1000 per port" +class PslTypPortStatus(PslTyp): + "Speed/flow control status per port" SPEED_NONE = 0x00 SPEED_10MH = 0x01 - SPEED_10ML = 0x02 + SPEED_10MF = 0x02 SPEED_100MH = 0x03 - SPEED_100ML = 0x04 + SPEED_100MF = 0x04 SPEED_1G = 0x05 + speed_to_string = { + SPEED_NONE: "Not conn.", + SPEED_10MH: "10Mbit/s half", + SPEED_10MF: "10Mbit/s", + SPEED_100MH: "100Mbit/s half", + SPEED_100MF: "100Mbit/s", + SPEED_1G: "1Gbit/s" + } + def unpack_py(self, value): # Python 3 uses an array of bytes, Python 2 uses a string if type(value) is str: rtn = { "port": struct.unpack(">b", value[0])[0], "speed": struct.unpack(">b", value[1])[0], - "rest": binascii.hexlify(value[2:]), + "flow": struct.unpack(">b", value[2])[0] } else: rtn = { "port": value[0], "speed": value[1], - "rest": binascii.hexlify(value[2:]).decode(), + "flow": value[2] } return rtn @@ -310,27 +385,115 @@ def is_setable(self): return False def print_result(self, value): - print("%-30s%4s%15s%10s" % ("Speed Statistic:", "Port", - "Speed", "FIXME")) + print("%-30s%4s%20s%15s" % ("Status:", "Port", + "Speed", "Flow control")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: - speed = row["speed"] - if speed == PslTypSpeedStat.SPEED_NONE: - speed = "Not conn." - if speed == PslTypSpeedStat.SPEED_10MH: - speed = "10 Mbit/s H" - if speed == PslTypSpeedStat.SPEED_10ML: - speed = "10 Mbit/s L" - if speed == PslTypSpeedStat.SPEED_100MH: - speed = "100 Mbit/s H" - if speed == PslTypSpeedStat.SPEED_100ML: - speed = "100 Mbit/s L" - if speed == PslTypSpeedStat.SPEED_1G: - speed = "1 Gbit/s" - print("%-30s%4d%15s%10s" % ("", row["port"], speed, row["rest"])) + speed = self.speed_to_string[row["speed"]] + + flow = "Enabled" + if row["flow"] == 0: + flow = "Disabled" + + print("%-30s%4d%20s%15s" % ("", row["port"], speed, flow)) + + def unpack_cmd(self, value): + return self.unpack_py(value) + + +################################################################################ + + +class PslTypAdminPortStatus(PslTyp): + "Max speed/flow control per port" + SPEED_DISABLE = 0x00 + SPEED_AUTO = 0x01 + SPEED_10MH = 0x02 + SPEED_10MF = 0x03 + SPEED_100MH = 0x04 + SPEED_100MF = 0x05 + + speed_to_string = { + SPEED_DISABLE: "Disable", + SPEED_AUTO: "Auto", + SPEED_10MH: "10M half", + SPEED_10MF: "10M", + SPEED_100MH: "100M half", + SPEED_100MF: "100M", + } + + string_to_speed = { + "DISABLE":SPEED_DISABLE, + "AUTO":SPEED_AUTO, + "10MH":SPEED_10MH, + "10M":SPEED_10MF, + "100MH":SPEED_100MH, + "100M":SPEED_100MF, + } + + def unpack_py(self, value): + # Python 3 uses an array of bytes, Python 2 uses a string + if type(value) is str: + rtn = { + "port": struct.unpack(">b", value[0])[0], + "speed": struct.unpack(">b", value[1])[0], + "flow": struct.unpack(">b", value[2])[0] + } + else: + rtn = { + "port": value[0], + "speed": value[1], + "flow": value[2] + } + + return rtn + + def pack_py(self, value): + port = int(value[0]) + speed = self.string_to_speed[value[1].upper()] + flow = (value[2].lower() == "on") + rtn = struct.pack(">bbb", port, speed, flow) + return rtn def unpack_cmd(self, value): return self.unpack_py(value) + def print_result(self, value): + print("%-30s%4s%20s%15s" % ("Status:", "Port", + "Speed", "Flow control")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + + for row in value: + speed = self.speed_to_string[row["speed"]] + flow = "On" + if row["flow"] == 0: + flow = "Off" + + print("%-30s%4d%20s%15s" % ("", row["port"], speed, flow)) + + def is_queryable(self): + return True + + def is_setable(self): + return True + + def get_num_args(self): + return 3 + + def get_metavar(self): + return ("PORT", "SPEED", "FLOW") + + def get_set_help(self): + out = "SPEED can be: NONE,AUTO,10MH,10M,100MH,100M, FLOW can be: ON, OFF" + return out + ################################################################################ @@ -363,6 +526,11 @@ def print_result(self, value): "Rec.", "Send", "Packets", "Broadcast pkt", "Multicast pkt", "CRC errors")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: print("%-30s%4d%15d%15d%15d%15d%15d%15d" % ("", row["port"], row["rec"], @@ -438,10 +606,15 @@ def pack_py(self, value): limit = self.string_to_speed[value[1]] rtn = struct.pack(">bbbh", int(value[0]), 0, 0, limit) return rtn - + def print_result(self, value): print("%-30s%4s%15s %s" % (self.get_name().capitalize(), "Port", "Limit", "FIXME")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: print("%-30s%4d%15s %s " % ("", row["port"], @@ -479,35 +652,71 @@ class PslTypVlanId(PslTyp): 8: 0x01 } + def set_total_ports(self, total_ports): + # Calculate number of bytes required to store bitmap of all ports + self.total_ports = total_ports + self.num_port_bytes = (total_ports + 7) // 8 + + def unpack_ports(self, port_bitmap, start = 0, length = 0): + if length == 0: + length = len(port_bitmap) - start + + port_bitmap = port_bitmap[start:start + length] + port_list = [] + base = 0 + + # Bytes are a bitmap of the ports from left-to-right + # (so most-significant bit in the first byte is port 1) + for ports in struct.unpack("%uB" % len(port_bitmap), port_bitmap): + for port in list(self.BIN_PORTS.keys()): + if (ports & self.BIN_PORTS[port] > 0): + port_list.append(port + base) + base += 8 + + return port_list + + def pack_ports(self, ports): + "helper method to pack ports to binary" + port_list = [0] * self.num_port_bytes + + if ports == "": + return port_list + + if type(ports) is not list: + ports = ports.split(",") + + for port in ports: + port = int(port) + base = (port - 1) // 8 + + if base < len(port_list): + port_list[base] |= self.BIN_PORTS[((port - 1) % 8) + 1] + else: + raise ValueError("Port '{}' is out of range".format(port)) + + # Return a packed structure here to allow compatibility with + # Python2 that will not allow multiple tuples to be unpacked in one + # line so the results from multple calls to 'pack_ports' cannot be + # passed to struct.pack unless we return a single value here (and not + # a tuple) + return struct.pack("%uB" % len(port_list), *port_list) + def unpack_py(self, value): - ports = struct.unpack(">B", value[2:])[0] - out_ports = [] - for port in list(self.BIN_PORTS.keys()): - if (ports & self.BIN_PORTS[port] > 0): - out_ports.append(port) + out_ports = self.unpack_ports(value, 2) + rtn = { "vlan_id": struct.unpack(">h", value[0:2])[0], "ports": out_ports } return rtn - - def pack_port(self, ports): - "helper method to pack ports to binary" - rtn = 0 - if ports == "": - return rtn - for port in ports.split(","): - rtn = rtn + self.BIN_PORTS[int(port)] - return rtn def pack_py(self, value): - ports = self.pack_port(value[1]) - rtn = struct.pack(">hB", int(value[0]), ports) - return rtn + ports = self.pack_ports(value[1]) + return struct.pack(">h%us" % len(ports), int(value[0]), ports) def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True @@ -520,12 +729,16 @@ def get_metavar(self): def print_result(self, value): print("%-30s%7s %s" % (self.get_name().capitalize(), "VLAN_ID", "Ports")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: print("%-30s%7d %s" % ("", int(row["vlan_id"]), ",".join([str(x) for x in row["ports"]]))) - ################################################################################ @@ -533,32 +746,22 @@ class PslTypVlan802Id(PslTypVlanId): "802Vlan is binary coded" def unpack_py(self, value): - # Python 3 uses an array of bytes, Python 2 uses a string - if type(value) is str: - tagged_ports = struct.unpack(">B", value[2])[0] - untagged_ports = struct.unpack(">B", value[3])[0] - else: - tagged_ports = value[2] - untagged_ports = value[3] - out_tagged_ports = [] - out_untagged_ports = [] - for port in list(self.BIN_PORTS.keys()): - if (tagged_ports & self.BIN_PORTS[port] > 0): - out_tagged_ports.append(port) - if (untagged_ports & self.BIN_PORTS[port] > 0): - out_untagged_ports.append(port) + port_len = (len(value) - 1) // 2 + + out_member_ports = self.unpack_ports(value, 2, port_len) + out_tagged_ports = self.unpack_ports(value, 2 + port_len, port_len) + rtn = { "vlan_id": struct.unpack(">h", value[0:2])[0], - "tagged_ports": out_tagged_ports, - "untagged_ports": out_untagged_ports + "member_ports":out_member_ports, + "tagged_ports": out_tagged_ports } return rtn - def pack_py(self, value): - tagged = self.pack_port(value[1]) - untagged = self.pack_port(value[2]) - rtn = struct.pack(">hBB", int(value[0]), tagged, untagged) + members = self.pack_ports(value[1]) + tagged = self.pack_ports(value[2]) + rtn = struct.pack(">h%us%us" % (len(members), len(tagged)), int(value[0]), members, tagged) return rtn def unpack_cmd(self, value): @@ -568,25 +771,22 @@ def get_num_args(self): return 3 def get_metavar(self): - return ("VLAN_ID", "TAGGED_PORTS", "UNTAGGED_PORTS") + return ("VLAN_ID", "MEMBER_PORTS", "TAGGED_PORTS") def print_result(self, value): - print("%-30s%7s %14s %s" % (self.get_name().capitalize(), "VLAN_ID", - "Tagged-Ports","Untagged-Ports")) - if type(value) is list: - for row in value: - print("%-30s%7d %14s %s" % ("", - int(row["vlan_id"]), - ",".join([str(x) for x in row["tagged_ports"]]), - ",".join([str(x) for x in row["untagged_ports"]]))) - else: - print("%-30s%7d %14s %s" % ("", - int(value["vlan_id"]), - ",".join([str(x) for x in value["tagged_ports"]]), - ",".join([str(x) for x in value["untagged_ports"]]))) - + print("%-30s%7s %18s %18s" % (self.get_name().capitalize(), "VLAN_ID", + "Member ports","Tagged ports")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + + for row in value: + print("%-30s%7d %18s %18s" % ("", + int(row["vlan_id"]), + ",".join([str(x) for x in row["member_ports"]]), + ",".join([str(x) for x in row["tagged_ports"]]))) - ################################################################################ @@ -629,6 +829,11 @@ def get_set_type(self): def print_result(self, value): print("%-30s%4s %s" % (self.get_name().capitalize(), "Port", "VLAN_ID")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: print("%-30s%4d %7d" % ("", row["port"], @@ -637,6 +842,37 @@ def print_result(self, value): def get_set_help(self): return "an untagged package on PORT will get this VLAN_ID" + + +################################################################################ + +class PslTypDeleteVlan(PslTyp): + "Delete vlan" + def unpack_py(self, value): + return struct.unpack(">h", value)[0] + + def pack_py(self, value): + rtn = struct.pack(">h", int(value)) + return rtn + + def pack_cmd(self, value): + return self.pack_py(value) + + def unpack_cmd(self, value): + return self.unpack_py(value) + + def is_queryable(self): + return False + + def is_setable(self): + return True + + def get_num_args(self): + return 1 + + def allow_multiple(self): + return True + ################################################################################ @@ -644,7 +880,7 @@ class UnknownValueException(Exception): "Found something which I don't know" -class PslTypQos(PslTyp): +class PslTypQosMode(PslTyp): "Quality of service is port_based or 802.1p" def unpack_py(self, value): # Python 3 uses an array of bytes, Python 2 uses a string @@ -657,7 +893,7 @@ def unpack_py(self, value): if (val == 0x02): return "802.1p" return val - + def pack_py(self, value): if (value == "802.1p"): return struct.pack(">B", 0x02) @@ -667,19 +903,19 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True - + def get_choices(self): return ["port_based","802.1p"] - + ################################################################################ class PslTypPortBasedQOS(PslTyp): "Port based quality of service" - + QOS_PRIORITY = { 0x01:"HIGH", 0x02:"MIDDLE", @@ -712,7 +948,7 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True @@ -721,13 +957,18 @@ def get_num_args(self): def get_metavar(self): return ("PORT","QOS") - + def get_set_help(self): return "QOS can be HIGH, MIDDLE, NORMAL, or LOW" def print_result(self, value): print("%-30s%4s %s" % (self.get_name().capitalize(), "Port", "Priority")) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + for row in value: print("%-30s%4d %s" % ("", row["port"], @@ -746,7 +987,7 @@ def unpack_py(self, value): # VLAN Id return struct.unpack(">h", value[2:])[0] raise UnknownValueException("Unknown value %d" % enabled) - + def pack_py(self, value): if (value == "none"): return struct.pack(">hh", 0, 0) @@ -754,10 +995,10 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True - + ################################################################################ @@ -820,12 +1061,26 @@ class PslTypPortMirror(PslTyp): 8: 0x01 } + def set_total_ports(self, total_ports): + # Calculate number of bytes required to store bitmap of all ports + self.total_ports = total_ports + self.num_port_bytes = (total_ports + 7) // 8 + def unpack_py(self, value): - dst_port, fixme, src_ports = struct.unpack(">bbb", value) + dst_port, fixme = struct.unpack(">BB", value[:2]) + # Remaining values are a bitmap of the source ports + src_port_list = value[2:] out_src_ports = [] - for port in list(self.BIN_PORTS.keys()): - if (src_ports & self.BIN_PORTS[port] > 0): - out_src_ports.append(port) + + base = 0 + + # Bytes are a bitmap of the source ports from left-to-right + # (so most-significant bit in the first byte is port 1) + for src_ports in struct.unpack("%uB" % len(src_port_list), src_port_list): + for port in list(self.BIN_PORTS.keys()): + if (src_ports & self.BIN_PORTS[port] > 0): + out_src_ports.append(port + base) + base += 8 if dst_port == 0: return "No Port Mirroring has been set up" @@ -837,13 +1092,30 @@ def unpack_py(self, value): return rtn def pack_py(self, value): - if int(value[0]) == 0: - return struct.pack(">bbb", 0, 0, 0) - dst_ports = 0 - for dport in value[1].split(","): - dst_ports += self.BIN_PORTS[int(dport)] - return struct.pack(">bbb", int(value[0]), 0, dst_ports) - + dst_port = int(value[0]) + src_ports = [0] * self.num_port_bytes + + if dst_port != 0: + if dst_port > self.total_ports: + raise ValueError("Destination port '{}' is out of range".format(dst_port)) + + port_list = value[1] + + # Convert comma-separated values into a list + if type(port_list) is not list: + port_list = port_list.split(",") + + for sport in port_list: + sport = int(sport) + idx = (sport - 1) // 8 + + if sport >= 1 and sport <= self.total_ports: + src_ports[idx] |= self.BIN_PORTS[((sport - 1) % 8) + 1] + else: + raise ValueError("Source port '{}' is out of range".format(sport)) + + return struct.pack(">BB%uB" % len(src_ports), dst_port, 0, *src_ports) + def unpack_cmd(self, value): return self.unpack_py(value) @@ -854,7 +1126,111 @@ def get_num_args(self): return 2 def get_metavar(self): - return ("DST_PORTS","SRC_PORTS") + return ("DST_PORT","SRC_PORTS") def get_set_help(self): - return "SET DST_PORTS and SRC_PORTS to 0 to disable" + return "SET DST_PORT and SRC_PORTS to 0 to disable" + +################################################################################ + +class PslTypCableTest(PslTyp): + + def unpack_py(self, value): + port, fixme = struct.unpack(">bb", value) + + rtn = { + "port": port, + "fixme": fixme, + } + return rtn + + def pack_py(self, value): + return struct.pack(">BB", int(value), 1) + + def is_queryable(self): + return False + + def is_setable(self): + return True + + def get_num_args(self): + return 1 + + def get_metavar(self): + return ("PORT") + + def get_set_type(self): + return int + +################################################################################ + +class PslTypCableTestResult(PslTyp): + "Cable test" + STATUS_OK = 0x00 + STATUS_NO_CABLE = 0x01 + STATUS_OPEN_CABLE = 0x02 + STATUS_SHORT_CIRCUIT = 0x03 + STATUS_FIBRE_CABLE = 0x04 + STATUS_SHORTED_CABLE = 0x05 + STATUS_UNKNOWN = 0x06 + STATUS_CROSSTALK = 0x07 + + status_to_string = { + STATUS_OK: "OK", + STATUS_NO_CABLE: "No cable", + STATUS_OPEN_CABLE: "Open cable", + STATUS_SHORT_CIRCUIT: "Short circuit", + STATUS_FIBRE_CABLE: "Fibre cable", + STATUS_SHORTED_CABLE: "Shorted cable", + STATUS_UNKNOWN: "Unknown", + STATUS_CROSSTALK: "Crosstalk" + } + + def unpack_py(self, value): + port, status, dist = struct.unpack(">BII", value) + rtn = { + "port": port, + "status": status, + "dist": dist + } + return rtn + + def pack_py(self, value): + return binascii.unhexlify("{:02x}".format(value[0])) + + def is_setable(self): + return False + + def print_result(self, value): + print(("%-30s%4s%20s%19s" % ("Cable status:", "Port", + "Status", "Fault distance (m)"))) + + # Make sure we have a list of values (even if it's just a list of one) + if type(value) != list: + value = [value] + + for row in value: + print(("%-30s%4d%20s%19u" % ("", row["port"], self.status_to_string[row["status"]], row["dist"]))) + + + def unpack_cmd(self, value): + return self.unpack_py(value) + +################################################################################ + +class PslTypSerialNum(PslTyp): + "switch's serial number" + def unpack_py(self, val): + values = struct.unpack("!B13sB6B", val) + one = values[0] # Should be 1 + zero = values[2] # Should be zero (null-terminator?) + fixup = values[3] # Six bytes (??) + + return values[1].decode() + + def unpack_cmd(self, value): + return self.unpack_py(value) + + def is_setable(self): + return False + diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua old mode 100644 new mode 100755 index 9847863..42f9d55 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -1,70 +1,106 @@ -- create nsdp protocol and its fields p_nsdp = Proto ("nsdp","Netgear Switch Description Protocol") -- local f_source = ProtoField.uint16("nsdp.src", "Source", base.HEX) +local e_error = ProtoExpert.new("nsdp.error", "Error", expert.group.RESPONSE_CODE, expert.severity.ERROR) local f_type = ProtoField.uint16("nsdp.type", "Type", base.HEX,{ - [0x101]="Data Request", - [0x102]="Data Response", - [0x103]="Change Request", - [0x104]="Change Response" + [0x101]="Data Request", + [0x102]="Data Response", + [0x103]="Change Request", + [0x104]="Change Response" }) + +local t_status = { + [0x00]="Success", + [0x01]="Protocol version not supported", + [0x02]="Command not supported", + [0x03]="TLV type not supported", + [0x04]="Invalid TLV length", + [0x05]="Invalid TLV value", + [0x06]="Manager IP is blocked by ACL", + [0x07]="Invalid password", + [0x08]="Firmware download requested", + [0x09]="Invalid username", + [0x0a]="Switch only supports management by browser", + [0x0d]="Invalid password", + [0x0e]="3 failed attempts. Switch is locked for 30 minutes", + [0x0f]="Switch management disabled. Use browser to enable", + [0x81]="TFTP call error", + [0x82]="TFTP Out of memory", + [0x83]="Firmware update failed", + [0x84]="TFTP timed out" +} +local f_status = ProtoField.uint8("nsdp.status", "Status", base.HEX, t_status) local f_source = ProtoField.ether("nsdp.src", "Source", base.HEX) local f_destination = ProtoField.ether("nsdp.dst", "Destination", base.HEX) local f_seq = ProtoField.uint16("nsdp.seq", "Seq", base.HEX) local f_len = ProtoField.uint16("nsdp.len", "Length", base.HEX) local f_data = ProtoField.string("nsdp.data", "Data", FT_STRING) local f_vlan_engine = ProtoField.uint8("nsdp.vlan_engine","VLAN Engine",base.HEX,{ [0x00]="None", - [0x01]="VLAN_Port_Based", - [0x02]="VLAN_ID_Based", - [0x03]="802.1Q_Port_Based", - [0x04]="802.1Q_Extended", + [0x01]="VLAN_Port_Based", + [0x02]="VLAN_ID_Based", + [0x03]="802.1Q_Port_Based", + [0x04]="802.1Q_Extended", }) - -local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX,{ - [0x0001] = "Model", - [0x0002] = "FIXME 0x0002 (2 Bytes)", - [0x0003] = "Name", - [0x0004] = "MAC", - [0x0005] = "Location", - [0x0006] = "IP-Address", - [0x0007] = "Netmask", - [0x0008] = "Gateway", - [0x0009] = "New Password", - [0x000a] = "Password", - [0x000b] = "DHCP Status", - [0x000c] = "FIXME 0x000c (1 Byte)", - [0x000d] = "Firmware Version", - [0x000e] = "Firmware 2 Version", - [0x000f] = "Active Firmware", - [0x0013] = "Reboot", - [0x0400] = "Factory Reset", - [0x1000] = "Port Traffic Statistic", - [0x1400] = "Reset Port Traffic Statistic", - [0x1800] = "Test Cable", - [0x2000] = "VLAN Engine", - [0x2400] = "VLAN-ID", - [0x2800] = "802VLAN-ID", +local t_cmd = { + [0x0001] = "Model", + [0x0002] = "FIXME 0x0002 (2 Bytes)", + [0x0003] = "Name", + [0x0004] = "MAC", + [0x0005] = "Location", + [0x0006] = "IP-Address", + [0x0007] = "Netmask", + [0x0008] = "Gateway", + [0x0009] = "New Password", + [0x000a] = "Password", + [0x000b] = "DHCP Status", + [0x000c] = "FIXME 0x000c (1 Byte)", + [0x000d] = "Firmware Version", + [0x000e] = "Firmware 2 Version", + [0x000f] = "Active Firmware", + [0x0013] = "Reboot", + [0x0014] = "Enhanced encryption", + [0x0017] = "Password nonce", + [0x0018] = "32-bit password hash", + [0x001a] = "64-bit password hash", + [0x0400] = "Factory Reset", + [0x0c00] = "Port status", + [0x1000] = "Port Traffic Statistic", + [0x1400] = "Reset Port Traffic Statistic", + [0x1800] = "Test Cable", + [0x1c00] = "Cable test result", + [0x2000] = "VLAN Engine", + [0x2400] = "VLAN-ID", + [0x2800] = "802VLAN-ID", + [0x2c00] = "Delete VLAN", [0x3000] = "vlan_pvid", - [0x3400] = "QOS", - [0x3800] = "Portbased QOS", - [0x4c00] = "Bandwidth Limit IN", - [0x5000] = "Bandwidth Limit OUT", - [0x5400] = "FIXME 0x5400 (1 Byte)", - [0x5800] = "Broadcast Bandwidth", - [0x5c00] = "Port Mirror", - [0x6000] = "Number of available Ports", - [0x6800] = "IGMP Snooping Status", - [0x6c00] = "Block Unknown Multicasts", - [0x7000] = "IGMP Header Validation", - [0x7400] = "FIMXE 0x7400 (8 Bytes)", - [0x0c00] = "Speed/Link Status", - [0xffff] = "End Request" -}) + [0x3400] = "QoS", + [0x3800] = "QoS port priority", + [0x4c00] = "Bandwidth Limit IN", + [0x5000] = "Bandwidth Limit OUT", + [0x5400] = "Broadcast filtering", + [0x5800] = "Broadcast Bandwidth", + [0x5c00] = "Port Mirror", + [0x6000] = "Number of available ports", + [0x6800] = "IGMP Snooping Status", + [0x6c00] = "Block Unknown Multicasts", + [0x7000] = "IGMP Header Validation", + [0x7400] = "Supported TLVs", + [0x7800] = "Serial number", + [0x9000] = "Loop detection", + [0x9400] = "Port Admin Status", + [0xffff] = "End Request" +} +local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX, t_cmd) +local f_fixme0002 = ProtoField.uint16("nsdp.fixme0002", "Fix me 0x0002", base.HEX) +local f_fixme000C = ProtoField.uint8("nsdp.fixme000C", "Fix me 0x000C", base.HEX) local f_password = ProtoField.string("nsdp.password", "Password", FT_STRING) +local f_enhancedEncryption = ProtoField.uint32("nsdp.enhancedencryption", "Enhanced encryption", base.HEX) +local f_passwordNonce = ProtoField.uint32("nsdp.passwordnonce", "Password nonce", base.HEX) +local f_passhash32 = ProtoField.uint32("nsdp.passwordhash32", "32-bit password hash", base.HEX) +local f_passhash64 = ProtoField.uint64("nsdp.passwordhash64", "64-bit password hash", base.HEX) local f_newpassword = ProtoField.string("nsdp.newpassword", "New password", FT_STRING) -local f_flags = ProtoField.uint16("nsdp.flags", "Flags", base.HEX, { - [0x000a] = "Password error" -}) +local f_errcmd = ProtoField.uint16("nsdp.errcmd", "Failed command", base.HEX, t_cmd) local f_model =ProtoField.string("nsdp.model","Model", FT_STRING) local f_name =ProtoField.string("nsdp.name","Name", FT_STRING) local f_macinfo = ProtoField.ether("nsdp.macinfo", "MAC info", base.HEX) @@ -77,241 +113,485 @@ local f_firmwarever_len = ProtoField.uint16("nsdp.firmwarever_len", "Firmware ve local f_firmwarever = ProtoField.string("nsdp.firmwarever", "Firmware version",FT_STRING) local f_firmware2ver = ProtoField.string("nsdp.firmware2ver", "Firmware 2 version",FT_STRING) local f_firmwareactive = ProtoField.uint8("nsdp.firmwareactive","Active firmware") -local speed_flags={ - [0x00]="None", - [0x01]="10M", - [0x03]="100M", - [0x05]="1000M" +local t_speed_flags={ + [0x00]="None", + [0x01]="10M (half-duplex)", + [0x02]="10M", + [0x03]="100M (half-duplex)", + [0x04]="100M", + [0x05]="1000M" +} + +local t_flow_control={ + [0x00]="Disabled", + [0x01]="Enabled" } -local f_speed = ProtoField.uint8("nsdp.speed","Speed",base.HEX, speed_flags) -local f_link = ProtoField.uint8("nsdp.link","Link",base.HEX) + +local f_speed = ProtoField.uint8("nsdp.speed","Speed",base.HEX, t_speed_flags) +local f_flow = ProtoField.uint8("nsdp.flow_control", "Flow control", base.HEX, t_flow_control) +local f_qos_mode = ProtoField.uint8("nsdp.qos_mode", "QoS mode", base.HEX, { + [0x01]="Port based", + [0x02]="802.1p/DSCP based" +}) +local t_port_prio = { + [0x01]="High", + [0x02]="Medium", + [0x03]="Normal", + [0x04]="Low" +} +local f_qos_port_prio = ProtoField.uint8("nsdp.qos_port_prio", "Priority", base.HEX, t_port_prio) local f_port=ProtoField.uint8("nsdp.port","Port Number") local f_rec=ProtoField.uint64("nsdp.recived","Bytes received") -local f_send=ProtoField.uint64("nsdp.send","Bytes send") +local f_send=ProtoField.uint64("nsdp.sent","Bytes sent") local f_pkt=ProtoField.uint64("nsdp.pkt","Total packets") local f_bpkt=ProtoField.uint64("nsdp.pkt_bcst","Broadcast packets") local f_mpkt=ProtoField.uint64("nsdp.pkt_mcst","Multicast packets") local f_crce=ProtoField.uint64("nsdp.crc_error","CRC errors") +local f_numports=ProtoField.uint8("ndsp.numports","Number of ports") +local f_supportedTLVs=ProtoField.uint64("nsdp.supportedtlvs","Supported TLVs",base.HEX) +local f_bcast_filtering=ProtoField.uint8("nsdp.bcast_filter", "Broadcast filtering", base.HEX, { + [0x00]="Disabled", + [0x03]="Enabled" +}) +local t_rate_limit={ + [0x00]="No limit", + [0x01]="512 Kbit/s", + [0x02]="1 Mbits/s", + [0x03]="2 Mbits/s", + [0x04]="4 Mbits/s", + [0x05]="8 Mbits/s", + [0x06]="16 Mbits/s", + [0x07]="32 Mbits/s", + [0x08]="64 Mbits/s", + [0x09]="128 Mbits/s", + [0x0a]="256 Mbits/s", + [0x0b]="512 Mbits/s", +} +local f_rate_limit=ProtoField.uint8("nsdp.rate_limit", "Rate", base.HEX, t_rate_limit) +local f_port_mirror_src=ProtoField.uint8("nsdp.port_mirror_src", "Source port(s)", base.HEX) +local f_port_mirror_dest=ProtoField.uint8("nsdp.port_mirror_dest", "Destination port", base.HEX) + +local f_pvid_vlan=ProtoField.uint16("nsdp.pvid_vlan", "VLAN") +local f_del_vlan=ProtoField.uint16("nsdp.del_vlan", "Delete VLAN") +local f_vlan=ProtoField.uint16("nsdp.vlan", "VLAN") +local f_vlan_ports=ProtoField.uint8("nsdp.vlan_ports", "VLAN ports", base.HEX) +local f_802_1q_vlan = ProtoField.uint16("nsdp.802_1q_vlan", "802.1q VLAN") +local f_802_1q_ports = ProtoField.uint8("nsdp.802_1q_ports", "802.1q ports", base.HEX) +local f_802_1q_tagged = ProtoField.uint8("nsdp.802_1q_tagged", "802.1q tagged", base.HEX) + +local f_cable_test=ProtoField.string("nsdp.cable_test", "Cable test result") +local t_cable_test_status={ + [0x00]="OK", + [0x01]="No cable", + [0x02]="Open cable", + [0x03]="Short circuit", + [0x04]="Fibre cable", + [0x05]="Shorted cable", + [0x06]="Unknown", + [0x07]="Crosstalk" +} +local f_cable_test_status=ProtoField.uint32("nsdp.cable_test_status", "Status", base.HEX, t_cable_test_status) +local f_cable_test_distance=ProtoField.uint32("nsdp.cable_test_distance", "Distance") + +local port_admin_speed={ + [0x00]="Disabled", + [0x01]="Auto", + [0x02]="10M (half-duplex)", + [0x03]="10M (full-duplex)", + [0x04]="100M (half-duplex)", + [0x05]="100M (full-duplex)" +} +local f_port_admin_speed = ProtoField.uint8("nsdp.port_admin_speed", "Speed", base.HEX, port_admin_speed) +local f_loop_detection = ProtoField.uint8("nsdp.loop_detection", "Loop detection", base.HEX, { + [0x00]="Disabled", + [0x01]="Enabled" +}) + +local f_serial_num = ProtoField.string("nsdp.serialnum", "Serial number", FT_STRING) --local f_debug = ProtoField.uint8("nsdp.debug", "Debug") -p_nsdp.fields = {f_type,f_source,f_destination,f_seq,f_cmd,f_password,f_newpassword,f_flags, +p_nsdp.experts = {e_error} + + +p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f_newpassword,f_errcmd, + f_enhancedEncryption,f_passwordNonce,f_passhash32, f_passhash64, + f_fixme0002, f_fixme000C, + f_qos_mode, f_qos_port_prio, + f_pvid_vlan, f_del_vlan, + f_vlan, f_vlan_ports, + f_802_1q_vlan, f_802_1q_ports, f_802_1q_tagged, + f_cable_test_status, f_cable_test_distance, + f_bcast_filtering,f_rate_limit,f_port_admin_speed, + f_port_mirror_src, f_port_mirror_dest, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, - f_pkt,f_bpkt,f_mpkt,f_crce,f_link,f_vlan_engine,f_ipaddr, + f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, f_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, f_firmware2ver, f_firmwareactive, - f_speed,f_location} + f_speed,f_flow,f_location,f_numports,f_supportedTLVs,f_loop_detection, + f_serial_num} + +-- Build a condensed string of port ranges from a bitmap +function port_list(portslist) + local list = {} + local lastPort = -1 + local firstPort = -1 + local base = 1 + + for i=0, portslist:len() - 1 do + ports = portslist(i,1):uint() + + for b=base, base + 8 do + if bit32.band(ports,0x80) ~= 0 then + if lastPort ~= (b - 1) then + list[#list+1] = "," + list[#list+1] = b + firstPort = b + end + lastPort = b + elseif lastPort ~= -1 then + if firstPort ~= lastPort then + list[#list+1] = "-" + list[#list+1] = tostring(lastPort) + end + firstPort = -1 + lastPort = -1 + end + ports = bit32.lshift(ports,1) + end + base = base + 8 + end + + if #list == 0 then + return "None" + else + return table.concat(list,"",2) + end +end -- nsdp dissector function function p_nsdp.dissector (buf, pkt, root) - -- validate packet length is adequate, otherwise quit - if buf:len() == 0 then return end - pkt.cols.protocol = p_nsdp.name + -- validate packet length is adequate, otherwise quit + if buf:len() == 0 then return end + pkt.cols.protocol = p_nsdp.name - -- create subtree for nsdp - subtree = root:add(p_nsdp, buf(0)) - local offset = 0 - local ptype = buf(offset,2):uint() - if ptype == 0x0104 then - if buf:len() == offset then - subtree:append_text(", password changed") - else - subtree:append_text(", logged in") - end - end - subtree:add(f_type, buf(offset,2)) - offset = offset + 4 - subtree:add(f_flags, buf(offset,2)) - offset = offset + 4 - subtree:add(f_source, buf(offset,6)) - offset = offset + 6 - subtree:add(f_destination, buf(offset,6)) - offset = offset + 8 - subtree:add(f_seq, buf(offset,2)) - offset = offset + 10 - while offset < buf:len() do - local cmd = buf(offset, 2):uint() - local len=buf(offset+2,2):uint() - local tree=0 - offset = offset + 4 - if cmd == 0x0001 then - tree=subtree:add(f_model,buf(offset,len)) - elseif cmd == 0x0003 then - tree=subtree:add(f_name,buf(offset,len)) - elseif cmd == 0x0004 and len==6 then - tree=subtree:add(f_macinfo,buf(offset,len)) - elseif cmd == 0x0004 then - tree=subtree:add(buf(offset,len),"MAC") - elseif cmd == 0x0005 then - tree=subtree:add(f_location,buf(offset,len)) - elseif cmd == 0x0006 and len==4 then - tree=subtree:add(f_ipaddr,buf(offset,len)) - elseif cmd == 0x0006 then - tree=subtree:add(buf(offset,len),"IP-Address") - elseif cmd == 0x0007 and len==4 then - tree=subtree:add(f_netmask,buf(offset,len)) - elseif cmd == 0x0007 then - tree=subtree:add(buf(offset,len),"Netmask") - elseif cmd == 0x0008 and len==4 then - tree=subtree:add(f_gateway,buf(offset,len)) - elseif cmd == 0x0008 then - tree=subtree:add(buf(offset,len),"Gateway") - elseif cmd == 0x0009 then - tree=subtree:add(f_newpassword, buf(offset,len)) - elseif cmd == 0x000a then - tree=subtree:add(f_password, buf(offset,len)) - elseif cmd == 0x000b and len==1 then - tree=subtree:add(f_dhcp_enable, buf(offset,len)) - -- 00 DHCP disabled - -- 01 DHCP enabled - -- CMD: 02 DHCP do a new query - elseif cmd == 0x000b then - tree=subtree:add(buf(offset,len),"Query DHCP") - elseif cmd == 0x000d then - tree=subtree:add(f_firmwarever,buf(offset,len)) - elseif cmd == 0x000e then - tree=subtree:add(f_firmware2ver,buf(offset,len)) - elseif cmd == 0x000f then - if len == 1 then - tree=subtree:add(f_firmwareactive,buf(offset,len)) + -- create subtree for nsdp + subtree = root:add(p_nsdp, buf(0)) + local status = 0 + local errcmd = 0 + local offset = 0 + local ptype = buf(offset,2):uint() + if ptype == 0x0104 then + if buf:len() == offset then + subtree:append_text(", password changed") else - tree=subtree:add(buf(offset,len),"Active Firmware?") + subtree:append_text(", logged in") + end + end + subtree:add(f_type, buf(offset,2)) + offset = offset + 2 + status = buf(offset,1) + if status:uint() ~= 0 then + local status_tree = subtree:add(f_status, status) + status = status:uint() + local errmsg=t_status[status] + if errmsg == nil then + errmsg = string.format("Unknown error: 0x%02x", status) end - elseif cmd==0x0c00 and len==3 then - tree=subtree:add(buf(offset,1),"Speed Statistic") - tree:add(f_port,buf(offset,1)) - tree:add(f_speed,buf(offset+1,1)) - tree:add(f_link,buf(offset+2,1)) - elseif cmd==0x1000 and len==0x31 then - tree=subtree:add(buf(offset,1),"Port Statistic") - tree:add(f_port,buf(offset,1)) - tree:add(f_rec,buf(offset+1,8)) - tree:add(f_send,buf(offset+1+8,8)) - tree:add(f_pkt,buf(offset+1+2*8,8)) - tree:add(f_bpkt,buf(offset+1+3*8,8)) - tree:add(f_mpkt,buf(offset+1+4*8,8)) - tree:add(f_crce,buf(offset+1+5*8,8)) - elseif cmd==0x1400 and len==0x01 then - tree=subtree:add(buf(offset,1),"Reset Port Statistic") - -- 1 Byte: 0x01 - elseif cmd==0x1800 and len==0x02 then - tree=subtree:add(buf(offset,len),"Test Cable") - -- 1 Byte Port 01=Port 1...08=Port 8 - -- 1 Byte alway 0x01 - elseif cmd==0x1c00 and len==0x01 then - -- 1 Byte Port + status_tree:add_proto_expert_info(e_error,errmsg) + errcmd = buf(offset + 2,2) + subtree:add(f_errcmd, errcmd) + errcmd = errcmd:uint() + else + status = status:uint() + end + offset = offset + 6 + subtree:add(f_source, buf(offset,6)) + offset = offset + 6 + subtree:add(f_destination, buf(offset,6)) + offset = offset + 8 + subtree:add(f_seq, buf(offset,2)) + offset = offset + 10 + if status == 0 then + while offset < buf:len() do + local cmd = buf(offset, 2):uint() + local len=buf(offset+2,2):uint() + local tree=0 + offset = offset + 4 + if cmd == 0x0001 then + tree=subtree:add(f_model,buf(offset,len)) + elseif cmd == 0x0002 then + if len==0x02 then + tree=subtree:add(f_fixme0002, buf(offset,len)) + else + tree=subtree:add(buf(offset,len), "Fix me 0002") + end + elseif cmd == 0x0003 then + tree=subtree:add(f_name,buf(offset,len)) + elseif cmd == 0x0004 and len==6 then + tree=subtree:add(f_macinfo,buf(offset,len)) + elseif cmd == 0x0004 then + tree=subtree:add(buf(offset,len),"MAC") + elseif cmd == 0x0005 then + tree=subtree:add(f_location,buf(offset,len)) + elseif cmd == 0x0006 and len==4 then + tree=subtree:add(f_ipaddr,buf(offset,len)) + elseif cmd == 0x0006 then + tree=subtree:add(buf(offset,len),"IP-Address") + elseif cmd == 0x0007 and len==4 then + tree=subtree:add(f_netmask,buf(offset,len)) + elseif cmd == 0x0007 then + tree=subtree:add(buf(offset,len),"Netmask") + elseif cmd == 0x0008 and len==4 then + tree=subtree:add(f_gateway,buf(offset,len)) + elseif cmd == 0x0008 then + tree=subtree:add(buf(offset,len),"Gateway") + elseif cmd == 0x0009 then + tree=subtree:add(f_newpassword, buf(offset,len)) + elseif cmd == 0x000a then + tree=subtree:add(f_password, buf(offset,len)) + elseif cmd == 0x000b and len==1 then + tree=subtree:add(f_dhcp_enable, buf(offset,len)) + -- 00 DHCP disabled + -- 01 DHCP enabled + -- CMD: 02 DHCP do a new query + elseif cmd == 0x000b then + tree=subtree:add(buf(offset,len),"Query DHCP") + elseif cmd == 0x000c then + if len==0x01 then + tree=subtree:add(f_fixme000C, buf(offset,len)) + else + tree=subtree:add(buf(offset,len), "Fix me 000c") + end + elseif cmd == 0x000d then + tree=subtree:add(f_firmwarever,buf(offset,len)) + elseif cmd == 0x000e then + tree=subtree:add(f_firmware2ver,buf(offset,len)) + elseif cmd == 0x000f then + if len == 1 then + tree=subtree:add(f_firmwareactive,buf(offset,len)) + else + tree=subtree:add(buf(offset,len),"Active Firmware?") + end + elseif cmd == 0x0014 then + if len == 4 then + tree=subtree:add(f_enhancedEncryption, buf(offset,len)) + else + tree=subtree:add(buf(offset,len),"Enhanced encryption?") + end + elseif cmd == 0x0017 then + if len == 4 then + tree=subtree:add(f_passwordNonce, buf(offset,4)) + else + tree=subtree:add(buf(offset,len),"Password nonce?") + end + elseif cmd == 0x0018 and len==4 then + tree=subtree:add(f_passhash32, buf(offset,4)) + elseif cmd == 0x001a and len==8 then + tree=subtree:add(f_passhash64, buf(offset,8)) + elseif cmd==0x0c00 then + if len==3 then + local port = buf(offset,1) + local speed = buf(offset+1,1) + local flow = buf(offset+2,1) + tree=subtree:add(buf(offset,1),string.format("Port status: Port:%u, Speed:%s, Flow control:%s", port:uint(), t_speed_flags[speed:uint()], t_flow_control[flow:uint()])) + tree:add(f_port,port) + tree:add(f_speed,speed) + tree:add(f_flow,flow) + else + tree=subtree:add(buf(offset,1),"Port status?") + end + elseif cmd==0x1000 and len==0x31 then + tree=subtree:add(buf(offset,1),"Port Statistic") + tree:add(f_port,buf(offset,1)) + tree:add(f_rec,buf(offset+1,8)) + tree:add(f_send,buf(offset+1+8,8)) + tree:add(f_pkt,buf(offset+1+2*8,8)) + tree:add(f_bpkt,buf(offset+1+3*8,8)) + tree:add(f_mpkt,buf(offset+1+4*8,8)) + tree:add(f_crce,buf(offset+1+5*8,8)) + elseif cmd==0x1400 and len==0x01 then + tree=subtree:add(buf(offset,1),"Reset Port Statistic") + -- 1 Byte: 0x01 + elseif cmd==0x1800 and len==0x02 then + local port=buf(offset,1) + tree=subtree:add(buf(offset,len), string.format("Test Cable - Port %u", port:uint())) + tree:add(f_port, port) + -- 1 Byte Port 01=Port 1...08=Port 8 + -- 1 Byte alway 0x01 + elseif cmd==0x1c00 and len==0x01 then + local port=buf(offset,1) + tree=subtree:add(port, string.format("Cable test result? - Port %u", port:uint())) + tree:add(f_port, port) + -- 1 Byte Port + elseif cmd==0x1c00 and len==0x09 then + local port=buf(offset,1) + local status=buf(offset+1,4) + local distance=buf(offset+5,4) + tree=subtree:add(buf(offset,len), string.format("Cable test result - Port:%d, Status:%s, Fault distance:%dm", + port:uint(), + t_cable_test_status[status:uint()], + distance:uint())) + tree:add(f_port, port) + tree:add(f_cable_test_status, status) + tree:add(f_cable_test_distance, distance) + elseif cmd==0x2000 and len==0x01 then + tree=subtree:add(f_vlan_engine,buf(offset,len)) + elseif cmd==0x2400 and len>=0x03 then + local vlan=buf(offset,2) + local ports=buf(offset+2, len - 2) + tree=subtree:add(buf(offset,len), string.format("VLAN status: VLAN:%u, Ports:%s", vlan:uint(), port_list(ports))) + tree:add(f_vlan, vlan) + tree:add(f_vlan_ports, ports) + elseif cmd==0x2800 and len>=0x04 then + local vlan=buf(offset,2) + local port_len = (len - 2) / 2 + local ports=buf(offset+2, port_len) + local tagged=buf(offset+2+port_len, port_len) + tree=subtree:add(buf(offset,len), string.format("802.1q status: VLAN:%u, Ports:%s, Tagged:%s", vlan:uint(), port_list(ports), port_list(tagged))) + tree:add(f_802_1q_vlan, vlan) + tree:add(f_802_1q_ports, ports) + tree:add(f_802_1q_tagged, tagged) + elseif cmd==0x2c00 then + tree=subtree:add(f_del_vlan, buf(offset,2)) + elseif cmd==0x3000 and len==0x03 then + local port=buf(offset,1) + local vlan=buf(offset+1,2) + tree=subtree:add(buf(offset,len), string.format("PVID: Port:%d, VLAN:%u", port:uint(), vlan:uint())) + tree:add(f_port, port) + tree:add(f_pvid_vlan, vlan) + elseif cmd==0x3400 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"QoS mode?") + else + tree=subtree:add(f_qos_mode, buf(offset,len)) + end + elseif cmd==0x3800 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"QoS port priority?") + else + local port=buf(offset, 1) + local prio=buf(offset + 1, 1) - elseif cmd==0x1c00 and len==0x09 then - -- 1 Byte Port - -- 00 00 01 00 00 00 00 == No Cable - -- 00 00 00 00 00 00 01 == OK - -- 00 00 00 00 00 00 04 == OK - elseif cmd==0x2000 and len==0x01 then - tree=subtree:add(f_vlan_engine,buf(offset,len)) - elseif cmd==0x2800 and len==0x04 then - tree=subtree:add(buf(offset,len),"FIXME") - -- 2 Bytes: VLAN ID (0x0ffe is all Ports - -- 1 Byte Port Hex 01=Port 8 02=Port 7 04=Port 6 08=Port 5 10=Port Port 4 20=Port 3 40=Port 2 80=Port 1 - -- 1 Byte Tagged Ports - elseif cmd==0x3000 and len==0x03 then - tree=subtree:add(buf(offset,len),"FIXME") - -- 1 Byte Port (not binary Port8=8; Port1=1) - -- 2 Bytes VLAN ID Port PVID - elseif cmd==0x3400 and len==0x01 then - tree=subtree:add(buf(offset,len),"Port Based Quality of Service") - -- 1 Byte 0x01== port based - -- 1 Byte 0x02== 802.1p based - elseif cmd==0x3800 and len==0x01 then - tree=subtree:add(buf(offset,len),"Port Based Quality of Service") - -- 1 Byte port - -- 1 Byte: - -- 0x01 == High Priority - -- 0x02 == Middle Priority - -- 0x03 == Normal Priority - -- 0x04 == Low Priority - elseif cmd==0x4c00 and len==0x05 then - tree=subtree:add(buf(offset,len),"FIXME") - -- 1 Byte Port (not binary Port8=8; Port1=1) - -- 2 Bytes Unknown - -- 2 Bytes Incomming Rate - -- 0x0000 No Limit - -- 0x0001 512 Kbits/s - -- 0x0002 1 Mbits/s - -- 0x0003 2 Mbits/s - -- 0x0004 4 Mbits/s - -- 0x0005 8 Mbits/s - -- 0x0006 16 Mbits/s - -- 0x0007 32 Mbits/s - -- 0x0008 64 Mbits/s - -- 0x0009 128 Mbits/s - -- 0x000a 256 Mbits/s - -- 0x000b 512 Mbits/s - elseif cmd==0x5000 and len==0x05 then - tree=subtree:add(buf(offset,len),"FIXME") - -- 1 Byte Port (not binary Port8=8; Port1=1) - -- 2 Bytes Unknown - -- 2 Bytes Outgoing Rate - -- 0x0000 No Limit - -- 0x0001 512 Kbits/s - -- 0x0002 1 Mbits/s - -- 0x0003 2 Mbits/s - -- 0x0004 4 Mbits/s - -- 0x0005 8 Mbits/s - -- 0x0006 16 Mbits/s - -- 0x0007 32 Mbits/s - -- 0x0008 64 Mbits/s - -- 0x0009 128 Mbits/s - -- 0x000a 256 Mbits/s - -- 0x000b 512 Mbits/s - elseif cmd==0x5c00 and len==0x03 then - tree=subtree:add(buf(offset,len),"Port Mirroring") - -- 00 00 00 = Disabled - -- 1 Byte destination port - -- 1 Byte 00 - -- 1 Byte source ports (binary port shema) - elseif cmd==0x5800 and len==0x05 then - tree=subtree:add(buf(offset,len),"Broadcast Filter") - -- 1 Byte Port (not binary Port8=8; Port1=1) - -- 2 Bytes Unknown - -- 2 Bytes Broadcast Rate - -- 0x0000 No Limit - -- 0x0001 512 Kbits/s - -- 0x0002 1 Mbits/s - -- 0x0003 2 Mbits/s - -- 0x0004 4 Mbits/s - -- 0x0005 8 Mbits/s - -- 0x0006 16 Mbits/s - -- 0x0007 32 Mbits/s - -- 0x0008 64 Mbits/s - -- 0x0009 128 Mbits/s - -- 0x000a 256 Mbits/s - -- 0x000b 512 Mbits/s - elseif cmd==0x6000 and len==0x01 then - tree=subtree:add(buf(offset,len),"Number of Ports???") - -- 1 Byte Port (not binary Port8=8; Port1=1) - elseif cmd==0x6c00 and len==0x01 then - tree=subtree:add(buf(offset,len),"Block unknown MultiCast Address") - -- 1 Byte Port (not binary Port8=8; Port1=1) - elseif cmd==0x7000 and len==0x04 then - tree=subtree:add(buf(offset,len),"IGMP Spoofing") - -- 00 00 00 00 Disabled - -- 00 01 Enabled - -- 2 Bytes VLAN ID - elseif cmd==0x7000 and len==0x01 then - tree=subtree:add(buf(offset,len),"Valid IGMP Spoofing") - -- 01 enabled - -- 00 disabled + tree=subtree:add(buf(offset,len), string.format("QoS port priority - Port:%u, prio: %s", port:uint(), t_port_prio[prio:uint()])) + tree:add(f_port, port) + tree:add(f_qos_port_prio, prio) + end + elseif cmd==0x4c00 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"Ingress rate limit?") + else + local port=buf(offset, 1) + local rate=buf(offset + 3, 2) + tree=subtree:add(buf(offset,len),string.format("Ingress limit: Port:%u, rate:%s", port:uint(), t_rate_limit[rate:uint()])) + tree:add(f_port, port) + tree:add(f_rate_limit, rate) + end + elseif cmd==0x5000 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"Egress rate limit?") + else + local port=buf(offset, 1) + local rate=buf(offset + 3, 2) + tree=subtree:add(buf(offset,len),string.format("Egress limit: Port:%u, rate:%s", port:uint(), t_rate_limit[rate:uint()])) + tree:add(f_port, port) + tree:add(f_rate_limit, rate) + end + elseif cmd==0x5400 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"Broadcast filtering?") + else + tree=subtree:add(f_bcast_filtering, buf(offset,len)) + end + elseif cmd==0x5800 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"Broadcast storm rate?") + else + local port=buf(offset, 1) + local rate=buf(offset + 3, 2) + tree=subtree:add(buf(offset,len),string.format("Storm control rate: Port:%u, rate:%s", port:uint(), t_rate_limit[rate:uint()])) + tree:add(f_port, port) + tree:add(f_rate_limit, rate) + end + elseif cmd==0x5c00 and len>=3 then + local dest=buf(offset,1) + local src=buf(offset+2, len - 2) + tree=subtree:add(buf(offset,len), string.format("Port mirroring: Source port(s):%s, Dest port:%u", port_list(src), dest:uint())) + tree:add(f_port_mirror_src, src) + tree:add(f_port_mirror_dest, dest) + elseif cmd==0x6000 then + if len==0x01 then + tree=subtree:add(f_numports, buf(offset,len)) + else + tree=subtree:add(buf(offset,len),"Number of ports?") + end + elseif cmd==0x6c00 and len==0x01 then + tree=subtree:add(buf(offset,len),"Block unknown MultiCast Address") + -- 1 Byte Port (not binary Port8=8; Port1=1) + elseif cmd==0x7000 and len==0x04 then + tree=subtree:add(buf(offset,len),"IGMP Spoofing") + -- 00 00 00 00 Disabled + -- 00 01 Enabled + -- 2 Bytes VLAN ID + elseif cmd==0x7000 and len==0x01 then + tree=subtree:add(buf(offset,len),"Valid IGMP Spoofing") + -- 01 enabled + -- 00 disabled + elseif cmd == 0x7400 then + if len==0x08 then + tree=subtree:add(f_supportedTLVs, buf(offset,len)) + else + tree=subtree:add(buf(offset,len), "Supported TLVs?") + end + elseif cmd == 0x7800 then + if len==0x00 then + tree=subtree:add(buf(offset,len),"Serial number?") + else + local serial=buf(offset+1, 13) + tree=subtree:add(buf(offset,len),string.format("Serial number: %s", serial:string())) + tree:add(f_serial_num, serial) + end + elseif cmd == 0x9000 then + if len==0x00 then + tree=subtree:add(buf(offset,len), "Loop detection?") + else + tree=subtree:add(f_loop_detection, buf(offset,len)) + end + elseif cmd==0x9400 then + if len==0x03 then + local port=buf(offset,1) + local speed=buf(offset+1,1) + local flow=buf(offset+2,1) + tree=subtree:add(buf(offset,len), string.format("Port admin status: Port:%d, Speed:%s, Flow control:%s", port:uint(), port_admin_speed[speed:uint()], t_flow_control[flow:uint()])) + tree:add(f_port, port) + tree:add(f_port_admin_speed, speed) + tree:add(f_flow, flow) + else + tree=subtree:add(buf(offset,len), "Port admin status?") + end + else + local name=t_cmd[cmd] + if name==nil then + name=string.format("CMD:0x%04x", cmd) + end + tree=subtree:add(buf(offset,len),name) + end + tree:add(f_cmd,buf(offset-4,2)) + tree:add(f_len,buf(offset-2,2)) + tree:add(buf(offset,len),"DATA") + offset=offset+len + end else - tree=subtree:add(buf(offset,len),"FIXME") + local len=buf(offset,2):uint() + if ptype == 0x0104 then + offset = offset + 2 + subtree:add(buf(offset,len),"DATA") + offset=offset+len + end end - tree:add(f_cmd,buf(offset-4,2)) - tree:add(f_len,buf(offset-2,2)) - tree:add(buf(offset,len),"DATA") - offset=offset+len - end end function p_nsdp.init() - -- init + -- init end local tcp_dissector_table = DissectorTable.get("udp.port")