From fc2a2597a5daa6e8b99794a08326c1bebc0dd47d Mon Sep 17 00:00:00 2001 From: Sam Russell Date: Sun, 13 May 2018 13:46:00 +1200 Subject: [PATCH] Can advertise IPv4 routes Contributes to #6 and #7 Can sendIPv4 advertisements, still need to do withdrawals and then both for IPv6 --- beeper.yaml | 3 + beeper/beeper.py | 19 ++- beeper/bgp_message.py | 76 +++++++++++- beeper/ip.py | 12 +- beeper/ip4.py | 3 + beeper/state_machine.py | 30 ++++- run.py | 6 + test/test_bgp_message.py | 29 ++++- test/test_state_machine.py | 245 ++++++++++++++++++++++--------------- 9 files changed, 313 insertions(+), 110 deletions(-) diff --git a/beeper.yaml b/beeper.yaml index a3e753b..166a8bc 100644 --- a/beeper.yaml +++ b/beeper.yaml @@ -6,6 +6,9 @@ routers: peers: - peer_ip: 172.25.0.111 peer_as: 65003 + routes: + - prefix: "10.1.234.0/24" + next_hop: 172.25.0.1 - router_id: 172.25.0.1 local_as: 65002 local_address: "2001:db9:1::1" diff --git a/beeper/beeper.py b/beeper/beeper.py index 11dd61b..29bf79f 100644 --- a/beeper/beeper.py +++ b/beeper/beeper.py @@ -1,7 +1,11 @@ +from copy import copy + from .stream_server import StreamServer from .state_machine import StateMachine from .peering import Peering +from .route import RouteAddition, RouteRemoval +from .ip import IPAddress, IPPrefix DEFAULT_BGP_PORT = 179 @@ -21,12 +25,12 @@ def __init__(self, local_address, bgp_port, local_as, self.peers = {} self.peerings = [] self.stream_server = None + self.routes_to_advertise = [] if not self.bgp_port: self.bgp_port = DEFAULT_BGP_PORT - def add_neighbor(self, connect_mode, peer_ip, - peer_as): + def add_neighbor(self, connect_mode, peer_ip, peer_as): if connect_mode != "passive": raise ValueError("Only passive BGP supported") if peer_ip in self.peers: @@ -37,6 +41,16 @@ def add_neighbor(self, connect_mode, peer_ip, "peer_as": peer_as } + def add_route(self, prefix, next_hop): + self.routes_to_advertise.append( + RouteAddition( + prefix=IPPrefix.from_string(prefix), + next_hop=IPAddress.from_string(next_hop), + as_path="", + origin="IGP" + ) + ) + def neighbor_states(self): states = [] for peering in self.peerings: @@ -69,6 +83,7 @@ def handle(self, socket, address): local_address=self.local_address, neighbor=peer["peer_ip"] ) + state_machine.routes_to_advertise = copy(self.routes_to_advertise) peering = Peering(state_machine, address, socket, self.route_handler) self.peerings.append(peering) self.peer_up_handler(peer_ip, peer["peer_as"]) diff --git a/beeper/bgp_message.py b/beeper/bgp_message.py index 973dfea..767bae5 100644 --- a/beeper/bgp_message.py +++ b/beeper/bgp_message.py @@ -219,6 +219,44 @@ def parse_mp_unreach_nlri(packed_mp_reach_nlri): 15: "mp_unreach_nlri", } +ORIGIN_NUMBERS = { + "IGP": 0, + "EGP": 1, + "INCOMPLETE": 2 +} + +def pack_origin(origin): + return struct.pack("!B", ORIGIN_NUMBERS[origin]) + +def pack_as_path(as_path): + # TODO actually do this + return b"" + +def pack_next_hop(next_hop): + return next_hop.address + +attribute_packers = { + "origin": pack_origin, + "as_path": pack_as_path, + "next_hop": pack_next_hop +} + +attribute_numbers = { + "origin" : 1, + "as_path" : 2, + "next_hop" : 3, + "mp_reach_nlri" : 14, + "mp_unreach_nlri" : 15, +} + +attribute_flags = { + "origin" : 0x40, + "as_path" : 0x40, + "next_hop" : 0x40, + #"mp_reach_nlri" : 14, + #"mp_unreach_nlri" : 15, +} + def parse_path_attributes(serialised_path_attributes): stream = BytesIO(serialised_path_attributes) path_attributes = {} @@ -278,7 +316,43 @@ def parse(cls, serialised_message): return cls(withdrawn_routes, path_attributes, nlri) def pack(self): - return b"" + # TODO pack withdrawn routes + packed_withdrawn_routes = b"" + packed_withdrawn_routes_length = struct.pack("!H", len(packed_withdrawn_routes)) + packed_path_attributes = self.pack_path_attributes() + packed_path_attributes_length = struct.pack("!H", len(packed_path_attributes)) + packed_nlri = self.pack_nlri() + return packed_withdrawn_routes_length + \ + packed_withdrawn_routes + \ + packed_path_attributes_length + \ + packed_path_attributes + \ + packed_nlri + + def pack_path_attributes(self): + packed_path_attributes = [] + sorted_attribute_pairs = sorted(self.path_attributes.items(), key=lambda x: attribute_numbers[x[0]]) + for name, path_attribute in sorted_attribute_pairs: + packed_entry = attribute_packers[name](path_attribute) + packed_header = struct.pack( + "!BBB", + attribute_flags[name], + attribute_numbers[name], + len(packed_entry) + ) + packed_path_attribute = packed_header + packed_entry + packed_path_attributes.append(packed_path_attribute) + + return b"".join(packed_path_attributes) + + def pack_nlri(self): + packed_nlri = [] + + for prefix in self.nlri: + # TODO this feels like it should be on IP4Prefix + packed_prefix = struct.pack("!B", prefix.length) + pack_prefix(prefix.prefix, prefix.length) + packed_nlri.append(packed_prefix) + + return b"".join(packed_nlri) def __str__(self): return "BgpUpdateMessage: Widthdrawn routes: %s, Path attributes: %s, NLRI: %s" % ( diff --git a/beeper/ip.py b/beeper/ip.py index da47b8c..9ca163a 100644 --- a/beeper/ip.py +++ b/beeper/ip.py @@ -1,5 +1,5 @@ -from .ip4 import IP4Address -from .ip6 import IP6Address +from .ip4 import IP4Address, IP4Prefix +from .ip6 import IP6Address, IP6Prefix class IPAddress: @staticmethod @@ -8,3 +8,11 @@ def from_string(string): return IP6Address.from_string(string) else: return IP4Address.from_string(string) + +class IPPrefix: + @staticmethod + def from_string(string): + if ":" in string: + return IP6Prefix.from_string(string) + else: + return IP4Prefix.from_string(string) diff --git a/beeper/ip4.py b/beeper/ip4.py index 93538bd..e3881a9 100644 --- a/beeper/ip4.py +++ b/beeper/ip4.py @@ -22,6 +22,9 @@ def __repr__(self): def __eq__(self, other): return self.address == other.address + def __hash__(self): + return hash(self.address) + class IP4Prefix: def __init__(self, prefix, length): self.prefix = prefix diff --git a/beeper/state_machine.py b/beeper/state_machine.py index d2ef3cd..3d4c36a 100644 --- a/beeper/state_machine.py +++ b/beeper/state_machine.py @@ -1,7 +1,7 @@ from eventlet.queue import Queue from .event import Event -from .bgp_message import BgpMessage, BgpOpenMessage +from .bgp_message import BgpMessage, BgpOpenMessage, BgpUpdateMessage from .bgp_message import BgpKeepaliveMessage, BgpNotificationMessage from .route import RouteAddition, RouteRemoval from .ip import IPAddress @@ -22,6 +22,7 @@ def __init__(self, local_as, peer_as, router_id, local_address, neighbor, hold_t self.keepalive_time = hold_time // 3 self.output_messages = Queue() self.route_updates = Queue() + self.routes_to_advertise = [] self.timers = { "hold": None, @@ -93,6 +94,8 @@ def handle_message_active_state(self, message, tick): def handle_message_open_confirm_state(self, message, tick): if message.type == BgpMessage.KEEPALIVE_MESSAGE: + for message in self.build_update_messages(): + self.output_messages.put(message) self.timers["hold"] = tick self.state = "established" elif message.type == BgpMessage.NOTIFICATION_MESSAGE: @@ -150,3 +153,28 @@ def process_route_update(self, update_message): withdrawal ) self.route_updates.put(route) + + def build_update_messages(self): + update_messages = [] + + # TODO handle withdrawals + route_additions = filter(lambda x: isinstance(x, RouteAddition), self.routes_to_advertise) + nlri_by_path = {} + for route_addition in route_additions: + # TODO we're assuming IPv4 here + path_attributes = { + "next_hop": route_addition.next_hop, + "as_path": route_addition.as_path, + "origin": route_addition.origin + } + path_key = tuple(path_attributes.items()) + + if path_key not in nlri_by_path: + nlri_by_path[path_key] = [] + + nlri_by_path[path_key].append(route_addition.prefix) + + for path_attributes, nlri in nlri_by_path.items(): + update_messages.append(BgpUpdateMessage([], dict(path_attributes), nlri)) + + return update_messages diff --git a/run.py b/run.py index 400f33a..480a9c6 100644 --- a/run.py +++ b/run.py @@ -40,6 +40,12 @@ def run(self): peer["peer_ip"], peer["peer_as"], ) + if "routes" in router: + for route in router["routes"]: + beeper.add_route( + route["prefix"], + route["next_hop"] + ) self.beepers.append(beeper) pool.spawn_n(beeper.run) pool.waitall() diff --git a/test/test_bgp_message.py b/test/test_bgp_message.py index 07c5e6b..7c744a8 100644 --- a/test/test_bgp_message.py +++ b/test/test_bgp_message.py @@ -1,4 +1,5 @@ -from beeper.bgp_message import BgpMessage, parse_bgp_message, BgpOpenMessage, BgpNotificationMessage, BgpKeepaliveMessage +from beeper.bgp_message import BgpMessage, parse_bgp_message, BgpOpenMessage +from beeper.bgp_message import BgpUpdateMessage, BgpNotificationMessage, BgpKeepaliveMessage from beeper.ip4 import IP4Prefix, IP4Address from beeper.ip6 import IP6Prefix, IP6Address import socket @@ -49,11 +50,26 @@ def test_notification_message_packs(self): self.assertEqual(serialised_message, expected_serialised_message) def test_update_message_new_routes_parses(self): - serialised_message = build_byte_string("0000002740010101400200400304c0a800218004040000000040050400000064c00808fe0901f4fe090258080a") + serialised_message = build_byte_string("0000000e40010101400200400304c0a80021080a") message = parse_bgp_message(BgpMessage.UPDATE_MESSAGE, serialised_message) self.assertEqual(message.nlri[0], IP4Prefix.from_string("10.0.0.0/8")) self.assertEqual(message.path_attributes["next_hop"], IP4Address.from_string("192.168.0.33")) self.assertEqual(message.path_attributes["origin"], "EGP") + self.assertEqual(message.path_attributes["as_path"], "") + + def test_update_message_new_routes_packs(self): + expected_serialised_message = build_byte_string("0000000e40010101400200400304c0a80021080a17c0a840") + nlri = [ + IP4Prefix.from_string("10.0.0.0/8"), + IP4Prefix.from_string("192.168.64.0/23") + ] + path_attributes = { + "next_hop": IP4Address.from_string("192.168.0.33"), + "origin": "EGP", + "as_path": "" + } + message = BgpUpdateMessage([], path_attributes, nlri) + self.assertEquals(message.pack(), expected_serialised_message) def test_update_message_withdrawn_routes_parses(self): serialised_message = build_byte_string("0004180a01010000") @@ -82,3 +98,12 @@ def test_update_v6_message_withdrawn_routes_parses(self): self.assertEqual(message.path_attributes["mp_unreach_nlri"]["withdrawn_routes"], expected_withdrawn_routes) + # def test_update_v6_message_new_routes_packs(self): + # expected_serialised_message = build_byte_string("0000004b400101004002040201fdeb800e3d0002012020010db80001000000000242ac110002fe800000000000000042acfffe110002007f20010db40000000000000000000000002f20010db30000") + # message = parse_bgp_message(BgpMessage.UPDATE_MESSAGE, serialised_message) + # origin = "IGP" + # self.assertEqual(message.path_attributes["mp_reach_nlri"]["next_hop"]["afi"], IP6Address.from_string("2001:db8:1::242:ac11:2")) + # self.assertEqual(message.path_attributes["mp_reach_nlri"]["next_hop"]["safi"], IP6Address.from_string("fe80::42:acff:fe11:2")) + # self.assertEqual(message.path_attributes["mp_reach_nlri"]["nlri"][0], IP6Prefix.from_string("2001:db4::/127")) + # self.assertEqual(message.path_attributes["mp_reach_nlri"]["nlri"][1], IP6Prefix.from_string("2001:db3::/47")) + diff --git a/test/test_state_machine.py b/test/test_state_machine.py index 47c2a76..0cfe888 100644 --- a/test/test_state_machine.py +++ b/test/test_state_machine.py @@ -17,44 +17,44 @@ def build_byte_string(hex_stream): class StateMachinePassiveActiveTestCase(unittest.TestCase): def setUp(self): self.tick = 10000 - self.beeper = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) - self.old_hold_timer = self.beeper.timers["hold"] - self.old_keepalive_timer = self.beeper.timers["keepalive"] - self.assertEqual(self.beeper.state, "active") - self.assertEqual(self.beeper.output_messages.qsize(), 0) + self.state_machine = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) + self.old_hold_timer = self.state_machine.timers["hold"] + self.old_keepalive_timer = self.state_machine.timers["keepalive"] + self.assertEqual(self.state_machine.state, "active") + self.assertEqual(self.state_machine.output_messages.qsize(), 0) def test_shutdown_message_advances_to_idle(self): - self.beeper.event(EventShutdown(), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventShutdown(), self.tick) + self.assertEqual(self.state_machine.state, "idle") def test_timer_expired_event_does_nothing(self): self.tick += 3600 - self.beeper.event(EventTimerExpired(), self.tick) - self.assertEqual(self.beeper.state, "active") - self.assertEqual(self.old_hold_timer, self.beeper.timers["hold"]) - self.assertEqual(self.old_keepalive_timer, self.beeper.timers["keepalive"]) - self.assertEqual(self.beeper.output_messages.qsize(), 0) - self.assertEqual(self.beeper.route_updates.qsize(), 0) + self.state_machine.event(EventTimerExpired(), self.tick) + self.assertEqual(self.state_machine.state, "active") + self.assertEqual(self.old_hold_timer, self.state_machine.timers["hold"]) + self.assertEqual(self.old_keepalive_timer, self.state_machine.timers["keepalive"]) + self.assertEqual(self.state_machine.output_messages.qsize(), 0) + self.assertEqual(self.state_machine.route_updates.qsize(), 0) def test_open_message_advances_to_open_confirm_and_sets_timers(self): message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), build_byte_string("010400020001")) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "open_confirm") - self.assertEqual(self.beeper.output_messages.qsize(), 2) - self.assertEqual(self.beeper.output_messages.get().type, BgpMessage.OPEN_MESSAGE) - self.assertEqual(self.beeper.output_messages.get().type, BgpMessage.KEEPALIVE_MESSAGE) - self.assertEqual(self.beeper.timers["hold"], self.tick) - self.assertEqual(self.beeper.timers["keepalive"], self.tick) + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "open_confirm") + self.assertEqual(self.state_machine.output_messages.qsize(), 2) + self.assertEqual(self.state_machine.output_messages.get().type, BgpMessage.OPEN_MESSAGE) + self.assertEqual(self.state_machine.output_messages.get().type, BgpMessage.KEEPALIVE_MESSAGE) + self.assertEqual(self.state_machine.timers["hold"], self.tick) + self.assertEqual(self.state_machine.timers["keepalive"], self.tick) def test_keepalive_message_advances_to_idle(self): message = BgpKeepaliveMessage() - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") def test_notification_message_advances_to_idle(self): message = BgpNotificationMessage(0, 0, b"") - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") def test_update_message_advances_to_idle(self): path_attributes = { @@ -63,66 +63,107 @@ def test_update_message_advances_to_idle(self): "origin" : "EGP" } message = BgpUpdateMessage([], path_attributes, [IP4Prefix.from_string("192.168.0.0/16")]) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") class StateMachineOpenConfirmTestCase(unittest.TestCase): def setUp(self): self.tick = 10000 - self.beeper = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) + self.state_machine = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), build_byte_string("010400020001")) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "open_confirm") - for _ in range(self.beeper.output_messages.qsize()): - self.beeper.output_messages.get() - self.old_hold_timer = self.beeper.timers["hold"] - self.old_keepalive_timer = self.beeper.timers["keepalive"] + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "open_confirm") + for _ in range(self.state_machine.output_messages.qsize()): + self.state_machine.output_messages.get() + self.old_hold_timer = self.state_machine.timers["hold"] + self.old_keepalive_timer = self.state_machine.timers["keepalive"] def test_shutdown_message_advances_to_idle_and_sends_notification(self): - self.beeper.event(EventShutdown(), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.event(EventShutdown(), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 6) # Cease def test_hold_timer_expired_event_advances_to_idle_and_sends_notification(self): self.tick = self.old_hold_timer - self.beeper.timers["hold"] = self.tick - 3600 - self.beeper.event(EventTimerExpired(), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.timers["hold"] = self.tick - 3600 + self.state_machine.event(EventTimerExpired(), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 4) # Hold Timer Expired def test_keepalive_timer_expired_event_sends_keepalive_and_resets_keepalive_timer(self): - self.beeper.timers["keepalive"] = self.tick - 3600 - self.beeper.event(EventTimerExpired(), self.tick) - self.assertEqual(self.beeper.state, "open_confirm") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.timers["keepalive"] = self.tick - 3600 + self.state_machine.event(EventTimerExpired(), self.tick) + self.assertEqual(self.state_machine.state, "open_confirm") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.KEEPALIVE_MESSAGE) - self.assertEqual(self.beeper.timers["keepalive"], self.tick) + self.assertEqual(self.state_machine.timers["keepalive"], self.tick) def test_notification_message_advances_to_idle(self): message = BgpNotificationMessage(0, 0, b"") - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") def test_keepalive_message_advances_to_established_and_resets_hold_timer(self): self.tick += 3600 message = BgpKeepaliveMessage() - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "established") - self.assertEqual(self.beeper.timers["hold"], self.tick) + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.timers["hold"], self.tick) + + def test_keepalive_message_sends_all_routes(self): + self.tick += 3600 + self.state_machine.routes_to_advertise = [ + RouteAddition( + IP4Prefix.from_string("10.0.0.0/8"), + IP4Address.from_string("192.168.1.33"), + "", + "IGP" + ), + RouteAddition( + IP4Prefix.from_string("192.168.64.0/23"), + IP4Address.from_string("192.168.1.33"), + "", + "IGP" + ), + RouteAddition( + IP4Prefix.from_string("192.168.128.0/23"), + IP4Address.from_string("192.168.1.34"), + "", + "IGP" + ) + ] + message = BgpKeepaliveMessage() + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.timers["hold"], self.tick) + self.assertEqual(self.state_machine.output_messages.qsize(), 2) + first_update = self.state_machine.output_messages.get() + second_update = self.state_machine.output_messages.get() + self.assertEqual(first_update.type, BgpMessage.UPDATE_MESSAGE) + self.assertEqual(second_update.type, BgpMessage.UPDATE_MESSAGE) + self.assertEqual(first_update.path_attributes["next_hop"], IP4Address.from_string("192.168.1.33")) + self.assertEqual(first_update.nlri, [ + IP4Prefix.from_string("10.0.0.0/8"), + IP4Prefix.from_string("192.168.64.0/23") + ]) + self.assertEqual(second_update.path_attributes["next_hop"], IP4Address.from_string("192.168.1.34")) + self.assertEqual(second_update.nlri, [ + IP4Prefix.from_string("192.168.128.0/23") + ]) def test_open_message_advances_to_idle_and_sends_notification(self): message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), build_byte_string("010400020001")) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 6) # Cease @@ -133,35 +174,35 @@ def test_update_message_advances_to_idle(self): "origin" : "EGP" } message = BgpUpdateMessage([], path_attributes, [IP4Prefix.from_string("192.168.0.0/16")]) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 5) # FSM error class StateMachineEstablishedTestCase(unittest.TestCase): def setUp(self): self.tick = 10000 - self.beeper = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) + self.state_machine = StateMachine(local_as=65001, peer_as=65002, local_address="1.1.1.1", router_id="1.1.1.1", neighbor="2.2.2.2", hold_time=240) message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), build_byte_string("010400020001")) - self.beeper.event(EventMessageReceived(message), self.tick) - for _ in range(self.beeper.output_messages.qsize()): - self.beeper.output_messages.get() + self.state_machine.event(EventMessageReceived(message), self.tick) + for _ in range(self.state_machine.output_messages.qsize()): + self.state_machine.output_messages.get() message = BgpKeepaliveMessage() - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "established") - self.old_hold_timer = self.beeper.timers["hold"] - self.old_keepalive_timer = self.beeper.timers["keepalive"] + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.old_hold_timer = self.state_machine.timers["hold"] + self.old_keepalive_timer = self.state_machine.timers["keepalive"] def test_keepalive_timer_expired_event_sends_keepalive_and_resets_keepalive_timer(self): - self.beeper.timers["keepalive"] = self.tick - 3600 - self.beeper.event(EventTimerExpired(), self.tick) - self.assertEqual(self.beeper.state, "established") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.timers["keepalive"] = self.tick - 3600 + self.state_machine.event(EventTimerExpired(), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.KEEPALIVE_MESSAGE) - self.assertEqual(self.beeper.timers["keepalive"], self.tick) + self.assertEqual(self.state_machine.timers["keepalive"], self.tick) def test_update_message_adds_route(self): path_attributes = { @@ -176,17 +217,17 @@ def test_update_message_adds_route(self): "origin" : "EGP" } message = BgpUpdateMessage([], path_attributes, [IP4Prefix.from_string("192.168.0.0/16")]) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "established") - self.assertEqual(self.beeper.route_updates.qsize(), 1) - self.assertEqual(self.beeper.route_updates.get(), RouteAddition(**route_attributes)) + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.route_updates.qsize(), 1) + self.assertEqual(self.state_machine.route_updates.get(), RouteAddition(**route_attributes)) def test_update_message_removes_route(self): message = BgpUpdateMessage([IP4Prefix.from_string("192.168.0.0/16")], [], []) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "established") - self.assertEqual(self.beeper.route_updates.qsize(), 1) - self.assertEqual(self.beeper.route_updates.get(), RouteRemoval(IP4Prefix.from_string("192.168.0.0/16"))) + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.route_updates.qsize(), 1) + self.assertEqual(self.state_machine.route_updates.get(), RouteRemoval(IP4Prefix.from_string("192.168.0.0/16"))) def test_update_v6_message_adds_route(self): path_attributes = { @@ -209,39 +250,39 @@ def test_update_v6_message_adds_route(self): "origin" : "EGP" } message = BgpUpdateMessage([], path_attributes, []) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "established") - self.assertEqual(self.beeper.route_updates.qsize(), 1) - self.assertEqual(self.beeper.route_updates.get(), RouteAddition(**route_attributes)) + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "established") + self.assertEqual(self.state_machine.route_updates.qsize(), 1) + self.assertEqual(self.state_machine.route_updates.get(), RouteAddition(**route_attributes)) def test_shutdown_message_advances_to_idle_and_sends_notification(self): - self.beeper.event(EventShutdown(), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.event(EventShutdown(), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 6) # Cease def test_hold_timer_expired_event_advances_to_idle_and_sends_notification(self): self.tick = self.old_hold_timer - self.beeper.timers["hold"] = self.tick - 3600 - self.beeper.event(EventTimerExpired(), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.timers["hold"] = self.tick - 3600 + self.state_machine.event(EventTimerExpired(), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 4) # Hold Timer Expired def test_notification_message_advances_to_idle(self): message = BgpNotificationMessage(0, 0, b"") - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") def test_open_message_advances_to_idle_and_sends_notification(self): message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), build_byte_string("010400020001")) - self.beeper.event(EventMessageReceived(message), self.tick) - self.assertEqual(self.beeper.state, "idle") - self.assertEqual(self.beeper.output_messages.qsize(), 1) - message = self.beeper.output_messages.get() + self.state_machine.event(EventMessageReceived(message), self.tick) + self.assertEqual(self.state_machine.state, "idle") + self.assertEqual(self.state_machine.output_messages.qsize(), 1) + message = self.state_machine.output_messages.get() self.assertEqual(message.type, BgpMessage.NOTIFICATION_MESSAGE) self.assertEqual(message.error_code, 6) # Cease