1 # Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 VRRP state machine implementation
20 VRRPManager creates/deletes VRRPRounter instances dynamically.
26 from ryu.base import app_manager
27 from ryu.controller import event
28 from ryu.controller import handler
29 from ryu.lib import hub
30 from ryu.lib.packet import vrrp
31 from ryu.services.protocols.vrrp import event as vrrp_event
32 from ryu.services.protocols.vrrp import api as vrrp_api
35 # TODO: improve Timer service and move it into framework
37 def __init__(self, handler_):
38 assert callable(handler_)
40 super(Timer, self).__init__()
41 self._handler = handler_
42 self._event = hub.Event()
45 def start(self, interval):
46 """interval is in seconds"""
50 self._thread = hub.spawn(self._timer, interval)
53 if self._thread is None:
56 hub.joinall([self._thread])
60 return self._thread is not None
62 def _timer(self, interval):
63 # Avoid cancellation during execution of self._callable()
64 cancel = self._event.wait(interval)
71 class TimerEventSender(Timer):
72 # timeout handler is called by timer thread context.
73 # So in order to actual execution context to application's event thread,
74 # post the event to the application
75 def __init__(self, app, ev_cls):
76 super(TimerEventSender, self).__init__(self._timeout)
81 self._app.send_event(self._app.name, self._ev_cls())
84 class VRRPParams(object):
85 def __init__(self, config):
87 self.master_adver_interval = None # In seconds
93 version = config.version
94 priority = config.priority
95 if config.version == vrrp.VRRP_VERSION_V2:
96 return (256.0 - priority) / 256.0
97 if config.version == vrrp.VRRP_VERSION_V3:
98 return (((256.0 - priority) * self.master_adver_interval) / 256.0)
99 raise ValueError('unknown vrrp version %d' % version)
102 def master_down_interval(self):
104 return (3.0 * self.master_adver_interval) + self.skew_time
107 @six.add_metaclass(abc.ABCMeta)
108 class VRRPState(object):
109 def __init__(self, vrrp_router):
110 super(VRRPState, self).__init__()
111 self.vrrp_router = vrrp_router
114 def master_down(self, ev):
122 def preempt_delay(self, ev):
126 def vrrp_received(self, ev):
130 def vrrp_shutdown_request(self, ev):
134 def vrrp_config_change_request(self, ev):
138 class VRRPRouter(app_manager.RyuApp):
139 _EVENTS = [vrrp_event.EventVRRPStateChanged]
141 _STATE_MAP = {} # should be overrided by concrete class
144 def register(version):
146 VRRPRouter._CONSTRUCTORS[version] = cls
151 def factory(name, monitor_name, interface, config, statistics, *args,
153 cls = VRRPRouter._CONSTRUCTORS[config.version]
154 app_mgr = app_manager.AppManager.get_instance()
155 kwargs = kwargs.copy()
156 kwargs['name'] = name
157 kwargs['monitor_name'] = monitor_name
158 kwargs['vrrp_interface'] = interface
159 kwargs['vrrp_config'] = config
160 kwargs['vrrp_statistics'] = statistics
161 return app_mgr.instantiate(cls, *args, **kwargs)
163 class _EventMasterDown(event.EventBase):
166 class _EventAdver(event.EventBase):
169 class _EventPreemptDelay(event.EventBase):
172 class _EventStatisticsOut(event.EventBase):
175 def __init__(self, *args, **kwargs):
176 super(VRRPRouter, self).__init__(*args, **kwargs)
177 self.name = kwargs['name']
178 self.monitor_name = kwargs['monitor_name']
179 self.interface = kwargs['vrrp_interface']
180 self.config = kwargs['vrrp_config']
181 self.statistics = kwargs['vrrp_statistics']
182 self.params = VRRPParams(self.config)
184 self.state_impl = None
187 self.master_down_timer = TimerEventSender(self, self._EventMasterDown)
188 self.adver_timer = TimerEventSender(self, self._EventAdver)
189 self.preempt_delay_timer = TimerEventSender(self,
190 self._EventPreemptDelay)
191 self.register_observer(self._EventMasterDown, self.name)
192 self.register_observer(self._EventAdver, self.name)
194 self.stats_out_timer = TimerEventSender(self,
195 self._EventStatisticsOut)
196 self.register_observer(self._EventStatisticsOut, self.name)
198 def send_advertisement(self, release=False):
199 if self.vrrp is None:
201 max_adver_int = vrrp.vrrp.sec_to_max_adver_int(
202 config.version, config.advertisement_interval)
203 self.vrrp = vrrp.vrrp.create_version(
204 config.version, vrrp.VRRP_TYPE_ADVERTISEMENT, config.vrid,
205 config.priority, max_adver_int, config.ip_addresses)
209 vrrp_ = vrrp_.create(vrrp_.type, vrrp_.vrid,
210 vrrp.VRRP_PRIORITY_RELEASE_RESPONSIBILITY,
211 vrrp_.max_adver_int, vrrp_.ip_addresses)
213 if self.vrrp.priority == 0:
214 self.statistics.tx_vrrp_zero_prio_packets += 1
215 # create packet frame each time to generate new ip identity
216 interface = self.interface
217 packet_ = vrrp_.create_packet(interface.primary_ip_address,
220 vrrp_api.vrrp_transmit(self, self.monitor_name, packet_.data)
221 self.statistics.tx_vrrp_packets += 1
223 def state_change(self, new_state):
224 old_state = self.state
225 self.state = new_state
226 self.state_impl = self._STATE_MAP[new_state](self)
227 state_changed = vrrp_event.EventVRRPStateChanged(
228 self.name, self.monitor_name, self.interface, self.config,
229 old_state, new_state)
230 self.send_event_to_observers(state_changed)
232 @handler.set_ev_handler(_EventMasterDown)
233 def master_down_handler(self, ev):
234 self.state_impl.master_down(ev)
236 @handler.set_ev_handler(_EventAdver)
237 def adver_handler(self, ev):
238 self.state_impl.adver(ev)
240 @handler.set_ev_handler(_EventPreemptDelay)
241 def preempt_delay_handler(self, ev):
242 self.state_impl.preempt_delay(ev)
244 @handler.set_ev_handler(vrrp_event.EventVRRPReceived)
245 def vrrp_received_handler(self, ev):
246 self.state_impl.vrrp_received(ev)
248 @handler.set_ev_handler(vrrp_event.EventVRRPShutdownRequest)
249 def vrrp_shutdown_request_handler(self, ev):
250 assert ev.instance_name == self.name
251 self.state_impl.vrrp_shutdown_request(ev)
253 @handler.set_ev_handler(vrrp_event.EventVRRPConfigChangeRequest)
254 def vrrp_config_change_request_handler(self, ev):
256 if ev.priority is not None:
257 config.priority = ev.priority
258 if ev.advertisement_interval is not None:
259 config.advertisement_interval = ev.advertisement_interval
260 if ev.preempt_mode is not None:
261 config.preempt_mode = ev.preempt_mode
262 if ev.preempt_delay is not None:
263 config.preempt_delay = ev.preempt_delay
264 if ev.accept_mode is not None:
265 config.accept_mode = ev.accept_mode
267 # force to recreate cached vrrp packet
270 self.state_impl.vrrp_config_change_request(ev)
272 @handler.set_ev_handler(_EventStatisticsOut)
273 def statistics_handler(self, ev):
274 # sends stats to somewhere here
275 # print self.statistics.get_stats()
276 self.stats_out_timer.start(self.statistics.statistics_interval)
278 # RFC defines that start timer, then change the state.
279 # This causes the race between state change and event dispatching.
280 # So our implementation does, state change, then start timer
283 class VRRPV2StateInitialize(VRRPState):
284 # In theory this shouldn't be called.
285 def master_down(self, ev):
286 self.vrrp_router.logger.warning('%s master_down',
287 self.__class__.__name__)
290 self.vrrp_router.logger.warning('%s adver', self.__class__.__name__)
292 def preempt_delay(self, ev):
293 self.vrrp_router.logger.warning('%s preempt_delay',
294 self.__class__.__name__)
296 def vrrp_received(self, ev):
297 self.vrrp_router.logger.warning('%s vrrp_received',
298 self.__class__.__name__)
300 def vrrp_shutdown_request(self, ev):
301 self.vrrp_router.logger.warning('%s vrrp_shutdown_request',
302 self.__class__.__name__)
304 def vrrp_config_change_request(self, ev):
305 self.vrrp_router.logger.warning('%s vrrp_config_change_request',
306 self.__class__.__name__)
309 class VRRPV2StateMaster(VRRPState):
310 def master_down(self, ev):
311 # should not reach here.
312 # In fact this can be happned due to event scheduling
313 vrrp_router = self.vrrp_router
314 vrrp_router.logger.debug('%s master_down %s %s',
315 self.__class__.__name__,
316 ev.__class__.__name__, vrrp_router.state)
319 vrrp_router = self.vrrp_router
320 vrrp_router.send_advertisement()
321 vrrp_router.adver_timer.start(
322 vrrp_router.config.advertisement_interval)
325 self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
328 def preempt_delay(self, ev):
329 self.vrrp_router.logger.warning('%s preempt_delay',
330 self.__class__.__name__)
332 def vrrp_received(self, ev):
333 vrrp_router = self.vrrp_router
334 vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
336 ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
337 config = vrrp_router.config
338 if vrrp_.priority == 0:
339 vrrp_router.send_advertisement()
340 vrrp_router.adver_timer.start(config.advertisement_interval)
342 params = vrrp_router.params
343 if (config.priority < vrrp_.priority or
344 (config.priority == vrrp_.priority and
345 vrrp.ip_address_lt(vrrp_router.interface.primary_ip_address,
347 vrrp_router.adver_timer.cancel()
349 vrrp_router.state_change(vrrp_event.VRRP_STATE_BACKUP)
350 vrrp_router.master_down_timer.start(
351 params.master_down_interval)
353 def vrrp_shutdown_request(self, ev):
354 vrrp_router = self.vrrp_router
355 vrrp_router.logger.debug('%s vrrp_shutdown_request',
356 self.__class__.__name__)
358 vrrp_router.adver_timer.cancel()
359 vrrp_router.send_advertisement(True)
360 vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
362 def vrrp_config_change_request(self, ev):
363 vrrp_router = self.vrrp_router
364 vrrp_router.logger.warning('%s vrrp_config_change_request',
365 self.__class__.__name__)
366 if ev.priority is not None or ev.advertisement_interval is not None:
367 vrrp_router.adver_timer.cancel()
371 class VRRPV2StateBackup(VRRPState):
372 def _master_down(self):
373 vrrp_router = self.vrrp_router
374 vrrp_router.send_advertisement()
376 # This action should be done router on
377 # EventVRRPStateChanged(VRRP_STATE_BACKUP->VRRP_STATE_MASTER)
379 # RFC3768 6.4.2 Backup
380 # o Broadcast a gratuitous ARP request containing the virtual
381 # router MAC address for each IP address associated with the
384 # RACE: actual router has the responsiblity to send garp.
385 # so due to thread scheduling there is a race between
386 # actual router sending GARP and VRRPRouter becoming
389 vrrp_router.preempt_delay_timer.cancel()
390 vrrp_router.state_change(vrrp_event.VRRP_STATE_MASTER)
391 vrrp_router.adver_timer.start(
392 vrrp_router.config.advertisement_interval)
394 def master_down(self, ev):
395 self.vrrp_router.logger.debug('%s master_down',
396 self.__class__.__name__)
400 # should not reach here
401 # In fact this can be happned due to event scheduling
402 vrrp_router = self.vrrp_router
403 vrrp_router.logger.debug('%s adver %s %s',
404 self.__class__.__name__,
405 ev.__class__.__name__, vrrp_router.state)
407 def preempt_delay(self, ev):
408 self.vrrp_router.logger.warning('%s preempt_delay',
409 self.__class__.__name__)
412 def vrrp_received(self, ev):
413 vrrp_router = self.vrrp_router
414 vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
416 _ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
417 if vrrp_.priority == 0:
418 vrrp_router.master_down_timer.start(vrrp_router.params.skew_time)
420 config = vrrp_router.config
421 params = vrrp_router.params
422 if (not config.preempt_mode or config.priority <= vrrp_.priority):
423 vrrp_router.preempt_delay_timer.cancel()
424 vrrp_router.master_down_timer.start(
425 params.master_down_interval)
426 elif (config.preempt_mode and config.preempt_delay > 0 and
427 config.priority > vrrp_.priority):
428 if not vrrp_router.preempt_delay_timer.is_running():
429 vrrp_router.preempt_delay_timer.start(config.preempt_delay)
430 vrrp_router.master_down_timer.start(
431 params.master_down_interval)
433 def vrrp_shutdown_request(self, ev):
434 vrrp_router = self.vrrp_router
435 vrrp_router.logger.debug('%s vrrp_shutdown_request',
436 self.__class__.__name__)
438 vrrp_router.master_down_timer.cancel()
439 vrrp_router.preempt_delay_timer.cancel()
440 vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
442 def vrrp_config_change_request(self, ev):
443 vrrp_router = self.vrrp_router
444 vrrp_router.logger.warning('%s vrrp_config_change_request',
445 self.__class__.__name__)
446 if ev.priority is not None and vrrp_router.config.address_owner:
447 vrrp_router.master_down_timer.cancel()
449 if ev.preempt_mode is not None or ev.preempt_delay is not None:
450 vrrp_router.preempt_delay_timer.cancel()
453 @VRRPRouter.register(vrrp.VRRP_VERSION_V2)
454 class VRRPRouterV2(VRRPRouter):
456 vrrp_event.VRRP_STATE_INITIALIZE: VRRPV2StateInitialize,
457 vrrp_event.VRRP_STATE_MASTER: VRRPV2StateMaster,
458 vrrp_event.VRRP_STATE_BACKUP: VRRPV2StateBackup,
461 def __init__(self, *args, **kwargs):
462 super(VRRPRouterV2, self).__init__(*args, **kwargs)
466 params.master_adver_interval = self.config.advertisement_interval
467 self.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
468 if self.config.address_owner:
469 self.send_advertisement()
471 # This action should be done router on
472 # EventVRRPStateChanged(None->VRRP_STATE_MASTER)
475 # o Broadcast a gratuitous ARP request containing the virtual
476 # router MAC address for each IP address associated with the
479 self.state_change(vrrp_event.VRRP_STATE_MASTER)
480 self.adver_timer.start(self.config.advertisement_interval)
482 self.state_change(vrrp_event.VRRP_STATE_BACKUP)
483 self.master_down_timer.start(params.master_down_interval)
485 super(VRRPRouterV2, self).start()
488 class VRRPV3StateInitialize(VRRPState):
489 # In theory this shouldn't be called.
490 def master_down(self, ev):
491 self.vrrp_router.logger.debug('%s master_down',
492 self.__class__.__name__)
495 self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
497 def preempt_delay(self, ev):
498 self.vrrp_router.logger.warning('%s preempt_delay',
499 self.__class__.__name__)
501 def vrrp_received(self, ev):
502 self.vrrp_router.logger.debug('%s vrrp_received',
503 self.__class__.__name__)
505 def vrrp_shutdown_request(self, ev):
506 self.vrrp_router.logger.debug('%s vrrp_shutdown_request',
507 self.__class__.__name__)
509 def vrrp_config_change_request(self, ev):
510 self.vrrp_router.logger.warning('%s vrrp_config_change_request',
511 self.__class__.__name__)
514 class VRRPV3StateMaster(VRRPState):
515 def master_down(self, ev):
516 # should not reach here
517 # In fact this can be happned due to event scheduling
518 vrrp_router = self.vrrp_router
519 vrrp_router.logger.debug('%s master_down %s %s',
520 self.__class__.__name__,
521 ev.__class__.__name__, vrrp_router.state)
524 vrrp_router = self.vrrp_router
525 vrrp_router.send_advertisement()
526 vrrp_router.adver_timer.start(
527 vrrp_router.config.advertisement_interval)
530 self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
533 def preempt_delay(self, ev):
534 self.vrrp_router.logger.warning('%s preempt_delay',
535 self.__class__.__name__)
537 def vrrp_received(self, ev):
538 vrrp_router = self.vrrp_router
539 vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
541 ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
542 config = vrrp_router.config
543 if vrrp_.priority == 0:
544 vrrp_router.send_advertisement()
545 vrrp_router.adver_timer.start(config.advertisement_interval)
547 params = vrrp_router.params
548 if (config.priority < vrrp_.priority or
549 (config.priority == vrrp_.priority and
550 vrrp.ip_address_lt(vrrp_router.interface.primary_ip_address,
552 vrrp_router.adver_timer.cancel()
553 params.master_adver_interval = vrrp_.max_adver_int_in_sec
555 vrrp_router.state_change(vrrp_event.VRRP_STATE_BACKUP)
556 vrrp_router.master_down_timer.start(
557 params.master_down_interval)
559 def vrrp_shutdown_request(self, ev):
560 vrrp_router = self.vrrp_router
561 vrrp_router.logger.debug('%s vrrp_shutdown_request',
562 self.__class__.__name__)
564 vrrp_router.adver_timer.cancel()
565 vrrp_router.send_advertisement(True)
566 vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
568 def vrrp_config_change_request(self, ev):
569 vrrp_router = self.vrrp_router
570 vrrp_router.logger.warning('%s vrrp_config_change_request',
571 self.__class__.__name__)
572 if ev.priority is not None or ev.advertisement_interval is not None:
573 vrrp_router.adver_timer.cancel()
577 class VRRPV3StateBackup(VRRPState):
578 def _master_down(self):
579 vrrp_router = self.vrrp_router
580 vrrp_router.send_advertisement()
582 # This action should be done by router on
583 # EventStateChange(VRRP_SATE_BACKUP -> VRRP_STATE_MASTER)
586 # (375) + If the protected IPvX address is an IPv4 address, then:
587 # (380) * Broadcast a gratuitous ARP request on that interface
588 # containing the virtual router MAC address for each IPv4
589 # address associated with the virtual router.
590 # (385) + else // ipv6
591 # (390) * Compute and join the Solicited-Node multicast
592 # address [RFC4291] for the IPv6 address(es) associated with
593 # the virtual router.
594 # (395) * For each IPv6 address associated with the virtual
595 # router, send an unsolicited ND Neighbor Advertisement with
596 # the Router Flag (R) set, the Solicited Flag (S) unset, the
597 # Override flag (O) set, the target address set to the IPv6
598 # address of the virtual router, and the target link-layer
599 # address set to the virtual router MAC address.
601 # RACE: actual router has the responsiblity to send garp.
602 # so due to thread scheduling there is a race between
603 # actual router sending GARP and VRRPRouter becoming
606 vrrp_router.preempt_delay_timer.cancel()
607 vrrp_router.state_change(vrrp_event.VRRP_STATE_MASTER)
608 vrrp_router.adver_timer.start(
609 vrrp_router.config.advertisement_interval)
611 def master_down(self, ev):
612 self.vrrp_router.logger.debug('%s master_down',
613 self.__class__.__name__)
617 # should not reach here
618 # In fact this can be happned due to event scheduling
619 vrrp_router = self.vrrp_router
620 vrrp_router.logger.debug('adver %s %s %s',
621 self.__class__.__name__,
622 ev.__class__.__name__, vrrp_router.state)
624 def preempt_delay(self, ev):
625 self.vrrp_router.logger.warning('%s preempt_delay',
626 self.__class__.__name__)
629 def vrrp_received(self, ev):
630 vrrp_router = self.vrrp_router
631 vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
633 _ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
634 if vrrp_.priority == 0:
635 vrrp_router.master_down_timer.start(vrrp_router.params.skew_time)
637 params = vrrp_router.params
638 config = vrrp_router.config
639 if (not config.preempt_mode or config.priority <= vrrp_.priority):
640 params.master_adver_interval = vrrp_.max_adver_int_in_sec
641 vrrp_router.master_down_timer.start(
642 params.master_down_interval)
643 elif (config.preempt_mode and config.preempt_delay > 0 and
644 config.priority > vrrp_.priority):
645 if not vrrp_router.preempt_delay_timer.is_running():
646 vrrp_router.preempt_delay_timer.start(config.preempt_delay)
647 vrrp_router.master_down_timer.start(
648 params.master_down_interval)
650 def vrrp_shutdown_request(self, ev):
651 vrrp_router = self.vrrp_router
652 vrrp_router.logger.debug('%s vrrp_shutdown_request',
653 self.__class__.__name__)
655 vrrp_router.preempt_delay_timer.cancel()
656 vrrp_router.master_down_timer.cancel()
657 vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
659 def vrrp_config_change_request(self, ev):
660 vrrp_router = self.vrrp_router
661 vrrp_router.logger.warning('%s vrrp_config_change_request',
662 self.__class__.__name__)
663 if ev.priority is not None and vrrp_router.config.address_owner:
664 vrrp_router.master_down_timer.cancel()
666 if ev.preempt_mode is not None or ev.preempt_delay is not None:
667 vrrp_router.preempt_delay_timer.cancel()
670 @VRRPRouter.register(vrrp.VRRP_VERSION_V3)
671 class VRRPRouterV3(VRRPRouter):
673 vrrp_event.VRRP_STATE_INITIALIZE: VRRPV3StateInitialize,
674 vrrp_event.VRRP_STATE_MASTER: VRRPV3StateMaster,
675 vrrp_event.VRRP_STATE_BACKUP: VRRPV3StateBackup,
678 def __init__(self, *args, **kwargs):
679 super(VRRPRouterV3, self).__init__(*args, **kwargs)
682 self.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
683 # Check role here and change accordingly
684 # Check config.admin_state
685 if self.config.address_owner or self.config.admin_state == 'master':
686 self.send_advertisement()
688 # This action should be done router on
689 # EventVRRPStateChanged(None->VRRP_STATE_MASTER)
692 # (115) + If the protected IPvX address is an IPv4 address, then:
693 # (120) * Broadcast a gratuitous ARP request containing the
694 # virtual router MAC address for each IP address associated
695 # with the virtual router.
696 # (125) + else // IPv6
697 # (130) * For each IPv6 address associated with the virtual
698 # router, send an unsolicited ND Neighbor Advertisement with
699 # the Router Flag (R) set, the Solicited Flag (S) unset, the
700 # Override flag (O) set, the target address set to the IPv6
701 # address of the virtual router, and the target link-layer
702 # address set to the virtual router MAC address.
704 self.state_change(vrrp_event.VRRP_STATE_MASTER)
705 self.adver_timer.start(self.config.advertisement_interval)
708 params.master_adver_interval = self.config.advertisement_interval
709 self.state_change(vrrp_event.VRRP_STATE_BACKUP)
710 self.master_down_timer.start(params.master_down_interval)
712 self.stats_out_timer.start(self.statistics.statistics_interval)
713 super(VRRPRouterV3, self).start()