From 8c152d1d6ee05d6bdacd51c78d0ce5215f1d60e6 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Tue, 29 Sep 2020 10:45:04 +0200 Subject: [PATCH 01/41] Fix some syntax to make it work with Python3 --- psl-cli.py | 8 ++++---- psl_class.py | 4 ++-- psl_typ.py | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index 27e8e61..64e6156 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -16,8 +16,8 @@ def discover(args, switch): data = switch.discover() if data != False: for entry in data.keys(): - print entry.get_name() + ': ' + data[entry] - print "" + print(entry.get_name() + ': ' + data[entry]) + print("") else: print("No result received...") print("did you try to adjust your timeout?") @@ -48,12 +48,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): diff --git a/psl_class.py b/psl_class.py index 1ab0762..761033d 100644 --- a/psl_class.py +++ b/psl_class.py @@ -352,7 +352,7 @@ def transmit(self, cmddict, mac): 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}) + firmwarevers = list(firmwarevers.values())[0].translate({ord("."):None}) # New firmwares put capital leter V in front ... if "V" == firmwarevers[0]: firmwarevers = firmwarevers[1:] @@ -374,7 +374,7 @@ def transmit(self, cmddict, mac): if cmd != self.CMD_PASSWORD: data += self.addudp(cmd, pdata) elif type(cmddict).__name__ == 'string': - print 'got string!' + print('got string!') data += cmddict data += self.addudp(self.CMD_END) self.send(ipadr, self.SENDPORT, data) diff --git a/psl_typ.py b/psl_typ.py index cbad2ab..771ef9e 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -82,7 +82,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): From 24826d4f78c9b880a38e1ece61d1c3a495fbcdf5 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Wed, 30 Sep 2020 14:21:01 +0200 Subject: [PATCH 02/41] Listen on both broadcast and unicast addresses for replies from switch Newer switches reply directly to the client rather than via broadcast. --- psl_class.py | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/psl_class.py b/psl_class.py index 761033d..04d3b4f 100644 --- a/psl_class.py +++ b/psl_class.py @@ -8,6 +8,7 @@ import random import struct import socket +import select import fcntl import psl_typ import inspect @@ -118,8 +119,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) @@ -148,12 +150,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,11 +200,19 @@ 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: From d1c9e57a8c31b0f5be68a02a6a314589dc72c32e Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Fri, 30 Oct 2020 16:56:33 +0100 Subject: [PATCH 03/41] Command 0x7400 retrieves supported TLVs (bitfield not yet decoded) --- psl_class.py | 2 +- wireshark/nsdp.lua | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/psl_class.py b/psl_class.py index 04d3b4f..69586ae 100644 --- a/psl_class.py +++ b/psl_class.py @@ -103,7 +103,7 @@ class ProSafeLinux: 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_END = psl_typ.PslTypEnd(0xffff, "END") CTYPE_QUERY_REQUEST = 0x0101 diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 9847863..4eb45ca 100644 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -56,7 +56,7 @@ local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX,{ [0x6800] = "IGMP Snooping Status", [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", - [0x7400] = "FIMXE 0x7400 (8 Bytes)", + [0x7400] = "Supported TLVs", [0x0c00] = "Speed/Link Status", [0xffff] = "End Request" }) @@ -92,6 +92,7 @@ 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_supportedTLVs=ProtoField.uint64("nsdp.supportedtlvs","Supported TLVs",base.HEX) --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, @@ -99,7 +100,7 @@ p_nsdp.fields = {f_type,f_source,f_destination,f_seq,f_cmd,f_password,f_newpassw f_pkt,f_bpkt,f_mpkt,f_crce,f_link,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_location,f_supportedTLVs} -- nsdp dissector function function p_nsdp.dissector (buf, pkt, root) @@ -300,6 +301,12 @@ function p_nsdp.dissector (buf, pkt, root) 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 else tree=subtree:add(buf(offset,len),"FIXME") end From 1e54863754d743008cbe70757c25257723e9d5c7 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Fri, 30 Oct 2020 17:15:04 +0100 Subject: [PATCH 04/41] Make leading whitespace in nsdp.lua consistent (4 spaces per soft tab) --- wireshark/nsdp.lua | 510 ++++++++++++++++++++++----------------------- 1 file changed, 255 insertions(+), 255 deletions(-) mode change 100644 => 100755 wireshark/nsdp.lua diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua old mode 100644 new mode 100755 index 4eb45ca..3e671c1 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -2,10 +2,10 @@ p_nsdp = Proto ("nsdp","Netgear Switch Description Protocol") -- local f_source = ProtoField.uint16("nsdp.src", "Source", base.HEX) 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 f_source = ProtoField.ether("nsdp.src", "Source", base.HEX) local f_destination = ProtoField.ether("nsdp.dst", "Destination", base.HEX) @@ -13,57 +13,57 @@ 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", + [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", [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", + [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] = "Supported TLVs", - [0x0c00] = "Speed/Link Status", - [0xffff] = "End Request" + [0x6800] = "IGMP Snooping Status", + [0x6c00] = "Block Unknown Multicasts", + [0x7000] = "IGMP Header Validation", + [0x7400] = "Supported TLVs", + [0x0c00] = "Speed/Link Status", + [0xffff] = "End Request" }) local f_password = ProtoField.string("nsdp.password", "Password", FT_STRING) local f_newpassword = ProtoField.string("nsdp.newpassword", "New password", FT_STRING) local f_flags = ProtoField.uint16("nsdp.flags", "Flags", base.HEX, { - [0x000a] = "Password error" + [0x000a] = "Password error" }) local f_model =ProtoField.string("nsdp.model","Model", FT_STRING) local f_name =ProtoField.string("nsdp.name","Name", FT_STRING) @@ -78,10 +78,10 @@ local f_firmwarever = ProtoField.string("nsdp.firmwarever", "Firmware version",F 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" + [0x00]="None", + [0x01]="10M", + [0x03]="100M", + [0x05]="1000M" } local f_speed = ProtoField.uint8("nsdp.speed","Speed",base.HEX, speed_flags) local f_link = ProtoField.uint8("nsdp.link","Link",base.HEX) @@ -104,221 +104,221 @@ p_nsdp.fields = {f_type,f_source,f_destination,f_seq,f_cmd,f_password,f_newpassw -- 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 + -- 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 - 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)) + 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)) + else + tree=subtree:add(buf(offset,len),"Active Firmware?") + 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 + 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 + 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 else - tree=subtree:add(buf(offset,len),"Active Firmware?") + tree=subtree:add(buf(offset,len),"FIXME") 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 - 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 - 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 - else - tree=subtree:add(buf(offset,len),"FIXME") + 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 - 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") From d37793bd0f6970b053f347c4202d2dfd206a6f97 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Fri, 30 Oct 2020 17:36:23 +0100 Subject: [PATCH 05/41] Show command name (or number if not recognized) instead of FIXME --- wireshark/nsdp.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 3e671c1..b9340e8 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -19,8 +19,7 @@ local f_vlan_engine = ProtoField.uint8("nsdp.vlan_engine","VLAN Engine",base.HEX [0x04]="802.1Q_Extended", }) - -local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX,{ +local t_cmd = { [0x0001] = "Model", [0x0002] = "FIXME 0x0002 (2 Bytes)", [0x0003] = "Name", @@ -59,7 +58,8 @@ local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX,{ [0x7400] = "Supported TLVs", [0x0c00] = "Speed/Link Status", [0xffff] = "End Request" -}) +} +local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX, t_cmd) local f_password = ProtoField.string("nsdp.password", "Password", FT_STRING) local f_newpassword = ProtoField.string("nsdp.newpassword", "New password", FT_STRING) local f_flags = ProtoField.uint16("nsdp.flags", "Flags", base.HEX, { @@ -307,7 +307,11 @@ function p_nsdp.dissector (buf, pkt, root) tree=subtree:add(buf(offset,len), "Supported TLVs?") end else - tree=subtree:add(buf(offset,len),"FIXME") + 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)) From 836d7275a369e5a5cc6368e9ecf287f115ec23ae Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Fri, 30 Oct 2020 17:42:21 +0100 Subject: [PATCH 06/41] Fix typo --- wireshark/nsdp.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index b9340e8..7e20ef6 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -87,7 +87,7 @@ local f_speed = ProtoField.uint8("nsdp.speed","Speed",base.HEX, speed_flags) local f_link = ProtoField.uint8("nsdp.link","Link",base.HEX) 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") From 33b886b40c88c26b4c9a1bdf8150a18a718d2c34 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 11:52:01 +0100 Subject: [PATCH 07/41] Create nsdp header after assembling commands. This ensures the sequence counter is always incremental even if building a sequence requires querying other information first. --- psl_class.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/psl_class.py b/psl_class.py index 69586ae..6dce52d 100644 --- a/psl_class.py +++ b/psl_class.py @@ -366,7 +366,7 @@ 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) + data = b'' firmwarevers = self.query(self.get_cmd_by_name("firmwarever"), mac) firmwarevers = list(firmwarevers.values())[0].translate({ord("."):None}) # New firmwares put capital leter V in front ... @@ -393,6 +393,10 @@ def transmit(self, cmddict, mac): 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() while message == None and transmit_counter < 3: From ebe29a4923f34a99223b501aad76b0b1896ad6bf Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 11:55:13 +0100 Subject: [PATCH 08/41] Cache IP address directly in ip_from_mac. --- psl_class.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/psl_class.py b/psl_class.py index 6dce52d..8a8e944 100644 --- a/psl_class.py +++ b/psl_class.py @@ -335,6 +335,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" From cac46242598d66f633dd7e0571b227647f129655 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 12:34:56 +0100 Subject: [PATCH 09/41] Add decoding of errors. Some work still needs to be done on the payload handling. Sometimes there's just payload starting with the length (no cmd field), sometimes the payload is as normal and includes the cmd field. --- psl_class.py | 100 +++++++---- wireshark/nsdp.lua | 434 +++++++++++++++++++++++++-------------------- 2 files changed, 304 insertions(+), 230 deletions(-) diff --git a/psl_class.py b/psl_class.py index 8a8e944..4c56968 100644 --- a/psl_class.py +++ b/psl_class.py @@ -129,6 +129,26 @@ def __init__(self): self.mac_cache = {} self.cmd_by_id = {} self.cmd_by_name = {} + self.errmsgs = { + 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" + } for key, value in inspect.getmembers(ProSafeLinux): if key.startswith("CMD_"): self.cmd_by_name[value.get_name()] = value @@ -238,49 +258,55 @@ 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]) + errmsg = self.errmsgs[status] + if errmsg is None: + errmsg = "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"], errmsg) + 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): diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 7e20ef6..1d8c3a0 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -1,12 +1,35 @@ -- 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" }) + +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) @@ -62,9 +85,7 @@ local t_cmd = { local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX, t_cmd) local f_password = ProtoField.string("nsdp.password", "Password", FT_STRING) 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) @@ -95,7 +116,10 @@ local f_crce=ProtoField.uint64("nsdp.crc_error","CRC errors") local f_supportedTLVs=ProtoField.uint64("nsdp.supportedtlvs","Supported TLVs",base.HEX) --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_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_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, @@ -110,6 +134,8 @@ function p_nsdp.dissector (buf, pkt, root) -- 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 @@ -120,204 +146,226 @@ function p_nsdp.dissector (buf, pkt, root) end end subtree:add(f_type, buf(offset,2)) - offset = offset + 4 - subtree:add(f_flags, buf(offset,2)) - offset = offset + 4 + 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 + 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 - 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)) - else - tree=subtree:add(buf(offset,len),"Active Firmware?") - 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 - 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 - elseif cmd == 0x7400 then - if len==0x08 then - tree=subtree:add(f_supportedTLVs, buf(offset,len)) + 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 == 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)) + else + tree=subtree:add(buf(offset,len),"Active Firmware?") + 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 + 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 + 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 else - tree=subtree:add(buf(offset,len), "Supported TLVs?") + 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 - 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) + 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 + local len=buf(offset,2):uint() + if ptype == 0x0104 then + offset = offset + 2 + subtree:add(buf(offset,len),"DATA") + offset=offset+len 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 From 8b3fbc9a4feb680df6f40bf7b615924c551119bd Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 12:59:46 +0100 Subject: [PATCH 10/41] Add decoding of 'number of ports' --- wireshark/nsdp.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 1d8c3a0..1dc26f8 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -74,7 +74,7 @@ local t_cmd = { [0x5400] = "FIXME 0x5400 (1 Byte)", [0x5800] = "Broadcast Bandwidth", [0x5c00] = "Port Mirror", - [0x6000] = "Number of available Ports", + [0x6000] = "Number of available ports", [0x6800] = "IGMP Snooping Status", [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", @@ -113,6 +113,7 @@ 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_debug = ProtoField.uint8("nsdp.debug", "Debug") @@ -124,7 +125,7 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f f_pkt,f_bpkt,f_mpkt,f_crce,f_link,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_supportedTLVs} + f_speed,f_location,f_numports,f_supportedTLVs} -- nsdp dissector function function p_nsdp.dissector (buf, pkt, root) @@ -326,9 +327,12 @@ function p_nsdp.dissector (buf, pkt, root) -- 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==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) From 66cdd63ab5cf47afbd1792827140049667e92bb5 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 13:41:45 +0100 Subject: [PATCH 11/41] Add support for broadcast filtering (0x5400) --- psl_class.py | 2 +- psl_typ.py | 29 +++++++++++++++++++++++++++++ wireshark/nsdp.lua | 13 ++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/psl_class.py b/psl_class.py index 4c56968..723546f 100644 --- a/psl_class.py +++ b/psl_class.py @@ -93,7 +93,7 @@ class ProSafeLinux: 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") diff --git a/psl_typ.py b/psl_typ.py index 771ef9e..7cea349 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -147,6 +147,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 ? diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 1dc26f8..fe1b8c5 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -71,7 +71,7 @@ local t_cmd = { [0x3800] = "Portbased QOS", [0x4c00] = "Bandwidth Limit IN", [0x5000] = "Bandwidth Limit OUT", - [0x5400] = "FIXME 0x5400 (1 Byte)", + [0x5400] = "Broadcast filtering", [0x5800] = "Broadcast Bandwidth", [0x5c00] = "Port Mirror", [0x6000] = "Number of available ports", @@ -115,12 +115,17 @@ 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 f_debug = ProtoField.uint8("nsdp.debug", "Debug") 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_bcast_filtering, 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_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, @@ -304,6 +309,12 @@ function p_nsdp.dissector (buf, pkt, root) -- 0x0009 128 Mbits/s -- 0x000a 256 Mbits/s -- 0x000b 512 Mbits/s + 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==0x5c00 and len==0x03 then tree=subtree:add(buf(offset,len),"Port Mirroring") -- 00 00 00 = Disabled From 0226a69216e26198fc16e9ecacf2a6b47c387ea8 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 15:23:20 +0100 Subject: [PATCH 12/41] Speed status (0x0c00) is now port status Data returned is speed and flow control status of each port --- psl_class.py | 2 +- psl_typ.py | 46 ++++++++++++++++++++++++---------------------- wireshark/nsdp.lua | 41 ++++++++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/psl_class.py b/psl_class.py index 723546f..152062f 100644 --- a/psl_class.py +++ b/psl_class.py @@ -78,7 +78,7 @@ class ProSafeLinux: CMD_FIRMWAREACTIVE = psl_typ.PslTypHex(0x000f, "firmware_active") CMD_REBOOT = psl_typ.PslTypAction(0x0013, "reboot") 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") diff --git a/psl_typ.py b/psl_typ.py index 7cea349..72af20a 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -309,28 +309,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 @@ -338,23 +347,16 @@ 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")) 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) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index fe1b8c5..2f5db87 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -60,6 +60,7 @@ local t_cmd = { [0x000f] = "Active Firmware", [0x0013] = "Reboot", [0x0400] = "Factory Reset", + [0x0c00] = "Port status", [0x1000] = "Port Traffic Statistic", [0x1400] = "Reset Port Traffic Statistic", [0x1800] = "Test Cable", @@ -79,7 +80,6 @@ local t_cmd = { [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", [0x7400] = "Supported TLVs", - [0x0c00] = "Speed/Link Status", [0xffff] = "End Request" } local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX, t_cmd) @@ -98,14 +98,22 @@ 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={ +local t_speed_flags={ [0x00]="None", - [0x01]="10M", - [0x03]="100M", + [0x01]="10M (half-duplex)", + [0x02]="10M", + [0x03]="100M (half-duplex)", + [0x04]="100M", [0x05]="1000M" } -local f_speed = ProtoField.uint8("nsdp.speed","Speed",base.HEX, speed_flags) -local f_link = ProtoField.uint8("nsdp.link","Link",base.HEX) + +local t_flow_control={ + [0x00]="Disabled", + [0x01]="Enabled" +} + +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_port=ProtoField.uint8("nsdp.port","Port Number") local f_rec=ProtoField.uint64("nsdp.recived","Bytes received") local f_send=ProtoField.uint64("nsdp.sent","Bytes sent") @@ -127,10 +135,10 @@ 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_bcast_filtering, 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_numports,f_supportedTLVs} + f_speed,f_flow,f_location,f_numports,f_supportedTLVs} -- nsdp dissector function function p_nsdp.dissector (buf, pkt, root) @@ -224,11 +232,18 @@ function p_nsdp.dissector (buf, pkt, root) else tree=subtree:add(buf(offset,len),"Active Firmware?") 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==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)) From 294f875e43d7f7312947f4dda0e91c4f5ff4e5c9 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 16:05:43 +0100 Subject: [PATCH 13/41] Show values of 0x0002 and 0x000c in Wireshark --- wireshark/nsdp.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 2f5db87..cec8f5d 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -83,6 +83,8 @@ local t_cmd = { [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_newpassword = ProtoField.string("nsdp.newpassword", "New password", FT_STRING) local f_errcmd = ProtoField.uint16("nsdp.errcmd", "Failed command", base.HEX, t_cmd) @@ -133,6 +135,7 @@ 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_fixme0002, f_fixme000C, f_bcast_filtering, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, @@ -191,6 +194,12 @@ function p_nsdp.dissector (buf, pkt, root) 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 @@ -222,6 +231,12 @@ function p_nsdp.dissector (buf, pkt, root) -- 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 From 4dbc73604b88788c31e4a55c7fd505f3ce56287a Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 16:30:11 +0100 Subject: [PATCH 14/41] Add decoding of ingress, egress and broadcast rate limits --- wireshark/nsdp.lua | 98 ++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index cec8f5d..59076e8 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -129,6 +129,21 @@ local f_bcast_filtering=ProtoField.uint8("nsdp.bcast_filter", "Broadcast filteri [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_debug = ProtoField.uint8("nsdp.debug", "Debug") p_nsdp.experts = {e_error} @@ -136,7 +151,7 @@ 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_fixme0002, f_fixme000C, - f_bcast_filtering, + f_bcast_filtering,f_rate_limit, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, f_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, @@ -305,69 +320,48 @@ function p_nsdp.dissector (buf, pkt, root) -- 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==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==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 then if len==0x01 then tree=subtree:add(f_numports, buf(offset,len)) From cce1d1b927f4873f4af878e116a050b934d08f7f Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 16:42:00 +0100 Subject: [PATCH 15/41] Decode QoS mode --- psl_class.py | 2 +- psl_typ.py | 2 +- wireshark/nsdp.lua | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/psl_class.py b/psl_class.py index 152062f..0fdc418 100644 --- a/psl_class.py +++ b/psl_class.py @@ -87,7 +87,7 @@ class ProSafeLinux: CMD_VLAN_ID = psl_typ.PslTypVlanId(0x2400, "vlan_id") CMD_VLAN802_ID = psl_typ.PslTypVlan802Id(0x2800, "vlan802_id") 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") diff --git a/psl_typ.py b/psl_typ.py index 72af20a..430c664 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -674,7 +674,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 diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 59076e8..c85f68e 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -68,7 +68,7 @@ local t_cmd = { [0x2400] = "VLAN-ID", [0x2800] = "802VLAN-ID", [0x3000] = "vlan_pvid", - [0x3400] = "QOS", + [0x3400] = "QoS", [0x3800] = "Portbased QOS", [0x4c00] = "Bandwidth Limit IN", [0x5000] = "Bandwidth Limit OUT", @@ -116,6 +116,10 @@ local t_flow_control={ 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 f_port=ProtoField.uint8("nsdp.port","Port Number") local f_rec=ProtoField.uint64("nsdp.recived","Bytes received") local f_send=ProtoField.uint64("nsdp.sent","Bytes sent") @@ -151,6 +155,7 @@ 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_fixme0002, f_fixme000C, + f_qos_mode, f_bcast_filtering,f_rate_limit, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, @@ -308,10 +313,12 @@ function p_nsdp.dissector (buf, pkt, root) 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==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 and len==0x01 then tree=subtree:add(buf(offset,len),"Port Based Quality of Service") -- 1 Byte port From 14aa1e1ce4d3e5021f5c2109cdee542361f4635c Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 31 Oct 2020 16:52:16 +0100 Subject: [PATCH 16/41] Add decoding of QoS port-based priorities --- wireshark/nsdp.lua | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index c85f68e..8800261 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -69,7 +69,7 @@ local t_cmd = { [0x2800] = "802VLAN-ID", [0x3000] = "vlan_pvid", [0x3400] = "QoS", - [0x3800] = "Portbased QOS", + [0x3800] = "QoS port priority", [0x4c00] = "Bandwidth Limit IN", [0x5000] = "Bandwidth Limit OUT", [0x5400] = "Broadcast filtering", @@ -120,6 +120,13 @@ 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.sent","Bytes sent") @@ -155,7 +162,7 @@ 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_fixme0002, f_fixme000C, - f_qos_mode, + f_qos_mode, f_qos_port_prio, f_bcast_filtering,f_rate_limit, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, @@ -319,14 +326,17 @@ function p_nsdp.dissector (buf, pkt, root) else tree=subtree:add(f_qos_mode, buf(offset,len)) end - 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==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) + + 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?") From 3a9ddcdb904bb7cda4f755ea295a5f29001a35da Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 11:11:39 +0100 Subject: [PATCH 17/41] Add 'port_admin' command (0x9400) This allows the maximum speed and flow control to be set/queried --- psl_class.py | 1 + psl_typ.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++ wireshark/nsdp.lua | 24 ++++++++++++- 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/psl_class.py b/psl_class.py index 0fdc418..c703454 100644 --- a/psl_class.py +++ b/psl_class.py @@ -104,6 +104,7 @@ class ProSafeLinux: CMD_IGMP_HEADER_VALIDATION = psl_typ.PslTypBoolean(0x7000, "igmp_header_validation") CMD_SUPPORTED_TLVS = psl_typ.PslTypHex(0x7400, "supported_tlvs") + CMD_PORT_ADMIN = psl_typ.PslTypAdminPortStatus(0x9400, "port_admin") CMD_END = psl_typ.PslTypEnd(0xffff, "END") CTYPE_QUERY_REQUEST = 0x0101 diff --git a/psl_typ.py b/psl_typ.py index 430c664..80ed251 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -365,6 +365,91 @@ def unpack_cmd(self, 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")) + 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 + + +################################################################################ + + class PslTypPortStat(PslTyp): "how many bytes are received/send on each port" def unpack_py(self, val): diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 8800261..9ca8ca4 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -80,6 +80,7 @@ local t_cmd = { [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", [0x7400] = "Supported TLVs", + [0x9400] = "Port Admin Status", [0xffff] = "End Request" } local f_cmd = ProtoField.uint16("nsdp.cmd", "Command", base.HEX, t_cmd) @@ -155,6 +156,15 @@ local t_rate_limit={ [0x0b]="512 Mbits/s", } local f_rate_limit=ProtoField.uint8("nsdp.rate_limit", "Rate", base.HEX, t_rate_limit) +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_debug = ProtoField.uint8("nsdp.debug", "Debug") p_nsdp.experts = {e_error} @@ -163,7 +173,7 @@ 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_fixme0002, f_fixme000C, f_qos_mode, f_qos_port_prio, - f_bcast_filtering,f_rate_limit, + f_bcast_filtering,f_rate_limit,f_port_admin_speed, f_model,f_name,f_macinfo,f_dhcp_enable,f_port,f_rec,f_send, f_pkt,f_bpkt,f_mpkt,f_crce,f_vlan_engine,f_ipaddr, f_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, @@ -403,6 +413,18 @@ function p_nsdp.dissector (buf, pkt, root) else tree=subtree:add(buf(offset,len), "Supported TLVs?") 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 From 6f25ff818fac411186da0dab4734d9e15d7fc480 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 11:50:12 +0100 Subject: [PATCH 18/41] Add 'loop detection' (0x9000) --- psl_class.py | 1 + wireshark/nsdp.lua | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/psl_class.py b/psl_class.py index c703454..eae4e27 100644 --- a/psl_class.py +++ b/psl_class.py @@ -104,6 +104,7 @@ class ProSafeLinux: CMD_IGMP_HEADER_VALIDATION = psl_typ.PslTypBoolean(0x7000, "igmp_header_validation") CMD_SUPPORTED_TLVS = psl_typ.PslTypHex(0x7400, "supported_tlvs") + 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") diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 9ca8ca4..2c258e4 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -80,6 +80,7 @@ local t_cmd = { [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", [0x7400] = "Supported TLVs", + [0x9000] = "Loop detection", [0x9400] = "Port Admin Status", [0xffff] = "End Request" } @@ -165,6 +166,10 @@ local port_admin_speed={ [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_debug = ProtoField.uint8("nsdp.debug", "Debug") p_nsdp.experts = {e_error} @@ -178,7 +183,7 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f 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_flow,f_location,f_numports,f_supportedTLVs} + f_speed,f_flow,f_location,f_numports,f_supportedTLVs,f_loop_detection} -- nsdp dissector function function p_nsdp.dissector (buf, pkt, root) @@ -413,6 +418,12 @@ function p_nsdp.dissector (buf, pkt, root) else tree=subtree:add(buf(offset,len), "Supported TLVs?") 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) From d8e7f913da52453bb2f6d8bcbfb820de4ba836cf Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 12:05:32 +0100 Subject: [PATCH 19/41] Decode port mirroring --- psl_typ.py | 8 ++++---- wireshark/nsdp.lua | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index 80ed251..ca7c4a8 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -954,10 +954,10 @@ def unpack_py(self, value): 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) + src_ports = 0 + for sport in value[1].split(","): + src_ports += self.BIN_PORTS[int(sport)] + return struct.pack(">bbb", int(value[0]), 0, src_ports) def unpack_cmd(self, value): return self.unpack_py(value) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 2c258e4..3d65329 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -157,6 +157,8 @@ local t_rate_limit={ [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 port_admin_speed={ [0x00]="Disabled", [0x01]="Auto", @@ -179,12 +181,44 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f f_fixme0002, f_fixme000C, f_qos_mode, f_qos_port_prio, 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_vlan_engine,f_ipaddr, f_netmask,f_gateway,f_firmwarever_len,f_firmwarever,f_len, f_firmware2ver, f_firmwareactive, f_speed,f_flow,f_location,f_numports,f_supportedTLVs,f_loop_detection} +-- Build a condensed string of port ranges from a bitmap +function port_list(ports) + local list = {} + local lastPort = -1 + local firstPort = -1 + for i=1,9 do + if bit32.band(ports,0x80) ~= 0 then + if lastPort ~= (i - 1) then + list[#list+1] = "," + list[#list+1] = i + firstPort = i + end + lastPort = i + 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 + + 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 @@ -389,11 +423,11 @@ function p_nsdp.dissector (buf, pkt, root) tree:add(f_rate_limit, rate) end 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) + local dest=buf(offset,1) + local src=buf(offset+2,1) + tree=subtree:add(buf(offset,len), string.format("Port mirroring: Source port(s):%s, Dest port:%u", port_list(src:uint()), 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)) From c736388c55d703edb6db912eeafde7cc794fa98c Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 14:45:11 +0100 Subject: [PATCH 20/41] Add decoding of vlan802_id (0x2800) and vlan_pvid (0x3000) --- wireshark/nsdp.lua | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 3d65329..2ea5fa6 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -159,6 +159,9 @@ local t_rate_limit={ 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_vlan = ProtoField.uint16("nsdp.vlan", "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 port_admin_speed={ [0x00]="Disabled", [0x01]="Auto", @@ -180,6 +183,7 @@ 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_fixme0002, f_fixme000C, f_qos_mode, f_qos_port_prio, + f_vlan, f_802_1q_ports, f_802_1q_tagged, 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, @@ -361,14 +365,19 @@ function p_nsdp.dissector (buf, pkt, root) 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 + local vlan=buf(offset,2) + local ports=buf(offset+2,1) + local tagged=buf(offset+3,1) + tree=subtree:add(buf(offset,len), string.format("802.1q status: VLAN:%u, Ports:%s, Tagged:%s", vlan:uint(), port_list(ports:uint()), port_list(tagged:uint()))) + tree:add(f_vlan, vlan) + tree:add(f_802_1q_ports, ports) + tree:add(f_802_1q_tagged, tagged) 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 + 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_vlan, vlan) elseif cmd==0x3400 then if len==0x00 then tree=subtree:add(buf(offset,len),"QoS mode?") From bb11e55057b3046cfabc7e5ca56223ff057bb56d Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 15:08:02 +0100 Subject: [PATCH 21/41] Add 'delete vlan' (0x2c00) --- psl_class.py | 1 + psl_typ.py | 28 ++++++++++++++++++++++++++++ wireshark/nsdp.lua | 3 +++ 3 files changed, 32 insertions(+) diff --git a/psl_class.py b/psl_class.py index eae4e27..182a4d2 100644 --- a/psl_class.py +++ b/psl_class.py @@ -86,6 +86,7 @@ class ProSafeLinux: 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.PslTypVlan(0x2c00, "delete_vlan") CMD_VLANPVID = psl_typ.PslTypVlanPVID(0x3000, "vlan_pvid") CMD_QUALITY_OF_SERVICE = psl_typ.PslTypQosMode(0x3400, "qos_mode") CMD_PORT_BASED_QOS = psl_typ.PslTypPortBasedQOS(0x3800, "port_based_qos") diff --git a/psl_typ.py b/psl_typ.py index ca7c4a8..af2b21d 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -752,6 +752,34 @@ def print_result(self, value): def get_set_help(self): return "an untagged package on PORT will get this VLAN_ID" + + +################################################################################ + +class PslTypVlan(PslTyp): + "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 + ################################################################################ diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 2ea5fa6..09a73ef 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -67,6 +67,7 @@ local t_cmd = { [0x2000] = "VLAN Engine", [0x2400] = "VLAN-ID", [0x2800] = "802VLAN-ID", + [0x2c00] = "Delete VLAN", [0x3000] = "vlan_pvid", [0x3400] = "QoS", [0x3800] = "QoS port priority", @@ -372,6 +373,8 @@ function p_nsdp.dissector (buf, pkt, root) tree:add(f_vlan, vlan) tree:add(f_802_1q_ports, ports) tree:add(f_802_1q_tagged, tagged) + elseif cmd==0x2c00 then + tree=subtree:add(f_vlan, buf(offset,2)) elseif cmd==0x3000 and len==0x03 then local port=buf(offset,1) local vlan=buf(offset+1,2) From a389a06424356abda32f66b924fce7b1caecbc73 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 1 Nov 2020 15:27:58 +0100 Subject: [PATCH 22/41] Allow creation of a new VLAN without assigning any ports --- psl_typ.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index af2b21d..167a00b 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -584,7 +584,8 @@ def get_set_help(self): class PslTypVlanId(PslTyp): "Vlan ports are binary coded" - BIN_PORTS = {1: 0x80, + BIN_PORTS = {0: 0x00, + 1: 0x80, 2: 0x40, 3: 0x20, 4: 0x10, @@ -686,16 +687,16 @@ def get_metavar(self): return ("VLAN_ID", "TAGGED_PORTS", "UNTAGGED_PORTS") def print_result(self, value): - print("%-30s%7s %14s %s" % (self.get_name().capitalize(), "VLAN_ID", + print("%-30s%7s %18s %18s" % (self.get_name().capitalize(), "VLAN_ID", "Tagged-Ports","Untagged-Ports")) if type(value) is list: for row in value: - print("%-30s%7d %14s %s" % ("", + print("%-30s%7d %18s %18s" % ("", 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" % ("", + print("%-30s%7d %18s %18s" % ("", int(value["vlan_id"]), ",".join([str(x) for x in value["tagged_ports"]]), ",".join([str(x) for x in value["untagged_ports"]]))) From 79191bbc0e8315cdba6dc8585dd47aa8312b166c Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 7 Nov 2020 15:08:40 +0100 Subject: [PATCH 23/41] Rename parameters of vlan802_id (0x2800) The second parameter contains all the ports that should be part of the VLAN. The third parameter contains which of those ports are tagged. --- psl_typ.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index 167a00b..7a5c5ba 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -651,30 +651,30 @@ class PslTypVlan802Id(PslTypVlanId): 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] + member_ports = struct.unpack(">B", value[2])[0] + tagged_ports = struct.unpack(">B", value[3])[0] else: - tagged_ports = value[2] - untagged_ports = value[3] + member_ports = value[2] + tagged_ports = value[3] + out_member_ports = [] out_tagged_ports = [] - out_untagged_ports = [] for port in list(self.BIN_PORTS.keys()): + if (member_ports & self.BIN_PORTS[port] > 0): + out_member_ports.append(port) 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) 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]) + members = self.pack_port(value[1]) untagged = self.pack_port(value[2]) - rtn = struct.pack(">hBB", int(value[0]), tagged, untagged) + rtn = struct.pack(">hBB", int(value[0]), members, untagged) return rtn def unpack_cmd(self, value): @@ -684,22 +684,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 %18s %18s" % (self.get_name().capitalize(), "VLAN_ID", - "Tagged-Ports","Untagged-Ports")) + "Member ports","Tagged ports")) if type(value) is list: for row in value: print("%-30s%7d %18s %18s" % ("", int(row["vlan_id"]), - ",".join([str(x) for x in row["tagged_ports"]]), - ",".join([str(x) for x in row["untagged_ports"]]))) + ",".join([str(x) for x in row["member_ports"]]), + ",".join([str(x) for x in row["tagged_ports"]]))) else: print("%-30s%7d %18s %18s" % ("", int(value["vlan_id"]), - ",".join([str(x) for x in value["tagged_ports"]]), - ",".join([str(x) for x in value["untagged_ports"]]))) + ",".join([str(x) for x in value["member_ports"]]), + ",".join([str(x) for x in value["tagged_ports"]]))) From 32f77db078e9b2dfbb6a8fc880f4e5a8ccbbd4a5 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 5 Dec 2020 13:37:07 +0100 Subject: [PATCH 24/41] Add support of new password mechanism --- psl_class.py | 200 ++++++++++++++++++++++++++++++++++++++++----- wireshark/nsdp.lua | 25 ++++++ 2 files changed, 206 insertions(+), 19 deletions(-) diff --git a/psl_class.py b/psl_class.py index 182a4d2..4220ba1 100644 --- a/psl_class.py +++ b/psl_class.py @@ -69,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") @@ -77,6 +77,9 @@ 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_PORT_STATUS = psl_typ.PslTypPortStatus(0x0c00, "port_status") CMD_PORT_STAT = psl_typ.PslTypPortStat(0x1000, "port_stat") @@ -114,6 +117,11 @@ class ProSafeLinux: CTYPE_TRANSMIT_REQUEST = 0x103 # CTYPE_TRANSMIT_RESPONSE = 0x104 + ENCTYPE_NONE = 0x00 + ENCTYPE_SIMPLE = 0x01 + ENCTYPE_HASH32 = 0x08 + ENCTYPE_HASH64 = 0x10 + RECPORT = 63321 SENDPORT = 63322 @@ -398,27 +406,24 @@ def transmit(self, cmddict, mac): transmit_counter = 0 ipadr = self.ip_from_mac(mac) data = b'' - firmwarevers = self.query(self.get_cmd_by_name("firmwarever"), mac) - firmwarevers = list(firmwarevers.values())[0].translate({ord("."):None}) - # New firmwares put capital leter V in front ... - if "V" == firmwarevers[0]: - firmwarevers = firmwarevers[1:] 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) for cmd, pdata in list(cmddict.items()): - if cmd != self.CMD_PASSWORD: + if cmd == self.CMD_PASSWORD: + result = self.add_password(mac, cmddict[cmd]) + + if type(result).__name__ == 'dict': + return result + + data += result; + elif cmd == self.CMD_NEW_PASSWORD: + result = self.add_new_password(mac, cmddict[cmd]) + + if type(result).__name__ == 'dict': + return result + + data += result; + else: data += self.addudp(cmd, pdata) elif type(cmddict).__name__ == 'string': print('got string!') @@ -438,6 +443,163 @@ def transmit(self, cmddict, mac): 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_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 diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 09a73ef..c3c1eb0 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -59,6 +59,10 @@ local t_cmd = { [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", @@ -89,6 +93,10 @@ 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_errcmd = ProtoField.uint16("nsdp.errcmd", "Failed command", base.HEX, t_cmd) local f_model =ProtoField.string("nsdp.model","Model", FT_STRING) @@ -182,6 +190,7 @@ 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_vlan, f_802_1q_ports, f_802_1q_tagged, @@ -328,6 +337,22 @@ function p_nsdp.dissector (buf, pkt, root) 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) From 50a88afdc818fe7653aa54f0b0f1d3b427bcf55f Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 14 Mar 2021 14:15:35 +0100 Subject: [PATCH 25/41] Make python3 the default interpreter --- psl-cli.py | 2 +- psl-cmd.py | 2 +- psl_class.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index 64e6156..f8f853b 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" 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 4220ba1..3a70492 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" From 5841d052aad2418c62b4b56074022540e22146f9 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 14 Mar 2021 14:22:29 +0100 Subject: [PATCH 26/41] Remove trailing whitespace --- psl-cli.py | 8 ++++---- psl_class.py | 6 +++--- psl_typ.py | 38 +++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index f8f853b..7d9e267 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -27,7 +27,7 @@ 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" cmds = {ProSafeLinux.CMD_PASSWORD: args.passwd[0]} @@ -148,7 +148,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 +165,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,7 +187,7 @@ def main(): dest=cmd.get_name(), action='store_true') else: - set_parser.add_argument("--" + cmd.get_name(), + set_parser.add_argument("--" + cmd.get_name(), nargs=cmd.get_num_args(), type=cmd.get_set_type(), help=cmd.get_set_help(), diff --git a/psl_class.py b/psl_class.py index 3a70492..fe0c3c3 100644 --- a/psl_class.py +++ b/psl_class.py @@ -249,7 +249,7 @@ def recv(self, maxlen=8192, sock=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: @@ -314,7 +314,7 @@ def parse_data(self, pack): elif value != None: data[cmd] = value if self.debug: - print("cmd_id %d of length %d :" % (cmd_id, cmdlen)) + 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 @@ -606,7 +606,7 @@ def passwd_exploit(self, mac, new): 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, diff --git a/psl_typ.py b/psl_typ.py index 7a5c5ba..efbef86 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -58,11 +58,11 @@ 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 @@ -553,7 +553,7 @@ 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")) @@ -606,7 +606,7 @@ def unpack_py(self, value): "ports": out_ports } return rtn - + def pack_port(self, ports): "helper method to pack ports to binary" rtn = 0 @@ -623,7 +623,7 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True @@ -669,7 +669,7 @@ def unpack_py(self, value): "tagged_ports": out_tagged_ports } return rtn - + def pack_py(self, value): members = self.pack_port(value[1]) @@ -700,9 +700,9 @@ def print_result(self, value): int(value["vlan_id"]), ",".join([str(x) for x in value["member_ports"]]), ",".join([str(x) for x in value["tagged_ports"]]))) - - + + ################################################################################ @@ -801,7 +801,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) @@ -811,19 +811,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", @@ -856,7 +856,7 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True @@ -865,7 +865,7 @@ 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" @@ -890,7 +890,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) @@ -898,10 +898,10 @@ def pack_py(self, value): def unpack_cmd(self, value): return self.unpack_py(value) - + def is_setable(self): return True - + ################################################################################ @@ -987,7 +987,7 @@ def pack_py(self, value): for sport in value[1].split(","): src_ports += self.BIN_PORTS[int(sport)] return struct.pack(">bbb", int(value[0]), 0, src_ports) - + def unpack_cmd(self, value): return self.unpack_py(value) From dcc76ed3759217728984fcb4c342dabe9876fd4c Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 12:13:16 +0100 Subject: [PATCH 27/41] Decode cable testing --- psl_class.py | 4 +-- psl_typ.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++ wireshark/nsdp.lua | 38 ++++++++++++++++++--- 3 files changed, 119 insertions(+), 7 deletions(-) diff --git a/psl_class.py b/psl_class.py index fe0c3c3..5318ea6 100644 --- a/psl_class.py +++ b/psl_class.py @@ -84,8 +84,8 @@ class ProSafeLinux: 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") diff --git a/psl_typ.py b/psl_typ.py index efbef86..d4534e3 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -1002,3 +1002,87 @@ def get_metavar(self): def get_set_help(self): return "SET DST_PORTS 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)"))) + if type(value) == type(list()): + for row in value: + print(("%-30s%4d%20s%19u" % ("", row["port"], self.status_to_string[row["status"]], row["dist"]))) + else: + print(("%-30s%4d%20s%19u" % ("", value["port"], self.status_to_string[value["status"]], value["dist"]))) + + + def unpack_cmd(self, value): + return self.unpack_py(value) + diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index c3c1eb0..379eebd 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -68,6 +68,7 @@ local t_cmd = { [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", @@ -171,6 +172,21 @@ local f_port_mirror_dest=ProtoField.uint8("nsdp.port_mirror_dest", "Destination local f_vlan = ProtoField.uint16("nsdp.vlan", "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", @@ -194,6 +210,7 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f f_fixme0002, f_fixme000C, f_qos_mode, f_qos_port_prio, f_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, @@ -378,16 +395,27 @@ function p_nsdp.dissector (buf, pkt, root) 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") + 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 - -- 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 + 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==0x2800 and len==0x04 then From 380baf23447829dc98ede7f3fb4c8af08e8f9023 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 13:41:00 +0100 Subject: [PATCH 28/41] Encapsulate errors in a separate class This makes it easier to use the ProSafeLinux class from other code and perform additional error handling as required. --- psl_class.py | 54 +++++++++++++++++++++++++++++----------------------- psl_typ.py | 16 ++++++++++++++++ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/psl_class.py b/psl_class.py index 5318ea6..605c77d 100644 --- a/psl_class.py +++ b/psl_class.py @@ -112,6 +112,25 @@ class ProSafeLinux: 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 @@ -140,31 +159,15 @@ def __init__(self): self.mac_cache = {} self.cmd_by_id = {} self.cmd_by_name = {} - self.errmsgs = { - 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" - } + 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 @@ -273,16 +276,19 @@ def parse_data(self, pack): status = struct.unpack(">B", pack[2:3])[0] if status != 0x00: errorcmd = self.get_cmd_by_hex(struct.unpack(">H", pack[4:6])[0]) - errmsg = self.errmsgs[status] - if errmsg is None: - errmsg = "0x{:02x}".format(status) + + 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["error"] = "{} - {}".format(data["error"], errmsg) + 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] diff --git a/psl_typ.py b/psl_typ.py index d4534e3..69e8592 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): From 4ff1d22f310bcd3ddfe004aacf908c99645ab896 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 13:49:29 +0100 Subject: [PATCH 29/41] Allow ports to be passed as a list as well as a comma-separated string --- psl_typ.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index 69e8592..4fbef30 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -628,8 +628,12 @@ def pack_port(self, ports): rtn = 0 if ports == "": return rtn - for port in ports.split(","): - rtn = rtn + self.BIN_PORTS[int(port)] + if type(ports) is list: + for port in ports: + rtn |= self.BIN_PORTS[int(port)] + else: + for port in ports.split(","): + rtn |= self.BIN_PORTS[int(port)] return rtn def pack_py(self, value): @@ -1000,8 +1004,12 @@ def pack_py(self, value): if int(value[0]) == 0: return struct.pack(">bbb", 0, 0, 0) src_ports = 0 - for sport in value[1].split(","): - src_ports += self.BIN_PORTS[int(sport)] + if type(value[1]) is list: + for sport in value[1]: + src_ports |= self.BIN_PORTS[int(sport)] + else: + for sport in value[1].split(","): + src_ports |= self.BIN_PORTS[int(sport)] return struct.pack(">bbb", int(value[0]), 0, src_ports) def unpack_cmd(self, value): From 14ca35ccf8ce6578521026c13ae4c66c23256dcc Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 15:53:15 +0100 Subject: [PATCH 30/41] Improve Wireshark decoding of vlan_pvid, delete_vlan and vlan802_id queries/results --- wireshark/nsdp.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 379eebd..fb561f6 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -169,7 +169,10 @@ local t_rate_limit={ 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_vlan = ProtoField.uint16("nsdp.vlan", "VLAN") + +local f_pvid_vlan=ProtoField.uint16("nsdp.pvid_vlan", "VLAN") +local f_del_vlan=ProtoField.uint16("nsdp.del_vlan", "Delete VLAN") +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) @@ -209,7 +212,8 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f f_enhancedEncryption,f_passwordNonce,f_passhash32, f_passhash64, f_fixme0002, f_fixme000C, f_qos_mode, f_qos_port_prio, - f_vlan, f_802_1q_ports, f_802_1q_tagged, + f_pvid_vlan, f_del_vlan, + 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, @@ -423,17 +427,17 @@ function p_nsdp.dissector (buf, pkt, root) local ports=buf(offset+2,1) local tagged=buf(offset+3,1) tree=subtree:add(buf(offset,len), string.format("802.1q status: VLAN:%u, Ports:%s, Tagged:%s", vlan:uint(), port_list(ports:uint()), port_list(tagged:uint()))) - tree:add(f_vlan, vlan) + 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_vlan, buf(offset,2)) + 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_vlan, vlan) + tree:add(f_pvid_vlan, vlan) elseif cmd==0x3400 then if len==0x00 then tree=subtree:add(buf(offset,len),"QoS mode?") From 63fdf4f6d3c7b1c8a8db5b9e8afe42a8f013b684 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 17:23:18 +0100 Subject: [PATCH 31/41] Add support for querying a switch's serial number First byte always seems to 1 The next thirteen bytes are the serial number in ASCII Then zero byte (null-terminator perhaps?) Then six unknown bytes - different from one switch to another but consistent within one switch --- psl_class.py | 1 + psl_typ.py | 18 ++++++++++++++++++ wireshark/nsdp.lua | 14 +++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/psl_class.py b/psl_class.py index 605c77d..2482a17 100644 --- a/psl_class.py +++ b/psl_class.py @@ -108,6 +108,7 @@ class ProSafeLinux: CMD_IGMP_HEADER_VALIDATION = psl_typ.PslTypBoolean(0x7000, "igmp_header_validation") 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") diff --git a/psl_typ.py b/psl_typ.py index 4fbef30..8f8edcf 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -1110,3 +1110,21 @@ def print_result(self, value): 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 index fb561f6..19849c7 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -86,6 +86,7 @@ local t_cmd = { [0x6c00] = "Block Unknown Multicasts", [0x7000] = "IGMP Header Validation", [0x7400] = "Supported TLVs", + [0x7800] = "Serial number", [0x9000] = "Loop detection", [0x9400] = "Port Admin Status", [0xffff] = "End Request" @@ -204,6 +205,8 @@ local f_loop_detection = ProtoField.uint8("nsdp.loop_detection", "Loop detection [0x01]="Enabled" }) +local f_serial_num = ProtoField.string("nsdp.serialnum", "Serial number", FT_STRING) + --local f_debug = ProtoField.uint8("nsdp.debug", "Debug") p_nsdp.experts = {e_error} @@ -221,7 +224,8 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f 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_flow,f_location,f_numports,f_supportedTLVs,f_loop_detection} + 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(ports) @@ -521,6 +525,14 @@ function p_nsdp.dissector (buf, pkt, root) 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?") From bdd8f55a67f2b5930377ed3a055bd29a9a6fc019 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Thu, 25 Mar 2021 18:02:26 +0100 Subject: [PATCH 32/41] Allow commands to be set multiple times in one transaction. This allows settings on a per-port or per-vlan basis to be set in one go rather than having to call the program multiple times. --- psl-cli.py | 6 +++++- psl_class.py | 14 +++++++++++--- psl_typ.py | 11 +++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index 7d9e267..cbcb615 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -187,12 +187,16 @@ def main(): dest=cmd.get_name(), action='store_true') else: + 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_class.py b/psl_class.py index 2482a17..105cd28 100644 --- a/psl_class.py +++ b/psl_class.py @@ -89,7 +89,7 @@ class ProSafeLinux: 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.PslTypVlan(0x2c00, "delete_vlan") + CMD_DEL_VLAN = psl_typ.PslTypDeleteVlan(0x2c00, "delete_vlan") CMD_VLANPVID = psl_typ.PslTypVlanPVID(0x3000, "vlan_pvid") CMD_QUALITY_OF_SERVICE = psl_typ.PslTypQosMode(0x3400, "qos_mode") CMD_PORT_BASED_QOS = psl_typ.PslTypPortBasedQOS(0x3800, "port_based_qos") @@ -431,7 +431,15 @@ def transmit(self, cmddict, mac): data += result; else: - data += self.addudp(cmd, pdata) + 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!') data += cmddict @@ -508,7 +516,7 @@ def add_password(self, mac, password): _hashpass[idx] ^= ord(password[i]) _hashpass = struct.pack(">BBBB", *_hashpass) - data = self.addudp(self.CMD_HASH, binascii.hexlify(_hashpass)) + data = self.addudp(self.CMD_PASSWORD_HASH, binascii.hexlify(_hashpass)) else: _hashpass += _hashpass; diff --git a/psl_typ.py b/psl_typ.py index 8f8edcf..b77d289 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -83,6 +83,10 @@ 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 + ############################################################################### @@ -777,8 +781,8 @@ def get_set_help(self): ################################################################################ -class PslTypVlan(PslTyp): - "Vlan" +class PslTypDeleteVlan(PslTyp): + "Delete vlan" def unpack_py(self, value): return struct.unpack(">h", value)[0] @@ -801,6 +805,9 @@ def is_setable(self): def get_num_args(self): return 1 + def allow_multiple(self): + return True + ################################################################################ From 6ec672a8a24ecf8e94f522e0d0eb1ba5a0d74293 Mon Sep 17 00:00:00 2001 From: Mario Haustein Date: Sun, 5 Dec 2021 12:00:48 +0100 Subject: [PATCH 33/41] Support discover multiple devices --- psl-cli.py | 7 ++++--- psl_class.py | 30 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index cbcb615..e7132e9 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -13,12 +13,13 @@ def discover(args, switch): "Search for Switches" print("Searching for ProSafe Plus Switches ...\n") - data = switch.discover() - if data != False: + for data in switch.discover(): + found = True for entry in data.keys(): print(entry.get_name() + ': ' + data[entry]) print("") - else: + + if not found: print("No result received...") print("did you try to adjust your timeout?") diff --git a/psl_class.py b/psl_class.py index 105cd28..875383f 100644 --- a/psl_class.py +++ b/psl_class.py @@ -266,8 +266,8 @@ 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" @@ -402,12 +402,24 @@ 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 @@ -449,10 +461,10 @@ def transmit(self, cmddict, mac): 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' } @@ -629,10 +641,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" From 53737b7a3a113821f71310e216b70642e8e57d8d Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Tue, 28 Jun 2022 18:57:06 +0200 Subject: [PATCH 34/41] Fix setting a single multi-value option Multi-value options usually make a change to a single port or VLAN so it makes sense to allow them to be specified multiple times so that multiple ports or VLANs can be specified in a single call. Without this fix, the program will crash if only a single multi-value option is passed --- psl-cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psl-cli.py b/psl-cli.py index e7132e9..ff82746 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -41,7 +41,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()] From d6f235a317ea4b8a9d76038c47832767188d7931 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Fri, 1 Jul 2022 16:54:37 +0200 Subject: [PATCH 35/41] Add missing Wireshark decoding of VLAN_ID --- wireshark/nsdp.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 19849c7..82dcab8 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -173,6 +173,8 @@ local f_port_mirror_dest=ProtoField.uint8("nsdp.port_mirror_dest", "Destination 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) @@ -216,6 +218,7 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f 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, @@ -426,6 +429,12 @@ function p_nsdp.dissector (buf, pkt, root) 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,1) + tree=subtree:add(buf(offset,len), string.format("VLAN status: VLAN:%u, Ports:%s", vlan:uint(), port_list(ports:uint()))) + tree:add(f_vlan, vlan) + tree:add(f_vlan_ports, ports) elseif cmd==0x2800 and len==0x04 then local vlan=buf(offset,2) local ports=buf(offset+2,1) From b59c169a6317d055b4c2eb9bf87a98c6ed678887 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sat, 2 Jul 2022 20:01:22 +0200 Subject: [PATCH 36/41] Fix variable name for tagged ports (802.1q) --- psl_typ.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index b77d289..a40256a 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -697,8 +697,8 @@ def unpack_py(self, value): def pack_py(self, value): members = self.pack_port(value[1]) - untagged = self.pack_port(value[2]) - rtn = struct.pack(">hBB", int(value[0]), members, untagged) + tagged = self.pack_port(value[2]) + rtn = struct.pack(">hBB", int(value[0]), members, tagged) return rtn def unpack_cmd(self, value): From a61c342caa8f3fa3ec8d99ef633dca46d234893a Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 3 Jul 2022 14:25:57 +0200 Subject: [PATCH 37/41] Make sure that the password (hash) is transmitted before anything else. This mainly affects older version of Python that don't have ordered dicts by default. --- psl_class.py | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/psl_class.py b/psl_class.py index 875383f..7b5a104 100644 --- a/psl_class.py +++ b/psl_class.py @@ -427,31 +427,34 @@ def transmit(self, cmddict, mac): data = b'' if type(cmddict).__name__ == 'dict': - for cmd, pdata in list(cmddict.items()): - if cmd == self.CMD_PASSWORD: - result = self.add_password(mac, cmddict[cmd]) + if self.CMD_PASSWORD in cmddict: + result = self.add_password(mac, cmddict[self.CMD_PASSWORD]) + + if type(result).__name__ == 'dict': + return result + + data += result; + del cmddict[self.CMD_PASSWORD] - if type(result).__name__ == 'dict': - return result + if self.CMD_NEW_PASSWORD in cmddict: + result = self.add_new_password(mac, cmddict[self.CMD_NEW_PASSWORD]) - data += result; - elif cmd == self.CMD_NEW_PASSWORD: - result = self.add_new_password(mac, cmddict[cmd]) + if type(result).__name__ == 'dict': + return result - if type(result).__name__ == 'dict': - return result + data += result; + del cmddict[self.CMD_NEW_PASSWORD] - data += result; + for cmd, pdata in list(cmddict.items()): + 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: - 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) + data += self.addudp(cmd, pdata) elif type(cmddict).__name__ == 'string': print('got string!') data += cmddict From b4fcd57dfac682be45e1736b2bf14d3241534d38 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 3 Jul 2022 14:31:16 +0200 Subject: [PATCH 38/41] Report number of ports in decimal rather than hex --- psl_class.py | 2 +- psl_typ.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/psl_class.py b/psl_class.py index 7b5a104..4017b4b 100644 --- a/psl_class.py +++ b/psl_class.py @@ -101,7 +101,7 @@ class ProSafeLinux: 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") diff --git a/psl_typ.py b/psl_typ.py index a40256a..2f88816 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -298,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): From c43bb8c32b648565f498e6a2aea1566e11b77555 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 3 Jul 2022 14:46:22 +0200 Subject: [PATCH 39/41] Fix support for switches with more than 8 ports Some commands (VLAN, 802.1q, port mirroring) use a bitmap to represent affected ports. The length of the data will therefore vary as a 16-port switch will require two bytes (16 bits). Until now the code assumed 1 byte per parameter (i.e. up to 8 ports) Parsing queried data is not too difficult as the code can work out the length of each parameter based on the length of the received data. Setting data is trickier as the command has to know how many ports the switch supports but the code was written to be stateless. If one of the affected commands is detected (for setting), the number of ports is first queried and then passed on to all the affected commands. --- psl-cli.py | 6 ++ psl_typ.py | 163 +++++++++++++++++++++++++++++---------------- wireshark/nsdp.lua | 60 +++++++++-------- 3 files changed, 145 insertions(+), 84 deletions(-) diff --git a/psl-cli.py b/psl-cli.py index ff82746..0300040 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -31,6 +31,12 @@ def exploit(args, switch): 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: diff --git a/psl_typ.py b/psl_typ.py index 2f88816..a1b60f7 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -622,8 +622,7 @@ def get_set_help(self): class PslTypVlanId(PslTyp): "Vlan ports are binary coded" - BIN_PORTS = {0: 0x00, - 1: 0x80, + BIN_PORTS = {1: 0x80, 2: 0x40, 3: 0x20, 4: 0x10, @@ -633,35 +632,67 @@ 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 - if type(ports) is list: - for port in ports: - rtn |= self.BIN_PORTS[int(port)] - else: - for port in ports.split(","): - 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) @@ -683,7 +714,6 @@ def print_result(self, value): int(row["vlan_id"]), ",".join([str(x) for x in row["ports"]]))) - ################################################################################ @@ -691,20 +721,11 @@ 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: - member_ports = struct.unpack(">B", value[2])[0] - tagged_ports = struct.unpack(">B", value[3])[0] - else: - member_ports = value[2] - tagged_ports = value[3] - out_member_ports = [] - out_tagged_ports = [] - for port in list(self.BIN_PORTS.keys()): - if (member_ports & self.BIN_PORTS[port] > 0): - out_member_ports.append(port) - if (tagged_ports & self.BIN_PORTS[port] > 0): - out_tagged_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], "member_ports":out_member_ports, @@ -712,11 +733,10 @@ def unpack_py(self, value): } return rtn - def pack_py(self, value): - members = self.pack_port(value[1]) - tagged = self.pack_port(value[2]) - rtn = struct.pack(">hBB", int(value[0]), members, tagged) + 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): @@ -1009,12 +1029,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" @@ -1026,16 +1060,29 @@ def unpack_py(self, value): return rtn def pack_py(self, value): - if int(value[0]) == 0: - return struct.pack(">bbb", 0, 0, 0) - src_ports = 0 - if type(value[1]) is list: - for sport in value[1]: - src_ports |= self.BIN_PORTS[int(sport)] - else: - for sport in value[1].split(","): - src_ports |= self.BIN_PORTS[int(sport)] - return struct.pack(">bbb", int(value[0]), 0, src_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) @@ -1047,10 +1094,10 @@ 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" ################################################################################ diff --git a/wireshark/nsdp.lua b/wireshark/nsdp.lua index 82dcab8..42f9d55 100755 --- a/wireshark/nsdp.lua +++ b/wireshark/nsdp.lua @@ -231,27 +231,34 @@ p_nsdp.fields = {f_type,f_status,f_source,f_destination,f_seq,f_cmd,f_password,f f_serial_num} -- Build a condensed string of port ranges from a bitmap -function port_list(ports) +function port_list(portslist) local list = {} local lastPort = -1 local firstPort = -1 - for i=1,9 do - if bit32.band(ports,0x80) ~= 0 then - if lastPort ~= (i - 1) then - list[#list+1] = "," - list[#list+1] = i - firstPort = i - end - lastPort = i - elseif lastPort ~= -1 then - if firstPort ~= lastPort then - list[#list+1] = "-" - list[#list+1] = tostring(lastPort) + 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 - firstPort = -1 - lastPort = -1 + ports = bit32.lshift(ports,1) end - ports = bit32.lshift(ports,1) + base = base + 8 end if #list == 0 then @@ -429,17 +436,18 @@ function p_nsdp.dissector (buf, pkt, root) 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 + elseif cmd==0x2400 and len>=0x03 then local vlan=buf(offset,2) - local ports=buf(offset+2,1) - tree=subtree:add(buf(offset,len), string.format("VLAN status: VLAN:%u, Ports:%s", vlan:uint(), port_list(ports:uint()))) + 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 + elseif cmd==0x2800 and len>=0x04 then local vlan=buf(offset,2) - local ports=buf(offset+2,1) - local tagged=buf(offset+3,1) - tree=subtree:add(buf(offset,len), string.format("802.1q status: VLAN:%u, Ports:%s, Tagged:%s", vlan:uint(), port_list(ports:uint()), port_list(tagged:uint()))) + 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) @@ -504,10 +512,10 @@ function p_nsdp.dissector (buf, pkt, root) tree:add(f_port, port) tree:add(f_rate_limit, rate) end - elseif cmd==0x5c00 and len==0x03 then + elseif cmd==0x5c00 and len>=3 then local dest=buf(offset,1) - local src=buf(offset+2,1) - tree=subtree:add(buf(offset,len), string.format("Port mirroring: Source port(s):%s, Dest port:%u", port_list(src:uint()), dest:uint())) + 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 From 5c0bb0ed509041f36f7858253be7a36deb56f476 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Sun, 3 Jul 2022 14:59:26 +0200 Subject: [PATCH 40/41] Don't crash if a single value is received when a list was expected --- psl_typ.py | 66 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/psl_typ.py b/psl_typ.py index a1b60f7..726df49 100644 --- a/psl_typ.py +++ b/psl_typ.py @@ -387,6 +387,11 @@ def is_setable(self): 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"]] @@ -460,6 +465,11 @@ def unpack_cmd(self, 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" @@ -516,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"], @@ -595,6 +610,11 @@ def pack_py(self, value): 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"], @@ -709,6 +729,11 @@ 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"]), @@ -751,19 +776,16 @@ def get_metavar(self): def print_result(self, value): print("%-30s%7s %18s %18s" % (self.get_name().capitalize(), "VLAN_ID", "Member ports","Tagged ports")) - if type(value) is list: - 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"]]))) - else: - print("%-30s%7d %18s %18s" % ("", - int(value["vlan_id"]), - ",".join([str(x) for x in value["member_ports"]]), - ",".join([str(x) for x in value["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"]]))) ################################################################################ @@ -807,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"], @@ -937,6 +964,11 @@ def get_set_help(self): 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"], @@ -1172,11 +1204,13 @@ def is_setable(self): def print_result(self, value): print(("%-30s%4s%20s%19s" % ("Cable status:", "Port", "Status", "Fault distance (m)"))) - if type(value) == type(list()): - for row in value: - print(("%-30s%4d%20s%19u" % ("", row["port"], self.status_to_string[row["status"]], row["dist"]))) - else: - print(("%-30s%4d%20s%19u" % ("", value["port"], self.status_to_string[value["status"]], value["dist"]))) + + # 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): From 3cbce0f3e18c14a1888245013da803fa886838b5 Mon Sep 17 00:00:00 2001 From: Richard Hulme Date: Wed, 6 Sep 2023 19:35:58 +0200 Subject: [PATCH 41/41] Fix uninitialised variable in 'discover' --- psl-cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/psl-cli.py b/psl-cli.py index 0300040..1b14a79 100755 --- a/psl-cli.py +++ b/psl-cli.py @@ -13,6 +13,8 @@ def discover(args, switch): "Search for Switches" print("Searching for ProSafe Plus Switches ...\n") + found = False + for data in switch.discover(): found = True for entry in data.keys():