Commit 2649abb6 authored by Pedro Oliveira's avatar Pedro Oliveira

Hello first try - almost done

parent bcefc317
import random
from threading import Timer
from Packet.Packet import Packet
from Packet.PacketPimOption import PacketPimOption
from Packet.PacketPimHeader import PacketPimHeader
from Main import Main
from utils import KEEP_ALIVE_PERIOD_TIMEOUT
class Hello:
TYPE = 0
HELLO_HOLD_TIME = 16 # TODO: configure via external file??
def __init__(self):
Main().add_protocol(Hello.TYPE, self)
self.thread = Timer(0, self.send_handle)
self.thread.start()
def send_handle(self):
for (ip, interface) in Main().interfaces.items():
self.force_send_handle(interface)
# reschedule timer
# Hello Timer(HT) MUST be set to random value between 0 and Triggered_Hello_Delay
hello_timer = random.uniform(0, Hello.HELLO_HOLD_TIME)
self.thread = Timer(hello_timer, self.send_handle)
self.thread.start()
def force_send_handle(self, interface):
ph = PacketPimHeader(Hello.TYPE)
ph.add_option(PacketPimOption(1, Hello.HELLO_HOLD_TIME))
ph.add_option(PacketPimOption(20, interface.generation_id))
packet = Packet(pim_header=ph)
interface.send(packet.bytes())
# TODO: ver melhor este metodo
def force_send_remove_handle(self, interface):
ph = PacketPimHeader(Hello.TYPE)
ph.add_option(PacketPimOption(1, KEEP_ALIVE_PERIOD_TIMEOUT))
ph.add_option(PacketPimOption(20, interface.generation_id))
packet = Packet(pim_header=ph)
interface.send(packet.bytes())
# receive handler
def receive_handle(self, packet):
if packet.ip_header is None:
return # TODO: MAYBE EXCEPCAO??
ip = packet.ip_header.ip
print("ip = ", ip)
# Unknown Neighbor
main = Main()
options = packet.pim_header.get_options()
if main.get_neighbor(ip) is None:
if (1 in options) and (20 in options):
print("entrou... non neighbor and options inside")
main.add_neighbor(packet.interface, ip, options[20], options[1])
return
print("entrou... non neighbor and no options inside")
# Already know Neighbor
else:
print("entrou... neighbor conhecido")
neighbor = main.get_neighbor(ip)
neighbor.heartbeat()
if 1 in options and neighbor.keep_alive_period != options[1]:
print("keep alive period diferente")
neighbor.set_keep_alive_period(options[1])
if 20 in options and neighbor.random_number != options[20]:
print("neighbor reiniciado")
neighbor.remove()
main.add_neighbor(packet.interface, ip, options[20], options[1])
import socket
import threading
import random
from Packet.ReceivedPacket import ReceivedPacket
class Interface:
#IF_IP = "10.0.0.1"
MCAST_GRP = '224.0.0.13'
# substituir ip por interface ou algo parecido
def __init__(self, ip_interface):
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_PIM)
# allow other sockets to bind this port too
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# explicitly join the multicast group on the interface specified
s.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(Interface.MCAST_GRP) + socket.inet_aton(ip_interface))
# set socket output interface
s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(ip_interface))
# set socket TTL to 1
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
# don't receive outgoing packets
s.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
self.socket = s
self.interface_enabled = True
# generation id
self.generation_id = random.getrandbits(32)
# run receive method in background
receive_thread = threading.Thread(target=self.receive)
receive_thread.daemon = True
receive_thread.start()
def receive(self):
from Main import Main
while self.interface_enabled:
(raw_packet, (ip, p)) = self.socket.recvfrom(256 * 1024)
packet = ReceivedPacket(raw_packet, self)
print("packet received bytes: ", packet.bytes())
print("pim type received = ", packet.pim_header.msg_type)
print("generation id received = ", packet.pim_header.options[1].option_value)
Main().protocols[packet.pim_header.msg_type].receive_handle(packet) # TODO: perceber se existe melhor maneira de fazer isto
def send(self, data):
self.socket.sendto(data, (Interface.MCAST_GRP, 0))
def remove(self):
self.interface_enabled = False
import netifaces
from prettytable import PrettyTable
from Interface import Interface
from Neighbor import Neighbor
class Main(object):
class __Main:
def __init__(self):
self.interfaces = {} # interfaces with multicast routing enabled
self.neighbors = {} # multicast router neighbors
self.protocols = {}
def add_interface(self, ip):
if ip not in self.interfaces:
interface = Interface(ip)
self.interfaces[ip] = interface
#self.protocols[0].force_send_handle(interface) # force send hello packet to added interface
# TODO: verificar melhor este metodo:
def remove_interface(self, ip):
self.protocols[0].force_send_remove_handle(self.interfaces[ip])
del self.interfaces[ip]
def add_neighbor(self, contact_interface, ip, random_number, keep_alive_period):
print("ADD NEIGHBOR")
if ip not in self.neighbors:
self.neighbors[ip] = Neighbor(contact_interface, ip, random_number, keep_alive_period)
print(self.neighbors.keys())
def get_neighbor(self, ip):
if ip not in self.neighbors:
return None
return self.neighbors[ip]
def remove_neighbor(self, ip):
if ip in self.neighbors:
del self.neighbors[ip]
def add_protocol(self, protocol_number, protocol_obj):
self.protocols[protocol_number] = protocol_obj
def list_neighbors(self):
t = PrettyTable(['Neighbor IP', 'KeepAlive', "Random Number"])
for ip, neighbor in self.neighbors.items():
t.add_row([ip, neighbor.keep_alive_period, neighbor.random_number])
print(t)
def list_enabled_interfaces(self):
t = PrettyTable(['Interface', 'IP', 'Status'])
for interface in netifaces.interfaces():
# TODO: fix same interface with multiple ips
ip = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
status = ip in self.interfaces
t.add_row([interface, ip, status])
print(t)
def main(self, ip_interfaces_to_add):
from Hello import Hello
Hello()
for ip in ip_interfaces_to_add:
self.add_interface(ip)
# MAIN SINGLETON
instance = None
def __new__(cls): # __new__ always a classmethod
if not Main.instance:
Main.instance = Main.__Main()
return Main.instance
def __getattr__(self, name):
return getattr(self.instance, name)
def __setattr__(self, name):
return setattr(self.instance, name)
from threading import Timer
from utils import KEEP_ALIVE_PERIOD_NO_TIMEOUT, KEEP_ALIVE_PERIOD_TIMEOUT
class Neighbor:
def __init__(self, contact_interface, ip, random_number, keep_alive_period):
self.contact_interface = contact_interface
self.ip = ip
self.random_number = random_number
self.neighbor_liveness_timer = None
self.set_keep_alive_period(keep_alive_period)
def set_keep_alive_period(self, keep_alive_period):
self.keep_alive_period = keep_alive_period
if self.neighbor_liveness_timer is not None:
self.neighbor_liveness_timer.cancel()
if keep_alive_period == KEEP_ALIVE_PERIOD_TIMEOUT:
self.remove()
elif keep_alive_period != KEEP_ALIVE_PERIOD_NO_TIMEOUT:
self.neighbor_liveness_timer = Timer(4 * keep_alive_period, self.remove)
self.neighbor_liveness_timer.start()
else:
self.neighbor_liveness_timer = None
def heartbeat(self):
if (self.keep_alive_period != KEEP_ALIVE_PERIOD_TIMEOUT) and \
(self.keep_alive_period != KEEP_ALIVE_PERIOD_NO_TIMEOUT):
print("HEARTBEAT")
if self.neighbor_liveness_timer is not None:
self.neighbor_liveness_timer.cancel()
self.neighbor_liveness_timer = Timer(4 * self.keep_alive_period, self.remove)
self.neighbor_liveness_timer.start()
def send(self, packet):
self.contact_interface.send(self.ip, packet)
def remove(self):
from Main import Main
print('HELLO TIMER EXPIRED... remove neighbor')
if self.neighbor_liveness_timer is not None:
self.neighbor_liveness_timer.cancel()
Main().remove_neighbor(self.ip)
class Packet:
# ter ip header
# pim header
# pim options
def __init__(self, ip_header=None, pim_header=None):
self.ip_header = ip_header
self.pim_header = pim_header
# maybe remover
def add_option(self, option):
self.pim_header.add_option(option)
def bytes(self):
return self.pim_header.bytes()
import struct
class PacketIpHeader:
IP_HDR = "! BBH HH BBH LL"
#IP_HDR2 = "! B"
IP_HDR_LEN = struct.calcsize(IP_HDR)
def __init__(self, ip):
self.ip = ip
import struct
from utils import checksum
class PacketPimHeader:
PIM_VERSION = 2
PIM_HDR = "! BB H"
PIM_HDR_LEN = struct.calcsize(PIM_HDR)
# HELLO: type = 0
# pim options
def __init__(self, msg_type):
self.options = []
self.msg_type = msg_type
def add_option(self, option):
self.options.append(option)
def get_options_bytes(self):
res = b''
#print(self.options[0].option_type)
self.options.sort(key=lambda x: x.option_type) # TODO: duvida... ordenar? maybe not
#print(self.options[0].option_type)
for opt in self.options:
res += opt.bytes()
return res
def get_options(self):
dictionary = {}
for option in self.options:
dictionary[option.option_type] = option.option_value
return dictionary
def bytes(self):
# obter mensagem e criar checksum
pim_vrs_type = (PacketPimHeader.PIM_VERSION << 4) + self.msg_type
msg_without_chcksum = struct.pack(PacketPimHeader.PIM_HDR, pim_vrs_type, 0, 0)
msg_without_chcksum += self.get_options_bytes()
pim_checksum = checksum(msg_without_chcksum)
msg = msg_without_chcksum[0:2] + struct.pack("! H", pim_checksum) + msg_without_chcksum[4:]
return msg
import struct
class PacketPimOption:
PIM_HDR_OPTS = "! HH"
PIM_HDR_OPTS_LEN = struct.calcsize(PIM_HDR_OPTS)
PIM_MSG_TYPES_LENGTH = {1: 2,
20: 4,
}
def __init__(self, option_type, option_value):
self.option_type = option_type
self.option_value = option_value
def bytes(self):
option_length = PacketPimOption.PIM_MSG_TYPES_LENGTH[self.option_type]
msg = struct.pack(PacketPimOption.PIM_HDR_OPTS, self.option_type, option_length)
return msg + struct.pack("! " + str(option_length) + "s", self.option_value.to_bytes(option_length, byteorder='big'))
import struct
from Packet.Packet import Packet
from Packet.PacketIpHeader import PacketIpHeader
from Packet.PacketPimHeader import PacketPimHeader
from Packet.PacketPimOption import PacketPimOption
from utils import checksum
class ReceivedPacket(Packet):
def __init__(self, raw_packet, interface):
self.interface = interface
#Parse ao packet e preencher objeto Packet
x = ReceivedPacket.parseIpHdr(raw_packet[:PacketIpHeader.IP_HDR_LEN])
print(x["HLEN"])
msg_without_ip_hdr = raw_packet[x["HLEN"]:]
self.ip_header = PacketIpHeader(x["SRC"])
# print(msg_without_ip_hdr)
pim_hdr = ReceivedPacket.parsePimHdr(msg_without_ip_hdr[0:PacketPimHeader.PIM_HDR_LEN])
msg_to_checksum = msg_without_ip_hdr[0:2] + b'\x00\x00' + msg_without_ip_hdr[4:]
print("checksum calculated: " + str(checksum(msg_to_checksum)))
if checksum(msg_to_checksum) != pim_hdr["CHECKSUM"]:
print("wrong checksum")
return # TODO: maybe excepcao
print(pim_hdr)
self.pim_header = PacketPimHeader(pim_hdr["TYPE"])
if pim_hdr["TYPE"] == 0: # hello
pim_options = ReceivedPacket.parsePimHdrOpts(msg_without_ip_hdr[PacketPimHeader.PIM_HDR_LEN:])
print(pim_options)
for option in pim_options:
self.pim_header.add_option(PacketPimOption(option["OPTION TYPE"], option["OPTION VALUE"]))
print(self.bytes())
def parseIpHdr(msg):
(verhlen, tos, iplen, ipid, frag, ttl, proto, cksum, src, dst) = \
struct.unpack(PacketIpHeader.IP_HDR, msg)
ver = (verhlen & 0xf0) >> 4
hlen = (verhlen & 0x0f) * 4
return {"VER": ver,
"HLEN": hlen,
"TOS": tos,
"IPLEN": iplen,
"IPID": ipid,
"FRAG": frag,
"TTL": ttl,
"PROTO": proto,
"CKSUM": cksum,
"SRC": src,
"DST": dst
}
def parsePimHdr(msg):
#print("parsePimHdr: ", msg.encode("hex"))
print("parsePimHdr: ", msg)
(pim_ver_type, reserved, checksum) = struct.unpack(PacketPimHeader.PIM_HDR, msg)
print(pim_ver_type, reserved, checksum)
return {"PIM VERSION": (pim_ver_type & 0xF0) >> 4,
"TYPE": pim_ver_type & 0x0F,
"RESERVED": reserved,
"CHECKSUM": checksum
}
def parsePimHdrOpts(msg):
options_list = []
# print(msg)
while msg != b'':
(option_type, option_length) = struct.unpack(PacketPimOption.PIM_HDR_OPTS, msg[:PacketPimOption.PIM_HDR_OPTS_LEN])
print(option_type, option_length)
msg = msg[PacketPimOption.PIM_HDR_OPTS_LEN:]
print(msg)
(option_value,) = struct.unpack("! " + str(option_length) + "s", msg[:option_length])
option_value_number = int.from_bytes(option_value, byteorder='big')
print("option value: ", option_value_number)
options_list.append({"OPTION TYPE": option_type,
"OPTION LENGTH": option_length,
"OPTION VALUE": option_value_number
})
msg = msg[option_length:]
return options_list
import time
from Main import Main
#indicar ips das interfaces pim
m = Main()
m.main(["10.0.0.1"])
'''
from Packet.PacketPimHeader import *
ph = PacketPimHeader(0)
po = PacketPimOption(1, 12408)
ph.add_option(po)
ph.add_option(PacketPimOption(20, 813183289))
packet = Packet(pim_header=ph)
m.interfaces["10.0.0.1"].send(packet.bytes())'''
time.sleep(100)
PrettyTable
netifaces
\ No newline at end of file
import array
'''
import struct
if struct.pack("H",1) == "\x00\x01": # big endian
def checksum(pkt):
if len(pkt) % 2 == 1:
pkt += "\0"
s = sum(array.array("H", pkt))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s
return s & 0xffff
else:
def checksum(pkt):
if len(pkt) % 2 == 1:
pkt += "\0"
s = sum(array.array("H", pkt))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s
return (((s>>8)&0xff)|s<<8) & 0xffff
'''
KEEP_ALIVE_PERIOD_NO_TIMEOUT = 0xFFFF
KEEP_ALIVE_PERIOD = 160
KEEP_ALIVE_PERIOD_TIMEOUT = 0
def checksum(pkt):
if len(pkt) % 2 == 1:
pkt += "\0"
s = sum(array.array("H", pkt))
s = (s >> 16) + (s & 0xffff)
s += s >> 16
s = ~s
return (((s >> 8) & 0xff) | s << 8) & 0xffff
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment