Přemysl Eric Janouch
019c4302ad
Lua detection functions used to cause fatal errors on failure to read. We could also reconsider treating detection errors as fatal.
255 lines
6.7 KiB
Lua
255 lines
6.7 KiB
Lua
--
|
|
-- pcap.lua: libpcap file format
|
|
--
|
|
-- Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
|
|
--
|
|
-- Permission to use, copy, modify, and/or distribute this software for any
|
|
-- purpose with or without fee is hereby granted.
|
|
--
|
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
--
|
|
|
|
local detect = function (c)
|
|
if #c < 4 then
|
|
return false
|
|
end
|
|
local magic = c:u32 ()
|
|
return magic == 0xa1b2c3d4 or magic == 0xd4c3b2a1
|
|
end
|
|
|
|
local detect_ng = function (c)
|
|
if #c < 8 then
|
|
return false
|
|
end
|
|
local magic = c (9):u32 ()
|
|
return c:u32 () == 0x0a0d0d0a
|
|
and (magic == 0x1a2b3c4d or magic == 0x4d3c2b1a)
|
|
end
|
|
|
|
-- Specified in http://www.tcpdump.org/linktypes.html
|
|
local link_types = {
|
|
[0] = "NULL",
|
|
[1] = "ETHERNET",
|
|
[3] = "AX25",
|
|
[6] = "IEEE802_5",
|
|
[7] = "ARCNET_BSD",
|
|
[8] = "SLIP",
|
|
[9] = "PPP",
|
|
[10] = "FDDI",
|
|
[50] = "PPP_HDLC",
|
|
[51] = "PPP_ETHER",
|
|
[100] = "ATM_RFC1483",
|
|
[101] = "RAW",
|
|
[104] = "C_HDLC",
|
|
[105] = "IEEE802_11",
|
|
[107] = "FRELAY",
|
|
[108] = "LOOP",
|
|
[113] = "LINUX_SLL",
|
|
[114] = "LTALK",
|
|
[117] = "PFLOG",
|
|
[119] = "IEEE802_11_PRISM",
|
|
[122] = "IP_OVER_FC",
|
|
[123] = "SUNATM",
|
|
[127] = "IEEE802_11_RADIOTAP",
|
|
[129] = "ARCNET_LINUX",
|
|
[138] = "APPLE_IP_OVER_IEEE1394",
|
|
[139] = "MTP2_WITH_PHDR",
|
|
[140] = "MTP2",
|
|
[141] = "MTP3",
|
|
[142] = "SCCP",
|
|
[143] = "DOCSIS",
|
|
[144] = "LINUX_IRDA",
|
|
[147] = "USER0",
|
|
[148] = "USER1",
|
|
[149] = "USER2",
|
|
[150] = "USER3",
|
|
[151] = "USER4",
|
|
[152] = "USER5",
|
|
[153] = "USER6",
|
|
[154] = "USER7",
|
|
[155] = "USER8",
|
|
[156] = "USER9",
|
|
[157] = "USER10",
|
|
[158] = "USER11",
|
|
[159] = "USER12",
|
|
[160] = "USER13",
|
|
[161] = "USER14",
|
|
[162] = "USER15",
|
|
[163] = "IEEE802_11_AVS",
|
|
[165] = "BACNET_MS_TP",
|
|
[166] = "PPP_PPPD",
|
|
[169] = "GPRS_LLC",
|
|
[170] = "GPF_T",
|
|
[171] = "GPF_F",
|
|
[177] = "LINUX_LAPD",
|
|
[187] = "BLUETOOTH_HCI_H4",
|
|
[189] = "USB_LINUX",
|
|
[192] = "PPI",
|
|
[195] = "IEEE802_15_4",
|
|
[196] = "SITA",
|
|
[197] = "ERF",
|
|
[201] = "BLUETOOTH_HCI_H4_WITH_PHDR",
|
|
[202] = "AX25_KISS",
|
|
[203] = "LAPD",
|
|
[204] = "PPP_WITH_DIR",
|
|
[205] = "C_HDLC_WITH_DIR",
|
|
[206] = "FRELAY_WITH_DIR",
|
|
[209] = "IPMB_LINUX",
|
|
[215] = "IEEE802_15_4_NONASK_PHY",
|
|
[220] = "USB_LINUX_MMAPPED",
|
|
[224] = "FC_2",
|
|
[225] = "FC_2_WITH_FRAME_DELIMS",
|
|
[226] = "IPNET",
|
|
[227] = "CAN_SOCKETCAN",
|
|
[228] = "IPV4",
|
|
[229] = "IPV6",
|
|
[230] = "IEEE802_15_4_NOFCS",
|
|
[231] = "DBUS",
|
|
[235] = "DVB_CI",
|
|
[236] = "MUX27010",
|
|
[237] = "STANAG_5066_D_PDU",
|
|
[239] = "NFLOG",
|
|
[240] = "NETANALYZER",
|
|
[241] = "NETANALYZER_TRANSPARENT",
|
|
[242] = "IPOIB",
|
|
[243] = "MPEG_2_TS",
|
|
[244] = "NG40",
|
|
[245] = "NFC_LLCP",
|
|
[247] = "INFINIBAND",
|
|
[248] = "SCTP",
|
|
[249] = "USBPCAP",
|
|
[250] = "RTAC_SERIAL",
|
|
[251] = "BLUETOOTH_LE_LL",
|
|
[253] = "NETLINK",
|
|
[254] = "BLUETOOTH_LINUX_MONITOR",
|
|
[255] = "BLUETOOTH_BREDR_BB",
|
|
[256] = "BLUETOOTH_LE_LL_WITH_PHDR",
|
|
[257] = "PROFIBUS_DL",
|
|
[258] = "PKTAP",
|
|
[259] = "EPON",
|
|
[260] = "IPMI_HPM_2",
|
|
[261] = "ZWAVE_R1_R2",
|
|
[262] = "ZWAVE_R3",
|
|
[263] = "WATTSTOPPER_DLM",
|
|
[264] = "ISO_14443",
|
|
[265] = "RDS",
|
|
[266] = "USB_DARWIN"
|
|
}
|
|
|
|
-- As described by https://wiki.wireshark.org/Development/LibpcapFileFormat
|
|
local decode = function (c)
|
|
if not detect (c ()) then error ("not a PCAP file") end
|
|
|
|
c.endianity = "le"
|
|
c:u32 ("PCAP magic: %s", function (u32)
|
|
if u32 == 0xa1b2c3d4 then return "little-endian" end
|
|
|
|
c.endianity = "be"
|
|
return "big-endian"
|
|
end)
|
|
|
|
local p, vmajor, vminor = c.position, c:u16 (), c:u16 ()
|
|
c (p, c.position - 1):mark ("PCAP version: %d.%d", vmajor, vminor)
|
|
|
|
local zone = c:s32 ("UTC to local TZ correction: %d seconds")
|
|
local sigfigs = c:u32 ("timestamp accuracy")
|
|
local snaplen = c:u32 ("max. length of captured packets: %d")
|
|
|
|
local network = c:u32 ("data link type: %s", function (u32)
|
|
name = link_types[u32]
|
|
if name then return name end
|
|
return "unknown: %d", u32
|
|
end)
|
|
|
|
local i = 0
|
|
while not c.eof do
|
|
c (c.position, c.position + 15):mark ("PCAP record %d header", i)
|
|
|
|
local p, ts_sec, ts_usec = c.position, c:u32 (), c:u32 ()
|
|
c (p, c.position - 1):mark ("timestamp: %s.%06d",
|
|
os.date ("!%F %T", ts_sec + zone), ts_usec)
|
|
local incl_len = c:u32 ("included record length: %d")
|
|
local orig_len = c:u32 ("original record length: %d")
|
|
|
|
local p = c.position
|
|
c.position = c.position + incl_len
|
|
-- TODO: also decode record contents as per the huge table
|
|
c (p, c.position - 1):mark ("PCAP record %d data", i)
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
hex.register { type="pcap", detect=detect, decode=decode }
|
|
|
|
local block_types = {
|
|
[0x0a0d0d0a] = "Section Header Block",
|
|
[0x00000001] = "Interface Description Block",
|
|
[0x00000003] = "Simple Packet Block",
|
|
[0x00000004] = "Name Resolution Block",
|
|
[0x00000005] = "Interface Statistics Block",
|
|
[0x00000006] = "Enhanced Packet Block",
|
|
|
|
[0x00000BAD] = "Custom Block",
|
|
[0x40000BAD] = "Custom Block"
|
|
}
|
|
|
|
local decode_shb = function (c)
|
|
local magic = c:u32 ()
|
|
local p, vmajor, vminor = c.position, c:u16 (), c:u16 ()
|
|
c (p, c.position - 1):mark ("PCAPNG version: %d.%d", vmajor, vminor)
|
|
-- XXX: what exactly does section_len mean?
|
|
local section_len = c:u64 ("section length: %d")
|
|
|
|
while not c.eof do
|
|
-- TODO: decode the meaning of options as well
|
|
local type = c:u16 ("option type: %d")
|
|
local length = c:u16 ("option length: %d")
|
|
|
|
local p = c.position
|
|
c.position = c.position + length + (-length & 3)
|
|
c (p, c.position - 1):mark ("option value")
|
|
end
|
|
end
|
|
|
|
local block_decoders = {
|
|
-- TODO: also decode other types of blocks
|
|
[0x0a0d0d0a] = decode_shb
|
|
}
|
|
|
|
-- As described by https://github.com/pcapng/pcapng
|
|
local decode_ng = function (c)
|
|
assert (c.position == 1)
|
|
if not detect_ng (c ()) then error ("not a PCAPNG file") end
|
|
|
|
c.endianity = "le"
|
|
c (9):u32 ("byte-order magic: %s", function (u32)
|
|
if u32 == 0x1a2b3c4d then return "little-endian" end
|
|
|
|
c.endianity = "be"
|
|
return "big-endian"
|
|
end)
|
|
|
|
while not c.eof do
|
|
local block_start = c.position
|
|
local block_type = c:u32 ("PCAPNG block type: %s", function (u32)
|
|
local name = block_types[u32]
|
|
if name then return name end
|
|
return "unknown: %d", u32
|
|
end)
|
|
local block_len = c:u32 ("PCAPNG block length: %d")
|
|
local data_start = c.position
|
|
c.position = block_start + block_len - 4
|
|
local decoder = block_decoders[block_type]
|
|
if decoder then decoder (c (data_start, c.position - 1)) end
|
|
c:u32 ("PCAPNG trailing block length: %d")
|
|
end
|
|
end
|
|
|
|
hex.register { type="pcapng", detect=detect_ng, decode=decode_ng }
|