Skip to content

Commit

Permalink
Add OpenSent state to state machine
Browse files Browse the repository at this point in the history
Working towards #6, now we need configure an OpenDelayTimer and plug in
  • Loading branch information
samrussell committed May 16, 2018
1 parent f2f9c2d commit 19a462a
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 4 deletions.
33 changes: 31 additions & 2 deletions beka/state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ def shutdown(self, message):
raise IdleError("State machine stopping: %s" % message)

def handle_timers(self, tick):
if self.state == "open_confirm" or self.state == "established":
if self.state == "open_sent" or self.state == "open_confirm" or self.state == "established":
if self.timers["hold"] + self.hold_time <= tick:
self.handle_hold_timer()
elif self.timers["keepalive"] + self.keepalive_time <= tick:
if self.state == "open_confirm" or self.state == "established":
if self.timers["keepalive"] + self.keepalive_time <= tick:
self.handle_keepalive_timer(tick)

def handle_hold_timer(self):
Expand All @@ -77,6 +78,8 @@ def handle_keepalive_timer(self, tick):
def handle_message(self, message, tick):# state machine
if self.state == "active":
self.handle_message_active_state(message, tick)
elif self.state == "open_sent":
self.handle_message_open_sent_state(message, tick)
elif self.state == "open_confirm":
self.handle_message_open_confirm_state(message, tick)
elif self.state == "established":
Expand Down Expand Up @@ -110,6 +113,32 @@ def handle_message_active_state(self, message, tick):
else:
self.shutdown("Invalid message in Active state: %s" % str(message))

def handle_message_open_sent_state(self, message, tick):
if isinstance(message, BgpOpenMessage):
# TODO sanity check incoming open message
if "fourbyteas" in message.capabilities:
self.fourbyteas = message.capabilities["fourbyteas"]

if self.open_handler:
self.open_handler(message.capabilities)

capabilities = {
"fourbyteas": [self.local_as]
}
ipv4_capabilities = {"multiprotocol": ["ipv4-unicast"]}
ipv6_capabilities = {"multiprotocol": ["ipv6-unicast"]}
if isinstance(self.local_address, IP4Address):
capabilities.update(ipv4_capabilities)
elif isinstance(self.local_address, IP6Address):
capabilities.update(ipv6_capabilities)
keepalive_message = BgpKeepaliveMessage()
self.output_messages.put(keepalive_message)
self.timers["hold"] = tick
self.timers["keepalive"] = tick
self.state = "open_confirm"
else:
self.shutdown("Invalid message in OpenSent state: %s" % str(message))

def handle_message_open_confirm_state(self, message, tick):
if isinstance(message, BgpKeepaliveMessage):
for message in self.build_update_messages():
Expand Down
72 changes: 70 additions & 2 deletions test/test_state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,76 @@ def test_update_message_advances_to_idle(self):
self.state_machine.event(EventMessageReceived(message), self.tick)
self.assertEqual(self.state_machine.state, "idle")

class StateMachineOpenSentTestCase(unittest.TestCase):
def setUp(self):
self.tick = 10000
self.open_handler = MagicMock()
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, open_handler=self.open_handler)
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")
# TODO trigger this correctly
self.state_machine.state = "open_sent"
self.state_machine.timers["hold"] = self.tick
self.assertEqual(self.state_machine.output_messages.qsize(), 0)

def test_shutdown_message_advances_to_idle(self):
with self.assertRaises(IdleError) as context:
self.state_machine.event(EventShutdown(), self.tick)
self.assertEqual(self.state_machine.state, "idle")
self.assertTrue("Shutdown requested" in str(context.exception))

def test_hold_timer_expired_event_advances_to_idle_and_sends_notification(self):
self.state_machine.timers["hold"] = self.tick - 3600
with self.assertRaises(IdleError) as context:
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.error_code, 4) # Hold Timer Expired

def test_keepalive_timer_expired_event_does_nothing(self):
self.state_machine.timers["keepalive"] = self.tick - 3600
self.state_machine.event(EventTimerExpired(), self.tick)
self.assertEqual(self.state_machine.state, "open_sent")
self.assertEqual(self.tick, self.state_machine.timers["hold"])
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):
capabilities = {"multiprotocol": "ipv4-unicast"}
message = BgpOpenMessage(4, 65002, 240, IP4Address.from_string("2.2.2.2"), capabilities)
self.state_machine.event(EventMessageReceived(message), self.tick)
self.assertEqual(self.state_machine.state, "open_confirm")
self.assertEqual(self.state_machine.output_messages.qsize(), 1)
self.open_handler.assert_called_with(capabilities)
self.assertTrue(isinstance(self.state_machine.output_messages.get(), BgpKeepaliveMessage))
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()
with self.assertRaises(IdleError) as context:
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"")
with self.assertRaises(IdleError) as context:
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 = {
"next_hop" : IP4Address.from_string("5.4.3.2"),
"as_path" : "65032 65011 65002",
"origin" : "EGP"
}
message = BgpUpdateMessage([], path_attributes, [IP4Prefix.from_string("192.168.0.0/16")])
with self.assertRaises(IdleError) as context:
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
Expand All @@ -98,7 +168,6 @@ def test_shutdown_message_advances_to_idle_and_sends_notification(self):
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.state_machine.timers["hold"] = self.tick - 3600
with self.assertRaises(IdleError) as context:
self.state_machine.event(EventTimerExpired(), self.tick)
Expand Down Expand Up @@ -318,7 +387,6 @@ def test_shutdown_message_advances_to_idle_and_sends_notification(self):
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.state_machine.timers["hold"] = self.tick - 3600
with self.assertRaises(IdleError) as context:
self.state_machine.event(EventTimerExpired(), self.tick)
Expand Down

0 comments on commit 19a462a

Please sign in to comment.