summaryrefslogtreecommitdiffstats
path: root/nselib/eigrp.lua
diff options
context:
space:
mode:
Diffstat (limited to 'nselib/eigrp.lua')
-rw-r--r--nselib/eigrp.lua382
1 files changed, 382 insertions, 0 deletions
diff --git a/nselib/eigrp.lua b/nselib/eigrp.lua
new file mode 100644
index 0000000..a4d6b9d
--- /dev/null
+++ b/nselib/eigrp.lua
@@ -0,0 +1,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;