This commit is contained in:
Yohann D'ANELLO 2021-01-01 17:56:48 +01:00
parent 7abaa7bcd4
commit ac249716f7
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
3 changed files with 154 additions and 147 deletions

View File

@ -1,9 +1,10 @@
# Copyright (C) 2020 by eichhornchen, ÿnérant # Copyright (C) 2020 by eichhornchen, ÿnérant
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from datetime import datetime from datetime import datetime
from random import randint, uniform from random import randint, uniform
from typing import Any, Tuple from typing import Any, Tuple
#from ipaddress import IPv6Address # from ipaddress import IPv6Address
from threading import Thread, RLock from threading import Thread, RLock
import curses import curses
import re import re
@ -30,7 +31,7 @@ class Hazelnut:
# See https://fr.wikipedia.org/wiki/Adresse_IPv6_mappant_IPv4 # See https://fr.wikipedia.org/wiki/Adresse_IPv6_mappant_IPv4
address = "::ffff:" + socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0] address = "::ffff:" + socket.getaddrinfo(address, None, socket.AF_INET)[0][4][0]
self.address = address #IPv6Address(address) self.address = address # IPv6Address(address)
self.port = port self.port = port
@ -61,7 +62,8 @@ class Squirrel(Hazelnut):
self.history = [] self.history = []
self.received_messages = dict() self.received_messages = dict()
self.recent_messages = dict() #of the form [Pkt(DataTLV), date of first reception, dict(neighbour, date of the next send, nb of times it has already been sent)] self.recent_messages = dict() # of the form [Pkt(DataTLV), date of first reception,
# dict(neighbour, date of the next send, nb of times it has already been sent)]
self.history_pad = curses.newpad(curses.LINES - 2, curses.COLS) self.history_pad = curses.newpad(curses.LINES - 2, curses.COLS)
self.input_pad = curses.newpad(1, curses.COLS) self.input_pad = curses.newpad(1, curses.COLS)
self.emoji_pad = curses.newpad(18, 12) self.emoji_pad = curses.newpad(18, 12)
@ -71,13 +73,12 @@ class Squirrel(Hazelnut):
for i in range(curses.COLOR_BLACK + 1, curses.COLOR_WHITE): for i in range(curses.COLOR_BLACK + 1, curses.COLOR_WHITE):
curses.init_pair(i + 1, i, curses.COLOR_BLACK) curses.init_pair(i + 1, i, curses.COLOR_BLACK)
#dictionnaries of neighbours # dictionnaries of neighbours
self.potentialhazelnuts = dict() self.potentialhazelnuts = dict()
self.activehazelnuts = dict() #of the form [hazelnut, time of last self.activehazelnuts = dict() # of the form [hazelnut, time of last hello,
#hello, time of last long hello, is symmetric] # time of last long hello, is symmetric]
self.nbNS = 0 self.nbNS = 0
self.minNS = 3 #minimal number of symmetric neighbours a squirrel needs self.minNS = 3 # minimal number of symmetric neighbours a squirrel needs to have.
#to have.
self.add_system_message(f"Listening on {self.address}:{self.port}") self.add_system_message(f"Listening on {self.address}:{self.port}")
self.add_system_message(f"I am {self.id}") self.add_system_message(f"I am {self.id}")
@ -89,13 +90,13 @@ class Squirrel(Hazelnut):
hazelnut = Hazelnut(address=address, port=port) hazelnut = Hazelnut(address=address, port=port)
return hazelnut return hazelnut
def is_active(self, hazel: Hazelnut) -> bool : def is_active(self, hazel: Hazelnut) -> bool:
return (hazel.address, hazel.port) in self.activehazelnuts return (hazel.address, hazel.port) in self.activehazelnuts
def is_potential(self, hazel: Hazelnut) -> bool : def is_potential(self, hazel: Hazelnut) -> bool:
return (hazel.address, hazel.port) in self.potentialhazelnuts return (hazel.address, hazel.port) in self.potentialhazelnuts
def remove_from_potential(self, address: str, port: int)-> None: def remove_from_potential(self, address: str, port: int) -> None:
self.potentialhazelnuts.pop((address, port), None) self.potentialhazelnuts.pop((address, port), None)
def find_hazelnut(self, address: str, port: int) -> Hazelnut: def find_hazelnut(self, address: str, port: int) -> Hazelnut:
@ -169,7 +170,7 @@ class Squirrel(Hazelnut):
self.handle_key_pressed(key) self.handle_key_pressed(key)
def handle_key_pressed(self, key: str) -> None: def handle_key_pressed(self, key: str) -> None: # noqa: C901
""" """
Process the key press from the user. Process the key press from the user.
""" """
@ -302,18 +303,18 @@ class Squirrel(Hazelnut):
Returns True iff the message was not already received previously. Returns True iff the message was not already received previously.
""" """
if (sender_id, nonce) not in self.received_messages: if (sender_id, nonce) not in self.received_messages:
#If it is a new message, add it to recent_messages # If it is a new message, add it to recent_messages
d = self.make_inundation_dict() d = self.make_inundation_dict()
pkt = Packet().construct(tlv) pkt = Packet().construct(tlv)
self.recent_messages[(sender_id, nonce)] = [pkt, time.time(), d] self.recent_messages[(sender_id, nonce)] = [pkt, time.time(), d]
#in all cases, remove the sender from the list of neighbours to be inundated # in all cases, remove the sender from the list of neighbours to be inundated
self.remove_from_inundation(relay, sender_id, nonce) self.remove_from_inundation(relay, sender_id, nonce)
if (sender_id, nonce) in self.received_messages: if (sender_id, nonce) in self.received_messages:
return False return False
self.add_message(msg) #for display purposes self.add_message(msg) # for display purposes
self.received_messages[(sender_id, nonce)] = Message(msg, sender_id, nonce) self.received_messages[(sender_id, nonce)] = Message(msg, sender_id, nonce)
return True return True
@ -322,11 +323,11 @@ class Squirrel(Hazelnut):
Takes the activehazels dictionnary and returns a list of [hazel, date+random, 0] Takes the activehazels dictionnary and returns a list of [hazel, date+random, 0]
""" """
res = dict() res = dict()
l = list(self.activehazelnuts.items()) hazels = list(self.activehazelnuts.items())
for key, hazel in l: for key, hazel in hazels:
if hazel[3]: #Only if the neighbour is symmetric if hazel[3]: # Only if the neighbour is symmetric
next_send = uniform(1, 2) next_send = uniform(1, 2)
res[key] = [hazel[0], time.time()+next_send, 0] res[key] = [hazel[0], time.time() + next_send, 0]
return res return res
def remove_from_inundation(self, hazel: Hazelnut, sender_id: int, nonce: int) -> None: def remove_from_inundation(self, hazel: Hazelnut, sender_id: int, nonce: int) -> None:
@ -335,26 +336,26 @@ class Squirrel(Hazelnut):
""" """
self.refresh_lock.acquire() self.refresh_lock.acquire()
if (sender_id, nonce) in self.recent_messages: if (sender_id, nonce) in self.recent_messages:
#If a peer is late in its acknowledgement, the absence of the previous if causes an error. # If a peer is late in its acknowledgement, the absence of the previous if causes an error.
self.recent_messages[(sender_id, nonce)][2].pop((hazel.address, hazel.port), None) self.recent_messages[(sender_id, nonce)][2].pop((hazel.address, hazel.port), None)
if not self.recent_messages[(sender_id, nonce)][2] : #If dictionnary is empty, remove the message if not self.recent_messages[(sender_id, nonce)][2]: # If dictionnary is empty, remove the message
self.recent_messages.pop((sender_id, nonce), None) self.recent_messages.pop((sender_id, nonce), None)
self.refresh_lock.release() self.refresh_lock.release()
def clean_inundation(self): def clean_inundation(self) -> None:
""" """
Remove messages which are overdue (older than 2 minutes) from the inundation dictionnary. Remove messages which are overdue (older than 2 minutes) from the inundation dictionnary.
""" """
self.refresh_lock.acquire() self.refresh_lock.acquire()
for key in self.recent_messages: for key in self.recent_messages:
if time.time()-self.recent_messages[key][1] > 120: if time.time() - self.recent_messages[key][1] > 120:
self.recent_messages.pop(key) self.recent_messages.pop(key)
self.refresh_lock.release() self.refresh_lock.release()
def main_inundation(self): def main_inundation(self) -> None:
""" """
The main inundation function. The main inundation function.
""" """
@ -363,23 +364,22 @@ class Squirrel(Hazelnut):
for key in self.recent_messages: for key in self.recent_messages:
k = list(self.recent_messages[key][2].keys()) k = list(self.recent_messages[key][2].keys())
for key2 in k: for key2 in k:
if time.time()>= self.recent_messages[key][2][key2][1] : if time.time() >= self.recent_messages[key][2][key2][1]:
self.add_system_message(f"inundating {self.recent_messages[key][2][key2][0].id} with message {key}") self.add_system_message(f"inundating {self.recent_messages[key][2][key2][0].id} with message {key}")
#send the packet if it is overdue # send the packet if it is overdue
self.send_packet(self.recent_messages[key][2][key2][0], self.recent_messages[key][0]) self.send_packet(self.recent_messages[key][2][key2][0], self.recent_messages[key][0])
#change the time until the next send # change the time until the next send
a = self.recent_messages[key][2][key2][2] a = self.recent_messages[key][2][key2][2]
self.recent_messages[key][2][key2][2] = a+1 self.recent_messages[key][2][key2][2] = a + 1
next_send = uniform(2**(a-1), 2**a) next_send = uniform(2 ** (a - 1), 2 ** a)
self.recent_messages[key][2][key2][1] = time.time()+next_send self.recent_messages[key][2][key2][1] = time.time() + next_send
if self.recent_messages[key][2][key2][2]>=5: #the neighbour is not reactive enough if self.recent_messages[key][2][key2][2] >= 5: # the neighbour is not reactive enough
gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, f"{self.id} No acknowledge") gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, f"{self.id} No acknowledge")
pkt = Packet().construct(gatlv) pkt = Packet().construct(gatlv)
self.send_packet( self.send_packet(self.recent_messages[key][2][key2][0], pkt)
self.recent_messages[key][2][key2][0], pkt)
self.activehazelnuts.pop(key2) self.activehazelnuts.pop(key2)
self.potentialhazelnuts[key2] = self.recent_messages[key][2][key2][0] self.potentialhazelnuts[key2] = self.recent_messages[key][2][key2][0]
self.recent_messages[key][2].pop(key2) self.recent_messages[key][2].pop(key2)
@ -591,8 +591,8 @@ class Squirrel(Hazelnut):
lp = len(self.potentialhazelnuts) lp = len(self.potentialhazelnuts)
val = list(self.potentialhazelnuts.values()) val = list(self.potentialhazelnuts.values())
for i in range(min(lp, max(0,self.minNS-self.nbNS))) : for i in range(min(lp, max(0, self.minNS - self.nbNS))):
a = randint(0, lp-1) a = randint(0, lp - 1)
res.append(val[a]) res.append(val[a])
self.refresh_lock.release() self.refresh_lock.release()
@ -604,7 +604,7 @@ class Squirrel(Hazelnut):
""" """
self.refresh_lock.acquire() self.refresh_lock.acquire()
for hazelnut in self.activehazelnuts.values() : for hazelnut in self.activehazelnuts.values():
htlv = HelloTLV().construct(16, self, hazelnut[0]) htlv = HelloTLV().construct(16, self, hazelnut[0])
pkt = Packet().construct(htlv) pkt = Packet().construct(htlv)
self.send_packet(hazelnut[0], pkt) self.send_packet(hazelnut[0], pkt)
@ -618,10 +618,10 @@ class Squirrel(Hazelnut):
""" """
self.refresh_lock.acquire() self.refresh_lock.acquire()
val = list(self.activehazelnuts.values()) #create a copy because the dict size will change val = list(self.activehazelnuts.values()) # create a copy because the dict size will change
for hazelnut in val : for hazelnut in val:
if time.time()-hazelnut[1]>2*60: if time.time() - hazelnut[1] > 2 * 60:
gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, "you did not talk to me") gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, "you did not talk to me")
pkt = Packet().construct(gatlv) pkt = Packet().construct(gatlv)
self.send_packet(hazelnut[0], pkt) self.send_packet(hazelnut[0], pkt)
@ -630,7 +630,6 @@ class Squirrel(Hazelnut):
self.refresh_lock.release() self.refresh_lock.release()
def send_neighbours(self) -> None: def send_neighbours(self) -> None:
""" """
Update the number of symmetric neighbours and Update the number of symmetric neighbours and
@ -638,23 +637,25 @@ class Squirrel(Hazelnut):
""" """
self.refresh_lock.acquire() self.refresh_lock.acquire()
nbNS = 0 nb_ns = 0
#could send the same to all neighbour, but it means that neighbour A could receive a message with itself in it -> if the others do not pay attention, trouble # could send the same to all neighbour, but it means that neighbour
for key, hazelnut in self.activehazelnuts.items() : # A could receive a message with itself in it -> if the others do not pay attention, trouble
if time.time()-hazelnut[2]<=2*60: for key, hazelnut in self.activehazelnuts.items():
nbNS+=1 if time.time() - hazelnut[2] <= 2 * 60:
nb_ns += 1
self.activehazelnuts[key][3] = True self.activehazelnuts[key][3] = True
ntlv = NeighbourTLV().construct(hazelnut[0].address,hazelnut[0].port) ntlv = NeighbourTLV().construct(hazelnut[0].address, hazelnut[0].port)
pkt = Packet().construct(ntlv) pkt = Packet().construct(ntlv)
for destination in self.activehazelnuts.values() : for destination in self.activehazelnuts.values():
if destination[0].id != hazelnut[0].id : if destination[0].id != hazelnut[0].id:
self.send_packet(destination[0], pkt) self.send_packet(destination[0], pkt)
else: else:
self.activehazelnuts[key][3] = False self.activehazelnuts[key][3] = False
self.nbNS = nbNS self.nbNS = nb_ns
self.refresh_lock.release() self.refresh_lock.release()
class Worm(Thread): class Worm(Thread):
""" """
The worm is the hazel listener. The worm is the hazel listener.
@ -678,6 +679,7 @@ class Worm(Thread):
self.squirrel.refresh_history() self.squirrel.refresh_history()
self.squirrel.refresh_input() self.squirrel.refresh_input()
class HazelManager(Thread): class HazelManager(Thread):
""" """
A process to cleanly manage the squirrel's neighbours A process to cleanly manage the squirrel's neighbours
@ -695,29 +697,30 @@ class HazelManager(Thread):
def run(self) -> None: def run(self) -> None:
while True: while True:
#First part of neighbour management: ensure the squirrel has enough # First part of neighbour management: ensure the squirrel has enough
#symmetric neighbours. # symmetric neighbours.
if time.time()-self.last_potential > 30: if time.time() - self.last_potential > 30:
to_contact = self.squirrel.potential_to_contact() to_contact = self.squirrel.potential_to_contact()
for hazel in to_contact : for hazel in to_contact:
self.squirrel.send_packet(hazel, self.hellopkt) self.squirrel.send_packet(hazel, self.hellopkt)
self.last_potential = time.time() self.last_potential = time.time()
#Second part: send long HelloTLVs to neighbours every 30 seconds # Second part: send long HelloTLVs to neighbours every 30 seconds
if time.time()-self.last_check > 30 : if time.time() - self.last_check > 30:
self.squirrel.add_system_message(f"I have {len(list(self.squirrel.activehazelnuts.values()))} friends") self.squirrel.add_system_message(f"I have {len(list(self.squirrel.activehazelnuts.values()))} friends")
self.squirrel.send_hello() self.squirrel.send_hello()
self.last_check = time.time() self.last_check = time.time()
#Third part: get rid of inactive neighbours # Third part: get rid of inactive neighbours
self.squirrel.verify_activity() self.squirrel.verify_activity()
#Fourth part: verify symmetric neighbours and send NeighbourTLV every minute # Fourth part: verify symmetric neighbours and send NeighbourTLV every minute
if time.time()-self.last_neighbour > 60 : if time.time() - self.last_neighbour > 60:
self.squirrel.send_neighbours() self.squirrel.send_neighbours()
self.last_neighbour = time.time() self.last_neighbour = time.time()
class Inondator(Thread): class Inondator(Thread):
""" """
A process to manage the inondation A process to manage the inondation
@ -729,12 +732,12 @@ class Inondator(Thread):
def run(self) -> None: def run(self) -> None:
while True: while True:
#clean the dictionnary # clean the dictionnary
if time.time()-self.last_check > 30 : if time.time() - self.last_check > 30:
self.squirrel.clean_inundation() self.squirrel.clean_inundation()
self.last_check = time.time() self.last_check = time.time()
#inundate # inundate
self.squirrel.main_inundation() self.squirrel.main_inundation()

View File

@ -123,13 +123,14 @@ class PadNTLV(TLV):
squirrel.add_system_message(f"I received {self.length} zeros, am I so a bag guy ? :cold_sweat:") squirrel.add_system_message(f"I received {self.length} zeros, am I so a bag guy ? :cold_sweat:")
@staticmethod @staticmethod
def construct(length: int) -> "Pad1TLV": def construct(length: int) -> "PadNTLV":
tlv = PadNTLV() tlv = PadNTLV()
tlv.type = 1 tlv.type = 1
tlv.length = length tlv.length = length
tlv.mbz = b'0'*length tlv.mbz = b'0' * length
return tlv return tlv
class HelloTLV(TLV): class HelloTLV(TLV):
type: int = 2 type: int = 2
length: int length: int
@ -157,23 +158,22 @@ class HelloTLV(TLV):
return data return data
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, squirrel: Any, sender: Any) -> None:
timeH = time.time() time_h = time.time()
if not squirrel.is_active(sender) : if not squirrel.is_active(sender):
sender.id = self.source_id #The sender we are given misses an id sender.id = self.source_id # The sender we are given misses an id
timeHL = time.time() time_hl = time.time()
else : else:
timeHL = squirrel.activehazelnuts[(sender.address, sender.port)][2] time_hl = squirrel.activehazelnuts[(sender.address, sender.port)][2]
if self.is_long and self.dest_id == squirrel.id : if self.is_long and self.dest_id == squirrel.id:
timeHL = time.time() time_hl = time.time()
#Make sure the sender is not in the potential hazelnuts # Make sure the sender is not in the potential hazelnuts
squirrel.remove_from_potential(sender.address, sender.port) squirrel.remove_from_potential(sender.address, sender.port)
#Add entry to/actualize the active hazelnuts dictionnary # Add entry to/actualize the active hazelnuts dictionnary
squirrel.activehazelnuts[(sender.address, sender.port)] = [sender, timeH,\ squirrel.activehazelnuts[(sender.address, sender.port)] = [sender, time_h, time_hl, True]
timeHL, True]
squirrel.nbNS += 1 squirrel.nbNS += 1
#squirrel.add_system_message(f"Aaaawwww, {self.source_id} spoke to me and said me Hello " # squirrel.add_system_message(f"Aaaawwww, {self.source_id} spoke to me and said me Hello "
# + ("long" if self.is_long else "short")) # + ("long" if self.is_long else "short"))
@property @property
@ -187,9 +187,9 @@ class HelloTLV(TLV):
tlv.source_id = squirrel.id if squirrel else 0 tlv.source_id = squirrel.id if squirrel else 0
if (destination is None) or destination.id == -1 or length == 8: if (destination is None) or destination.id == -1 or length == 8:
tlv.length = 8 tlv.length = 8
tlv.dest_id = None #if the destination id is not known, or tlv.dest_id = None # if the destination id is not known, or
#if the destination was not precised, send a short hello # if the destination was not precised, send a short hello
else : else:
tlv.length = 16 tlv.length = 16
tlv.dest_id = destination.id tlv.dest_id = destination.id
return tlv return tlv
@ -214,19 +214,22 @@ class NeighbourTLV(TLV):
self.port.to_bytes(2, sys.byteorder) self.port.to_bytes(2, sys.byteorder)
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, squirrel: Any, sender: Any) -> None:
if not (str(self.ip_address),self.port) in squirrel.activehazelnuts and not (str(self.ip_address),self.port) in squirrel.potentialhazelnuts: if not (str(self.ip_address), self.port) in squirrel.activehazelnuts \
squirrel.potentialhazelnuts[(str(self.ip_address), self.port)] = squirrel.new_hazel(str(self.ip_address), self.port) and not (str(self.ip_address), self.port) in squirrel.potentialhazelnuts:
#squirrel.add_system_message(f"New potential friend {self.ip_address}:{self.port}!") squirrel.potentialhazelnuts[(str(self.ip_address), self.port)] = \
squirrel.new_hazel(str(self.ip_address), self.port)
# squirrel.add_system_message(f"New potential friend {self.ip_address}:{self.port}!")
@staticmethod @staticmethod
def construct(address: str, port: int) -> "NeighbourTLV": def construct(address: str, port: int) -> "NeighbourTLV":
tlv = NeighbourTLV() tlv = NeighbourTLV()
tlv.type = 3 tlv.type = 3
tlv.length = 18 #A priori... tlv.length = 18 # A priori...
tlv.ip_address = IPv6Address(address) tlv.ip_address = IPv6Address(address)
tlv.port = port tlv.port = port
return tlv return tlv
class DataTLV(TLV): class DataTLV(TLV):
type: int = 4 type: int = 4
length: int length: int
@ -331,14 +334,15 @@ class AckTLV(TLV):
tlv.nonce = nonce tlv.nonce = nonce
return tlv return tlv
class GoAwayType(Enum): class GoAwayType(Enum):
UNKNOWN = 0 UNKNOWN = 0
EXIT = 1 EXIT = 1
TIMEOUT = 2 TIMEOUT = 2
PROTOCOL_VIOLATION = 3 PROTOCOL_VIOLATION = 3
class GoAwayTLV(TLV):
class GoAwayTLV(TLV):
type: int = 6 type: int = 6
length: int length: int
code: GoAwayType code: GoAwayType
@ -357,20 +361,21 @@ class GoAwayTLV(TLV):
self.message.encode("UTF-8")[:self.length - 1] self.message.encode("UTF-8")[:self.length - 1]
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, squirrel: Any, sender: Any) -> None:
if squirrel.is_active(sender) : if squirrel.is_active(sender):
squirrel.activehazelnuts.pop((sender.address, sender.port)) squirrel.activehazelnuts.pop((sender.address, sender.port))
squirrel.potentialhazelnuts[(sender.address, sender.port)] = sender squirrel.potentialhazelnuts[(sender.address, sender.port)] = sender
squirrel.add_system_message("Some told me that he went away : "+self.message) squirrel.add_system_message("Some told me that he went away : " + self.message)
@staticmethod @staticmethod
def construct(GAtype: GoAwayType, message: str) -> "GoAwayTLV": def construct(ga_type: GoAwayType, message: str) -> "GoAwayTLV":
tlv = GoAwayTLV() tlv = GoAwayTLV()
tlv.type = 6 tlv.type = 6
tlv.code = GAtype tlv.code = ga_type
tlv.message = message tlv.message = message
tlv.length = 1 + len(tlv.message) tlv.length = 1 + len(tlv.message)
return tlv return tlv
class WarningTLV(TLV): class WarningTLV(TLV):
type: int = 7 type: int = 7
length: int length: int

View File

@ -2,7 +2,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import curses import curses
import time
from argparse import ArgumentParser from argparse import ArgumentParser
from typing import Any from typing import Any
@ -80,9 +79,9 @@ class Squinnondation:
pkt = Packet().construct(htlv) pkt = Packet().construct(htlv)
squirrel.send_packet(hazelnut, pkt) squirrel.send_packet(hazelnut, pkt)
## if squirrel.port != 8082: # if squirrel.port != 8082:
## hazelnut = Hazelnut(address='::1', port=8082) # hazelnut = Hazelnut(address='::1', port=8082)
## squirrel.potentialhazelnuts['::1',8082] = hazelnut # squirrel.potentialhazelnuts['::1', 8082] = hazelnut
Worm(squirrel).start() Worm(squirrel).start()
HazelManager(squirrel).start() HazelManager(squirrel).start()