dissect-wireshark: Lua-based Wireshark dissector for NDN packets

Change-Id: Ic81f3050d5f1480e9e98f3d54b01ca2cffca56b4
Refs: #3092
diff --git a/tools/dissect-wireshark/ndn.lua b/tools/dissect-wireshark/ndn.lua
new file mode 100644
index 0000000..d0b21a2
--- /dev/null
+++ b/tools/dissect-wireshark/ndn.lua
@@ -0,0 +1,617 @@
+-- Copyright (c) 2015,  Regents of the University of California.
+--
+-- This file is part of ndn-tools (Named Data Networking Essential Tools).
+-- See AUTHORS.md for complete list of ndn-tools authors and contributors.
+--
+-- ndn-tools is free software: you can redistribute it and/or modify it under the terms
+-- of the GNU General Public License as published by the Free Software Foundation,
+-- either version 3 of the License, or (at your option) any later version.
+--
+-- ndn-tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+-- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+-- PURPOSE.  See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+--
+-- @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/>
+
+
+-- inspect.lua (https://github.com/kikito/inspect.lua) can be used for debugging.
+-- See more at http://stackoverflow.com/q/15175859/2150331
+-- local inspect = require('inspect')
+
+-- NDN protocol
+p_ndnproto = Proto ("ndn", "Named Data Network (NDN)") -- to create a 'Proto' object
+
+-- 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)
+
+-- 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
+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 ]
+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
+end
+
+function dump_packet_status()
+  --print(inspect(pending_packets))
+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
+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
+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
+
+  if ( is_original ) then
+    buf = optional_params["buf"]
+    length = buf:len()
+  else
+    raw_bytes = optional_params["raw_bytes"]
+    length = raw_bytes:len()
+  end
+
+  local current_pos = 0
+  local _size_num_including_header = 0
+
+  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]
+
+  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
+
+    -- print("type:" .. _type_uint)
+
+    if ( isFirst ) then
+      _size_num_including_header = _size_num_including_header + 1
+    end
+    current_pos = current_pos + 1
+
+    -- 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
+
+    if ( isFirst ) then
+        _size_num_including_header = _size_num_including_header + 1
+    end
+    current_pos = current_pos + 1
+
+    if ( _size_num == 253 ) then
+      if ( is_original ) then
+        _size_num = buf( current_pos, 2 ):uint()
+      else
+        _size_num = bytearray_to_int( raw_bytes, current_pos, 2 )
+      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()
+      else
+        _size_num = bytearray_to_int( raw_bytes, current_pos, 4 )
+      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
+
+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 add_subtree( info, data )
+  local child_tree = { ["data"] = data, ["children"] = {} }
+  table.insert( info["children"], child_tree )
+  return child_tree
+end
+
+function create_empty_ndntlv_info()
+  return { ["data"] = nil, ["children"] = {} }
+end
+
+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 )
+      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
+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
+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
+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 )
+
+  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 )
+
+    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 ( pending_packet_number <= packet_number ) then
+
+        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 = {}
+
+        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
+
+              table.insert( used_packet_numbers, temp_packet_number )
+              temp_packet_number = get_next_element( pending_packet_numbers, temp_packet_number )
+            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
+    --dump_packet_status()
+  end
+end
+
+-- Initialization routine
+function p_ndnproto.init()
+end
+
+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) .. " : "
+      end
+      tmp = tmp .. (buf:range(i,1).." ")
+      if (i+1) % 16 == 0 then
+        tmp = tmp .. ("\n")
+      end
+  end
+  print(tmp)
+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