dissect-wireshark: Refactored dissector implementation
Change-Id: I913044e5f5e9f42dc62d5d81da758f12177bb149
Refs: #3092
diff --git a/tools/dissect-wireshark/README.md b/tools/dissect-wireshark/README.md
index 3d6b07d..6284650 100644
--- a/tools/dissect-wireshark/README.md
+++ b/tools/dissect-wireshark/README.md
@@ -1,18 +1,49 @@
-ndn-dissect-wireshark
-=====================
+NDN Packet Dissector for Wireshark
+==================================
-A Wireshark dissector for [Named Data Networking (NDN) packets](http://named-data.net/doc/ndn-tlv/).
+**NDN packet dissector requires at least version 1.12.6 of Wireshark with LUA support enabled**
-The dissector is able to process and visualize structure of NDN packets encapsulated in
-IPv4/IPv6 UDP packets with source of destination port 6363, IPv4/IPv6 TCP packets with
-source or destination port 6363, IPv4/IPv6 TCP/HTTP WebSocket packets (any port).
+The dissection of [Named Data Networking (NDN) packets](http://named-data.net/doc/ndn-tlv/) is
+supported in the following cases:
-Note that when UDP packet is fragmented, only the first fragment is getting dissected.
-For TCP packets, the dissector assumes that NDN packet starts at the packet boundary,
-therefore some NDN packets will not be properly dissected. The same limitation applies to
-WebSocket packets.
+- NDN packets are encapsulated in IPv4/IPv6 UDP packets with source or destination port
+ 6363 or 56363.
-Currently, the dissector does not support NDNLPv2 packets, Link, SelectedDelegation fields.
+- NDN packets are encapsulated in IPv4/IPv6 TCP segments with source or destination
+ port 6363.
+
+- NDN packets are encapsulated in IPv4/IPv6 TCP/HTTP WebSocket packets with source or
+ destination port 9696.
+
+## Available dissection features
+
+- When UDP packet is fragmented, the dissection is performed after the full IP reassembly.
+ If the full reassembly is not possible (e.g., a wrong checksum or missing segments),
+ dissection is not performed.
+
+- When multiple NDN packets are part of a single UDP datagram, TCP segment, or WebSocket
+ payload, all NDN packets are dissected.
+
+- When a single NDN packet is scattered across multiple TCP segments or WebSocket
+ payloads, it is dissected after the successful reconstruction of the necessary portion
+ of the TCP stream. If the reconstruction of the necessary portion of the TCP stream is
+ not possible (e.g., missing segments), the dissection is not performed.
+
+- When an NDN packet is not aligned to the segment or payload boundary, the dissector
+ searches for any valid NDN packet within the segment using heuristics defined by the
+ following pseudocode:
+
+ for each offset in range (0, packet length)
+ type <- read TLV VarNumber from (buffer + offset)
+ length <- read TLV VarNumber from (buffer + offset + length of type field)
+
+ if type is either 5 or 6 // Type of NDN Interest of Data packet)
+ and length is less 8800 // Current (soft) limit for NDN packet size
+ then
+ dissect NDN packet from (buffer + offset)
+ end if
+
+Currently, the dissector does not support NDNLPv2 packets.
## Usage
diff --git a/tools/dissect-wireshark/ndn.lua b/tools/dissect-wireshark/ndn.lua
index d0b21a2..643bcb0 100644
--- a/tools/dissect-wireshark/ndn.lua
+++ b/tools/dissect-wireshark/ndn.lua
@@ -17,6 +17,7 @@
-- @author Qi Zhao <https://www.linkedin.com/pub/qi-zhao/73/835/9a3>
-- @author Seunghyun Yoo <http://relue2718.com/>
-- @author Seungbae Kim <https://sites.google.com/site/sbkimcv/>
+-- @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
-- inspect.lua (https://github.com/kikito/inspect.lua) can be used for debugging.
@@ -24,594 +25,376 @@
-- local inspect = require('inspect')
-- NDN protocol
-p_ndnproto = Proto ("ndn", "Named Data Network (NDN)") -- to create a 'Proto' object
+ndn = Proto("ndn", "Named Data Networking (NDN)")
--- Type and Length fields
-local f_packet_type = ProtoField.uint16("ndn.type", "Type", base.DEC_HEX)
-local f_packet_size = ProtoField.uint16("ndn.length", "Length", base.DEC_HEX)
+-----------------------------------------------------
+-----------------------------------------------------
+-- Field formatting helpers
--- Interest or Data packets
-local f_interest = ProtoField.string("ndn.interest", "Interest", FT_STRING)
-local f_data = ProtoField.string("ndn.data", "Data", FT_STRING)
-
--- Name field
-local f_name = ProtoField.string("ndn.name", "Name", FT_STRING)
-local f_namecomponent = ProtoField.string("ndn.namecomponent", "Name Component", FT_STRING)
-local f_implicitSHA = ProtoField.string("ndn.implicitsha", "Implicit SHA 256 Digest Component", FT_STRING)
-
--- Sub-fields of Interest packet
-local f_interest_selector = ProtoField.string("ndn.selector", "Selector", FT_STRING)
-local f_interest_nonce = ProtoField.uint16("ndn.nonce", "Nonce", base.DEC_HEX)
-local f_interest_scope = ProtoField.string("ndn.scope", "Scope", FT_STRING)
-local f_interest_interestlifetime = ProtoField.uint16("ndn.interestlifetime", "Interest Life Time", base.DEC_HEX)
-
--- Sub-fields of Interest/Selector field
-local f_interest_selector_minsuffix = ProtoField.uint16("ndn.minsuffix", "Min Suffix Components", base.DEC_HEX)
-local f_interest_selector_maxsuffix = ProtoField.uint16("ndn.maxsuffix", "Max Suffix Components", base.DEC_HEX)
-local f_interest_selector_keylocator = ProtoField.string("ndn.keylocator", "Publisher Public Key Locator", FT_STRING)
-local f_interest_selector_exclude = ProtoField.string("ndn.exclude", "Exclude", FT_STRING)
-local f_interest_selector_childselector = ProtoField.uint16("ndn.childselector", "Child Selector", base.DEC_HEX)
-local f_interest_selector_mustbefresh = ProtoField.string("ndn.mustbefresh", "Must Be Fresh", FT_STRING)
-local f_interest_selector_any = ProtoField.string("ndn.any", "Any", FT_STRING)
-
--- Sub-fields of Data packet
-local f_data_metainfo = ProtoField.string("ndn.metainfo", "Meta Info", FT_STRING)
-local f_data_content = ProtoField.string("ndn.content", "Content", FT_STRING)
-local f_data_signatureinfo = ProtoField.string("ndn.signatureinfo", "Signature Info", FT_STRING)
-local f_data_signaturevalue = ProtoField.string("ndn.signaturevalue", "Signature Value", FT_STRING)
-
--- Sub-fields of Data/MetaInfo field
-local f_data_metainfo_contenttype = ProtoField.uint16("ndn.contenttype", "Content Type", base.DEC_HEX)
-local f_data_metainfo_freshnessperiod = ProtoField.uint16("ndn.freshnessperiod", "Freshness Period", base.DEC_HEX)
-local f_data_metainfo_finalblockid = ProtoField.string("ndn.finalblockid", "Final Block ID", FT_STRING)
-
--- Sub-fields of Data/Signature field
-local f_data_signature_signaturetype = ProtoField.uint16("ndn.signaturetype", "Signature Type", base.DEC_HEX)
-local f_data_signature_keylocator = ProtoField.string("ndn.keylocator", "Key Locator", FT_STRING)
-local f_data_signature_keydigest = ProtoField.string("ndn.keydigest", "Key Digest", FT_STRING)
-
--- Add protofields in NDN protocol
-p_ndnproto.fields = {f_packet_type, f_packet_size, f_data, f_interest, f_name, f_namecomponent, f_implicitSHA, f_interest_selector, f_interest_nonce, f_interest_scope, f_interest_interestlifetime, f_interest_selector_mustbefresh, f_interest_selector_minsuffix, f_interest_selector_maxsuffix, f_interest_selector_keylocator, f_interest_selector_exclude, f_interest_selector_childselector, f_interest_selector_any, f_data_metainfo, f_data_content, f_data_signatureinfo, f_data_signaturevalue, f_data_metainfo_contenttype, f_data_metainfo_freshnessperiod, f_data_metainfo_finalblockid, f_data_signature_signaturetype, f_data_signature_keylocator, f_data_signature_keydigest}
-
--- ndntlv_info = { data: { field, type, string }, children: {} }
-
--- To handle the fragmented packets
--- type: map
--- * key: (host ip address, host port number)
--- * value: type: map
--- * key: packet number
--- * value: packet status
-local pending_packets = {}
-local CONST_STR_TRUNCATED = "TRUNCATED"
-local CONST_STR_NDNTLV = "NDNTLV"
-local GLOBAL_PACKET_INDEX = 0
-
-function set_packet_status( packet_key, packet_number, status_key, status_value )
- if type( pending_packets[ packet_key ] ) ~= "table" then
- pending_packets[ packet_key ] = {}
- end
- if type( pending_packets[ packet_key ][ packet_number ] ) ~= "table" then
- pending_packets[ packet_key ][ packet_number ] = {}
- end
- pending_packets[ packet_key ][ packet_number ][ status_key ] = status_value
+-- Borrowed from http://lua-users.org/wiki/StringRecipes
+function escapeString(str)
+ if (str) then
+ str = string.gsub(str, "\n", "\r\n")
+ str = string.gsub(str, "([^%w %-%_%.%~])",
+ function (c) return string.format ("%%%02X", string.byte(c)) end)
+ str = string.gsub(str, " ", "+")
+ end
+ return str
end
-function get_packet_status( packet_key, packet_number, status_key )
- if type( pending_packets[ packet_key ] ) ~= "table" then
- return nil
- end
- if type( pending_packets[ packet_key ][ packet_number ] ) ~= "table" then
- return nil
- end
- return pending_packets[ packet_key ][ packet_number ][ status_key ]
+function getUriFromNameComponent(block)
+ -- @todo Implement proper proper URL escaping
+ return block.tvb(block.offset + block.typeLen + block.lengthLen, block.length):string()
end
-function get_keys_from( table )
- local keyset = {}
- local n = 0
- for k, v in pairs( table ) do
- n = n + 1
- keyset[n] = k
- end
- return keyset
+function getUriFromName(nameBlock)
+ if (nameBlock.elements == nil) then
+ return ""
+ else
+ components = {}
+ for i, block in pairs(nameBlock.elements) do
+ table.insert(components, getUriFromNameComponent(block))
+ end
+
+ return "/" .. table.concat(components, "/")
+ end
end
-function dump_packet_status()
- --print(inspect(pending_packets))
+function getNonNegativeInteger(b)
+ if (b.length == 1) then
+ return b.tvb(b.offset + b.typeLen + b.lengthLen, 1):uint()
+ elseif (b.length == 2) then
+ return b.tvb(b.offset + b.typeLen + b.lengthLen, 2):uint()
+ elseif (b.length == 4) then
+ return b.tvb(b.offset + b.typeLen + b.lengthLen, 4):uint()
+ -- Something strange with uint64, not supporting it for now
+ -- elseif (b.length == 8) then
+ -- return b.tvb(b.offset + b.typeLen + b.lengthLen, 8):uint64()
+ else
+ return 0xFFFFFFFF;
+ end
end
-function bytearray_to_int( raw_bytes, offset, length )
- local ret = 0
- for i = offset, offset + length - 1 do
- ret = ret * 256 + raw_bytes:get_index( i )
- end
- return ret
+function getUriFromExclude(block)
+ -- @todo
+ return ""
end
-function deepcopy(orig)
- local orig_type = type(orig)
- local copy
- if orig_type == 'table' then
- copy = {}
- for orig_key, orig_value in next, orig, nil do
- copy[deepcopy(orig_key)] = deepcopy(orig_value)
- end
- setmetatable(copy, deepcopy(getmetatable(orig)))
- else -- number, string, boolean, etc
- copy = orig
- end
- return copy
+function getTrue(block)
+ return "Yes"
end
-function parse_ndn_tlv( packet_key, packet_number, is_original, max_size, optional_params, ndntlv_info )
- local raw_bytes = nil
- local buf = nil
- local length = nil
+local AppPrivateBlock1 = 128
+local AppPrivateBlock2 = 32767
- if ( is_original ) then
- buf = optional_params["buf"]
- length = buf:len()
- else
- raw_bytes = optional_params["raw_bytes"]
- length = raw_bytes:len()
- end
+function getGenericBlockInfo(block)
+ local name = ""
- local current_pos = 0
- local _size_num_including_header = 0
+ if (block.type < AppPrivateBlock1) then
+ name = "RESERVED_1"
+ elseif (AppPrivateBlock1 <= block.type and block.type < 253) then
+ name = "APP_TAG_1"
+ elseif (253 <= block.type and block.type < AppPrivateBlock2) then
+ name = "RESERVED_3"
+ else
+ name = "APP_TAG_3"
+ end
- local ret = true -- a result of a ndn-tlv parser
- local isFirst = false -- flag that is going to be enabled when the first buffer arrives [BUGGY]
+ return name .. ", Type: " .. block.type .. ", Length: " .. block.length
+end
- while ( current_pos < length ) do
- isFirst = ( current_pos == 0 )
+-----------------------------------------------------
+-----------------------------------------------------
- -- extract TYPE
- local _type_uint = nil
- if ( is_original ) then
- _type_uint = buf( current_pos, 1 ):uint()
- else
- _type_uint = bytearray_to_int( raw_bytes, current_pos, 1 )
- end
+local NDN_DICT = {
+ -- Interest or Data packets
+ [5] = {name = "Interest" , summary = true},
+ [6] = {name = "Data" , summary = true},
- -- print("type:" .. _type_uint)
+ -- Name field
+ [7] = {name = "Name" , field = ProtoField.string("ndn.name", "Name") , value = getUriFromName},
+ [1] = {name = "ImplicitSha256DigestComponent", field = ProtoField.string("ndn.implicitsha256", "ImplicitSha256DigestComponent"), value = getUriFromNameComponent},
+ [8] = {name = "NameComponent" , field = ProtoField.string("ndn.namecomponent", "NameComponent") , value = getUriFromNameComponent},
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + 1
- end
- current_pos = current_pos + 1
+ -- Sub-fields of Interest packet
+ [9] = {name = "Selectors" , summary = true},
+ [10] = {name = "Nonce" , field = ProtoField.bytes("ndn.nonce", "Nonce")},
+ [12] = {name = "InterestLifetime" , field = ProtoField.uint32("ndn.interestlifetime", "InterestLifetime", base.DEC) , value = getNonNegativeInteger},
- -- extract SIZE
- local _size_num = nil
- if ( is_original ) then
- _size_num = buf( current_pos, 1 ):uint()
- else
- _size_num = bytearray_to_int( raw_bytes, current_pos, 1 )
- end
+ -- Sub-fields of Interest/Selector field
+ [13] = {name = "MinSuffixComponents" , field = ProtoField.uint32("ndn.minsuffix", "MinSuffixComponents") , value = getNonNegativeInteger},
+ [14] = {name = "MaxSuffixComponents" , field = ProtoField.uint32("ndn.maxsuffix", "MaxSuffixComponents") , value = getNonNegativeInteger},
+ [15] = {name = "PublisherPublicKeyLocator" , summary = true},
+ [16] = {name = "Exclude" , field = ProtoField.string("ndn.exclude", "Exclude") , value = getUriFromExclude},
+ [17] = {name = "ChildSelector" , field = ProtoField.uint32("ndn.childselector", "ChildSelector", base.DEC) , value = getNonNegativeInteger},
+ [18] = {name = "MustBeFresh" , field = ProtoField.string("ndn.mustbefresh", "MustBeFresh") , value = getTrue},
+ [19] = {name = "Any" , field = ProtoField.string("ndn.any", "Any") , value = getTrue},
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + 1
- end
- current_pos = current_pos + 1
+ -- Sub-fields of Data packet
+ [20] = {name = "MetaInfo" , summary = true},
+ [21] = {name = "Content" , field = ProtoField.string("ndn.content", "Content")},
+ [22] = {name = "SignatureInfo" , summary = true},
+ [23] = {name = "SignatureValue" , field = ProtoField.bytes("ndn.signaturevalue", "SignatureValue")},
- if ( _size_num == 253 ) then
- if ( is_original ) then
- _size_num = buf( current_pos, 2 ):uint()
+ -- Sub-fields of Data/MetaInfo field
+ [24] = {name = "ContentType" , field = ProtoField.uint32("ndn.contenttype", "Content Type", base.DEC) , value = getNonNegativeInteger},
+ [25] = {name = "FreshnessPeriod" , field = ProtoField.uint32("ndn.freshnessperiod", "FreshnessPeriod", base.DEC) , value = getNonNegativeInteger},
+ [26] = {name = "FinalBlockId" , field = ProtoField.string("ndn.finalblockid", "FinalBlockId") , value = getUriFromNameComponent},
+
+ -- Sub-fields of Data/Signature field
+ [27] = {name = "SignatureType" , field = ProtoField.uint32("ndn.signaturetype", "SignatureType", base.DEC) , value = getNonNegativeInteger},
+ [28] = {name = "KeyLocator" , summary = true},
+ [29] = {name = "KeyDigest" , field = ProtoField.bytes("ndn.keydigest", "KeyDigest")},
+
+ -- Other fields
+ [30] = {name = "LinkPreference" , field = ProtoField.uint32("ndn.link_preference", "LinkPreference", base.DEC) , value = getNonNegativeInteger},
+ [31] = {name = "LinkDelegation" , summary = true},
+ [32] = {name = "SelectedDelegation" , field = ProtoField.uint32("ndn.selected_delegation", "SelectedDelegation", base.DEC), value = getNonNegativeInteger},
+}
+
+
+-- -- Add protofields in NDN protocol
+ndn.fields = {
+}
+for key, value in pairs(NDN_DICT) do
+ table.insert(ndn.fields, value.field)
+end
+
+
+-----------------------------------------------------
+-----------------------------------------------------
+
+-- block
+-- .tvb
+-- .offset
+-- .type
+-- .typeLen
+-- .length
+-- .lengthLen
+-- .size = .typeLen + .lengthLen + .length
+
+function addInfo(block, root) -- may be add additional context later
+ local info = NDN_DICT[block.type]
+
+ if (info == nil) then
+ info = {}
+ info.value = getGenericBlockInfo
+ end
+
+ local treeInfo
+ if (info.value == nil) then
+
+ if (info.field ~= nil) then
+ treeInfo = root:add(info.field, block.tvb(block.offset, block.size))
else
- _size_num = bytearray_to_int( raw_bytes, current_pos, 2 )
+ treeInfo = root:add(block.tvb(block.offset, block.size), info.name)
end
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + _size_num + 2
- end
- current_pos = current_pos + 2
- elseif ( _size_num == 254 ) then
- if ( is_original ) then
- _size_num = buf( current_pos, 4 ):uint()
+
+ treeInfo:append_text(", Type: " .. block.type .. ", Length: " .. block.length)
+ else
+ block.value = info.value(block)
+
+ if (info.field ~= nil) then
+ treeInfo = root:add(info.field, block.tvb(block.offset, block.size), block.value)
else
- _size_num = bytearray_to_int( raw_bytes, current_pos, 4 )
+ treeInfo = root:add(block.tvb(block.offset, block.size), info.name)
end
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + _size_num + 4
- end
- current_pos = current_pos + 4
- elseif ( _size_num == 255 ) then
- print("## error ## lua doesn't support 8 bytes of number variables.")
- if ( is_original ) then
- _size_num = buf( current_pos, 8 ):uint64() -- can lua number be larger than 32 bits? -- the type 'userdata'
- else
- _size_num = bytearray_to_int( raw_bytes, current_pos, 8 )
- end
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + _size_num + 8
- end
- current_pos = current_pos + 8
- else
- if ( isFirst ) then
- _size_num_including_header = _size_num_including_header + _size_num
- end
- end
-
- -- subtree:add( f_packet_size, _size )
- local type_size_info = " (Type: " .. _type_uint .. ", Size: " .. _size_num .. ")"
-
- -- need to check which one should be used: either _size_num or _size_num_including_header
- if ( max_size ~= -1 and max_size < _size_num ) then
- if ( is_original ) then
- set_packet_status( packet_key, packet_number, "error", "The size of sub ndn-tlv packet can't exceed the parent's one." )
- end
- ret = false
- break
- end
-
- if ( isFirst ) then
- if ( is_original ) then
- set_packet_status( packet_key, packet_number, "expected_size", _size_num_including_header )
- end
- end
-
- if ( _type_uint == 18 ) then
- if ( is_original ) then
- set_packet_status( packet_key, packet_number, "error", "the type of field is 18 (but why is this an error?).")
- end
- return ret
- end
-
- if ( current_pos + _size_num > length ) then
- if ( is_original ) then
- set_packet_status( packet_key, packet_number, "status", CONST_STR_TRUNCATED)
- end
- ret = false
- break
- end
-
- local _payload = nil
- local new_optional_params = {}
- if ( is_original ) then
- _payload = buf( current_pos, _size_num )
- new_optional_params["buf"] = _payload
- else
- new_optional_params["raw_bytes"] = raw_bytes:subset( current_pos, _size_num )
- end
- current_pos = current_pos + _size_num
-
- local child_tree = nil
-
- if ( _type_uint == 5 ) then -- interest packet can contain sub NDN-TLV packets
- -- Interest packet
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_interest, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 6 ) then
- -- Data packet
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_data, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 7 ) then
- -- Name
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_name, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 8 ) then
- -- Name Component
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_namecomponent, _payload, _payload:string(ENC_UTF_8) .. type_size_info } )
- end
- elseif ( _type_uint == 1 ) then
- -- Implicit SHA 256 Digest Component
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_implicitSHA, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 9 ) then
- -- Selectors
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_interest_selector, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 10 ) then
- -- Nonce
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_nonce, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 11 ) then
- -- Scope
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_scope, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 12 ) then
- -- Interest Lifetime
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_interestlifetime, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 13 ) then
- -- Selectors / Min Suffix Components
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_selector_minsuffix, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 14 ) then
- -- Selectors / Max Suffix Components
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_selector_maxsuffix, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 15 ) then
- -- Selectors / Publish Key Locator
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_interest_selector_keylocator, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 16 ) then
- -- Selectors / Exclude
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_interest_selector_exclude, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 17 ) then
- -- Selectors / Child Selector
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_selector_childselector, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 18 ) then
- -- Selectors / Must be Fresh
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_selector_mustbefresh, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 19 ) then
- -- Selectors / Any
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_interest_selector_any, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 20 ) then
- -- MetaInfo
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_data_metainfo, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 21 ) then
- -- Content
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_content, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 22 ) then
- -- SignatureInfo
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_data_signatureinfo, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 23 ) then
- -- SignatureValue
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_signaturevalue, _payload, _payload:string() .. type_size_info } )
- end
- elseif ( _type_uint == 24 ) then
- -- MetaInfo / ContentType
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_metainfo_contenttype, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 25 ) then
- -- MetaInfo / FreshnessPeriod
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_metainfo_freshnessperiod, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 26 ) then
- -- MetaInfo / FinalBlockId
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_data_metainfo_finalblockid, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 27 ) then
- -- Signature / SignatureType
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_signature_signaturetype, _payload, _payload:uint(), nil, type_size_info } )
- end
- elseif ( _type_uint == 28 ) then
- -- Signature / KeyLocator
- if ( is_original ) then
- child_tree = add_subtree( ndntlv_info, { f_data_signature_keylocator, _payload, type_size_info } )
- end
- ret = ret and parse_ndn_tlv( packet_key, packet_number, is_original, _size_num, new_optional_params, child_tree )
- elseif ( _type_uint == 29 ) then
- -- Signature / KeyDigest
- if ( is_original ) then
- add_subtree( ndntlv_info, { f_data_signature_keydigest, _payload, _payload:string() .. type_size_info } );
- end
- else
- --print("## warning ## unhandled type_uint: ", _type_uint)
- ret = false
- -- if the packet seems to be a NDN packet, it would be better idea to add some warning messages in the subtress instead of returning false.
- end
- end
- return ret
+ end
+ block.root = treeInfo
+ return block.root
end
-function create_subtree_from( info, subtree )
- for k, v in pairs( info["children"] ) do
- local data = v["data"]
- if type(data) == "table" then
- local child_tree = subtree:add( unpack( data ) )
- create_subtree_from( v, child_tree )
- end
- end
-end
+function addSummary(block)
+ if (block.elements == nil) then
+ return
+ end
-function add_subtree( info, data )
- local child_tree = { ["data"] = data, ["children"] = {} }
- table.insert( info["children"], child_tree )
- return child_tree
-end
+ local info = NDN_DICT[block.type]
+ if (info == nil or info.summary == nil) then
+ return
+ end
-function create_empty_ndntlv_info()
- return { ["data"] = nil, ["children"] = {} }
-end
+ local summary = {}
-function parse_buffer_and_update( packet_key, packet_number, is_original, pkt, root, optional_params )
- -- TODO: need to set the maximum length
- local ndntlv_info = create_empty_ndntlv_info()
- local was_ndntlv_packet = parse_ndn_tlv( packet_key, packet_number, is_original, -1, optional_params, ndntlv_info )
-
- if was_ndntlv_packet then
- local buf = nil
- if ( is_original ) then
- buf = optional_params["buf"]
- set_packet_status( packet_key, packet_number, "ndntlv_info", ndntlv_info )
- set_packet_status( packet_key, packet_number, "status", CONST_STR_NDNTLV )
- else
- buf = ByteArray.tvb( optional_params["raw_bytes"], optional_params["tvb_name"] )
- ndntlv_info = create_empty_ndntlv_info()
- parse_ndn_tlv( packet_key, packet_number, true, -1, { ["buf"] = buf }, ndntlv_info )
-
- local used_packet_numbers = optional_params["used_packet_numbers"]
-
- for k,v in pairs(used_packet_numbers) do
- set_packet_status( packet_key, v, "ndntlv_info", ndntlv_info )
- set_packet_status( packet_key, v, "status", CONST_STR_NDNTLV )
+ for k, subblock in pairs(block.elements) do
+ if (subblock.value ~= nil) then
+ local info = NDN_DICT[subblock.type]
+ if (info ~= nil) then
+ table.insert(summary, info.name .. ": " .. subblock.value)
+ end
end
- end
- end
+ end
- -- print( packet_key .. "--" .. packet_number .. ".." .. tostring(was_ndntlv_packet) )
-
- -- It needs to check whether the packet type is NDN-TLV.
- local saved_ndntlv_info = get_packet_status( packet_key, packet_number, "ndntlv_info" )
- local parsed = get_packet_status( packet_key, packet_number, "parsed" )
- if saved_ndntlv_info ~= nil then
- pkt.cols.protocol = p_ndnproto.name -- set the protocol name to NDN
- if ( parsed ~= true ) then
- set_packet_status( packet_key, packet_number, "parsed", true )
- local subtree = root:add( p_ndnproto, buf ) -- create subtree for ndnproto
- create_subtree_from( saved_ndntlv_info, subtree )
- end
- end
+ if (#summary > 0) then
+ block.summary = table.concat(summary, ", ")
+ if (block.value == nil) then
+ block.value = block.summary
+ end
+ block.root:append_text(", " .. block.summary)
+ end
end
--- # not efficient
--- # lua -- doesn't support the random access...?
-function get_next_element( tbl, current_value )
- for k, v in pairs( tbl ) do
- if ( v > current_value ) then
- return v
- end
- end
- return current_value
+-----------------------------------------------------
+-----------------------------------------------------
+
+function readVarNumber(tvb, offset)
+ local firstOctet = tvb(offset, 1):uint()
+ if (firstOctet < 253) then
+ return firstOctet, 1
+ elseif (firstOctet == 253) then
+ return tvb(offset + 1, 2):uint(), 3
+ elseif (firstOctet == 254) then
+ return tvb(offset + 1, 4):uint(), 5
+ elseif (firstOctet == 255) then
+ return tvb(offset + 1, 8):uint64(), 6
+ end
end
--- # not efficient
-function get_previous_element( tbl, current_value )
- local prev = current_value
- for k, v in pairs( tbl ) do
- if ( v < current_value ) then
- prev = v
- else
- break
- end
- end
- return prev
+function getBlock(tvb, offset)
+ local block = {}
+ block.tvb = tvb
+ block.offset = offset
+
+ block.type, block.typeLen = readVarNumber(block.tvb, block.offset)
+ block.length, block.lengthLen = readVarNumber(block.tvb, block.offset + block.typeLen)
+
+ block.size = block.typeLen + block.lengthLen + block.length
+
+ return block
end
--- ndnproto dissector function
-function p_ndnproto.dissector( buf, pkt, root )
- -- validate packet length is adequate, otherwise quit
- local length = buf:len()
- local packet_number = pkt.number -- an unique serial for each packet
- local packet_key = tostring(pkt.src) .. ":" .. tostring(pkt.src_port) .. ":" .. tostring(pkt.dst) .. ":" .. tostring(pkt.dst_port)
- print("## info ## packet[" .. packet_number .. "], length = " .. length )
- set_packet_status( packet_key, packet_number, "parsed", false )
+function findNdnPacket(tvb)
+ offset = 0
- if length == 0 then
- else
- local raw_bytes = buf:range():bytes()
- parse_buffer_and_update( packet_key, packet_number, true, pkt, root, { ["buf"] = buf } )
- set_packet_status( packet_key, packet_number, "buffer", raw_bytes )
+ while offset + 2 < tvb:len() do
+ local block = getBlock(tvb, offset)
- local pending_packet_numbers = get_keys_from( pending_packets[ packet_key ] )
- for k, v in pairs( pending_packet_numbers ) do
- local pending_packet_number = v
+ if ((block.type == 5 or block.type == 6) and block.length <= 8800) then
+ return block
+ end
- if ( pending_packet_number <= packet_number ) then
+ offset = offset + 1
+ end
- local status = get_packet_status( packet_key, pending_packet_number, "status" )
- local expected_size = get_packet_status( packet_key, pending_packet_number, "expected_size" )
- local used_packet_numbers = {}
+ return nil
+end
- if ( status == CONST_STR_TRUNCATED ) then
- local merged_temp_buf = ByteArray.new()
- local temp_packet_number = pending_packet_number
- local pending_packet_number_end = 0
- while (merged_temp_buf:len() < expected_size) do
- local temp_buf = get_packet_status( packet_key, temp_packet_number, "buffer" )
- if ( temp_buf == nil ) then
- break
- else
- merged_temp_buf:append( temp_buf )
- pending_packet_number_end = temp_packet_number
+function getSubBlocks(block)
+ local valueLeft = block.length
+ local subBlocks = {}
- table.insert( used_packet_numbers, temp_packet_number )
- temp_packet_number = get_next_element( pending_packet_numbers, temp_packet_number )
+ while valueLeft > 0 do
+ local child = getBlock(block.tvb,
+ block.offset + block.typeLen + block.lengthLen + (block.length - valueLeft))
+
+ valueLeft = valueLeft - child.size
+ table.insert(subBlocks, child)
+ end
+
+ if (valueLeft == 0) then
+ return subBlocks
+ else
+ return nil
+ end
+end
+
+-----------------------------------------------------
+-----------------------------------------------------
+
+-- NDN protocol dissector function
+function ndn.dissector(tvb, pInfo, root) -- Tvb, Pinfo, TreeItem
+
+ if (tvb:len() ~= tvb:reported_len()) then
+ return 0 -- ignore partially captured packets
+ -- this can/may be re-enabled only for unfragmented UDP packets
+ end
+
+ local ok, block = pcall(findNdnPacket, tvb)
+ if (not ok) then
+ return 0
+ end
+
+ if (block == nil or block.offset == nil) then
+ -- no valid NDN packets found
+ return 0
+ end
+
+ local nBytesLeft = tvb:len() - block.offset
+ -- print (pInfo.number .. ":: Found block: " .. block.type .. " of length " .. block.size .. " bytesLeft: " .. nBytesLeft)
+
+ while (block.size <= nBytesLeft) do
+ -- Create TreeItems
+ block.tree = root:add(ndn, tvb(block.offset, block.size))
+
+ local queue = {block}
+ while (#queue > 0) do
+ local block = queue[1]
+ table.remove(queue, 1)
+
+ block.elements = getSubBlocks(block)
+ local subtree = addInfo(block, block.tree)
+
+ if (block.elements ~= nil) then
+ for i, subBlock in pairs(block.elements) do
+ subBlock.tree = subtree
+ table.insert(queue, subBlock)
end
- end
- if ( merged_temp_buf:len() >= expected_size ) then
- local merged_tvb_name = "Reassembled (" .. pending_packet_number .. "-" .. pending_packet_number_end .. ")"
- local merged_parser_option = {
- ["raw_bytes"] = merged_temp_buf,
- ["tvb_name"] = merged_tvb_name,
- ["pending_packet_number"] = pending_packet_number,
- ["pending_packet_number_end"] = pending_packet_number_end,
- ["used_packet_numbers"] = used_packet_numbers,
- }
- print(pending_packet_number .. ".." .. pending_packet_number_end)
- parse_buffer_and_update( packet_key, packet_number, false, pkt, root, merged_parser_option )
- end
- end
+ end
end
- end
- --dump_packet_status()
- end
-end
--- Initialization routine
-function p_ndnproto.init()
-end
+ -- Make summaries
+ local queue = {block}
+ while (#queue > 0) do
+ local block = queue[1]
+ if (block.visited ~= nil or block.elements == nil) then
+ -- try to make summary
+ table.remove(queue, 1)
-local websocket_dissector_table = DissectorTable.get("ws.port")
-websocket_dissector_table:add("1-65535", p_ndnproto)
-
-local tcp_dissector_table = DissectorTable.get("tcp.port")
-tcp_dissector_table:add("6363", p_ndnproto)
-
-local udp_dissector_table = DissectorTable.get("udp.port")
-udp_dissector_table:add("6363", p_ndnproto)
-
-print("ndntlv.lua is successfully loaded.")
-
-----------------------------------------------------------------------
--- helper functions
-----------------------------------------------------------------------
-function dump_buf(buf)
- print("buffer.length = "..buf:len())
- local tmp = ""
- for i=0, buf:len()-1 do
- if i % 16 == 0 then
- tmp = tmp .. string.format("%04d",i) .. " : "
+ addSummary(block)
+ else
+ for i, subBlock in pairs(block.elements) do
+ table.insert(queue, 1, subBlock)
+ end
+ block.visited = true
+ end
end
- tmp = tmp .. (buf:range(i,1).." ")
- if (i+1) % 16 == 0 then
- tmp = tmp .. ("\n")
+
+ local info = NDN_DICT[block.type]
+ if (info ~= nil) then
+ block.tree:append_text(", " .. NDN_DICT[block.type].name .. ", " .. block.summary)
end
- end
- print(tmp)
+
+ nBytesLeft = nBytesLeft - block.size
+
+ if (nBytesLeft > 0) then
+ ok, block = pcall(getBlock, tvb, tvb:len() - nBytesLeft)
+ if (not ok) then
+ break
+ end
+ end
+ end
+
+ pInfo.cols.protocol = tostring(pInfo.cols.protocol) .. " (" .. ndn.name .. ")"
+
+ if (nBytesLeft > 0 and block.size > nBytesLeft) then
+ pInfo.desegment_offset = tvb:len() - nBytesLeft
+
+ -- Originally, I set desegment_len to the exact lenght, but it mysteriously didn't work for TCP
+ -- pInfo.desegment_len = block.size -- this will not work to desegment TCP streams
+ pInfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
+ end
end
-function print_table(tbl, indent)
- if not indent then indent = 0 end
- for k, v in pairs(tbl) do
- formatting = string.rep(" ", indent) .. k .. ": "
- if type(v) == "table" then
- print(formatting)
- print_table(v, indent+1)
- elseif type(v) == 'boolean' then
- print(formatting , tostring(v))
- else
- print(formatting , v)
- end
- end
-end
+local udpDissectorTable = DissectorTable.get("udp.port")
+udpDissectorTable:add("6363", ndn)
+udpDissectorTable:add("56363", ndn)
+
+local tcpDissectorTable = DissectorTable.get("tcp.port")
+tcpDissectorTable:add("6363", ndn)
+
+local websocketDissectorTable = DissectorTable.get("ws.port")
+-- websocketDissectorTable:add("9696", ndn)
+websocketDissectorTable:add("1-65535", ndn)
+
+-- print("ndn.lua is successfully loaded")
+io.stderr:write("ndn.lua is successfully loaded\n")