]> git.sur5r.net Git - ngadmin/blob - wireshark/nsdp.lua
Handle ports duplex mode
[ngadmin] / wireshark / nsdp.lua
1
2 p_nsdp = Proto("nsdp", "Netgear Switch Description Protocol")
3
4 local f_version = ProtoField.uint8("nsdp.version", "Version", base.DEC)
5 local f_code = ProtoField.uint8("nsdp.code", "Operation Code", base.DEC)
6 local f_error = ProtoField.uint8("nsdp.error", "Error Code", base.DEC)
7 local f_errattr = ProtoField.uint16("nsdp.errattr", "Erroneous Attribute", base.HEX)
8 local f_clientmac = ProtoField.ether("nsdp.clientmac", "Client MAC")
9 local f_switchmac = ProtoField.ether("nsdp.switchmac", "Switch MAC")
10 local f_seqnum = ProtoField.uint32("nsdp.seqnum", "Sequence Number", base.DEC)
11
12 p_nsdp.fields = {
13         f_version,
14         f_code,
15         f_error,
16         f_errattr,
17         f_clientmac,
18         f_switchmac,
19         f_seqnum
20 }
21
22
23
24 local op_codes = {
25         [1] = "Read Request",
26         [2] = "Read Reply",
27         [3] = "Write Request",
28         [4] = "Write Reply"
29 }
30
31
32 local error_codes = {
33         [0] = "OK",
34         [3] = "Read Only",
35         [4] = "Write Only",
36         [5] = "Invalid Value",
37         [7] = "Access Denied"
38 }
39
40
41 local status_codes = {
42         [0] = "down",
43         [1] = "10M half-duplex",
44         [2] = "10M full-duplex",
45         [3] = "100M half-duplex",
46         [4] = "100M full-duplex",
47         [5] = "1000M full-duplex"
48 }
49
50
51 local bitrates_codes = {
52         [0] = "unlimited",
53         [1] = "512K",
54         [2] = "1M",
55         [3] = "2M",
56         [4] = "4M",
57         [5] = "8M",
58         [6] = "16M",
59         [7] = "32M",
60         [8] = "64M",
61         [9] = "128M",
62         [10] = "256M",
63         [11] = "512M"
64 }
65
66
67 local vlan_type_codes = {
68         [0] = "disabled",
69         [1] = "port basic",
70         [2] = "port advanced",
71         [3] = "802.1Q basic",
72         [4] = "802.1Q advanced"
73 }
74
75
76 local qos_type_codes = {
77         [1] = "port based",
78         [2] = "802.1p"
79 }
80
81
82 local prio_codes = {
83         [1] = "high",
84         [2] = "medium",
85         [3] = "normal",
86         [4] = "low"
87 }
88
89
90
91
92 local function dissect_port_statistics(buffer, offset, subtree)
93         subtree:add(buffer(offset + 4, 1), string.format("Port: %i", buffer(offset + 4, 1):uint()))
94         subtree:add(buffer(offset + 4 + 1 + 8 * 0, 8), "Received:", tostring(buffer(offset + 4 + 1 + 8 * 0, 8):uint64()))
95         subtree:add(buffer(offset + 4 + 1 + 8 * 1, 8), "Sent: ", tostring(buffer(offset + 4 + 1 + 8 * 1, 8):uint64()))
96         subtree:add(buffer(offset + 4 + 1 + 8 * 5, 8), "CRC Errors:", tostring(buffer(offset + 4 + 1 + 8 * 5, 8):uint64()))
97 end
98
99
100 local function dissect_port_status(buffer, offset, subtree)
101         local st = buffer(offset + 5, 1):uint()
102         subtree:add(buffer(offset + 4, 1), string.format("Port: %i", buffer(offset + 4, 1):uint()))
103         subtree:add(buffer(offset + 5, 1), string.format("Status: %i (%s)", st, status_codes[st] or "unk"))
104 end
105
106
107 local function dissect_qos_type(buffer, offset, subtree)
108         local t = buffer(offset + 4, 1):uint()
109         subtree:add(buffer(offset + 4, 1), string.format("QoS Type: %i (%s)", t, qos_type_codes[t] or "unk"))
110 end
111
112
113 local function dissect_qos_config(buffer, offset, subtree)
114         local p = buffer(offset + 5, 1):uint()
115         subtree:add(buffer(offset + 4, 1), string.format("Port: %i", buffer(offset + 4, 1):uint()))
116         subtree:add(buffer(offset + 5, 1), string.format("Priority: %i (%s)", p, prio_codes[p] or "unk"))
117 end
118
119
120 local function dissect_bitrate(buffer, offset, subtree)
121         local sp = buffer(offset + 5, 4):uint()
122         subtree:add(buffer(offset + 4, 1), string.format("Port: %i", buffer(offset + 4, 1):uint()))
123         subtree:add(buffer(offset + 5, 4), string.format("Speed: %i (%s)", sp, bitrates_codes[sp] or "unk"))
124 end
125
126
127 local function dissect_vlan_type(buffer, offset, subtree)
128         local vt = buffer(offset + 4, 1):uint()
129         subtree:add(buffer(offset + 4, 1), string.format("VLAN Type: %i (%s)", vt, vlan_type_codes[vt] or "unk"))
130 end
131
132
133 local function parse_ports(val)
134         local ports = ""
135         
136         for i = 8, 1, -1 do
137                 if (val % 2 == 1) then
138                         ports = ports..i.." "
139                 end
140                 val = math.floor(val / 2)
141         end
142         
143         return ports
144 end
145
146
147 local function dissect_vlan_port_conf(buffer, offset, subtree)
148         subtree:add(buffer(offset + 4, 2), string.format("VLAN: %u", buffer(offset + 4, 2):uint()))
149         
150         if (buffer(offset + 2, 2):uint() >= 3) then
151                 subtree:add(buffer(offset + 6, 1), "Ports:", parse_ports(buffer(offset + 6, 1):uint()))
152         end
153 end
154
155
156 local function dissect_vlan_8021q_conf(buffer, offset, subtree)
157         subtree:add(buffer(offset + 4, 2), string.format("VLAN: %u", buffer(offset + 4, 2):uint()))
158         
159         if (buffer(offset + 2, 2):uint() >= 4) then
160                 subtree:add(buffer(offset + 6, 1), "Ports:", parse_ports(buffer(offset + 6, 1):uint()))
161                 subtree:add(buffer(offset + 7, 1), "Tagged Ports:", parse_ports(buffer(offset + 7, 1):uint()))
162         end
163 end
164
165
166 local function dissect_vlan_pvid(buffer, offset, subtree)
167         subtree:add(buffer(offset + 4, 1), string.format("Port: %i", buffer(offset + 4, 1):uint()))
168         subtree:add(buffer(offset + 5, 2), string.format("VLAN: %u", buffer(offset + 5, 2):uint()))
169 end
170
171
172 local function dissect_mirror(buffer, offset, subtree)
173         local op = buffer(offset + 4, 1):uint()
174         
175         if (op == 0) then
176                 subtree:add(buffer(offset + 4, 1), "Disabled")
177         else
178                 subtree:add(buffer(offset + 4, 1), "Output Port:", op)
179                 subtree:add(buffer(offset + 6, 1), "Ports:", parse_ports(buffer(offset + 6, 1):uint()))
180         end
181 end
182
183
184 local function dissect_igmp_enablevlan(buffer, offset, subtree)
185         subtree:add(buffer(offset + 4, 2), string.format("Enable: %u", buffer(offset + 4, 2):uint()))
186         subtree:add(buffer(offset + 6, 2), string.format("VLAN: %u", buffer(offset + 6, 2):uint()))
187 end
188
189
190 local attributes = {
191         [0x0001] = {name = "Product", dissect = "string"}, 
192         [0x0003] = {name = "Name", dissect = "string"}, 
193         [0x0004] = {name = "MAC", dissect = "ether"}, 
194         [0x0006] = {name = "IP", dissect = "ipv4"}, 
195         [0x0007] = {name = "Mask", dissect = "ipv4"}, 
196         [0x0008] = {name = "Gateway", dissect = "ipv4"}, 
197         [0x0009] = {name = "New Password", dissect = "string"}, 
198         [0x000A] = {name = "Password", dissect = "string"}, 
199         [0x000B] = {name = "DHCP", dissect = "uint"}, 
200         [0x000D] = {name = "Firmware Version", dissect = "string"}, 
201         [0x0010] = {name = "Firmware Upgrade", dissect = "uint"}, 
202         [0x0013] = {name = "Restart", dissect = "uint"}, 
203         [0x0014] = {name = "Encrypt Passwords", dissect = "uint"}, 
204         [0x0400] = {name = "Defaults", dissect = "uint"}, 
205         [0x0C00] = {name = "Port Status", dissect = dissect_port_status}, 
206         [0x1000] = {name = "Port Statistics", dissect = dissect_port_statistics}, 
207         [0x1400] = {name = "Reset Ports Statistics", dissect = "uint"}, 
208         [0x1800] = {name = "Cabletest Do", dissect = nill}, 
209         [0x1C00] = {name = "Cabletest Result", dissect = nill}, 
210         [0x2000] = {name = "VLAN Type", dissect=dissect_vlan_type}, 
211         [0x2400] = {name = "VLAN Port Conf", dissect=dissect_vlan_port_conf}, 
212         [0x2800] = {name = "VLAN 802.1Q Conf", dissect=dissect_vlan_8021q_conf}, 
213         [0x2C00] = {name = "Destroy VLAN", dissect = "uint"}, 
214         [0x3000] = {name = "VLAN PVID", dissect = dissect_vlan_pvid}, 
215         [0x3400] = {name = "QoS Type", dissect = dissect_qos_type}, 
216         [0x3800] = {name = "QoS Config", dissect = dissect_qos_config}, 
217         [0x4C00] = {name = "Input Bitrate", dissect = dissect_bitrate}, 
218         [0x5000] = {name = "Output Bitrate", dissect = dissect_bitrate}, 
219         [0x5400] = {name = "Broadcast Filtering State", dissect = "uint"}, 
220         [0x5800] = {name = "Broadcast Filtering Bitrate", dissect = dissect_bitrate}, 
221         [0x5C00] = {name = "Mirror", dissect = dissect_mirror}, 
222         [0x6000] = {name = "Ports Count", dissect = "uint"}, 
223         [0x6400] = {name = "Max 802.1Q VLAN Group", dissect = nill}, 
224         [0x6800] = {name = "IGMP Enable & VLAN", dissect = dissect_igmp_enablevlan}, 
225         [0x6C00] = {name = "Block Unknown IGMP Addresses", dissect = "uint"}, 
226         [0x7000] = {name = "Validate IGMPv3 Headers", dissect = "uint"}, 
227         [0x7400] = {name = "TLV Bitmap", dissect = nill}, 
228         [0xFFFF] = {name = "End", dissect = nill}
229 }
230
231
232
233 local function dissect_header(buffer, subtree)
234         subtree:add(f_version, buffer(0, 1))
235         subtree:add(f_code, buffer(1, 1)):append_text(" ("..(op_codes[buffer(1, 1):uint()] or "unknown")..")")
236         
237         local errcode = buffer(2, 1):uint()
238         local errattr = buffer(4, 2):uint()
239         subtree:add(f_error, buffer(2, 1)):append_text(" ("..(error_codes[errcode] or "unknown")..")")
240         
241         -- add the erroneous attribute only if an error occurred
242         if (errattr ~= 0) then
243                 local atf = attributes[errattr]
244                 subtree:add(f_errattr, buffer(4, 2)):append_text(" ("..(atf and atf.name or "unk")..")")
245         end
246         
247         subtree:add(f_clientmac, buffer(8, 6))
248         subtree:add(f_switchmac, buffer(14, 6))
249         subtree:add(f_seqnum, buffer(20, 4))
250 end
251
252
253 local function dissect_attributes(buffer, subtree)
254         local offset = 32
255         
256         while (offset < buffer:len()) do
257                 if (offset + 4 > buffer:len()) then
258                         -- no room for an attribute header, it is an error
259                         subtree:add(buffer(offset), "Junk"):set_expert_flags(PI_MALFORMED, PI_ERROR)
260                         break
261                 end
262                 
263                 local code = buffer(offset, 2):uint()
264                 local len = buffer(offset + 2, 2):uint()
265                 local atf = attributes[code]
266                 
267                 local attr = subtree:add(buffer(offset, math.min(4 + len, buffer:len() - offset)), string.format("Attribute: 0x%04X (%s)", code, atf and atf.name or "unk"))
268                 attr:add(buffer(offset, 2), string.format("Code: 0x%04X", code))
269                 attr:add(buffer(offset + 2, 2), string.format("Length: %u", len))
270                 
271                 if (offset + 4 + len > buffer:len()) then
272                         -- attribute length is bigger than remaining packet size, it is an error
273                         attr:append_text(" [malformed]")
274                         attr:set_expert_flags(PI_MALFORMED, PI_ERROR)
275                         break
276                 end
277                 
278                 
279                 if (len <= 0) then
280                         -- no data, display nothing
281                 elseif (atf == nil or atf.dissect == nil) then
282                         -- unknown attribute, display raw bytes
283                         attr:add(buffer(offset + 4, len), "Data:", tostring(buffer(offset + 4, len):bytes()))
284                 elseif (type(atf.dissect) == "function" ) then
285                         -- custom sub-dissector for complex type
286                         atf.dissect(buffer, offset, attr)
287                 else
288                         -- simple type, directly show it
289                         local func = assert(loadstring("return function(buffer, offset, len) return tostring(buffer(offset + 4, len):"..atf.dissect.."()) end"))() -- ugly, isn't it ?
290                         attr:add(buffer(offset + 4, len), atf.name..":", func(buffer, offset, len))
291                 end
292                 
293                 offset = offset + 4 + len
294         end
295 end
296
297
298 function p_nsdp.dissector(buffer, pinfo, tree)
299         pinfo.cols.protocol = p_nsdp.name
300         local subtree = tree:add(p_nsdp, buffer())
301         
302         -- stop if the packet is too small to be valid
303         if (buffer:len() < 32) then
304                 return
305         end
306         
307         dissect_header(buffer, subtree)
308         
309         -- stop if it is just a header
310         if (buffer:len() == 32) then
311                 return
312         end
313         
314         local attr_list = subtree:add(buffer(32), "Attributes list")
315         dissect_attributes(buffer, attr_list)
316 end
317
318
319 function p_nsdp.init ()
320 end
321
322
323 local udp_dissector_table = DissectorTable.get("udp.port")
324 udp_dissector_table:add(63322, p_nsdp)
325 udp_dissector_table:add(63321, p_nsdp)
326
327