#! /usr/bin/env python
# Script that process stdout from pcf and creates IRI messages
#
# Copyright (C) 2014 Martin Holkovic 
#
# 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 3 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, see <http://www.gnu.org/licenses/>.


# Pri funkciach, ktorych nazov konci na velke pismeno, dane pismeno urcuje aky typ hodnoty vraca
# dana funkcia. Moznosti:
# D - decimal number
# H - hexadecimal number
# S - string
# B - binary data
# F - float
# priklad: icmpv6_typeH - vrati polozku typ z icmpv6 hlavicky v Hexa podobe
#
# Farby pouzite pri vystupe s debugovymi hlaskami:
# bledomodra - informacie o prijatom packete
# zelena - spravy odoslane IRI-IIF alebo na sit
# zlta - oznamuje zmeny s uloziskom dat
# cervena - chyby, warningy
# fialova - data vydolovane zo sprav
#

import sys
import time
import os
import socket
import threading
import getopt
import inspect
import signal

sys.path.append(os.path.join(os.path.dirname(__file__), '../../../'))

from time import gmtime, strftime
from pcapy import *
from modules.sockets.li2 import connectUnixSocket
from modules.af.time_priority_queue import TimePriorityQueue
from modules.tools import signals


# globalne premenne
socket1 = None # obsahuje socket pre komunikaciu s IRI-IIF
socket2 = None # obsahuje socket pre odosielani dat do siete
dbNews = None # obsahuje nove ziskane IP adresy zo spravy Neighbor Soliciatation, dane adresy su mozno duplicitne
dbGroups = None # obsahuje zoznam multicastovych adres na ktore modul odpoveda pri dotazi od smerovaca
dbAddresses = None # obsahuje zoznam vsetkych aktualne platnych IPv6 adres
dbMac = None # obsahuje zoznam vsetkych aktualne platnych IPv6 adres zeradenych podla MAC adresy
maxHash = 8 # pocet unikatnych indexov v ramci hashovaci tabulky (parameter db)
timer = TimePriorityQueue(None) # fronta
timerThread = None # premenna identifikujuca druhe vlakno procesu
run = True # premenna pomocou ktorejsa ukoncuje cinnost druheho vlakna
debugMode = None # hodnota urcuje ci sa budu vypisovat pomocne hlasky pri behu programu, nastavuje sa parametrom
lock = threading.RLock() # blokuje pristup do kritickej sekcie
mode = "online" # prepinanie medzi rezimami online a offline, oboje zachytava data z interfacu avsak pri offline rezime sa neposielaju IRI spravy
mainPid = 0
################################################################################
#### Vseobecne funkcie #########################################################
################################################################################


def lineno():
    return inspect.currentframe().f_back.f_lineno

# pomocna funkcia na vypisovanie debugovych hlasok
def debug(string, color = "white"):
    mode = debugMode#[0]
    colorIndex = 0
    if mode.find("r") != -1 and color == "red":
        colorIndex = 1
    elif mode.find("g") != -1 and color == "green":
        colorIndex = 2
    elif mode.find("y") != -1 and color == "yellow":
        colorIndex = 3
    elif mode.find("b") != -1 and color == "blue":
        colorIndex = 4
    elif mode.find("p") != -1 and color == "purple":
        colorIndex = 5
    elif mode.find("c") != -1 and color == "cyan":
        colorIndex = 6
    
    if colorIndex != 0:
        unixTime = str(time.time())
        parts = unixTime.split('.')
        if len(parts[1]) == 1:
            parts[1] = parts[1] + "0"
        resultTime = strftime("%d %b %Y %H:%M:%S", time.localtime()) + "." + parts[1]
        if os.isatty(sys.stderr.fileno()) == True: # jedna sa o vystup na terminal
            print >> sys.stderr, "\033[0;3" + str(colorIndex) + "m" + resultTime + ": " + string + "\033[39m"
        else:
            print >> sys.stderr, resultTime + ": " + string

# funkcia na odoslanie spravy na socket
def send_to_socket(string, color):
    global socket1
    debug("IRI-IIF: " + string, color)
    if mode == "online": # pri rezime offline sa neposielaju na IRI-IIF spravy
        socket1.send(string + "\n")

# vypocet unix time aktualneho casu spolu s milisekundami
def time_nowF():
    unixTime = str(time.time())
    parts = unixTime.split('.')
    if len(parts[1]) == 1:
        parts[1] = parts[1] + "0"
    return float(str(int(time.mktime(time.localtime()))) + "." + parts[1])

# funkcia na ukoncenie druheho vlakna procesu
def kill_thread():
    global run, timer, timerThread
    run = False
    #timer.push(time_nowF(), None)
    #timerThread.join()

# skontroluje ci je PID aktivne
def check_pid(pid):        
    try:
        os.kill(pid, 0)
    except OSError:
        return False
    else:
        return True

################################################################################
#### Funkcie na pracu s odoslanim dat ##########################################
################################################################################

def char_to_int(char):
    char = ord(char)
    if char >= 97:
        return char - 87
    elif char >= 48:
        return char - 48

def int_to_char(integer):
    if integer > 9:
        return chr(87 + integer)
    else:
        return chr(48 + integer)


def int_to_string(integer):
    i = integer
    part1 = i / 4096
    i = i % 4096
    part2 = i / 256
    i = i % 256
    part3 = i / 16
    part4 = i % 16
    return int_to_char(part1) + int_to_char(part2) + int_to_char(part3) + int_to_char(part4)

def checksum(msg):
    sum = 0
    for i in range(0, len(msg), 4):
        b3 = char_to_int(msg[i+0]) * 16 * 16 * 16
        b2 = char_to_int(msg[i+1]) * 16 * 16
        b1 = char_to_int(msg[i+2]) * 16
        b0 = char_to_int(msg[i+3])
        sum = sum + b3 + b2 + b1 + b0
        if sum > 65535:
            sum = sum - 65535
    numericChecksum = ~sum & 0xffff
    return int_to_string(numericChecksum)
    return numericChecksum

def multicast_join(address):
    global socket2
    string = ""
    i = len(address) - 1
    while True:
        if address[i] != ":":
            string = address[i] + string
            i = i - 1
        else:
            for j in range(0, 4-len(string)):
                string = "0" + string
            i = i - 1
            string = address[i] + string
            i = i - 1
            if address[i] == ":":
                string = "0"
            else:
                string = address[i] + string
            break
    
    
    address = "ff0200000000000000000001ff" + string
    sourceMac = "000000000000" #6cf049e0b94c
    destinationMac = "333300000016" #"ffffffffffff" #333300000016
    chck = checksum("00000000000000000000000000000000ff0200000000000000000000000000160000001c0000003a8f0000000000000104000000" + address)
    res=""

    message = destinationMac + sourceMac + "86dd600000000024000100000000000000000000000000000000ff0200000000000000000000000000163a000502000001008f00" + chck + "0000000104000000" + address
    for i in range(0, len(message), 2):
        res+=chr(int(message[i]+message[i+1], 16))
    socket2.send(res) # odeslani paketu

################################################################################
#### Funkcie na pracu s ukladanymi datami ######################################
################################################################################

# kalkulacia indexu do pola zo stringu, rozsah 0-maxHash
def db_calculate_hashD(string):
    global maxHash
    summary = 0
    for i in range(0, len(string)): # prejde kazdym znakom v stringu
        num = ord(string[i]) # zistime si ASCII hodnotu znaku
        summary = summary + num # ciselnu hodnotu pricitame k sume
        
    summary = summary%maxHash # hodnota indexu je v rozsahu 0-maxHash
    return summary


# vytvorenie jednorozmerneho rozmerneho pola pre ukladanie dat
def db_groups_create():
    global dbGroups
    dbGroups = [] # prazdne pole

# vypis celej tabulky
def db_groups_print():
    global dbGroups
    maxLengthAddress = 0
    maxLengthTime = 13
    maxLength = 0
    for i in range(0, len(dbGroups)): # prejdem kazdu polozku
        if len(dbGroups[i][0]) > maxLengthAddress: # hladam maximum
            maxLengthAddress = len(dbGroups[i][0])
    
    maxLength = maxLengthAddress + maxLengthTime + 7
    print "Tabulka multicast adres:"
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')
    
    for i in range(0, len(dbGroups)): # prejdem kazdu polozku
        sys.stdout.write('| ')
        sys.stdout.write(dbGroups[i][0])
        for i in range(0, maxLengthAddress - len(dbGroups[i][0])):
            sys.stdout.write(' ')
        sys.stdout.write(' | ')
        print dbGroups[i][1],
        sys.stdout.write(' |')
        print ""
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')

# vyhladanie multicast adresy v ulozisku
def db_groups_search(address):
    global dbGroups
    for i in range(0, len(dbGroups)): # prejdem kazdu polozku
        if dbGroups[i][0] == address: # nasiel som hladanu adresu
            return dbGroups[i] # vratim celu polozku
    return None # ak sa polozka nenajde vratim None

# vlozenie novej multicast adresy do uloziska
# funkcia zaroven odosiela spravu do siete ohladom prihlasenie do multicast skupiny
def db_groups_insert(address, sourceMessage, sourceMac):
    global dbGroups
    elem = db_groups_search(address) # zistenie ci uz dana polozka existuje
    endTime = time_nowF() + 55 # zistim cas do kedy sa bude dana polozka v tabulke nachadzat
    
    if elem != None and sourceMessage == "MLR": # polozka uz je ulozena, funkciu vyvolala MLR sprava
        db_groups_remove(address) # vymazem ju aby sa polozka pridala na konec zeznamu
    elif elem != None: # polozka je uz ulozena (funkciu vyvolala NS sprava)
        return # nepredlzujem svoju platnost v skupine
    #else: # prihlasenie do novej skupiny
    #    # modul zatal negeneruje traffic debug("NET: prihlaseni do " + address + " skupiny", "green")
    
    debug("Klient " + sourceMac + " sa prihlasuje do multicast skupiny " + address, "purple")
    multicast_join(address)
    debug("NET: ohlaseni is_exclude() ze pocuvam v skupine " + address, "green")
    
    elem = [address, endTime] # vytvorenie novej polozky
    debug("Tabulka multicast adres: pridanie zaznamu [" + address + ", " + str(endTime) + "]", "yellow")
    dbGroups.append(elem) # pridanie do uloziska

# kontrola multicastovych skupin, ktore su ulozene uz 30 sekund a preto sa uz v nich nemusime nachadzat
def db_groups_validate():
    global dbGroups
    #with lock:
    if True:
        lock.acquire()
        for i in range(0, len(dbGroups)): # prejdem kazdu polozku
            if dbGroups[0][1] <= time_nowF(): # modul uz je v skupine viac ako 60 sekund a preto uz tam byt nemusi
                savedAddress = dbGroups[0][0] # multicast adresa ulozena pri adrese
                savedTime = dbGroups[0][1] # casova hodnota ulozena pri adrese
                elem = [savedAddress, savedTime] # vytvorenie novej polozky
                debug("Tabulka multicast adres: odstranenie uz neplatneho zaznamu [" + savedAddress + ", " + str(savedTime) + "]", "yellow")
                dbGroups.remove(elem) # odstraneni polozky
            else:
                lock.release()
                return # polozky su zeradene podla casu a proto pri narazeni na prve vacsi cislo sa moze skoncit
        lock.release()

# funkcia prejde kazdy zaznam a pre kazdu adresu potvrdi svoju ucast
def db_groups_send_confirm():
    global dbGroups
    for i in range(0, len(dbGroups)): # prejdem kazdu polozku
        debug("NET: ohlaseni is_exclude() ze pocuvam v skupine " + dbGroups[i][0], "green")
        multicast_join(dbGroups[i][0])


# odstranenie multicast adresy z uloziska
def db_groups_remove(address):
    global dbGroups
    elem = db_groups_search(address) # zistenie ci uz dana polozka existuje
    
    if elem != None: # polozka sa nasla
        savedTime = elem[1] # casova hodnota ulozena pri adrese
        elem = [address, savedTime] # vytvorenie novej polozky
        debug("Tabulka multicast adres: odstranenie zaznamu [" + address + ", " + str(savedTime) + "]", "yellow")
        dbGroups.remove(elem) # odstraneni z uloziska



# vytvorenie jednorozmerneho rozmerneho pola pre ukladanie dat
def db_news_create():
    global dbNews
    dbNews = [] # prazdne pole

# vypis celej tabulky
def db_news_print():
    global dbNews
    maxLengthCol0 = 0
    maxLengthCol1 = 0
    maxLengthCol2 = 0
    maxLength = 0
    for i in range(0, len(dbNews)): # prejdem kazdu polozku
         # hladam maximum
        if len(dbNews[i][0]) > maxLengthCol0:
            maxLengthCol0 = len(dbNews[i][0])
        if len(dbNews[i][1]) > maxLengthCol1:
            maxLengthCol1 = len(dbNews[i][1])
        if len(str(dbNews[i][2])) > maxLengthCol2:
            maxLengthCol2 = len(str(dbNews[i][2]))
    
    maxLength = maxLengthCol0 + maxLengthCol1 + maxLengthCol2 + 10
    
    print "Tabulka mozno duplicitnych adres:"
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')
    
    for i in range(0, len(dbNews)): # prejdem kazdu polozku
        sys.stdout.write('| ')
        
        sys.stdout.write(dbNews[i][0])
        for i in range(0, maxLengthCol0 - len(dbNews[i][0])):
            sys.stdout.write(' ')
            
        sys.stdout.write(' | ')
        
        sys.stdout.write(dbNews[i][1])
        for i in range(0, maxLengthCol1 - len(dbNews[i][1])):
            sys.stdout.write(' ')
        
        sys.stdout.write(' | ')
        
        print dbNews[i][2],
        sys.stdout.write(' |')
        print ""
    
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')

# vyhladanie IPv6 adresy v ulozisku
def db_news_search(address):
    global dbNews
    for i in range(0, len(dbNews)): # prejdem kazdu polozku
        if dbNews[i][0] == address: # nasiel som hladanu adresu
            return dbNews[i] # vratim celu polozku
    return None # ak sa polozka nenajde vratim None

# vlozenie novej IPv6 a MAC adresy do uloziska (taktiez update ked zaznam uz existuje)
def db_news_insert(address, mac):
    global dbNews
    elem = db_news_search(address) # zistenie ci uz dana polozka existuje
    endTime = time_nowF() + 1 # zistim cas do kedy bude adresa povazovana za mozno duplicitnu
    
    if elem == None: # polozka zatial neexistuje a preto ju pridam
        elem = [address, mac, endTime] # vytvorenie novej polozky
        debug("Tabulka mozno duplicitnych adres: pridanie zaznamu [" + address + ", " + mac + ", " + str(endTime) + "]", "yellow")
        dbNews.append(elem) # pridanie do uloziska
    # riesi prijatie 2x ND:
    #else: # polozka uz existuje a preto len predlzim jej zivotnost
    #    debug("Tabulka mozno duplicitnych adres: aktualizovanie zaznamu [" + address + ", " + elem[1] + ", " + str(elem[2]) + "] na [" + address + ", " + mac + ", " + str(endTime) + "]", "yellow")
    #    for i in range(0, len(dbNews)): # prejdem kazdu polozku
    #        if dbNews[i][0] == address: # nasiel som hladanu adresu
    #            dbNews[i][1] = mac # prepisem aj MAC adresu pretoze opakovana kontrola duplicity mohla prijst od niekoho ineho
    #            dbNews[i][2] = endTime

# kontrola adres, ktore su ulozene uz 1 sekundu a preto mozu byt povazovane za platne
# funkcia zaroven odosiela spravu BEGIN na IRI-IIF na jednotlive IPv6 adresy
def db_news_validate():
    global dbNews
    
    #with lock:
    if True:
        lock.acquire()
        for i in range(0, len(dbNews)): # prejdem kazdu polozku
            if dbNews[0][2] <= time_nowF(): # polozka uz je ulozena minimalne 1 sekundu a preto je platna
                savedAddress = dbNews[0][0] # IPv6 adresa ulozena pri adrese
                savedMac = dbNews[0][1] # MAC adresa ulozena pri adrese
                savedTime = dbNews[0][2] # casova hodnota ulozena pri adrese
                elem = [savedAddress, savedMac, savedTime] # vytvorenie novej polozky
                debug("Tabulka mozno duplicitnych adres: odstranenie potvrdeneho zaznamu [" + savedAddress + ", " + savedMac + ", " + str(savedTime) + "]", "yellow")
                dbNews.remove(elem) # odstraneni 

                debug("Klient " + savedMac + " si potvrdil unikatnost adresy " + savedAddress, "purple")
                db_addresses_insert(savedAddress, savedMac)
                db_addresses_refresh(savedAddress) # aktualizujem posledny cas pouzitia adresy na teraz
            else:
                lock.release()
                return # polozky su zeradene podla casu a proto pri narazeni na prve vacsi cislo sa moze skoncit
        lock.release()

# odstranenie zaznamu z uloziska
def db_news_remove(address):
    global dbNews
    elem = db_news_search(address) # zistenie ci uz dana polozka existuje
    
    if elem != None: # polozka sa nasla
        savedMac = elem[1] # MAC adresa ulozena pri adrese
        savedTime = elem[2] # casova hodnota ulozena pri adrese
        elem = [address, savedMac, savedTime] # vytvorenie novej polozky
        debug("Tabulka mozno duplicitnych adres: odstranenie zaznamu [" + address + ", " + savedMac + ", " + str(savedTime) + "]", "yellow")
        dbNews.remove(elem) # odstraneni z uloziska



# vytvorenie pola (ulozisko 2) pre ukladanie dat, velkost maxHash polozek
def db_mac_create():
    global dbMac
    dbMac = [] # prazdne pole
    for i in range(0, maxHash):
        dbMac.append([]) # vytvori prazdnu polozku

# vypis celej tabulky
def db_mac_print():
    global dbMac
    maxLengthCol0 = len(str(maxHash))
    maxLengthCol1 = 0
    maxLengthCol2 = 0
    maxLength = 0
    for i in range(0, len(dbMac)): # prejdem kazdu polozku
        # hladam maximum
        for j in range(0, len(dbMac[i])): # prejdem kazdu subpolozku
            if len(dbMac[i][j][0]) > maxLengthCol1:
                maxLengthCol1 = len(dbMac[i][j][0])
            for k in range(0, len(dbMac[i][j][1])): # prejdem kazdu subsubpolozku
                if len(dbMac[i][j][1][k]) > maxLengthCol2:
                    maxLengthCol2 = len(dbMac[i][j][1][k])
    
    maxLength = maxLengthCol0 + maxLengthCol1 + maxLengthCol2 + 10
    
    print "Tabulka aktualnych adres zeradenych podla MAC adresy:"
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')
    
    for i in range(0, len(dbMac)): # prejdem kazdu polozku
        if len(dbMac[i]) == 0:
            continue
        for j in range(0, len(dbMac[i])): # prejdem kazdu subpolozku
            for k in range(0, len(dbMac[i][j][1])): # prejdem kazdu subsubpolozku
                
                sys.stdout.write('| ')
                for l in range(0, maxLengthCol0 - len(str(i))):
                    sys.stdout.write(' ')
                print i,
                
                sys.stdout.write(' | ')
                sys.stdout.write(dbMac[i][j][0])
                for l in range(0, maxLengthCol1 - len(dbMac[i][j][0])):
                    sys.stdout.write(' ')
                
                sys.stdout.write(' | ')
                sys.stdout.write(dbMac[i][j][1][k])
                for l in range(0, maxLengthCol2 - len(dbMac[i][j][1][k])):
                    sys.stdout.write(' ')
                
                sys.stdout.write(' |')
                print ""
    
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')

# vlozenie novej IPv6 a MAC adresy do uloziska 2
def db_mac_insert(mac, address):
    global dbMac
    
    index = db_calculate_hashD(mac) # vypocet indexu do hashovacej tabulky z MAC adresy
    for i in range(0, len(dbMac[index])): # prejdem kazdu polozku s rovnakym hash indexom
        if dbMac[index][i][0] == mac: # rovnaka MAC adresa, staci pridat IP
            debug("Tabulka aktivnych adres (podla MAC): pridanie adresy " + address + " k MAC " + mac, "yellow")
            dbMac[index][i][1].append(address) # pridanie adresy do uloziska 2
            return
    
    # sem sa v kode dostaneme ked v tabulke nie je zaznam z danou MAC adresou
    debug("Tabulka aktivnych adres (podla MAC): pridanie zaznamu MAC " + mac + " spolu s adresou " + address, "yellow")
    elem = [mac, [address]] # vytvorenie polozky s danou MAC a IP
    dbMac[index].append(elem) # pridanie zaznamu do uloziska 2

# odstranenie zaznamu z uloziska 2
def db_mac_remove(mac, address):
    global dbMac
    
    index = db_calculate_hashD(mac) # vypocet indexu do hashovacej tabulky z MAC adresy
    for i in range(0, len(dbMac[index])): # prejdem kazdu polozku s rovnakym hash indexom
        if dbMac[index][i][0] == mac: # rovnaka MAC adresa
            debug("Tabulka aktivnych adres (podla MAC): odstranenie adresy " + address + " od MAC adresy " + mac, "yellow")
            dbMac[index][i][1].remove(address) # odstraneni adresy z uloziska 2
            
            if len(dbMac[index][i][1]) == 0: # k danej MAC adrese nie je priradena ziadna IP adresa
                debug("Tabulka aktivnych adres (podla MAC): odstranenie zaznamu s MAC adresu " + mac, "yellow")
                elem = [mac, []]
                dbMac[index].remove(elem) # odstraneni zaznamu s MAC z uloziska 2

# upravenie polozky v ulozisku 2
def db_mac_update(oldMac, newMac, address):
    global dbMac
    oldIndex = db_calculate_hashD(oldMac) # vypocet indexu do hashovacej tabulky ze starej MAC adresy
    newIndex = db_calculate_hashD(newMac) # vypocet indexu do hashovacej tabulky z novej MAC adresy
    
    #db_mac_remove(oldMac, address)
    #db_mac_insert(newMac, address)



# vytvorenie pola pre ukladanie dat, velkost maxHash polozek
def db_addresses_create():
    global dbAddresses
    dbAddresses = [] # prazdne pole
    for i in range(0, maxHash):
        dbAddresses.append({}) # vytvori prazdnu polozku

# vypis celej tabulky
def db_addresses_print():
    global dbAddresses
    maxLengthCol0 = len(str(maxHash))
    maxLengthCol1 = 0 # solicited adresa
    maxLengthCol2 = 0 # IPv6 adresa
    maxLengthCol3 = 0 # MAC adresa
    maxLengthCol4 = 1 # flag
    maxLengthCol5 = 13 # unix time
    maxLengthCol6 = 2 # timeout
    maxLength = 0
    for i in range(0, len(dbAddresses)): # prejdem kazdu polozku
        # hladam maximum
        if len(dbAddresses[i]) == 0:
            continue
        for solicitedItem in dbAddresses[i]: # prejdem kazdu subpolozku
            if len(solicitedItem) > maxLengthCol1: # solicited adresa
                maxLengthCol1 = len(solicitedItem)
            for ipItem in range(0, len(dbAddresses[i][solicitedItem][3])): # prejdem kazdu subsubpolozku
                if len(dbAddresses[i][solicitedItem][3][ipItem][0]) > maxLengthCol2: # IPv6 adresa
                    maxLengthCol2 = len(dbAddresses[i][solicitedItem][3][ipItem][0])
                if len(dbAddresses[i][solicitedItem][3][ipItem][1]) > maxLengthCol3: # MAC adresa
                    maxLengthCol3 = len(dbAddresses[i][solicitedItem][3][ipItem][1])
    
    maxLength = maxLengthCol0 + maxLengthCol1 + maxLengthCol2 + maxLengthCol3 + maxLengthCol4 + maxLengthCol5 + maxLengthCol6 + 7
    
    print "Tabulka aktualnych adres:"
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')
    
    
    for i in range(0, len(dbAddresses)): # prejdem kazdu polozku
        if len(dbAddresses[i]) == 0:
            continue
        for solicitedItem in dbAddresses[i]: # prejdem kazdu subpolozku
            for ipItem in range(0, len(dbAddresses[i][solicitedItem][3])): # prejdem kazdu subsubpolozku
                
                # cislo hash indexu
                sys.stdout.write(' ')
                for l in range(0, maxLengthCol0 - len(str(i))):
                    sys.stdout.write(' ')
                print i,
                
                # solicited adresa
                sys.stdout.write(' ')
                sys.stdout.write(solicitedItem)
                for l in range(0, maxLengthCol1 - len(solicitedItem)):
                    sys.stdout.write(' ')
                
                # flag
                sys.stdout.write(' ')
                for l in range(0, maxLengthCol4 - len(dbAddresses[i][solicitedItem][1])):
                    sys.stdout.write(' ')
                sys.stdout.write(dbAddresses[i][solicitedItem][1])
                
                # unix time
                sys.stdout.write(' ')
                for l in range(0, maxLengthCol5 - len(str(dbAddresses[i][solicitedItem][2]))):
                    sys.stdout.write(' ')
                sys.stdout.write(str(dbAddresses[i][solicitedItem][2]))
                
                # ipv6
                sys.stdout.write(' ')
                sys.stdout.write(dbAddresses[i][solicitedItem][3][ipItem][0])
                for l in range(0, maxLengthCol2 - len(dbAddresses[i][solicitedItem][3][ipItem][0])):
                    sys.stdout.write(' ')
                
                # mac
                sys.stdout.write(' ')
                sys.stdout.write(dbAddresses[i][solicitedItem][3][ipItem][1])
                for l in range(0, maxLengthCol3 - len(dbAddresses[i][solicitedItem][3][ipItem][1])):
                    sys.stdout.write(' ')
                
                # timeout
                sys.stdout.write(' ')
                for l in range(0, maxLengthCol6 - len(str(dbAddresses[i][solicitedItem][3][ipItem][2]))):
                    sys.stdout.write(' ')
                sys.stdout.write(str(dbAddresses[i][solicitedItem][3][ipItem][2]))
                
                #sys.stdout.write('|')
                print ""
    
    for i in range(0, maxLength):
        sys.stdout.write('-')
    sys.stdout.write('\n')

# vyhladanie IPv6 adresy v ulozisku
def db_addresses_search(address):
    global dbAddresses
    solicited = calculate_solicited_addressS(address) # vypocet solicited adresy podla adresy
    index = db_calculate_hashD(solicited) # vypocet indexu do hashovacej tabulky z solicited adresy
    
    for solicitedItem in dbAddresses[index]: # prejdem kazdu polozku s rovnakym hash indexom
        if solicitedItem == solicited: # rovnaka solicited adresa
            for ipItem in range(0, len(dbAddresses[index][solicitedItem][3])):  # prejdem kazdu polozku s rovnakou solicited adresou
                if dbAddresses[index][solicitedItem][3][ipItem][0] == address: # rovnaka IP adresa
                    return dbAddresses[index][solicitedItem][3][ipItem] # vratim polozku s danou adresou
    
    return None # ak sa polozka nenajde vratim None

# upravenie polozky v ulozisku
def db_addresses_update(address, mac):
    global dbAddresses
    solicited = calculate_solicited_addressS(address) # vypocet solicited adresy podla IPv6 adresy
    index = db_calculate_hashD(solicited) # vypocet indexu do hashovacej tabulky z solicited adresy
    for solicitedItem in dbAddresses[index]: # prejdem kazdu polozku s rovnakym hash indexom
        if solicitedItem == solicited: # rovnaka solicited adresa
            for ipItem in range(0, len(dbAddresses[index][solicitedItem][3])):  # prejdem kazdu polozku s rovnakou solicited adresou
                if dbAddresses[index][solicitedItem][3][ipItem][0] == address: # rovnaka IP adresa
                    oldMac = dbAddresses[index][solicitedItem][3][ipItem][1]
                    debug("Tabulka aktivnych adres: updatovanie zaznamu [" + address + ", " + oldMac + "] na [" + address + ", " + mac + "]", "yellow")
                    dbAddresses[index][solicitedItem][3][ipItem][1] = mac
                    send_to_socket("('slaac', "+str(time_nowF())+", 'END', 'IP address is no longer connected', [('MAC','"+oldMac+"'),('IPv6','"+address+"')])", "green") # odoslanie IRI-END
                    send_to_socket("('slaac', "+str(time_nowF())+", 'BEGIN', 'User has generated new IP address', [('MAC','"+mac+"'),('IPv6','"+address+"')])", "green") # odeslani IRI-BEGIN
                    return
    
    debug("Pokus o updatovanie adresy " + address + " z tabulky mozno aktivnych adres, avsak taka polozka tam neni.", "red")

# vlozenie novej IPv6 a MAC adresy do uloziska
def db_addresses_insert(address, mac):
    global dbAddresses
    #with lock:
    if True:
        lock.acquire()
        elem = db_addresses_search(address) # zistenie ci uz dana IP adresa je priradena

        if elem != None: # polozka uz je priradena
            if elem[1] == mac: # IP adresa je uz spravne ulozena
                debug("Klient " + mac + " potvrdzuje ze ma stale adresu " + address, "purple")
                send_to_socket("('slaac', "+str(time_nowF())+", 'CONTINUE', 'User confirms, that he is still using IP address', [('MAC','"+mac+"'),('IPv6','"+address+"')])", "green") # odeslani IRI-CONTINUE
            else: # adresa je sice ulozena ale inemu uzivatelovi
                db_addresses_update(address, mac) # upravenie polozky (mac adresy)
        else: # polozka zatial nie je ulozena
            solicited = calculate_solicited_addressS(address) # vypocet solicited adresy podla adresy
            index = db_calculate_hashD(solicited) # vypocet indexu do hashovacej tabulky z solicited adresy
            for solicitedItem in dbAddresses[index]: # prejdem kazdu polozku s rovnakym hash indexom
                if solicitedItem == solicited: # dana solicited adresa uz je ulozena
                    debug("Tabulka aktivnych adres: pridanie zaznamu [" + address + ", " + mac + "] k solicited adrese " + solicited, "yellow")
                    dbAddresses[index][solicitedItem][3].append([address, mac, 0]) # pridanie adresy do uloziska
                    #dbAddresses[index][solicitedItem][3][address] = mac # pridanie adresy do uloziska
                    send_to_socket("('slaac', "+str(time_nowF())+", 'BEGIN', 'User has generated new IP address', [('MAC','"+mac+"'),('IPv6','"+address+"')])", "green") # odeslani IRI-BEGIN
                    lock.release()
                    return

            # sem sa v kode dostaneme ked v tabulke nie je zaznam z danou solicited adresou
            debug("Tabulka aktivnych adres: vytvorenie zaznamu so solicited adresou  " + solicited, "yellow")
            debug("Tabulka aktivnych adres: pridanie zaznamu [" + address + ", " + mac + "] k solicited adrese " + solicited, "yellow")
            
            dbAddresses[index][solicited] = [solicited, "A", time_nowF(), [[address, mac, 0]]] # pridanie zaznamu do uloziska
            send_to_socket("('slaac', "+str(time_nowF())+", 'BEGIN', 'User has generated new IP address', [('MAC','"+mac+"'),('IPv6','"+address+"')])", "green") # odeslani IRI-BEGIN
        lock.release()

# kontrola adres, ktore su starsi ako preneseny parameter a oznacene ako zastarale, tie vymazem
# funkcia zaroven odosiela spravu END na IRI-IIF na jednotlive IPv6 adresy
def db_addresses_validate():
    global dbAddresses
    #with lock:
    if True:
        lock.acquire()
        for i in range(0, len(dbAddresses)): # prejdem kazdu polozku
            for solicitedItem in dbAddresses[i]: # prejdem kazdu subpolozku
                if dbAddresses[i][solicitedItem][1] == "Z": # stare zastarale adresy
                    maximum = len(dbAddresses[i][solicitedItem][3])
                    for ipItem in range(0, maximum): # prejdem kazdu subsubpolozku
                        oldAddress = dbAddresses[i][solicitedItem][3][ipItem][0]
                        oldMac = dbAddresses[i][solicitedItem][3][ipItem][1]
                        dbAddresses[i][solicitedItem][3].pop(ipItem) # odstranenie zaznamu
                        debug("Tabulka aktivnych adres: odstranenie neaktualneho zaznamu [" + oldAddress + ", " + oldMac + "]", "yellow")
                        send_to_socket("('slaac', "+str(time_nowF())+", 'END', 'IP address is no longer connected', [('MAC','"+oldMac+"'),('IPv6','"+oldAddress+"')])", "green") # odoslanie IRI-END
                        maximum = maximum - 1
                    dbAddresses[i].pop(solicitedItem) # odstranenie solicited zaznamu
                    debug("Tabulka aktivnych adres: odstranenie neaktualnej solicited adresy " + solicitedItem, "yellow")
                    #db_addresses_validate() # rekurzivne volanie
                    #return
        lock.release()

# nastavenie priznaku na vsetky IPv6 adresy z urcitej solicited skupiny
def db_addresses_solicited_flag(solicitedAddress, flag, time):
    global dbAddresses
    if solicitedAddress == "::": # nastaveni priznaku na secky adresy ze seckych solicited skupin
        for i in range(0, len(dbAddresses)): # prejdem kazdy hash index
            for solicitedItem in dbAddresses[i]: # prejdem kazdu polozku s danym hash indexom
                dbAddresses[i][solicitedItem][1] = flag
                dbAddresses[i][solicitedItem][2] = time
                debug("Tabulka aktivnych adres: nastavenie flagu " + flag + " na solicited rozsah " + dbAddresses[i][solicitedItem][0], "yellow")
    else:
        index = db_calculate_hashD(solicitedAddress) # vypocet indexu do hashovacej tabulky z solicited adresy
        for solicitedItem in dbAddresses[index]: # prejdem kazdu polozku s rovnakym hash indexom
            if dbAddresses[index][solicitedItem][0] == solicitedAddress: # rovnaka solicited adresa
                dbAddresses[index][solicitedItem][1] = flag
                dbAddresses[index][solicitedItem][2] = time
                debug("Tabulka aktivnych adres: nastavenie flagu " + flag + " na solicited rozsah " + solicitedAddress, "yellow")

# odstranit secky IPv6 adresy priradene k danej solicited adrese a MAC adrese
# funkcia zaroven odosiela IRI-END spravy na prislusne adresy, ktore patri k danej solicited adrese
def db_addresses_solicited_remove(solicitedAddress, mac):
    global dbAddresses
    #with lock:
    if True:
        lock.acquire()
        index = db_calculate_hashD(solicitedAddress) # vypocet indexu do hashovacej tabulky z solicited adresy
        for solicitedItem in dbAddresses[index].keys(): # prejdem kazdu polozku s rovnakym hash indexom
            if solicitedItem == solicitedAddress: # rovnaka solicited adresa
                maximum = len(dbAddresses[index][solicitedItem][3])
                for ipItem in range(0, maximum):  # prejdem kazdu polozku s rovnakou solicited adresou
                    if dbAddresses[index][solicitedItem][3][ipItem][1] == mac: # rovnaka MAC adresa
                        oldAddress = dbAddresses[index][solicitedItem][3][ipItem][0]
                        dbAddresses[index][solicitedItem][3][ipItem][2] = 0
                        debug("Tabulka aktivnych adres: odstranenie neaktualneho zaznamu [" + oldAddress + ", " + mac + "]", "yellow")
                        dbAddresses[index][solicitedItem][3].pop(ipItem) # odstranenie zaznamu
                        send_to_socket("('slaac', "+str(time_nowF())+", 'END', 'IP address is no longer connected', [('MAC','" + mac + "'),('IPv6','"+oldAddress+"')])", "green") # odoslanie IRI-END
                        maximum = maximum - 1
                
                if len(dbAddresses[index][solicitedItem][3]) == 0: # ziadna IP adresa nie je priradena k danej solicited adrese
                    debug("Tabulka aktivnych adres: odstranenie prazdnej solicited adresy " + solicitedAddress, "yellow")
                    dbAddresses[index].pop(solicitedItem) # odstranenie solicited zaznamu
                lock.release()
                return
        lock.release()

# funkcia kontroluje kedy naposledy bola znama aktivita z nejakej IPv6 adresy a inkrementuje ich neaktualnost
def db_addresses_refresh(address):
    global dbAddresses
    solicited = calculate_solicited_addressS(address) # vypocet solicited adresy podla adresy
    index = db_calculate_hashD(solicited) # vypocet indexu do hashovacej tabulky z solicited adresy
    #with lock:
    if True:
        lock.acquire()
        for solicitedItem in dbAddresses[index]: # prejdem kazdu polozku s rovnakym hash indexom
            if solicitedItem == solicited: # rovnaka solicited adresa
                for ipItem in range(0, len(dbAddresses[index][solicitedItem][3])):  # prejdem kazdu polozku s rovnakou solicited adresou
                    if dbAddresses[index][solicitedItem][3][ipItem][0] == address: # rovnaka IP adresa
                        dbAddresses[index][solicitedItem][3][ipItem][2] = 0
        lock.release()

# funkcia kontroluje kedy naposledy bola znama aktivita z nejakej IPv6 adresy a inkrementuje ich neaktualnost
def db_addresses_timeout():
    global dbAddresses
    #with lock:
    if True:
        lock.acquire()
        for i in range(0, len(dbAddresses)): # prejdem kazdu polozku
            for solicitedItem in dbAddresses[i]: # prejdem kazdu subpolozku
                maximum = len(dbAddresses[i][solicitedItem][3])
                for ipItem in range(0, maximum): # prejdem kazdu subsubpolozku
                    if ipItem == maximum:
                        break
                    if dbAddresses[i][solicitedItem][3][ipItem][2] == 6:
                        address = dbAddresses[i][solicitedItem][3][ipItem][0]
                        mac = dbAddresses[i][solicitedItem][3][ipItem][1]
                        debug("Tabulka aktivnych adres: odstranenie neaktualneho zaznamu [" + address + ", " + mac + "]", "yellow")
                        dbAddresses[i][solicitedItem][3].pop(ipItem) # odstranenie zaznamu
                        maximum = maximum - 1
                        ipItem = ipItem - 1
                        send_to_socket("('slaac', " + str(time_nowF()) + ", 'END', 'IP address is no longer connected', [('MAC','" + mac + "'),('IPv6','" + address + "')])", "green") # odoslanie IRI-END
                    else:
                        dbAddresses[i][solicitedItem][3][ipItem][2] = dbAddresses[i][solicitedItem][3][ipItem][2] + 1
    
        lock.release()


################################################################################
#### Funkcie na prekonvertovanie dat do inej zobrazovacej podoby ###############
################################################################################

#funkcia na vypocet ciselnej hodnoty zo stringu
def str_to_int(string):
    result = 0
    for char in string: # prejdem kazdy znak zo vstupu
        result = result*256 + ord(char) # vysledok 
    return result

#string prekonvertujem po bajtoch do hexa podoby
def str_to_hex(string):
    hexResult = ""
    for char in string: # prejdem kazdy znak zo vstupu
        hexa = hex(ord(char)).replace('0x', '') # ziskam hexa podobu a odstranim znak "0x"
        if len(hexa) == 1: #kazdy bajt musi mat 2 zobrazene znaky
            hexResult += "0"
        hexResult += hexa
    return hexResult

#cislo prekonvertujem do hexa podoby !!! funguje len do 16 bitov(2 bajty) z dovodu konstanty 65520 !!!
def int_to_hex(num):
    hexResult = ""
    if num == 0: # specialny pripad
        hexResult = "0"
    while num > 0:
        rest = num % 16
        if rest > 9: #znak A-F
            char = chr(rest + 97 - 10)
        else: # cislice 0-9
            char = chr(rest + 48)
        hexResult = char + hexResult
        num = num & 65520 #vynulujem dolne 4 bity
        num = num / 16 # posuv o 4 bity do prava
    return hexResult


################################################################################
#### Funkcie na prekonvertovanie surovych dat do ludskej podoby ################
################################################################################

#prekonvertuje string do podoby MAC adresy
# MAC adresa musi byt dlha 12 znakov (t.j. zadana bez oddelovacov ":")
def str_to_mac(string):
    result = ""
    for i in range(0, 12): #prejde postupne 12 znakov
        result += string[i]
        if i%2 == 1 and i != 11: #medzi pary bajtov vlozi ":"
            result += ":"
    return result

#prekonvertuje string do podoby IPv4 adresy
# IPv4 adresa musi byt zadana v podobe 4 bajtov
def str_to_ip4(string):
    ip = ""
    for i in range(0,4): # prejde postupne 4 bajty
        ip += str(str_to_int(string[i:i+1])) #bajt prevedie na cislo a vytvori z neho string
        if i != 3: # medzi jednotlive casti sa vlozi oddelovac "."
            ip += "."
    return ip

#prekonvertuje string do IPv6 podoby
def str_to_ip6(string):
    ip = ""
    for i in range(0,len(string),2): # medzi kazde 2 bloky vlozim oddelovac ":"
        if i != 0:
            ip += ":"
        num = str_to_int(string[i:i+2])
        ip += int_to_hex(num)
    # v adrese nahradim najvacsi vyskyt :0:0: za ::
    for i in range(7, 1, -1):
        # vytvorim si string znakov :0:0:0:, ktory neskor vyhladavam v texte
        search = ""
        for j in range(0, i):
            search += ":0"
        
        if search + ":" in ip: # :0:0: ma vacsiu prioritu ako :0:0
            ip = ip.replace(search + ":", "::", 1) # postupnost :0:0:0: nahradim :: ale max 1x
            return ip #aby sa nenahradzovalo viac ako 1x
        if search in ip:
            ip = ip.replace(search, "::", 1) # postupnost :0:0:0 nahradim :: ale max 1x
            if ip == "0::":
                return "::"
            return ip #aby sa nenahradzovalo viac ako 1x
    return ip


################################################################################
#### Funkcie na pracu s ethernem datami ########################################
################################################################################

# funkcia vrati zo vstupnych dat ethernet hlavicku
def ether_headerB(data):
    #etherType = str_to_hex(data[12:14])
    # nepodporuje etherType = 0 (velkost headru je 0)
    # pri nahravani cez any nemoze byt VLAN ID 1757 s priority 4
    # pri nahravani cez any musi za L2 byt hned IPv6 header
    if str_to_hex(data[12:14]) == "8100": # 802.1Q VLAN tag na beznom rozhrani (napr. eth0)
        return data[0:18]
    elif str_to_hex(data[12:14]) == "0000": # nahrate cez interface any
        if str_to_hex(data[14:16]) == "86dd": # bez VLAN
            return data[0:16]
        else: # s VLAN
            return data[0:18]
    else: # etherType
        return data[0:14]

# funkcia vrati z ethernet hlavicky zapuzdrene data
def ether_payloadB(data, headerLength):
    return data[headerLength:len(data)]

# funkcia vrati z ethernet hlavicky zdrojovu MAC adresu
def ether_source_macS(etherHeader):
    return str_to_mac(str_to_hex(etherHeader[6:12]))

# funkcia vrati z ethernet hlavicky typ zapuzdreneho protokolu
def ether_typeH(etherHeader):
    length = len(etherHeader)
    return str_to_hex(etherHeader[length-2:length])



################################################################################
#### Funkcie na pracu s IPv6 datami ############################################
################################################################################

# calkulacia solicited addresy podla IPv6 adresy
def calculate_solicited_addressS(address):
    parts = address.split(':') # rozdelenie adresy podla jednotlivych blokov
    part1 = parts[len(parts)-2] # druha od konca
    part2 = parts[len(parts)-1] # posledna
    if len(parts) < 3: # zly tvar MAC adresy
        debug("Pokus o vytvorenie solicited adresy z IPv6 adresy " + address + ".", "red")

    result = "ff02::1:ff" # konstatna cast adresy

    # z predposlednej casti IPv6 adresy je potrebne zapisat spodnych 8 bitov
    if len(part1) == 0: # nulova dlzka znamena to ze je zadana adresa v style fe80::1
        result += "00"
    elif len(part1) == 1:  # dlzka 1 znamena to ze prvych 12 bitov adresy je 0 a tak je zapis v style fe80::1:2
        result += "0" + part1
    else: # dlkza je aspon 2 a preto staci skopirovat spodne 2 znaky
        result += part1[-2] + part1[-1]

    result += ":" + part2 # posledna cast IPv6 adresy sa nijako nemeni

    return result

# funkcia vyhlada v IPv6 hlavicke zdrojovu adresu
def ipv6_source_address(data):
    return str_to_ip6(data[8:24])
    
# funkcia vyhlada v IPv6 hlavicke cilovu adresu
def ipv6_destination_address(data):
    return str_to_ip6(data[24:40])

################################################################################
#### Funkcie na pracu s ICMPv6 datami ##########################################
################################################################################


# funkcia vyhlada v zretazenych hlavickach ICMPv6 hlavicku
def icmpv6_headerB(data):
    if len(data) < 56: # ochrana proti chybnym packetom, ktorych dlzka nezodpoveda minimu
        return ""
    if str_to_hex(data[6:7]) == "00": # Next header: IPv6 hop-by-hop option (0x00)
        if str_to_hex(data[40:41]) == "3a": # Hop-by-Hop Option> Next header: ICMPv6 (0x3a)
            return data[48:len(data)]
    elif str_to_hex(data[6:7]) == "3a": # Next header: ICMPv6 (0x3a)
        return data[40:len(data)]
    
    return "" # prazdny string znamena, ze sa ICMPv6 hlavicka nenasla

# funkcia vyhlada v ICMPv6 hlavicke polozku Type
def icmpv6_typeH(data):
    return str_to_hex(data[0:1])

# funkcia vyhlada v ICMPv6 sprave IPv6 adresu
def icmpv6_addressS(data):
    return str_to_ip6(data[8:24])

# zistenie poctu zaznamov v icmpv6 sprave
def icmpv6_records_numberD(data):
    return str_to_int(data[6:8])

# zistenie velkosti doplnkovych dat
def icmpv6_record_aux_data_lengthD(data, index):
    return str_to_int(data[index+1:index+2])

# zistenie poctu zdrojov pre dane multicastove vysielanie
def icmpv6_record_number_of_sourcesD(data, index):
    return int(str_to_int(data[index+2:index+4]))

# zistenie multicast skupiny z daneho zaznamu
def icmpv6_record_address(data, index):
    return str_to_ip6(data[index+4:index+20])

# zistenie typu zaznamu
def icmpv6_record_typeH(data, index):
    return str_to_hex(data[index+0:index+1])

# zisti do kolko milisekund maju stanice odpovedat na to ci sa nachadzaju este v danej multicast skupine
def icmpv6_max_responseD(data):
    return str_to_int(data[5:6])

################################################################################
#### Hlavna funkcia ktora spracovava kazdy prijaty frame #######################
################################################################################

#funkcia spracovavajuca vsetky prijate data zo siete
def callback(hdr, data):
    global db, dbNews, dbGroups
    
    etherHeader = ether_headerB(data) # zistenie ethernet hlavicky
    sourceMac = ether_source_macS(etherHeader) # MAC adresa klienta
    
    if sourceMac == "00:00:00:00:00:00":
        return
    
    if ether_typeH(etherHeader) == "86dd": # IPv6
        ipv6Data = ether_payloadB(data, len(etherHeader)) # vysekanie dat IPv6 (odstranenie nizsich vrstev)
        sourceIpv6Address = ipv6_source_address(ipv6Data)
        icmpv6Data = icmpv6_headerB(ipv6Data) # najdenie hlavicky ICMPv6
        
        if icmpv6Data != "": # v danom pakete sa nasla ICMPv6 hlavicka
            db_addresses_refresh(sourceIpv6Address) # aktualizujem posledny cas pouzitia adresy na teraz
            icmpv6Type = icmpv6_typeH(icmpv6Data) # zistenie parametru Type    
            
            if icmpv6Type == "82": # Multicast Listener Query (130), smerovac zistuje ci je nekdo v skupine
                multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                if "ff02::1:ff" in multicastAddress or multicastAddress == "::": # klient sa prihlasuje do solicited multicast adresy (ff02::1:ff - prefix solicited multicast adresy)
                    debug("Prijata ICMPv6 sprava: Multicast Listener Query (130)", "cyan")
                    debug("Router " + sourceMac + " zistuje ci nekdo pocuva na adrese " + multicastAddress, "purple")
                    
                    if multicastAddress == "::": # vsetky skupiny
                        debug("NET: ohlaseni is_exclude() ze pocuvam v skupine ff02::16", "green")
                        db_groups_send_confirm() # na vsetky adresy v ktorych je modul prihlaseny sa potvrdi ucast v skupine
                    else: # router kontroluje konkretnu adresu skupiny
                        if db_groups_search(multicastAddress) != None: # modul je v danej skupine prihlaseny
                            debug("NET: ohlaseni is_exclude() ze pocuvam v skupine " + multicastAddress, "green")
                            multicast_join(multicastAddress)
                    
                    maximumResponseTime = icmpv6_max_responseD(icmpv6Data) / 1000.0 # maximalne spozdenie v sekundach odpovedi pre router
                    debug("Odpovede na dotazovanu adresu " + multicastAddress + " musia prist do " + str(maximumResponseTime) + " s", "purple")
                    db_addresses_solicited_flag(multicastAddress, "Z", time_nowF()) # vyhledam secky dotknute IPv6 adresy a nastavim flag ze sa uz mozno nepouzivaju + aktualnu hodnotu time
                    timer.push(time_nowF() + maximumResponseTime + 2, "validate") # do daneho casu sa musi ozvat niekto z multicast skupiny
                elif multicastAddress == "ff02::16": # vsetky MLDv2 smerovace
                    debug("NET: ohlaseni is_exclude() ze pocuvam v skupine ff02::16", "green")
                    multicast_join("ff02::16")
            elif icmpv6Type == "83": # Multicast Listener Report (131), prihlaseni do skupiny
                multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                if "ff02::1:ff" in multicastAddress: # klient sa prihlasuje do solicited multicast adresy (ff02::1:ff - prefix solicited multicast adresy)
                    debug("Prijata ICMPv6 sprava: Multicast Listener Report (131)", "cyan")
                    db_groups_insert(multicastAddress, "MLR", sourceMac) # pridam sa do danej multicast skupiny, pripadne si tam predlzim svoje umistneni
                    db_addresses_solicited_flag(multicastAddress, "A", time_nowF()) # zaktualizovanie vsetkych IP adres z daneho rozsahu
                
            elif icmpv6Type == "84": # Multicast Listener Done (132), odhlaseni ze skupiny
                multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                if "ff02::1:ff" in multicastAddress: # klient sa odhlasuje z solicited multicast adresy (ff02::1:ff - prefix solicited multicast adresy)
                    debug("Prijata ICMPv6 sprava: Multicast Listener Done (132)", "cyan")
                    debug("Klient " + sourceMac + " sa odhlasuje z multicast skupiny " + multicastAddress, "purple")
                    db_addresses_solicited_remove(multicastAddress, sourceMac) # odeslani IRI-END na secky IP adresy patrici k danej solicited a MAC adrese
            
            elif icmpv6Type == "8f": # Multicast Listener Report Message v2 (143), zmena v skupine
                zobrazenePrijatiSpravy = 0
                multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                numberOfRecords = icmpv6_records_numberD(icmpv6Data) # pocet zaznamov v danej sprave
                recordPosition = 8 # ukazovatel do packetu kery ukazuje na dalsi zaznam
                
                for i in range(0, numberOfRecords): # prejde dany pocet zaznamov
                    # vypocet posunu na dalsi zaznam
                    recordAuxDataLength = icmpv6_record_aux_data_lengthD(icmpv6Data, recordPosition)
                    recordNumberOfSources = icmpv6_record_number_of_sourcesD(icmpv6Data, recordPosition)
                    recordLength = 20 + recordNumberOfSources * 16 + recordAuxDataLength * 4
                    
                    recordAddress = icmpv6_record_address(icmpv6Data, recordPosition) # adresa, ktorej sa tyka zaznam
                    recordType = icmpv6_record_typeH(icmpv6Data, recordPosition) # typ zaznamu
                    
                    if "ff02::1:ff" in recordAddress:
                        if zobrazenePrijatiSpravy == 0:
                            zobrazenePrijatiSpravy = 1
                            debug("Prijata ICMPv6 sprava: Multicast Listener Report Message v2 (143)", "cyan")
                        
                        if recordType == "01" or recordType == "02": # klient potvrdzuje ze je stale sucastou danej skupiny
                            db_addresses_solicited_flag(recordAddress, "A", time_nowF()) # nastavi secky IP adresy spadajuce do daneho solicited rozsahu jak aktualne
                        
                        if (recordType == "03" and recordNumberOfSources > 0) or recordType == "04": # prihlasenie do multicast skupiny
                            debug("Klient " + sourceMac + " sa prihlasuje do multicast skupiny " + recordAddress, "purple")
                            db_groups_insert(recordAddress, "MLR", sourceMac) # prihlaseni do danej multicast skupiny
                        
                        elif recordType == "03": # odhlaseni ze skupiny
                            debug("Klient " + sourceMac + " sa odhlasuje z multicast skupiny " + recordAddress, "purple")
                            db_addresses_solicited_remove(recordAddress, sourceMac) # odeslani IRI-END na secky IP adresy patrici k danej solicited a MAC adresy
                    
                    recordPosition += recordLength # posun na dalsi zaznam pri dalsej iteracii
                
            elif icmpv6Type == "87": # Type: Neighbor Solicitation (135), kontrola duplicity
                if sourceIpv6Address == "::":
                    debug("Prijata ICMPv6 sprava: Neighbor Solicitation (135)", "cyan")
                    multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                    debug("Klient " + sourceMac + " kontroluje duplicitu " + multicastAddress + " adresy", "purple")
                    db_news_insert(multicastAddress, sourceMac)
                    solicitedAddress = calculate_solicited_addressS(multicastAddress)
                    db_groups_insert(solicitedAddress, "NS", sourceMac) # pridam sa do danej multicast skupiny, pripadne si tam predlzim svoje umistneni
                
            elif icmpv6Type == "88": # Type: Type: Neighbor Advertisement (136), odpoved na kontrolu duplicity
                debug("Prijata ICMPv6 sprava: Type: Neighbor Advertisement (136)", "cyan")
                multicastAddress = icmpv6_addressS(icmpv6Data) # adresa multicast skupiny
                debug("Klient " + sourceMac + " odpoveda na kontrolu duplicity adresy " + multicastAddress, "purple")
                db_addresses_refresh(multicastAddress) # aktualizujem posledny cas pouzitia adresy na teraz
                db_news_remove(multicastAddress) # odstranenie adresy z tabulky mozno duplicitnych adres
                db_addresses_insert(multicastAddress, sourceMac) # pridat adresu do aktivnych adres v pripade ze tam neni



################################################################################
#### Funkcia main + timer ######################################################
################################################################################

def _MAIN():
    pass

def queueThread(queue, socketName):
    global mainPid
    """ Manages currently active interceptions """
    while run: # Infinite loop
        value = queue.pop()
        
        if value == "dad": # kontrola mozno duplicitnych adres
            db_news_validate() # adresy starsie ako sekundu prehlasim za platne
            timer.push(time_nowF() + 0.5, "dad") # nekonecny casovac
        elif value == "multicast": # kontrola multicastovych skupin
            db_groups_validate() # do skupin v ktorych sme uz viac ako 60 sekund sa dalej nebudeme hlasit
            timer.push(time_nowF() + 30.0, "multicast") # nekonecny casovac 
        elif value == "timeout": # kontrola platnosti zaznamov
            db_addresses_timeout() # vyhledam adresy ktore su starsie ako prenesena hodnota a oznacene ako zastarale
            timer.push(time_nowF() + 60.0, "timeout") # nekonecny casovac

def main(argv):
    global socket1, socket2, debugMode, db, dbNews, dbGroups, dbAddresses, dbMac, run, timerThread, timer, mode#, mainPid
    
    if os.geteuid() != 0: # kontrola ci je user root
        sys.stderr.write("You must be root to run this script.\n")
        exit()
    
    db_groups_create() #inicializovanie ulozneho priestoru pre multicast adresy
    db_news_create() #inicializovanie ulozneho priestoru pre mozno duplicitne adresy
    db_addresses_create() #inicializovanie ulozneho priestoru pre aktualne platne adresy
    
    timerThread = threading.Thread(target = queueThread, args=(timer, None))
    timerThread.start()
    
    #mainPid = os.getppid()
    
    timer.push(time_nowF() + 1.0, "dad") # spustenie pravidelneho 1 sec casovaca na kontrolu mozno duplicitnych adres
    timer.push(time_nowF() + 30.0, "multicast") # spustenie pravidelneho 30 sec casovaca na kontrolu ci sa ma modul este nachadzat v multicast skupine
    timer.push(time_nowF() + 60.0, "timeout") # spustenie pravidelneho hodinoveho casovaca kedy sa kontroluje timeout jednotlivych zaznamov
    
    
    string = "any"
    shift = 0
    debugMode = ""
    
    try:
        opts, args = getopt.getopt(argv, "i:o", ["interface=","offline"]) # spracovanie parametrov
    except getopt.GetoptError, err:
        sys.stderr.write(str(err) + "\n")
        kill_thread()
        sys.exit(2)
    
    for param, value in opts: # prejdenie parametrov
        if param in("-i, --interface"):
            string = value
            shift = shift + 2
        
        elif param in ("-o,--offline"):
            mode = "offline"
            shift = shift + 1
    
    
    if len(argv) > shift: # kontrola ci sa nachadza zadany mod programu
        if argv[shift] == "info":
            debugMode = "grp" # jednotlive pismenka znamena ktory farebny vypis sa ma vypisat (vid zacatek suboru)
        else:
            debugMode = "grpybc" # jednotlive pismenka znamena ktory farebny vypis sa ma vypisat (vid zacatek suboru)
    
    
    if mode == "online":
        socket1 = connectUnixSocket("/tmp/iricol")
    
    try:
        reader = open_live(string, 1500, 0, 100) # zive zachytavanie
        socket2 = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, 0x8100)
        socket2.bind((string, 0x8100))
    except Exception, e:
        sys.stderr.write("Cannot open device %s: %s\n" % (string, str(e)))
        reader = None
    
    
    if reader:
        reader.setfilter("")
        reader.loop(0, callback)
    kill_thread() # po skonceni procesu ukoncim aj druhe vlakno


if __name__ == "__main__":
    
    signals.setHandlerForCommonSignals(signals.signalExceptionHandler)
    try:
        main(sys.argv[1:])
    except Exception, e:
        kill_thread()
        #print "SLAAC: Ukonceni na vyjimce - " + str(e)
