def handle_client(self, client_socket, address): """Listens on the client_socket at address. This function is expected to be run in a thread dispatched by the LISTEN thread. @client_socket socket_instance produced by accept() @address tuple of (ip, port) """ while True: try: data = client_socket.recv(PACKET_LEN) if not data: raise Exception('Client disconnected') packet = BBBPacket.from_bytes(data) print( 'Received packet from {} destined to {} of type {}'.format( packet.src, packet.dst, packet.type)) if self.verify(packet): print('Verified packet from {} destined to {} of type {}'. format(packet.src, packet.dst, packet.type)) self.handle_packet(packet, address) except Exception as e: print(e) client_socket.close() return False
def update_neighbors(self): """Periodically sends out routing information to neighbors. Implements Split Horizon to avoid Count-to-Infinity problems. """ while True: # Calculate a dictionary where each key is the ip of a neighbor # and the value is a list of all the destinations we should display # format: {neighbor_ip: [dst_ip]} route_updates = { n: [d for d in self.routes if self.routes[d] != n] for n in self.neighbors } # Iterate through calculated updates for neighbor, routes in route_updates.items(): if routes: # Get old socket for the neighbors_ip if it exists try: neighbor_socket = self.sockets[neighbor] except KeyError: # If it does not exist, make a new one, store it in # the list of open sockets and dispatch a client thread neighbor_endpoint = (neighbor, ROUTER_PORT) neighbor_socket = socket.socket() neighbor_socket.connect(neighbor_endpoint) self.sockets[neighbor] = neighbor_socket threading.Thread(target=self.handle_client, args=(neighbor_socket, neighbor_endpoint)).start() # Create and send appropriate route packet for neighbor self.sqn_lock.acquire() route_packet = BBBPacket( src=self.ip_address, dst=neighbor, type=BBBPacketType.ROUTEUPDATE, payload=json.dumps(routes), seq=self.sqn_counter, ) self.sqn_counter += 1 self.sqn_lock.release() self.sign(route_packet) self.socket_lock.acquire() neighbor_socket.sendall(route_packet.to_bytes()) self.socket_lock.release() time.sleep(30)
def test_handle_flood_flood(self): router1 = BasicRouter('1.1.1.1', test=True) router2 = BasicRouter('2.2.2.2', test=True) router1.neighbors.add('2.2.2.2') router1.neighbors.add('3.3.3.3') router1.sockets['2.2.2.2'] = Mock() router1.sockets['3.3.3.3'] = Mock() message = 'hello world' packet = BBBPacket('2.2.2.2', '3.3.3.3', BBBPacketType.FLOOD, message, 0) router2.sign(packet) router1.keys['2.2.2.2'] = router2.packet_key.publickey() router1.handle_flood(packet, ('2.2.2.2', 9999)) # Packet should be sent to 3.3.3.3, but not 2.2.2.2 router1.sockets['2.2.2.2'].sendall.assert_not_called() router1.sockets['3.3.3.3'].sendall.assert_called_with( packet.to_bytes())
def send_hello_flood(self, dst, count): """Function to simply send a packet with hello string as its payload. Invoked via CLI. """ for i in range(int(count)): for address in self.neighbors: self.sqn_lock.acquire() packet = BBBPacket( src=self.ip_address, dst=dst, type=BBBPacketType.FLOOD, payload="hello-{0}".format(i), seq=self.sqn_counter, ) self.sqn_counter += 1 self.sqn_lock.release() self.sign(packet) self.socket_lock.acquire() self.sockets[address].sendall(packet.to_bytes()) self.socket_lock.release() time.sleep(10)
def test_verify_sequence_number(self): router = BasicRouter('1.1.1.1', test=True) message = 'hello world' packet = BBBPacket('1.1.1.1', '2.2.2.2', BBBPacketType.FLOOD, message, 0) router.sign(packet) router.keys['1.1.1.1'] = router.packet_key.publickey() assert router.verify(packet) # Sequence number is the same, so we should reject this packet. packet2 = BBBPacket('1.1.1.1', '2.2.2.2', BBBPacketType.FLOOD, message, 0) router.sign(packet2) assert not router.verify(packet2) # Sequence number is higher, so we should accept this packet. packet3 = BBBPacket('1.1.1.1', '2.2.2.2', BBBPacketType.FLOOD, message, 1) router.sign(packet3) assert router.verify(packet3)
def test_simple_sign_verify(self): router = BasicRouter('1.1.1.1', test=True) message = 'hello world' packet = BBBPacket('1.1.1.1', '2.2.2.2', BBBPacketType.FLOOD, message, 0) router.sign(packet) router.keys['1.1.1.1'] = router.packet_key.publickey() assert router.verify(packet) router.keys['1.1.1.1'] = RSA.generate(2048).publickey() assert not router.verify(packet)
def test_simple_sign_verify_encoding(self): router = BasicRouter('1.1.1.1', test=True) message = 'hello world' packet = BBBPacket('1.1.1.1', '2.2.2.2', BBBPacketType.FLOOD, message, 0) router.sign(packet) pem_key = router.packet_key.publickey().export_key().decode() print(pem_key) reread_key = RSA.import_key(pem_key.encode()) router.keys['1.1.1.1'] = reread_key assert router.verify(packet)
def test_sqn_numbers(self): router1 = BasicRouter('1.1.1.1', test=True) router2 = BasicRouter('2.2.2.2', test=True) routes = ['3.3.3.3', '4.4.4.4', '5.5.5.5'] route_packet = BBBPacket( '2.2.2.2', '1.1.1.1', BBBPacketType.ROUTEUPDATE, json.dumps(routes), 0, ) router2.sign(route_packet) router1.keys['2.2.2.2'] = router2.packet_key.publickey() assert router1.verify(route_packet) message = 'hello world' packet = BBBPacket('2.2.2.2', '1.1.1.1', BBBPacketType.FLOOD, message, 1) router2.sign(packet) assert router1.verify(packet)
def __init__(self, topo_path="simple.json"): """Constructor @topo_path name of the topology file to parse """ self.sockets = {} seq_num = 0 # Load the network topology from the json file self.topology = {} with open(path_join(TOPO_DIRECTORY, topo_path), 'r') as topo_file: self.topology = json.loads(topo_file.read()) while True: for host, config in self.topology.items(): # get corresponding socket for host print("attempting to connect to: {0}".format(host)) endpoint = (host, ROUTER_PORT) try: host_socket = self.sockets[endpoint] except KeyError: # create and store a new socket if necessary host_socket = socket.socket() self.sockets[endpoint] = host_socket host_socket.connect(endpoint) # create configuration packet and send it to the host config_packet = BBBPacket( src=host_socket.getsockname()[0], dst=host, type=BBBPacketType.MASTERCONFIG, payload=json.dumps(config), seq=seq_num, ) host_socket.sendall(config_packet.to_bytes()) seq_num += 1 sleep(TOPO_UPDATE_PERIOD)
def test_handle_flood_receive(self): router1 = BasicRouter('1.1.1.1', test=True) router2 = BasicRouter('2.2.2.2', test=True) router1.neighbors.add('2.2.2.2') router1.neighbors.add('3.3.3.3') router1.sockets['2.2.2.2'] = Mock() router1.sockets['3.3.3.3'] = Mock() message = 'hello world' packet = BBBPacket('2.2.2.2', '1.1.1.1', BBBPacketType.FLOOD, message, 0) router2.sign(packet) router1.keys['2.2.2.2'] = router2.packet_key.publickey() router1.handle_flood(packet, ('2.2.2.2', 9999)) # Packet destined for router1. Shouldn't be flooded router1.sockets['2.2.2.2'].sendall.assert_not_called() router1.sockets['3.3.3.3'].sendall.assert_not_called()
def test_handle_flood_byzantine(self): router1 = FaultyFloodingRouter('1.1.1.1', test=True) router2 = BasicRouter('2.2.2.2', test=True) router1.neighbors.add('2.2.2.2') router1.neighbors.add('3.3.3.3') router1.sockets['2.2.2.2'] = Mock() router1.sockets['3.3.3.3'] = Mock() message = 'hello world' packet = BBBPacket('2.2.2.2', '3.3.3.3', BBBPacketType.FLOOD, message, 0) router2.sign(packet) router1.keys['2.2.2.2'] = router2.packet_key.publickey() router1.handle_flood(packet, ('2.2.2.2', 9999)) # Packet should be dropped router1.sockets['2.2.2.2'].sendall.assert_not_called() router1.sockets['3.3.3.3'].sendall.assert_not_called()
def test_handle_route_update(self): router1 = BasicRouter('1.1.1.1', test=True) router2 = BasicRouter('2.2.2.2', test=True) routes = ['3.3.3.3', '4.4.4.4', '5.5.5.5'] route_packet = BBBPacket( '2.2.2.2', '1.1.1.1', BBBPacketType.ROUTEUPDATE, json.dumps(routes), 0, ) router2.sign(route_packet) router1.keys['2.2.2.2'] = router2.packet_key.publickey() router1.handle_routeupdate(route_packet) assert '2.2.2.2' in router1.neighbors for route in routes: assert route in router1.routes assert router1.routes[route] == '2.2.2.2' assert router1.routes['2.2.2.2'] == '2.2.2.2'
def test_json_encoding(self): packet = BBBPacket('2.2.2.2', '3.3.3.3', BBBPacketType.FLOOD, 'memes', 0) packet_bytes = packet.to_bytes() packet_redux = BBBPacket.from_bytes(packet_bytes) assert packet.__dict__ == packet_redux.__dict__ h = SHA256.new(packet_bytes) h_redux = SHA256.new(packet_redux.to_bytes()) assert h.hexdigest() == h_redux.hexdigest() router1 = BasicRouter('1.1.1.1', test=True) router1.sign(packet) packet_bytes = packet.to_bytes() packet_redux = BBBPacket.from_bytes(packet_bytes) assert packet.__dict__ == packet_redux.__dict__ router1.keys['2.2.2.2'] = router1.packet_key.publickey() assert router1.verify(packet_redux) router1.sqn_numbers['2.2.2.2'] = -1 assert router1.verify(packet)