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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
--- A library supporting parsing and generating a limited subset of the Cisco' EIGRP packets.
--
-- @author Hani Benhabiles
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
-- Version 0.1
-- 19/07/2012 - First version.
local table = require "table"
local stdnse = require "stdnse"
local strbuf = require "strbuf"
local string = require "string"
local ipOps = require "ipOps"
local packet = require "packet"
_ENV = stdnse.module("eigrp", stdnse.seeall)
-- TLV Type constants
TLV = {
PARAM = 0x0001,
AUTH = 0x0002,
SEQ = 0x0003,
SWVER = 0x0004,
MSEQ = 0x0005,
STUB = 0x0006,
TERM = 0x0007,
TIDLIST = 0x0008,
REQ = 0x0101,
INT = 0x0102,
EXT = 0x0103,
COM = 0x0104,
INT6 = 0x0402,
EXT6 = 0x0403,
COM6 = 0x0404,
}
-- External protocols constants
EXT_PROTO = {
NULL = 0x00,
IGRP = 0x01,
EIGRP = 0x02,
Static = 0x03,
RIP = 0x04,
HELLO = 0x05,
OSPF = 0x06,
ISIS = 0x07,
EGP = 0x08,
BGP = 0x09,
IDRP = 0x10,
Connected = 0x11,
}
-- Packets opcode constants
OPCODE = {
UPDATE = 0x01,
RESERVED = 0x02,
QUERY = 0x03,
REPLY = 0x04,
HELLO = 0x05,
}
-- The EIGRP Class
EIGRP = {
--- Creates a new instance of EIGRP class.
-- @param opcode integer Opcode. Defaults to 5 (Hello)
-- @param as integer Autonomous System. Defaults to 0.
-- @param routerid integer virtual router ID. defaults to 0.
-- @param flags integer flags field value. Defaults to 0.
-- @param seq integer sequence value. Defaults to 0.
-- @param ack integer acknowledge value. Defaults to 0.
-- @param Checksum integer EIGRP packet checksum. Calculated automatically
-- if not manually set.
-- @param Table TLVs table.
-- @return o Instance of EIGRP
new = function(self, opcode, as, routerid, flags, seq, ack, checksum, tlvs)
local o = {
ver = 2,
opcode = opcode or TLV.HELLO,
as = as or 0,
routerid = routerid or 0,
flags = flags or 0,
seq = seq or 0x00,
ack = ack or 0x00,
checksum = checksum,
tlvs = tlvs or {},
}
setmetatable(o, self)
self.__index = self
return o
end,
--- Parses a raw eigrp packet and returns a structured response.
-- @param eigrp_raw string EIGRP Raw packet.
-- @return response table Structured eigrp packet.
parse = function(eigrp_raw)
if type(eigrp_raw) ~= 'string' then
stdnse.debug1("eigrp.lua: parse input should be string.")
return
end
if #eigrp_raw < 20 then
stdnse.debug1("eigrp.lua: raw packet size lower then 20.")
return
end
local tlv
local eigrp_packet = {}
local index = 1
eigrp_packet.ver,
eigrp_packet.opcode,
eigrp_packet.checksum,
eigrp_packet.flags,
eigrp_packet.seq,
eigrp_packet.ack,
eigrp_packet.routerid,
eigrp_packet.as, index = string.unpack(">BBI2I4I4I4I2I2", eigrp_raw, index)
eigrp_packet.tlvs = {}
while index < #eigrp_raw do
tlv = {}
tlv.type, tlv.length, index = string.unpack(">I2I2", eigrp_raw, index)
if tlv.length == 0x00 then
-- In case someone wants to DoS us :)
stdnse.debug1("eigrp.lua: stopped parsing due to null TLV length.")
break
end
-- TODO: These padding calculations seem suspect, especially the ones
-- that assume a static length for a variable-length field like TLV.SEQ
if tlv.type == TLV.PARAM then
-- Parameters
local k = {}
k[1], k[2], k[3], k[4], k[5], k[6], tlv.htime, index = string.unpack(">BBBBBBI2", eigrp_raw, index)
tlv.k = k
index = index + tlv.length - 12
elseif tlv.type == TLV.AUTH then
tlv.authtype,
tlv.authlen,
tlv.keyid,
tlv.keyseq, index = string.unpack(">I2I2I4I4", eigrp_raw, index)
-- Null pad == tlv.length - What was already parsed - authlen
tlv.digest, index = string.unpack(">I2", eigrp_raw, index + (tlv.length - tlv.authlen - index + 1))
elseif tlv.type == TLV.SEQ then
-- Sequence
tlv.address, index = string.unpack(">s2", eigrp_raw, index)
tlv.address = ipOps.str_to_ip(tlv.address)
index = index + tlv.length - 7
elseif tlv.type == TLV.SWVER then
-- Software version
tlv.majv,
tlv.minv,
tlv.majtlv,
tlv.mintlv, index = string.unpack(">BBBB", eigrp_raw, index)
index = index + tlv.length - 8
elseif tlv.type == TLV.MSEQ then
-- Next Multicast Sequence
tlv.mseq, index = string.unpack(">I4", eigrp_raw, index)
index = index + tlv.length - 8
elseif tlv.type == TLV.STUB then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.TERM then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.TIDLIST then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.REQ then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.INT then
-- Internal Route
tlv.nexth, index = string.unpack(">I4", eigrp_raw, index)
tlv.nexth = ipOps.fromdword(tlv.nexth)
tlv.mask, index = string.unpack(">I2", eigrp_raw, index + 15)
-- Destination varies in length
-- e.g trailing 0's are omitted
-- if length = 29 => destination is 4 bytes
-- if length = 28 => destination is 3 bytes
-- if length = 27 => destination is 2 bytes
-- if length = 26 => destination is 1 byte
local dst = {0,0,0,0}
for i = 1, (4 + tlv.length - 29) do
dst[i], index = string.unpack("B", eigrp_raw, index)
end
tlv.dst = table.concat(dst, '.')
elseif tlv.type == TLV.EXT then
-- External Route
tlv.nexth,
tlv.orouterid,
tlv.oas,
tlv.tag,
tlv.emetric,
-- Skip 2 reserved bytes
tlv.eproto,
tlv.eflags,
tlv.lmetrics,
tlv.mask, index = string.unpack(">I4I4I4I4I4xxBBc16B", eigrp_raw, index)
tlv.nexth = ipOps.fromdword(tlv.nexth)
tlv.orouterid = ipOps.fromdword(tlv.orouterid)
-- Destination varies in length
-- if length = 49 => destination is 4 bytes
-- if length = 48 => destination is 3 bytes
-- if length = 47 => destination is 2 bytes
-- if length = 46 => destination is 1 byte
local dst = {0,0,0,0}
for i = 1, (4 + tlv.length - 49) do
dst[i], index = string.unpack("B", eigrp_raw, index)
end
tlv.dst = table.concat(dst, '.')
elseif tlv.type == TLV.COM then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.INT6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.EXT6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
elseif tlv.type == TLV.COM6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
index = index + tlv.length - 4
else
stdnse.debug1("eigrp.lua: eigrp.lua: TLV type %d unknown.", tlv.type)
index = index + tlv.length - 4
end
table.insert(eigrp_packet.tlvs, tlv)
end
return eigrp_packet
end,
--- Adds a TLV table to the table of TLVs.
-- @param tlv TLV table.
addTLV = function(self, tlv)
if type(tlv) == 'table' then
table.insert(self.tlvs, tlv)
else
stdnse.debug1("eigrp.lua: TLV should be a table, not %s", type(tlv))
end
end,
--- Checks if TLV type is one that should contain routing information.
-- @param tlvtype integer TLV type integer to check.
-- @return status true if tlvtype is a routing information tlv.
isRoutingTLV = function(tlvtype)
if tlvtype == 0x101 or tlvtype == 0x102
or tlvtype == 0x103 or tlvtype == 0x104
or tlvtype == 0x402 or tlvtype == 0x403
or tlvtype == 0x404 then
return true
end
end,
--- Sets the EIGRP version.
-- @param ver integer version to set.
setVersion = function(self, ver)
self.ver = ver
end,
--- Sets the EIGRP Packet opcode
-- @param opcode integer EIGRP opcode.
setOpcode = function(self, opcode)
self.opcode = opcode
end,
--- Sets the EIGRP packet checksum
-- @param integer checksum Checksum to set.
setChecksum = function(self, checksum)
self.checksum = checksum
end,
--- Sets the EIGRP packet flags field.
-- @param flags Flags integer value.
setFlags = function(self, flags)
self.flags = flags
end,
--- Sets the EIGRP packet sequence field.
-- @param seq EIGRP sequence.
setSequence = function(self, seq)
self.seq = seq
end,
--- Sets the EIGRP Packet acknowledge field.
-- @param ack EIGRP acknowledge.
setAcknowledge = function(self, ack)
self.ack = ack
end,
--- Sets the EIGRP Packet Virtual Router ID.
-- @param routerid EIGRP Virtual Router ID.
setRouterID = function(self, routerid)
self.routerid = routerid
end,
--- Sets the EIGRP Packet Autonomous System.
-- @param as EIGRP A.S.
setAS = function(self, as)
self.as = as
end,
--- Sets the EIGRP Packet tlvs
-- @param tlvs table of EIGRP tlvs.
setTlvs = function(self, tlvs)
self.tlvs = tlvs
end,
--- Converts the request to a string suitable to be sent over a socket.
-- @return data string containing the complete request to send over the socket
__tostring = function(self)
local data = strbuf.new()
data = data .. string.pack(">BBI2I4I4I4I2I2",
self.ver, -- Version 2
self.opcode, -- Opcode: Hello
self.checksum or 0, -- Calculated later.
self.flags, -- Flags
self.seq, -- Sequence 0
self.ack, -- Acknowledge 0
self.routerid, -- Virtual Router ID 0
self.as) -- Autonomous system
for _, tlv in pairs(self.tlvs) do
if tlv.type == TLV.PARAM then
data = data .. string.pack(">I2I2 BBBBBB I2",
TLV.PARAM,
12, -- Length
tlv.k[1], tlv.k[2], tlv.k[3], tlv.k[4], tlv.k[5], tlv.k[6],
tlv.htime)
elseif tlv.type == TLV.AUTH then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.SEQ then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.SWVER then
data = data .. string.pack(">I2I2 BB BB",
TLV.SWVER,
0x0008,
tonumber(tlv.majv), tonumber(tlv.minv),
tonumber(tlv.majtlv), tonumber(tlv.mintlv))
elseif tlv.type == TLV.MSEQ then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.STUB then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.TERM then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.TIDLIST then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.REQ then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.INT then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.EXT then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.COM then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.INT6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.EXT6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
elseif tlv.type == TLV.COM6 then
-- TODO
stdnse.debug1("eigrp.lua: TLV type %d skipped due to no parser.", tlv.type)
else
stdnse.debug1("eigrp.lua: TLV type %d unknown.", tlv.type)
end
end
data = strbuf.dump(data)
-- In the end, correct the checksum if not manually set
if not self.checksum then
data = data:sub(1,2) .. string.pack(">I2", packet.in_cksum(data)) .. data:sub(5)
end
return data
end,
}
return _ENV;
|