-
Notifications
You must be signed in to change notification settings - Fork 5
/
chaos.lua
223 lines (199 loc) · 9.48 KB
/
chaos.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
-- Chaosnet dissector for Wireshark, based on AMS hack for Chaos-over-UDP
-- Install in ~/.config/wireshark/plugins (for Linux or macOS).
-- or ~/.local/lib/wireshark/plugins.
-- To show Chaosnet packets on Ethernet
-- sudo tshark -O chaos ether proto 0x804
-- To skip all RUT packets (assuming ether header length 14):
-- sudo tshark -O chaos ether proto 0x804 and ether[15] != 8
-- To show only Chaos-over-IP packets (IP protocol 16):
-- sudo tshark -O chaos ip proto 16
-- To show only a specific host, and skip all RUT packets (assuming IP header length 20):
-- sudo tshark -O chaos ip proto 16 and ip host 10.0.1.73 and ip[21] != 8
-- To show only Chaos-over-UDP packets (normally UDP port 42042):
-- sudo tshark -O chaos udp port 42042
-- To show only a specific host, and skip all RUT packets (assuming UDP header length 8+chudp hdr 4):
-- sudo tshark -O chaos udp port 42042 and host 10.0.1.73 and udp[12] != 8
-- Example combination:
-- sudo tshark -O chaos \(ether proto 0x804 and ether[15] != 8\) or \(ip proto 16 and ip[21] != 8\)
-- To see less detail, skip "-O chaos".
-- For the Wireshark lua stuff, see https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html
chaos = Proto("chaos", "Chaosnet")
chudp = Proto("chudp", "Chaos-over-UDP")
local CHAOS_HDR_LEN = 16
local ef_too_short = ProtoExpert.new("chaos.too_short.expert", "Packet too short", expert.group.MALFORMED, expert.severity.ERROR)
chaos.experts = { ef_too_short }
-- Chaos opcode names
local opc_names = { "RFC", "OPN", "CLS", "FWD", "ANS", "SNS", "STS",
"RUT", "LOS", "LSN", "MNT", "EOF", "UNC", "BRD" }
-- Parse a BRD packet's subnet mask, returning the number of nets in it, and the list of subnetwork numbers
function parse_subnet_mask(tvb, len)
-- lua is a pretty ghastly language, isn't it?
local nets = {}
local nnets = 0
for i=0,len-1 do
for j=0,7 do
local r = tvb(i,1):int()
if r % (2^j + 2^j) >= 2^j then -- see http://lua-users.org/wiki/BitwiseOperators
nets[nnets+1] = i*8+j
nnets = nnets + 1
end
end
end
return nnets,nets
end
function chudp.dissector(tvbuf, pinfo, tree)
pinfo.cols.protocol:set("CHUDP")
local plen = tvbuf:reported_length_remaining()
local subtree = tree:add(chudp, tvbuf:range(0, plen), "Chaos-over-UDP")
-- Chaosnet UDP Header.
local chudp_tree = subtree:add(tvbuf(0, 4), "CHUDP header")
chudp_tree:add(tvbuf(0, 1), "Version: " .. tvbuf(0, 1)) -- version 1 is defined
chudp_tree:add(tvbuf(1, 1), "Function: " .. tvbuf(1, 1)) -- 1 means PKT
chudp_tree:add(tvbuf(2, 1), "Argument 1: " .. tvbuf(2, 1)) -- mbz for version 1
chudp_tree:add(tvbuf(3, 1), "Argument 2: " .. tvbuf(3, 1)) -- mbz for version 1
-- Go dissect the chaos packet
local ch = Dissector.get("chaos")
local tv = tvbuf(4, plen - 4):tvb()
ch:call(tv, pinfo, tree)
end
-- swap a big-endian TVBrange to be a little-endian, to show strings etc
function swap_tv(intv,pklen)
local inarr,inlen = intv:bytes()
if inarr == nil or inlen == nil or inlen == 0 then
return intv
end
local outarr = ByteArray.new()
local minlen = math.min(pklen,inlen)
outarr:set_size(minlen)
-- first swap all the even bytes
for i=0,minlen-2,2 do
outarr:set_index(i, inarr:get_index(i+1))
outarr:set_index(i+1, inarr:get_index(i))
end
-- then maybe the last
if minlen % 2 == 1 then
outarr:set_index(minlen-1, inarr:get_index(minlen))
end
return ByteArray.tvb(outarr,"swapped"):range()
end
function chaos.dissector(tvb, pinfo, tree)
local bendian = false
-- It would be nice to just call this with fourth arg true if Chaos-over-UDP case (big-endian),
-- but adding an argument doesn't seem to work, so here is a horrible workaround.
-- port_type seems to be 0 for Chaos-over-Ether and Chaos-over-IP, but 3 for Chaos-over-UDP.
-- Note: Need to do something when CHUDP v2 gets done, with standard order of bytes.
if pinfo['port_type'] == 3 then -- aargh
bendian = true
end
-- Shows protocol fields correctly, but for strings only suffixes [BE] to indicated they are swapped.
pinfo.cols.protocol:set("Chaos"..(bendian and " (BE)" or "")) -- keep it terse
local pktlen = tvb:reported_length_remaining()
local pktlen_remaining = pktlen
local subtree = tree:add(chaos, tvb:range(0, pktlen), "Chaosnet (length "..pktlen..(bendian and ", BE" or "")..")")
if pktlen < CHAOS_HDR_LEN then
tree:add_proto_expert_info(ef_too_short)
return
end
-- Chaosnet header.
local chaos_tree = subtree:add(tvb(0, CHAOS_HDR_LEN), "Header")
-- Note: MSB is opcode, but 16b-word is swapped
local opcode = bendian and tvb(0,1):uint() or tvb(1,1):uint()
local opdesc = ""
if opcode > 0 and opcode <= #opc_names then
opdesc = " (" .. opc_names[opcode] .. ")"
elseif opcode >= 128 and opcode < 192 then -- #o200 and #o300 respectively
opdesc = " (DAT)"
elseif opcode >= 192 then
opdesc = " (DAT2)"
end
chaos_tree:add(tvb(0, 2), "Operation: " .. string.format("%#o",opcode) .. opdesc)
--- 4 bit forwarding count, 12 bit data count
-- note swappedness
local d = bendian and tvb:range(2,2):int() or tvb:range(2,2):le_int()
local forward_count = bit.rshift(d,12)
local data_count = bit.band(d, 0xfff)
local src = bendian and string.format("%#o",tvb(8, 2):uint()) or string.format("%#o",tvb(8, 2):le_uint())
local srcidx = bendian and tvb(10, 2):uint() or tvb(10, 2):le_uint()
local dest = bendian and string.format("%#o",tvb(4, 2):uint()) or string.format("%#o",tvb(4, 2):le_uint())
local destidx = bendian and tvb(6, 2):uint() or tvb(6, 2):le_uint()
local pktno = bendian and tvb(12, 2):uint() or tvb(12, 2):le_uint()
local ackno = bendian and tvb(14, 2):uint() or tvb(14, 2):le_uint()
-- add the brief info
pinfo.cols.info:set("<"..src..","..string.format("%#04x",srcidx).."> => <"..dest..","..string.format("%#04x",destidx).."> "..string.format("%#04x",pktno)..opdesc)
chaos_tree:add(tvb(2, 2), "Fwd count: " .. forward_count .. ", Data length: " .. data_count)
chaos_tree:add(tvb(4, 4), "Dest address " .. dest .. ", index " .. string.format("%#04x",destidx))
chaos_tree:add(tvb(8, 4), "Source address " .. src .. ", index " .. string.format("%#04x",srcidx))
chaos_tree:add(tvb(12, 4), "Packet nr: " .. string.format("%#04x",pktno).. ", Ack nr: " .. string.format("%#04x",ackno))
-- Data.
if opcode == 8 then -- RUT, complex content
local data_tree = subtree:add(tvb(CHAOS_HDR_LEN, data_count), "RUT data (routing table)")
for i=0,data_count-4,4 do
local netnum = bendian and tvb(CHAOS_HDR_LEN+i,2):uint() or tvb(CHAOS_HDR_LEN+i,2):le_uint()
local cost = bendian and tvb(CHAOS_HDR_LEN+i+2,2):uint() or tvb(CHAOS_HDR_LEN+i+2,2):le_uint()
data_tree:add(tvb(CHAOS_HDR_LEN+i,4), "Net "..string.format("%#o",netnum).." cost "..cost)
end
elseif opcode == 2 or opcode == 7 then -- OPN or STS
local data_tree = subtree:add(tvb(CHAOS_HDR_LEN, data_count), "Window management data")
local rcpt = bendian and tvb:range(CHAOS_HDR_LEN, 2):uint() or tvb:range(CHAOS_HDR_LEN, 2):le_uint()
local wind = bendian and tvb:range(CHAOS_HDR_LEN+2, 2):uint() or tvb:range(CHAOS_HDR_LEN+2, 2):le_uint()
data_tree:add(tvb(CHAOS_HDR_LEN, 4), "Receipt pkt "..string.format("%#04x",rcpt)..", window size "..wind)
else
local repr
local truncated = data_count > 64 and "..." or ""
if opcode == 1 or opcode == 3 or opcode == 9 then -- RFC or CLS or LOS
local s0
if bendian then
s0 = swap_tv(tvb(CHAOS_HDR_LEN, math.min(data_count+(data_count%2), 64)),data_count):string()
else
s0 = tvb(CHAOS_HDR_LEN, math.min(data_count, 64)):string()
end
local s = s0..truncated..(bendian and " [BE]" or "")
repr = "String ("..data_count.."): " .. s
pinfo.cols.info:append(" "..s)
elseif opcode == 14 then -- BRD
-- Ack field says how long the subnet bitmap is
local ackn = bendian and tvb(14,2):uint() or tvb(14,2):le_uint()
local subs = tvb(CHAOS_HDR_LEN, ackn) -- subnet mask is here
local nn,n = parse_subnet_mask(subs,ackn) -- parse it, get number of nets and a list of them
-- contact name
local contact
if bendian then
contact = swap_tv(tvb(CHAOS_HDR_LEN+ackn,data_count+(data_count%2)-ackn),data_count-ackn):string().." [BE]"
else
contact = tvb(CHAOS_HDR_LEN+ackn,data_count-ackn):string()
end
local ns
if nn > 5 then
ns = n[1].." to "..((#n)-1) -- if many, only give range (zero-based)
else
ns = table.concat(n,",") -- else give the full list
end
repr = "Broadcast to "..nn.." subnet"..(nn == 1 and "" or "s").." ("..ns.."), contact: "..contact
pinfo.cols.info:append(" "..contact)
else
-- Show it as a string if it is DAT
if opcode >= 128 and opcode < 192 then
local s0
if bendian then
s0 = swap_tv(tvb(CHAOS_HDR_LEN, math.min(data_count+(data_count%2), 64)),data_count):string()
else
s0 = tvb(CHAOS_HDR_LEN, math.min(data_count, 64)):string()
end
repr = "Data ("..data_count.."): " .. s0 ..truncated
else
repr = "Data ("..data_count.."): " .. tvb(CHAOS_HDR_LEN, math.min(data_count, 64))..truncated
end
end
local data_tree = subtree:add(tvb(CHAOS_HDR_LEN, data_count), repr)
end
-- End. Return the number of bytes we consumed
return CHAOS_HDR_LEN + data_count
end
-- Add it for Chaos-over-IP
DissectorTable.get("ip.proto"):add(16, chaos)
-- and add it for Ethernet
DissectorTable.get("ethertype"):add(0x0804, chaos)
-- and to UDP table
udp_table = DissectorTable.get("udp.port")
udp_table:add(42042, chudp)
udp_table:add(42043, chudp)