#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import subprocess
import re
import socket
import datetime
# remove mdns because of info string with '"'s
DISPLAY_FILTER = 'ip.version==6 || (ip.proto==0x11 && udp.dstport!=5353)'
INITIAL_ADJUST_TIME_INTER_NODE = 50
#ADJUST_TIME_INTER_NODE = 550

# list of messages displayed by tshark
g_messages_by_entity      = {}
g_messages                = {}
g_sync_messages_by_entity = {}

LMA_STR  = 'LMA'
MAG_STR  = 'MAG'
MAG1_STR = 'MAG1'
MAG2_STR = 'MAG2'
MN_STR   = 'MN'
CN_STR   = 'CN'
AP1_STR  = 'AP1'
AP2_STR  = 'AP2'

RADIUS_PROTOCOL_COLOR = '\"#00ff00\"'
NDP_PROTOCOL_COLOR    = '\"#0000ff\"'
PMIP_PROTOCOL_COLOR   = '\"#ff0000\"'
DEFAULT_COLOR         = '\"#000000\"'


LMA_CN_ETH_ADDR         = '00:0a:5e:40:60:6f'
LMA_MAGS_ETH_ADDR       = '00:17:31:84:d9:f7'
MAG1_LMA_ETH_ADDR       = '00:17:31:84:45:74'
MAG2_LMA_ETH_ADDR       = '00:17:31:84:45:cc'
MAG_MN_ETH_ADDR         = '00:11:22:33:44:55'
MN_ETH_ADDR             = '00:1f:3c:57:78:1d'
CN_ETH_ADDR             = '00:18:8b:b0:50:aa'
AP1_ETH_ADDR            = '5c:d9:98:6c:e2:14'
AP2_ETH_ADDR            = '34:08:04:99:e5:10'
MULTICAST_ETH_ADDR      = '33:33'
MULTICAST_IPV4_ETH_ADDR = '01:00:5e'
BROADCAST_IPV4_ETH_ADDR = 'ff:ff:ff:ff:ff:ff'


#TIME ALIGNEMENT VARIABLES
g_lma_mag1_diff_time = datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 0, 0, 0, 0)
g_lma_mag2_diff_time = datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 0, 0, 0, 0)

NULL_TIME_DELTA              = datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 1, 1, 1, 1)
ADJUST_TIME_INTER_NODE_MICRO_TIME_DELTA = datetime.datetime(2000, 1, 1, 1, 1, 1, INITIAL_ADJUST_TIME_INTER_NODE) - datetime.datetime(2000, 1, 1, 1, 1, 1, 0)

g_time_shift_messages     = {}

g_messages_by_entity[LMA_STR]  = {}
g_messages_by_entity[MAG1_STR] = {}
g_messages_by_entity[MAG2_STR] = {}
#g_messages_by_entity[MN_STR]   = {}
#g_messages_by_entity[CN_STR]   = {}
#g_messages_by_entity[AP1_STR]  = {}
#g_messages_by_entity[AP2_STR]  = {}

g_sync_messages_by_entity[LMA_STR]  = {}
g_sync_messages_by_entity[MAG1_STR] = {}
g_sync_messages_by_entity[MAG2_STR] = {}
#g_sync_messages_by_entity[MN_STR]   = {}
#g_sync_messages_by_entity[CN_STR]   = {}
#g_sync_messages_by_entity[AP1_STR]  = {}
#g_sync_messages_by_entity[AP2_STR]  = {}
################################################################################
def eth2entity(mac_address, entity='UNKNOWN ENTITY'):
################################################################################
	if mac_address.startswith(LMA_CN_ETH_ADDR) == True:
		return LMA_STR
	elif mac_address.startswith(LMA_MAGS_ETH_ADDR) == True:
		return LMA_STR
	elif mac_address.startswith(MAG_MN_ETH_ADDR) == True:
		if entity.startswith(MAG1_STR) == True:
			return MAG1_STR
		elif entity.startswith(MAG2_STR) == True:
			return MAG2_STR
		return UNKNOWN_MAG_ENTITY
	if mac_address.startswith(MAG1_LMA_ETH_ADDR) == True:
		return MAG1_STR
	elif mac_address.startswith(MAG2_LMA_ETH_ADDR) == True:
		return MAG2_STR
	elif mac_address.startswith(MN_ETH_ADDR) == True:
		return MN_STR
	elif mac_address.startswith(CN_ETH_ADDR) == True:
		return CN_STR
	elif mac_address.startswith(MULTICAST_ETH_ADDR) == True:
		return entity
	elif mac_address.startswith(AP1_ETH_ADDR) == True:
		return AP1_STR
	elif mac_address.startswith(AP2_ETH_ADDR) == True:
		return AP2_STR
	elif mac_address.startswith(MULTICAST_IPV4_ETH_ADDR) == True:
		return entity
	elif mac_address.startswith(BROADCAST_IPV4_ETH_ADDR) == True:
		return '*'
	print >>sys.stderr, 'UNKNOWN ENTITY eth@= %s' % mac_address
	return 'UNKNOWN'


################################################################################
def check_ipv6(n):
################################################################################
	try:
		socket.inet_pton(socket.AF_INET6, n)
		return True
	except socket.error:
		return False


################################################################################
def usage():
################################################################################
	print >>sys.stderr, '%s: <capture_on_lma_if_mags.pcap>  <capture_on_lma_mag_if_correspondant_node.pcap>  <capture_on_mag1_if_access_point.pcap> <capture_on_mag1_if_lma.pcap> <capture_on_mag2_if_access_point.pcap> <capture_on_mag2_if_lma.pcap> [tshark additional options] "wireshark display filter"' % sys.argv[0]
	sys.exit(1)


################################################################################
def load_messages(entity_str, pcap_file):
################################################################################
	global g_messages_by_entity
	messages = {}
	#################################################################
	# FIRST PASS TO GET INTERPRETED PROTOCOL INFO BY TSHARK BUILT-INS
	#################################################################

	tshark_cmd = ['tshark','-n','-t','a']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex = re.compile('^ *(\d+) +(\d+\:\d+\:\d+\.\d+) +(\S*) -> (\S*) (.*?)$')
	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line

		ret = regex.match(line)
		if ret != None:
			msg = {}
			msg['num'] = ret.group(1)
			msg['date'] = ret.group(2)
			msg['src'] = ret.group(3)
			msg['dst'] = ret.group(4)
			msg['msg'] = ret.group(5).strip()
			if msg['msg'].count('MIPv6 Binding') > 0:
				msg['colors'] = 'linecolour='+PMIP_PROTOCOL_COLOR+' , textcolour='+PMIP_PROTOCOL_COLOR
			elif msg['msg'].count('RADIUS') > 0:
				msg['colors'] = 'linecolour='+RADIUS_PROTOCOL_COLOR+' , textcolour='+RADIUS_PROTOCOL_COLOR
			elif msg['msg'].count('ICMPv6 Neighbor') > 0:
				msg['colors'] = 'linecolour='+NDP_PROTOCOL_COLOR+' , textcolour='+NDP_PROTOCOL_COLOR
			elif msg['msg'].count('ICMPv6 Router') > 0:
				msg['colors'] = 'linecolour='+NDP_PROTOCOL_COLOR+' , textcolour='+NDP_PROTOCOL_COLOR
			else:
				msg['colors'] = 'linecolour='+DEFAULT_COLOR+' , textcolour='+DEFAULT_COLOR

			if msg['msg'].count('Syslog') > 0:
				msg['msg'] = msg['msg'][59:]

			messages[msg['date']] = msg
			#print >>sys.stderr, "msg key=%s : %s handled by regex" % (msg['date'], msg)
		else:
			print >>sys.stderr, "line '%s' not handled by regex !" % line
			#break
	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)
	#################################################################
	# SECOND PASS TO GET ETHERNET ADDRESS and other custom fields
	#################################################################
	tshark_cmd = ['tshark','-n','-t','a','-T','fields','-e','frame.number','-e','frame.time','-e','eth.src','-e','eth.dst','-e','frame.protocols']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex = re.compile('^ *(\d+)\\t(\S*) +(\d+\,\ \d+) +(\d+\:\d+\:\d+\.\d+)\\t(\S*)\\t(\S*)\\t(\S*) *')
	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line

		ret = regex.match(line)
		if ret != None:
			msg_2nd_pass = {}
			msg_2nd_pass['num']        = ret.group(1)
			msg_2nd_pass['date']       = ret.group(4)[0:-3]
			msg_2nd_pass['eth_src']    = ret.group(5)
			msg_2nd_pass['eth_dst']    = ret.group(6)
			msg_2nd_pass['protocols']  = ret.group(7)
			msg_1st_pass               = messages[msg_2nd_pass['date']]
			msg_1st_pass['eth_src']    = msg_2nd_pass['eth_src']
			msg_1st_pass['eth_dst']    = msg_2nd_pass['eth_dst']
			msg_1st_pass['protocols']  = msg_2nd_pass['protocols']
			entity_src                 = eth2entity(msg_1st_pass['eth_src'], entity_str)
			msg_1st_pass['entity_src'] = entity_src
			entity_dst                 = eth2entity(msg_1st_pass['eth_dst'], entity_str)
			msg_1st_pass['entity_dst'] = entity_dst


			# approximation but I cannot find a very simple way to get the 1st ipv6 header when they are 2!
			protocols = msg_2nd_pass['protocols'].split(':');
			num_ipv6 = 0
			for protocol in protocols:
				if protocol == 'ipv6':
					num_ipv6 = num_ipv6 + 1
			if num_ipv6 > 1:
				if msg_1st_pass['msg'].count('ICMPv6 Echo') > 0:
					msg_1st_pass['tunnel'] = True
				else:
					msg_1st_pass['tunnel'] = False
			else:
				msg_1st_pass['tunnel'] = False

			messages[msg_2nd_pass['date']] = msg_1st_pass
			#print >>sys.stderr, entity_str, "Final msg= '%s'" % messages[msg_2nd_pass['date']]
		else:
			print >>sys.stderr, "line '%s' not handled by regex !" % line
			#break
	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)

	#################################################################
	# Third PASS TO GET RAW DATA
	#################################################################
	tshark_cmd = ['tshark','-n','-t','a','-T','text','-x']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex     = re.compile('^ *(\d+) +(\d+\:\d+\:\d+\.\d+) +(\S*) -> (\S*) (.*?)$')
	regex_raw = re.compile('^ *(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) (.*?)$')
	msg_3rd_pass         = {}
	msg_data             = []
	msg_1st_pass         = {}
	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line

		ret = regex.match(line)
		if ret != None:
			msg_3rd_pass         = {}
			msg_data             = []
			waited_index         = 0
			msg_3rd_pass['num']  = ret.group(1)
			msg_3rd_pass['date']  = ret.group(2)
			msg_1st_pass         = messages[msg_3rd_pass['date']]
			line = sharkout.readline()
		else:
			ret = regex_raw.match(line)
			if ret != None:
				if waited_index != int(ret.group(1),16):
					print >>sys.stderr, 'error waited index= %d index is %d' % (waited_index, int(ret.group(1), 16))
					sys.exit(1)

				waited_index = waited_index + 16

				for i in range(2, min(18, ret.lastindex)):
					msg_data.append(ret.group(i))


			else:
				# we assume that it means the end the display of raw data
				if msg_1st_pass['msg'].count('ICMPv6 Echo') > 0:
					if msg_1st_pass['tunnel'] == False:
						msg_1st_pass['msg'] = msg_1st_pass['msg']+' seq ' + str(int(msg_data[60], 16)*256 + int(msg_data[61], 16))
					else:
						msg_1st_pass['msg'] = msg_1st_pass['msg']+' seq ' + str(int(msg_data[100], 16)*256 + int(msg_data[101], 16))
					messages[msg_3rd_pass['date']] = msg_1st_pass


	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)


	regex_date = re.compile('(\d+)\:(\d+)\:(\d+)\.(\d+)')
	#now merge messages in global dictionnary
	for time_key in messages.iterkeys():
		message =  messages[time_key]

		ret = regex_date.match(message['date'])
		if ret == None:
			print >>sys.stderr, "Date '%s' not handled by regex !" % message['date']
			sys.exit(1)
		else:
			dtime = datetime.datetime(2000, 1, 1, int(ret.group(1)), int(ret.group(2)), int(ret.group(3)), int(ret.group(4)))

			while (dtime in g_messages_by_entity[entity_str]):
				# add 1 micro second
				dtime = dtime + (datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 1, 1, 1, 0))
				print >>sys.stderr, "Warning Incrementing time by 1 microsecond for entity %s Message %s" % (str(dtime), message['msg'])
				#test only, to remove
				sys.exit(1)
			g_messages_by_entity[entity_str][dtime] = 	message
	#print >>sys.stderr, "\n\n", g_messages_by_entity, ":\n", g_messages_by_entity

################################################################################
def load_time_sync_message(entity_str, pcap_file, message_shift_filter_string, message_drift_filter_string):
################################################################################
	global g_time_shift_messages
	global g_sync_messages_by_entity

	g_time_shift_messages[entity_str] = {}
	msg_count = 0;
	#################################################################
	# FIRST PASS TO GET INTERPRETED PROTOCOL INFO BY TSHARK BUILT-INS
	#################################################################

	tshark_cmd = ['tshark','-n','-t','a']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex = re.compile('^ *(\d+) +(\d+\:\d+\:\d+\.\d+) +(\S*) -> (\S*) (.*?)$')
	regex_date = re.compile('(\d+)\:(\d+)\:(\d+)\.(\d+)')

	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line
		ret = regex.match(line)
		if ret != None:
			msg = {}
			msg['num'] = ret.group(1)
			msg['date'] = ret.group(2)
			msg['src'] = ret.group(3)
			msg['dst'] = ret.group(4)
			msg['msg'] = ret.group(5).strip()

			ret = regex_date.match(msg['date'])
			if ret == None:
				print >>sys.stderr, "Date '%s' not handled by regex !" % msg['date']
				sys.exit(1)
			else:
				dtime = datetime.datetime(2000, 1, 1, int(ret.group(1)), int(ret.group(2)), int(ret.group(3)), int(ret.group(4)))
				if msg['msg'].count(message_shift_filter_string) > 0:
					if msg_count == 0:
						g_time_shift_messages[entity_str] = {}
					g_time_shift_messages[entity_str][dtime] = msg
					msg_count = msg_count + 1

				if msg['msg'].count(message_drift_filter_string) > 0:
					msg['datetime'] = dtime
					g_sync_messages_by_entity[entity_str][msg['date']] = msg


		else:
			print >>sys.stderr, "line '%s' not handled by regex !" % line
			#break
	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)

	if msg_count == 0:
		print >>sys.stderr, "Could not find message for computing time shift for entity %s" % entity_str
		sys.exit(1)


	#################################################################
	# SECOND PASS TO GET ETHERNET ADDRESS and other custom fields
	#################################################################
	tshark_cmd = ['tshark','-n','-t','a','-T','fields','-e','frame.number','-e','frame.time','-e','eth.src','-e','eth.dst','-e','frame.protocols']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex = re.compile('^ *(\d+)\\t(\S*) +(\d+\,\ \d+) +(\d+\:\d+\:\d+\.\d+)\\t(\S*)\\t(\S*)\\t(\S*) *')
	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line

		ret = regex.match(line)
		if ret != None:
			msg_2nd_pass = {}
			msg_2nd_pass['num']        = ret.group(1)
			msg_2nd_pass['date']       = ret.group(4)[0:-3]
			if msg_2nd_pass['date'] in g_sync_messages_by_entity[entity_str]:
				msg_2nd_pass['eth_src']    = ret.group(5)
				msg_2nd_pass['eth_dst']    = ret.group(6)
				msg_2nd_pass['protocols']  = ret.group(7)
				msg_1st_pass               = g_sync_messages_by_entity[entity_str][msg_2nd_pass['date']]
				msg_1st_pass['eth_src']    = msg_2nd_pass['eth_src']
				msg_1st_pass['eth_dst']    = msg_2nd_pass['eth_dst']
				msg_1st_pass['protocols']  = msg_2nd_pass['protocols']
				entity_src                 = eth2entity(msg_1st_pass['eth_src'], entity_str)
				msg_1st_pass['entity_src'] = entity_src
				entity_dst                 = eth2entity(msg_1st_pass['eth_dst'], entity_str)
				msg_1st_pass['entity_dst'] = entity_dst


				# approximation but I cannot find a very simple way to get the 1st ipv6 header when they are 2!
				protocols = msg_2nd_pass['protocols'].split(':');
				num_ipv6 = 0
				for protocol in protocols:
					if protocol == 'ipv6':
						num_ipv6 = num_ipv6 + 1
				if num_ipv6 > 1:
					if msg_1st_pass['msg'].count('ICMPv6 Echo') > 0:
						msg_1st_pass['tunnel'] = True
					else:
						msg_1st_pass['tunnel'] = False
				else:
					msg_1st_pass['tunnel'] = False

				g_sync_messages_by_entity[entity_str][msg_2nd_pass['date']] = msg_1st_pass
				#print >>sys.stderr, entity_str, "Final msg= '%s'" % messages[msg_2nd_pass['date']]
		else:
			print >>sys.stderr, "line '%s' not handled by regex !" % line
			#break
	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)

	#################################################################
	# Third PASS TO GET RAW DATA
	#################################################################
	tshark_cmd = ['tshark','-n','-t','a','-T','text','-x']
	tshark_cmd.extend(sys.argv[7:-1])
	tshark_cmd.append('-r')
	tshark_cmd.append(pcap_file)
	tshark_cmd.append(DISPLAY_FILTER)
	#print >>sys.stderr, 'tshark cmd= %s' % tshark_cmd
	# start tshark subprocess and prepare a pipe to which it will write stdout
	shark = subprocess.Popen(tshark_cmd, stdout=subprocess.PIPE)
	sharkout = shark.stdout
	regex     = re.compile('^ *(\d+) +(\d+\:\d+\:\d+\.\d+) +(\S*) -> (\S*) (.*?)$')
	regex_raw = re.compile('^ *(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) +(\S*) (.*?)$')
	msg_3rd_pass         = {}
	msg_data             = []
	msg_1st_pass         = {}

	in_sync = False

	while True:
		line = sharkout.readline()
		# eof encountered
		if len(line) == 0:
			break
		#print >>sys.stderr, "line='%s'" % line

		ret = regex.match(line)
		if ret != None:
			msg_3rd_pass         = {}
			msg_data             = []
			waited_index         = 0
			msg_3rd_pass['num']  = ret.group(1)
			msg_3rd_pass['date']  = ret.group(2)
			if msg_3rd_pass['date'] in g_sync_messages_by_entity[entity_str]:
				msg_1st_pass         = g_sync_messages_by_entity[entity_str][msg_3rd_pass['date']]
				line = sharkout.readline()
				in_sync = True
			else:
				in_sync = False
		else:
			ret = regex_raw.match(line)
			if ret != None:
				if waited_index != int(ret.group(1),16):
					print >>sys.stderr, 'error waited index= %d index is %d' % (waited_index, int(ret.group(1), 16))
					sys.exit(1)

				waited_index = waited_index + 16

				for i in range(2, min(18, ret.lastindex)):
					msg_data.append(ret.group(i))


			else:
				if in_sync == True:
					# we assume that it means the end the display of raw data
					if msg_1st_pass['msg'].count('ICMPv6 Echo') > 0:
						if msg_1st_pass['tunnel'] == False:
							msg_1st_pass['msg'] = msg_1st_pass['msg']+' seq ' + str(int(msg_data[60], 16)*256 + int(msg_data[61], 16))
						else:
							msg_1st_pass['msg'] = msg_1st_pass['msg']+' seq ' + str(int(msg_data[100], 16)*256 + int(msg_data[101], 16))
						print >>sys.stderr, 'Deleting    g_sync_messages_by_entity[%s][%s] = \n%s' % (entity_str, msg_3rd_pass['date'], msg_3rd_pass)
						del g_sync_messages_by_entity[entity_str][msg_3rd_pass['date']]
						print >>sys.stderr, 'Replaced by g_sync_messages_by_entity[%s][%s] = \n%s' % (entity_str, msg_1st_pass['datetime'], msg_1st_pass)
						g_sync_messages_by_entity[entity_str][msg_1st_pass['datetime']] = msg_1st_pass

	# synchronously wait for tshark termination
	shark.wait()
	if shark.returncode != 0:
		print >>sys.stderr, "tshark returned error code %d" % shark.returncode
		sys.exit(1)




	#print >>sys.stderr, "\n\n", entity_str, ":\n", g_sync_messages_by_entity[entity_str]


################################################################################
def compute_initial_clock_diff_between_entities():
################################################################################
	global g_time_shift_messages
	global g_lma_mag1_diff_time
	global g_lma_mag2_diff_time

	if (g_time_shift_messages.has_key(LMA_STR) == True) and (g_time_shift_messages.has_key(MAG1_STR) == True) and (g_time_shift_messages.has_key(MAG2_STR) == True):
		print >>sys.stderr, "OK we can compute clock shifts"
		mag1_msg = {}
		mag2_msg = {}

        # MAG1
		for key1 in sorted(g_time_shift_messages[MAG1_STR].iterkeys()):
			mag1_msg  =  g_time_shift_messages[MAG1_STR][key1]
			mag1_time = key1
			break
        # MAG2
		for key2 in sorted(g_time_shift_messages[MAG2_STR].iterkeys()):
			mag2_msg  =  g_time_shift_messages[MAG2_STR][key2]
			mag2_time = key2
			break
        # LMA
		lma_mag1 = False
		lma_mag2 = False

		for key in sorted(g_time_shift_messages[LMA_STR].iterkeys()):
			message =  g_time_shift_messages[LMA_STR][key]
			if  (lma_mag1==False)and(message['src']==mag1_msg['src'])and(message['dst']==mag1_msg['dst'])and(message['msg']==mag1_msg['msg']):
				#print >>sys.stderr, "Message Mag1 LMA %s" % message
				g_lma_mag1_diff_time = key - mag1_time
				print >>sys.stderr, "diff LMA -> MAG1 " , g_lma_mag1_diff_time
				lma_mag1 = True
			elif (lma_mag2 == False)and(message['src']==mag2_msg['src'])and(message['dst']==mag2_msg['dst'])and(message['msg']==mag2_msg['msg']):
				#print >>sys.stderr, "Message Mag2 LMA %s" % message
				g_lma_mag2_diff_time = key - mag2_time
				print >>sys.stderr, "diff LMA -> MAG2 " , g_lma_mag2_diff_time
				lma_mag2 = True
			if (lma_mag1 == True) and (lma_mag2 == True):
				break
	else:
		print >>sys.stderr, "ERROR could not compute clock shifts"
		sys.exit(1)


################################################################################
def merge_messages_and_apply_time_shift(message_drift_adjust):
################################################################################
	global g_sync_messages_by_entity
	global g_messages_by_entity
	global g_lma_mag1_diff_time
	global g_lma_mag2_diff_time
	global g_messages

	#regex = re.compile('^ *(\d+)\:(\d+)\:(\d+)\.(\d+):*')

	messages_by_entity           = {}
	messages_by_entity[LMA_STR]  = {}
	messages_by_entity[MAG1_STR] = {}
	messages_by_entity[MAG2_STR] = {}

	sync_messages_by_entity           = {}
	sync_messages_by_entity[MAG1_STR] = {}
	sync_messages_by_entity[MAG2_STR] = {}

	#--------------------------------------------------------------------------------------
	messages_by_entity[LMA_STR].update(g_messages_by_entity[LMA_STR])
	g_messages_by_entity[LMA_STR] = {}
	#--------------------------------------------------------------------------------------
	print >>sys.stderr, "Applying initial diff time to MAG1 Messages", g_lma_mag1_diff_time
	#print >>sys.stderr, g_messages_by_entity[MAG1_STR]
	for key in g_messages_by_entity[MAG1_STR].iterkeys():
		message =  g_messages_by_entity[MAG1_STR][key]
		message_time = key + g_lma_mag1_diff_time
		while (message_time in g_messages):
			# add 1 micro second
			message_time = message_time + (datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 1, 1, 1, 0))
			print >>sys.stderr, "[MAG1] Warning Incrementing time by 1 microsecond for  '%s'" % message['msg']
		#if message['msg'].count(message_drift_adjust) > 0:
		#	if message['msg'] not in g_sync_messages_by_entity[LMA_STR]:
		#		g_sync_messages_by_entity[MAG1_STR][message['msg']]   = message_time
		messages_by_entity[MAG1_STR][message_time] = 	message
	g_messages_by_entity[MAG1_STR] = {}

	#--------------------------------------------------------------------------------------
	print >>sys.stderr, "Applying initial diff time to MAG1 Sync Messages", g_lma_mag1_diff_time
	for key in g_sync_messages_by_entity[MAG1_STR].iterkeys():
		message =  g_sync_messages_by_entity[MAG1_STR][key]
		message_time = key + g_lma_mag1_diff_time
		sync_messages_by_entity[MAG1_STR][message['msg']]   = message_time
	g_sync_messages_by_entity[MAG1_STR] = {}


	#--------------------------------------------------------------------------------------
	print >>sys.stderr, "Applying initial diff time to MAG2 Messages", g_lma_mag2_diff_time
	#print >>sys.stderr, g_messages_by_entity[MAG2_STR]
	for key in g_messages_by_entity[MAG2_STR].iterkeys():
		message =  g_messages_by_entity[MAG2_STR][key]
		message_time = key + g_lma_mag2_diff_time
		while (message_time in g_messages):
			# add 1 micro second
			message_time = message_time + (datetime.datetime(2000, 1, 1, 1, 1, 1, 1) - datetime.datetime(2000, 1, 1, 1, 1, 1, 0))
			print >>sys.stderr, "[MAG2] Warning Incrementing time by 1 microsecond for  '%s'" % message['msg']
		#if message['msg'].count(message_drift_adjust) > 0:
		#	if message['msg'] not in g_sync_messages_by_entity[LMA_STR]:
		#		g_sync_messages_by_entity[MAG2_STR][message['msg']]   = message_time
		messages_by_entity[MAG2_STR][message_time] = 	message
	g_messages_by_entity[MAG2_STR] = {}

	#--------------------------------------------------------------------------------------
	print >>sys.stderr, "Applying initial diff time to MAG2 Sync Messages", g_lma_mag2_diff_time
	for key in g_sync_messages_by_entity[MAG2_STR].iterkeys():
		message =  g_sync_messages_by_entity[MAG2_STR][key]
		message_time = key + g_lma_mag2_diff_time
		sync_messages_by_entity[MAG2_STR][message['msg']]   = message_time
	g_sync_messages_by_entity[MAG2_STR] = {}

	#--------------------------------------------------------------------------------------
	# NOW while merging messages try to detect errors due to clocks drifts between entities
	# assuming that there is now no initial clock shifts
	# We may assume that clock drift is linear, but ... let's go with a neanderthal algorithm
	time_drift_mag1 = NULL_TIME_DELTA
	time_drift_mag2 = NULL_TIME_DELTA

	#print >>sys.stderr, "\n\n", MAG1_STR, ":\n", sync_messages_by_entity[MAG1_STR]
	#print >>sys.stderr, "\n\n", MAG2_STR, ":\n", sync_messages_by_entity[MAG2_STR]
	lma_time_key  = datetime.datetime(2000, 1, 1, 0, 0, 0, 0)
	mag1_time_key = datetime.datetime(2000, 1, 1, 0, 0, 0, 1)
	mag2_time_key = datetime.datetime(2000, 1, 1, 0, 0, 0, 2)
	keys_to_add_mag1 = {}
	keys_to_add_mag2 = {}


	for lma_time_key in sorted(messages_by_entity[LMA_STR].iterkeys()):
		message_lma =  messages_by_entity[LMA_STR][lma_time_key ]

		g_messages[lma_time_key] = message_lma

		if lma_time_key  in g_sync_messages_by_entity[LMA_STR]:
			if message_lma['msg'] in sync_messages_by_entity[MAG1_STR]:
				mag1_time_key = sync_messages_by_entity[MAG1_STR][message_lma['msg']]
				if (message_lma['entity_src'] == LMA_STR) and (message_lma['entity_dst'] == MAG1_STR):
					if (mag1_time_key + time_drift_mag1) <= lma_time_key:
						time_drift_mag1 = lma_time_key - mag1_time_key + ADJUST_TIME_INTER_NODE_MICRO_TIME_DELTA
						print >>sys.stderr, "Found LMA-MAG1 drift for ", message_lma['msg'], " ", time_drift_mag1, "\tTime MAG 1:", mag1_time_key, "/Time LMA:", lma_time_key
					#else:
					#	# null drift
					#	if time_drift_mag1 != NULL_TIME_DELTA:
					#		print >>sys.stderr, "Reseting LMA-MAG1 drift to 0 for ", message_lma['msg']
					#		time_drift_mag1 = NULL_TIME_DELTA
				elif (message_lma['entity_src'] == MAG1_STR) and (message_lma['entity_dst'] == LMA_STR):
					if (mag1_time_key + time_drift_mag1) >= lma_time_key:
						time_drift_mag1 = lma_time_key - mag1_time_key - ADJUST_TIME_INTER_NODE_MICRO_TIME_DELTA
						print >>sys.stderr, "Found MAG1-LMA drift for ", message_lma['msg'], " ", time_drift_mag1
					#else:
						# null drift
					#	if time_drift_mag1 != NULL_TIME_DELTA:
					#		print >>sys.stderr, "Reseting MAG1-LMA drift to 0 for ", message_lma['msg']
					#		time_drift_mag1 = NULL_TIME_DELTA

			if message_lma['msg'] in sync_messages_by_entity[MAG2_STR]:
				mag2_time_key = sync_messages_by_entity[MAG2_STR][message_lma['msg']]
				if (message_lma['entity_src'] == LMA_STR) and (message_lma['entity_dst'] == MAG2_STR):
					if (mag2_time_key + time_drift_mag1) <= lma_time_key:
						time_drift_mag2 = lma_time_key - mag2_time_key + ADJUST_TIME_INTER_NODE_MICRO_TIME_DELTA
						print >>sys.stderr, "Found LMA-MAG2 drift for ", message_lma['msg'], " ", time_drift_mag2
					#else:
					#	# null drift
					#	if time_drift_mag2 != NULL_TIME_DELTA:
					#		print >>sys.stderr, "Reseting LMA-MAG2 drift to 0 for ", message_lma['msg']
					#		time_drift_mag2 = NULL_TIME_DELTA
				elif (message_lma['entity_src'] == MAG2_STR) and (message_lma['entity_dst'] == LMA_STR):
					if (mag2_time_key + time_drift_mag1) >= lma_time_key:
						time_drift_mag2 = lma_time_key - mag2_time_key - ADJUST_TIME_INTER_NODE_MICRO_TIME_DELTA
						print >>sys.stderr, "Found LMA-MAG2 drift for ", message_lma['msg'], " ", time_drift_mag2
					#else:
						# null drift
					#	if time_drift_mag2 != NULL_TIME_DELTA:
					#		print >>sys.stderr, "Reseting LMA-MAG2 drift to 0 for ", message_lma['msg']
					#		time_drift_mag2 = NULL_TIME_DELTA

		if time_drift_mag1 != NULL_TIME_DELTA:
			for mag_key in sorted(keys_to_add_mag1.iterkeys()):
				if mag_key < mag1_time_key:
					g_messages[mag_key] = keys_to_add_mag1[mag_key]
				else:
					print >>sys.stderr, "Retro-Applying MAG1 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag1), keys_to_add_mag1[mag_key]['msg'], keys_to_add_mag1[mag_key]['entity_src'], keys_to_add_mag1[mag_key]['entity_dst'], str(mag_key), str(mag_key + time_drift_mag1) )
					g_messages[mag_key + time_drift_mag1] = keys_to_add_mag1[mag_key]
			keys_to_add_mag1 = {}

		if time_drift_mag2 != NULL_TIME_DELTA:
			for mag_key in sorted(keys_to_add_mag2.iterkeys()):
				if mag_key < mag2_time_key:
					g_messages[mag_key] = keys_to_add_mag2[mag_key]
				else:
					print >>sys.stderr, "Retro-Applying MAG2 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag2), keys_to_add_mag2[mag_key]['msg'], keys_to_add_mag2[mag_key]['entity_src'], keys_to_add_mag2[mag_key]['entity_dst'], str(mag_key), str(mag_key + time_drift_mag2) )
					g_messages[mag_key + time_drift_mag2] = keys_to_add_mag2[mag_key]
			keys_to_add_mag2 = {}


		keys_to_delete = []
		keys_to_add    = {}
		for mag1_key in sorted(messages_by_entity[MAG1_STR].iterkeys()):
			if (mag1_key + time_drift_mag1) <= lma_time_key:
				message_mag1 =  messages_by_entity[MAG1_STR][mag1_key]
				print >>sys.stderr, "Applying MAG1 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag1), message_mag1['msg'], message_mag1['entity_src'], message_mag1['entity_dst'], str(mag1_key), str(mag1_key + time_drift_mag1) )
				new_key = mag1_key + time_drift_mag1
				keys_to_add_mag1[new_key] = message_mag1
				keys_to_delete.append(mag1_key)
			else:
				new_key = mag1_key + time_drift_mag1
				keys_to_add[new_key] = messages_by_entity[MAG1_STR][mag1_key]
				keys_to_delete.append(mag1_key)
				#break
		for a_key_to_del in keys_to_delete:
			del messages_by_entity[MAG1_STR][a_key_to_del]
		for key in sync_messages_by_entity[MAG1_STR].iterkeys():
			message_time =  sync_messages_by_entity[MAG1_STR][key]
			message_time =  message_time + time_drift_mag1
			sync_messages_by_entity[MAG1_STR][key]   = message_time
		messages_by_entity[MAG1_STR].update(keys_to_add)
		time_drift_mag1 = NULL_TIME_DELTA

		keys_to_delete = []
		keys_to_add = {}
		for mag2_key in sorted(messages_by_entity[MAG2_STR].iterkeys()):
			if (mag2_key + time_drift_mag2) <= lma_time_key:
				message_mag2 =  messages_by_entity[MAG2_STR][mag2_key]
				print >>sys.stderr, "Applying MAG2 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag2), message_mag2['msg'], message_mag2['entity_src'], message_mag2['entity_dst'], str(mag2_key), str(mag2_key + time_drift_mag2) )
				new_key = mag2_key + time_drift_mag2
				keys_to_add_mag2[new_key] = message_mag2
				del messages_by_entity[MAG2_STR][mag2_key]
			else:
				new_key = mag2_key + time_drift_mag2
				keys_to_add[new_key] = messages_by_entity[MAG2_STR][mag2_key]
				keys_to_delete.append(mag2_key)
				#break
		for a_key_to_del in keys_to_delete:
			del messages_by_entity[MAG2_STR][a_key_to_del]
		for key in sync_messages_by_entity[MAG2_STR].iterkeys():
			message_time =  sync_messages_by_entity[MAG2_STR][key]
			message_time =  message_time + time_drift_mag2
			sync_messages_by_entity[MAG2_STR][key]   = message_time

		messages_by_entity[MAG2_STR].update(keys_to_add)
		time_drift_mag2 = NULL_TIME_DELTA

	#finally, flush remaining messages
	for mag1_key in sorted(messages_by_entity[MAG1_STR].iterkeys()):
		message_mag1 =  messages_by_entity[MAG1_STR][mag1_key]
		print >>sys.stderr, "Applying MAG1 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag1), message_mag1['msg'], message_mag1['entity_src'], message_mag1['dst'], str(mag1_key), str(mag1_key + time_drift_mag1) )
		new_key = mag1_key + time_drift_mag1
		g_messages[new_key] = message_mag1

	for mag2_key in sorted(messages_by_entity[MAG2_STR].iterkeys()):
		message_mag2 =  messages_by_entity[MAG2_STR][mag2_key]
		print >>sys.stderr, "Applying MAG2 drift %s for %s\t%s -> %s\tTime %s -> %s" % (str(time_drift_mag2), message_mag2['msg'], message_mag2['entity_src'], message_mag2['dst'], str(mag2_key), str(mag2_key + time_drift_mag2) )
		new_key = mag2_key + time_drift_mag2
		g_messages[new_key] = message_mag2

	messages_by_entity[LMA_STR] = {}
	messages_by_entity[MAG1_STR] = {}
	messages_by_entity[MAG2_STR] = {}


n = len(sys.argv)
if n < 7:
	usage()


dfilter = sys.argv[-1]


print >>sys.stderr, "Loading LMA messages '%s'" % sys.argv[1]
load_messages(LMA_STR,  sys.argv[1])

print >>sys.stderr, "Loading again LMA messages '%s' for time shift computation" % sys.argv[1]
load_time_sync_message(LMA_STR,  sys.argv[1], 'RADIUS Access-Request', 'ICMPv6 Echo')

print >>sys.stderr, "Loading LMA messages '%s'" % sys.argv[2]
load_messages(LMA_STR,  sys.argv[2])

print >>sys.stderr, "Loading MAG1 messages '%s'" % sys.argv[3]
load_messages(MAG1_STR, sys.argv[3])

print >>sys.stderr, "Loading MAG1 messages '%s' for time shift computation" % sys.argv[4]
load_time_sync_message(MAG1_STR, sys.argv[4], 'RADIUS Access-Request', 'ICMPv6 Echo')


print >>sys.stderr, "Loading MAG2 messages '%s'" % sys.argv[5]
load_messages(MAG2_STR, sys.argv[5])

print >>sys.stderr, "Loading MAG2 messages '%s' for time shift computation" % sys.argv[6]
load_time_sync_message(MAG2_STR, sys.argv[6], 'RADIUS Access-Request', 'ICMPv6 Echo')

print >>sys.stderr, "Compting time shift between LMA/MAG1/MAG2"
compute_initial_clock_diff_between_entities()

merge_messages_and_apply_time_shift('ICMPv6 Echo')


# print msc generated file on stdout
print("msc {")
print("width = \"2048\";")
print("  %s, %s, %s, %s, %s, %s, %s;" % (MN_STR, MAG1_STR, MAG2_STR, LMA_STR, CN_STR, AP1_STR, AP2_STR))
print("  %s rbox %s [label=\"%s\"], " % (MN_STR, MN_STR, MN_STR))
print("  %s rbox %s [label=\"%s\"], " % (MAG1_STR, MAG1_STR, MAG1_STR))
print("  %s rbox %s [label=\"%s\"], " % (MAG2_STR, MAG2_STR, MAG2_STR))
print("  %s rbox %s [label=\"%s\"], " % (LMA_STR, LMA_STR, LMA_STR))
print("  %s rbox %s [label=\"%s\"], " % (CN_STR, CN_STR, CN_STR))
print("  %s rbox %s [label=\"%s\"], " % (AP1_STR, AP1_STR, AP1_STR))
print("  %s rbox %s [label=\"%s\"]; " % (AP2_STR, AP2_STR, AP2_STR))

G_lma_mag1_tunnel = False
G_lma_mag2_tunnel = False

for key in sorted(g_messages.iterkeys()):
	message =  g_messages[key]


	#print >>sys.stderr, "key  '%s'" % key
	if message['entity_src'] == 'UNKNOWN' or message['entity_dst'] == 'UNKNOWN':
		print("  #  %s=>%s [ label = \"[%s] %s\", %s ] ;" % (message['entity_src'], message['entity_dst'], str(key)[11:], message['msg'], message['colors']))
	else :
		print("  %s=>%s [ label = \"[%s] %s\", %s ] ;" % (message['entity_src'], message['entity_dst'], str(key)[11:], message['msg'], message['colors']))
print("}")
