-- 
-- HD-PLC protocol analyzer
-- By Panasonic AVC networks
-- Copyright 2014.7 Panasonic AVC networks
--
-- This program 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 2
-- of the License, or (at your option) any later version.
--
-- This program 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 this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

g_HdplcFrmLen = 0
g_HdrTS = 0
g_HdrCINR = 0
g_HdrRSSI = 0
g_HdrBitMap0 = 0
g_HdrBitMap1 = 0
g_buffer = nil
--Reassembled Ethernet segments
etherSegmt = {}
etherSegmt_len = {}
etherSegmt_pktnum = {}
etherSegmt_num = 0
etherSegmt_Sumlen = 0
etherSegmt_string = ""
etherSegmt_pktnum_ressm = {}
etherSegmtsBuf_ressm = {}
etherSegmtsBuf_ressm_tmp = {}
g_bufferSegmt_ressm = {}
etherSegmt_string_ressm = {}
etherSegmt_num_ressm = {}
--TS
g_HdrTS_ressm = {}
--CiNR
g_HdrCINR_ressm = {}
--Rssi
g_HdrRSSI_ressm = {}
-- BitMap0
g_HdrBitMap0_ressm = {}
-- BitMap1
g_HdrBitMap1_ressm = {}

local function init()
	g_HdplcFrmLen = 0
	g_HdrTS = 0
	g_HdrCINR = 0
	g_HdrRSSI = 0
	g_HdrBitMap0 = 0
	g_HdrBitMap1 = 0
	g_buffer = nil
	--Reassembled Ethernet segments
	etherSegmt = {}          -- segment frame information
	etherSegmt_len = {}      -- sumlength of segment frames
	etherSegmt_pktnum = {}   -- packet number of each segment frame
	etherSegmt_num = 0       -- index of each segment frame
	etherSegmt_Sumlen = 0    -- sumlength of segment frames
	etherSegmt_string = ""   -- display string of segment package
	g_DataAggregatedMSDU_len = 0
end

local function DecToHexChar(DecData)
	local hex_char
	if DecData == 10 then
		hex_char = 'a'
	elseif DecData == 11 then
		hex_char = 'b'
	elseif DecData == 12 then
		hex_char = 'c'
	elseif DecData == 13 then
		hex_char = 'd'
	elseif DecData == 14 then
		hex_char = 'e'
	elseif DecData == 15 then
		hex_char = 'f'
	else
		hex_char =  DecData
	end
	
	return hex_char
end

local function DecToHexString(DecData)
	-- 1byte Dec to string
	-- example : DecData is 0x12
	-- return is 12
	local high
	local low
	local high_string
	local low_string
	local hex_string
	
	low = DecData % 16
	high = (DecData-low) / 16
	
	low_string = DecToHexChar(low)
	high_string = DecToHexChar(high)
	hex_string = high_string..low_string
	return hex_string
end

local function DecDataToHexString(DecData, DataLen)
	local Hex_string = "0x"
	
	while DataLen ~= 0 do
		Hex_string = Hex_string..DecToHexString(bit.band(bit.rshift(DecData,8*(DataLen-1)),0xff))
		DataLen = DataLen - 1
	end
	
	return Hex_string
end

local function UsrdataTobit(UsrData, BitOffset, BitLen)
	local bit_string = "0b"
	local uint8_data = UsrData:uint()
	local bit_data = 0
	
	while BitLen ~=0 do
		bit_data = bit.band(bit.rshift(uint8_data, BitOffset + BitLen - 1), 0x01)
		bit_string = bit_string..bit_data
		BitLen = BitLen - 1
	end
	
	return bit_string

end

local data_dis = Dissector.get("data")

local function HdplcFrame_Data_dissector(buf,pkt,root,offset)
	--[[
		 Dessect the HD-PLC Data frame
	--]]
	
end

local function HdplcFrame_ACK_dissector(buf,pkt,root,offset)
	--[[
		 dissect the HD-PLC ACK frame
				ACK frameType only has frame control field
	--]]

end

local function HdplcFrame_RCE_dissector(buf,pkt,root,offset)
	--[[
		 Dessect the HD-PLC RCE frame
	--]]
			
end


local function HdplcFrame_CER_dissector(buf,pkt,root,offset)
	--[[
		 Dessect the HD-PLC CER frame
	--]]

end


local function HdplcFrame_Beacon_dissector(buf,pkt,root,offset)
	--[[
		 Dessect the HD-PLC Beacon frame
	--]]

	
end


local function HdplcFrame_Manage_dissector(buf,pkt,root,offset)
	--[[
		 Dessect the HD-PLC Manage frame
	--]]

end

--[[
	 Ehter frame dissection including HD-PLC frame
--]]
local function HdplcFrame_dissector(buf,pkt,root)

	set_color_filter_slot(1, "hdplcframe")
	pkt.cols.protocol = "HD-PLC"
	
	local buf_new
	local array_new
	local isOnlyOneEther = 0
	
	local bufLen	= buf:len()
	-- Length of Rsvd, flag, opt, length in HD-PLC capture header is 6. If Length < 6, error.
	if  bufLen < 6 then
		return false
	end
	
	local offset = 0
	-- 2 bytes reserved
	offset = offset + 2
	--[[
		dissect the frame
	--]]
	--dissect the HD-PLC HD-PLC capture header
	local v_HdFrmeHdr = ""--buf(offset, 14)-------extend
	--Flag
	local v_HdrFlg = buf(offset, 1):uint()
	offset = offset + 1
	--Opt
	local v_HdrOpt = buf(offset, 1):uint()
	offset = offset + 1
	--Length
	local v_HdrLen = buf(offset, 2):le_uint()
	offset = offset + 2
	
	
	if v_HdrFlg == 1 and v_HdrOpt == 1 then
		-- Length of HD-PLC frame is so short that it's placed in one packet of ether frame
				
		-- Save the HD-PLC frame length 
		g_HdplcFrmLen = v_HdrLen

		isOnlyOneEther = 1
	elseif v_HdrFlg == 0 and v_HdrOpt == 1 then
		-- Length of HD-PLC frame is so long that it's divided into N packets of ether frame 
		-- Here is processing of saving the fisrt ether frame 
		-- Don't dissector until all packets are collected. Just display the data.
		
		-- saving HD-PLC frame in the first ether packet
		if pkt.visited == true then
			
			
		else
			init()
			
			-- Save the HD-PLC frame length 
			g_HdplcFrmLen = v_HdrLen
					
			
			etherSegmt_num = etherSegmt_num + 1
			etherSegmt[etherSegmt_num] = "[ Frame : "..pkt.number..",	payload : 0 - 1467	(1468 bytes) ]"
			etherSegmt_pktnum[etherSegmt_num] = pkt.number
			etherSegmt_len[etherSegmt_num] = pkt.len - 14 - 28 - 2
			etherSegmt_Sumlen = etherSegmt_Sumlen + etherSegmt_len[etherSegmt_num]
			
			etherSegmtsBuf_ressm_tmp[etherSegmt_num] = buf(0,bufLen):bytes()
		end
		
		set_color_filter_slot(2, "hdplcframe")
		
		-- display as data
		local subtree = root:add(p_HdplcFrame, buf)
		data_dis:call(buf,pkt,subtree)
		pkt.cols.info = "Reassembled HD-PLC frame"
		pkt.cols.protocol = "HD-PLC"
		return true
	elseif v_HdrFlg == 0 and v_HdrOpt == 0 then
		-- Here is processing of saving the ether frames from number 2 to (N-1) packet
		-- Don't dissector until all packets are collected. Just display the data.
			
		-- saving HD-PLC frame in 2 to (N-1) ether packet
		if pkt.visited == true then
		
		else
			-- calculate the HD-PLC frame length in every ether frame
			g_HdplcFrmLen = g_HdplcFrmLen + v_HdrLen
			
			if etherSegmt_num == 0 then
				-- first packet is not received
				return false
			end
			
			etherSegmt_num = etherSegmt_num + 1
			etherSegmt[etherSegmt_num] = "[ Frame : "..pkt.number..",	payload : "..(1468 + (etherSegmt_num-2)*1492).." - "..(1467 + (etherSegmt_num-1)*1492).."	(1492 bytes) ]"
			etherSegmt_pktnum[etherSegmt_num] = pkt.number
			etherSegmt_len[etherSegmt_num] = pkt.len - 14 - 4 - 2
			etherSegmt_Sumlen = etherSegmt_Sumlen + etherSegmt_len[etherSegmt_num]
			
			etherSegmtsBuf_ressm_tmp[etherSegmt_num] = buf(6,bufLen-6):bytes()
		end
		
		set_color_filter_slot(2, "hdplcframe")
		
		-- display as data
		local subtree = root:add(p_HdplcFrame, buf)
		data_dis:call(buf,pkt,subtree)
		pkt.cols.protocol = "HD-PLC"
		pkt.cols.info = "Reassembled HD-PLC frame"
		return true
	elseif v_HdrFlg == 1 and v_HdrOpt == 0 then
		-- Here is processing of saving the last ether frame 		
		-- As all packets are collected, display.
		
		if pkt.visited == true then
			local t_etherSegmt = root:add(f_etherSegmt, "")
			t_etherSegmt:set_text(etherSegmt_string_ressm[pkt.number])
			for i=1,etherSegmt_num_ressm[pkt.number] do
				t_etherSegmt:add(f_framenum, etherSegmt_pktnum_ressm[pkt.number][i])
			end
			
			local local_buffer = etherSegmtsBuf_ressm[pkt.number]
			buf_new = local_buffer:tvb("HD-PLC frames made in one")
			
		else
			-- calculate the HD-PLC frame length in every ether frame
			g_HdplcFrmLen = g_HdplcFrmLen + v_HdrLen

			if etherSegmt_num == 0 then
				-- first packet is not received
				return false
			end
			
			etherSegmt_num = etherSegmt_num + 1
			etherSegmt[etherSegmt_num] = "[ Frame : "..pkt.number..",	payload : "..(1468 + (etherSegmt_num-2)*1492).." - "..(1467 + (etherSegmt_num-2)*1492 + pkt.len - 14 - 4 - 2).."	("..(pkt.len-14-4-2).." bytes) ]"
			etherSegmt_pktnum[etherSegmt_num] = pkt.number
			etherSegmt_len[etherSegmt_num] = pkt.len - 14 - 4 - 2
			etherSegmt_Sumlen = etherSegmt_Sumlen + etherSegmt_len[etherSegmt_num]
			
			etherSegmt_string = etherSegmt_num.." Reassembled Ethernet segments ("..etherSegmt_Sumlen.." bytes) : "
			for i=1,etherSegmt_num do
				etherSegmt_string = etherSegmt_string.."#"..etherSegmt_pktnum[i].."("..etherSegmt_len[i]..") "
			end
			
			local t_etherSegmt = root:add(f_etherSegmt, "")
			t_etherSegmt:set_text(etherSegmt_string)
			for i=1,etherSegmt_num do
				local t_temp = t_etherSegmt:add(f_framenum, etherSegmt_pktnum[i])
				t_temp:set_text(etherSegmt[i])
			end
				
			etherSegmt_string_ressm[pkt.number] = etherSegmt_string
			etherSegmt_num_ressm[pkt.number] = etherSegmt_num
			local tmp_1 = {}
			for i=1,etherSegmt_num do
				tmp_1[i] = etherSegmt_pktnum[i]
			end
			etherSegmt_pktnum_ressm[pkt.number] = tmp_1
				
			etherSegmtsBuf_ressm_tmp[etherSegmt_num] = buf(6,bufLen-6):bytes()
			for i=1,etherSegmt_num do
				if i == 1 then
					etherSegmtsBuf_ressm[pkt.number] = etherSegmtsBuf_ressm_tmp[i]
				else
					etherSegmtsBuf_ressm[pkt.number]:append(etherSegmtsBuf_ressm_tmp[i])
				end
			end

			buf_new = etherSegmtsBuf_ressm[pkt.number]:tvb("HD-PLC frames made in one")	
			
			
		end
	
	else
		-- no this specification
		return false
	end
	
	
	--HD-PLC frame tree node
	if isOnlyOneEther == 1 then
	
		local t = root:add(p_HdplcFrame, buf(30))
	
		--[[
			 HD-PLC frame :
				There are only 6 kinds of HD-PLC frame to be dissected. 
				Namely Beacon, Control, ACK, RCE, CER, DATA.
				Frame Type in FC decides what kind it is.
					Frame Type:
						0b0000 : Data
						0b0001 : ACK
						0b0010 : RCE
						0b0011 : CER
						0b0100 : Beacon
						0b0101 : Stream End
						0b0110 : Management
						0b0111 : RTS/CTS
						0b1000 ~ 0b1111: reserved		
		--]]
		-- Frame Control offset
		offset = 2 + 28
		if buf:len() < (offset+36) then
			data_dis:call(buf,pkt,t)
			return true
		end
		local frameType	= buf(offset+18, 1):uint()
		if frameType == 0 then
			set_color_filter_slot(2, "hdplcframe")
			pkt.cols.info	= "Data"
			HdplcFrame_Data_dissector(buf,pkt,t,offset)
		elseif frameType == 1 then
			set_color_filter_slot(3, "hdplcframe")
			pkt.cols.info	= "ACK"
			HdplcFrame_ACK_dissector(buf,pkt,t,offset)
		elseif frameType == 2 then
			set_color_filter_slot(4, "hdplcframe")
			pkt.cols.info	= "RCE"
			HdplcFrame_RCE_dissector(buf,pkt,t,offset)
		elseif frameType == 3 then
			set_color_filter_slot(5, "hdplcframe")
			pkt.cols.info	= "CER"
			HdplcFrame_CER_dissector(buf,pkt,t,offset)
		elseif frameType == 4 then
			set_color_filter_slot(6, "hdplcframe")
			pkt.cols.info	= "Beacon(BSSID:"..DecToHexString(buf(offset,1):uint())..":"
								..DecToHexString(buf(offset+1,1):uint())..":"
								..DecToHexString(buf(offset+2,1):uint())..":"
								..DecToHexString(buf(offset+3,1):uint())..":"
								..DecToHexString(buf(offset+4,1):uint())..":"
								..DecToHexString(buf(offset+5,1):uint())..")"
			HdplcFrame_Beacon_dissector(buf,pkt,t,offset)
		elseif frameType == 6 then
			set_color_filter_slot(7, "hdplcframe")
			pkt.cols.info	= "Manage"
			HdplcFrame_Manage_dissector(buf,pkt,t,offset)
		else
			pkt.cols.info	= "Other"
			local v_DataRawData = buf(offset, buf:len()-offset)
			offset = offset + buf:len()-offset
			t:add(f_DataRawData, v_DataRawData)	
		end
		
		
		--[[
		 display packet information
		--]]
		pkt.cols.protocol = "HD-PLC"
		pkt.dst = buf(2+28+6,6):ether()	
		pkt.src = buf(2+28+12,6):ether()
		
	else
		--local t = root:add(p_HdplcFrame, buf_new(30+14))
		local t_packet = root:add(f_DataRawData, buf())
		data_dis:call(buf,pkt,t_packet)
		local t = root:add(p_HdplcFrame, buf_new(30))
		t:append_text(", Timestamp:"..g_HdrTS..", CINR:"..g_HdrCINR..", RSSI:"..g_HdrRSSI)
	
	
		--[[
			 HD-PLC frame :
				There are only 6 kinds of HD-PLC frame to be dissected. 
				Namely Beacon, Control, ACK, RCE, CER, DATA.
				Frame Type in FC decides what kind it is.
					Frame Type:
						0b0000 : Data
						0b0001 : ACK
						0b0010 : RCE
						0b0011 : CER
						0b0100 : Beacon
						0b0101 : Stream End
						0b0110 : Management
						0b0111 : RTS/CTS
						0b1000 ~ 0b1111: reserved		
		--]]
		-- Frame Control offset
		--offset = 14 + 2 + 28
		offset = 2 + 28
		if buf_new:len() < (offset+36) then
			data_dis:call(buf_new,pkt,t)
			return true
		end
		local frameType	= buf_new(offset+18, 1):uint()
		if frameType == 0 then
			set_color_filter_slot(2, "hdplcframe")
			pkt.cols.info	= "Data"
			HdplcFrame_Data_dissector(buf_new,pkt,t,offset)
		elseif frameType == 1 then
			set_color_filter_slot(3, "hdplcframe")
			pkt.cols.info	= "ACK"
			HdplcFrame_ACK_dissector(buf_new,pkt,t,offset)
		elseif frameType == 2 then
			set_color_filter_slot(4, "hdplcframe")
			pkt.cols.info	= "RCE"
			HdplcFrame_RCE_dissector(buf_new,pkt,t,offset)
		elseif frameType == 3 then
			set_color_filter_slot(5, "hdplcframe")
			pkt.cols.info	= "CER"
			HdplcFrame_CER_dissector(buf_new,pkt,t,offset)
		elseif frameType == 4 then
			set_color_filter_slot(6, "hdplcframe")
			pkt.cols.info	= "Beacon(BSSID:"..DecToHexString(buf_new(offset,1):uint())..":"
								..DecToHexString(buf_new(offset+1,1):uint())..":"
								..DecToHexString(buf_new(offset+2,1):uint())..":"
								..DecToHexString(buf_new(offset+3,1):uint())..":"
								..DecToHexString(buf_new(offset+4,1):uint())..":"
								..DecToHexString(buf_new(offset+5,1):uint())..")"
			HdplcFrame_Beacon_dissector(buf_new,pkt,t,offset)
		elseif frameType == 6 then
			set_color_filter_slot(7, "hdplcframe")
			pkt.cols.info	= "Manage"
			HdplcFrame_Manage_dissector(buf_new,pkt,t,offset)
		else
			pkt.cols.info	= "Other"
			local v_DataRawData = buf_new(offset, buf_new:len()-offset)
			offset = offset + buf:len()-offset
			t:add(f_DataRawData, v_DataRawData)	
		end
		
		
		--[[
		 display packet information
		--]]
		
		--pkt.dst = buf_new(14+2+28+6,6):ether()	
		--pkt.src = buf_new(14+2+28+12,6):ether()
		pkt.dst = buf_new(2+28+6,6):ether()	
		pkt.src = buf_new(2+28+12,6):ether()
	
	end
	
	return true
	
end

--[[
	HD-PLC protol dissection function
--]]
function p_HdplcFrame.dissector(buf,pkt,root) 
	if HdplcFrame_dissector(buf,pkt,root) then
		--valid hdplc diagram
	else
		if buf:len() ~= 0 then
			--Display as data
			local t = root:add(p_HdplcFrame, buf)
			data_dis:call(buf,pkt,t)
		end	
	end
end

-- add to DissectorTable
local ethertype_table = DissectorTable.get("ethertype")
-- ethertype=0x8876
ethertype_table:add(34934, p_HdplcFrame)

