--- /dev/null
+# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
+# Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+VRRP state machine implementation
+
+VRRPManager creates/deletes VRRPRounter instances dynamically.
+"""
+
+import abc
+import six
+
+from ryu.base import app_manager
+from ryu.controller import event
+from ryu.controller import handler
+from ryu.lib import hub
+from ryu.lib.packet import vrrp
+from ryu.services.protocols.vrrp import event as vrrp_event
+from ryu.services.protocols.vrrp import api as vrrp_api
+
+
+# TODO: improve Timer service and move it into framework
+class Timer(object):
+ def __init__(self, handler_):
+ assert callable(handler_)
+
+ super(Timer, self).__init__()
+ self._handler = handler_
+ self._event = hub.Event()
+ self._thread = None
+
+ def start(self, interval):
+ """interval is in seconds"""
+ if self._thread:
+ self.cancel()
+ self._event.clear()
+ self._thread = hub.spawn(self._timer, interval)
+
+ def cancel(self):
+ if self._thread is None:
+ return
+ self._event.set()
+ hub.joinall([self._thread])
+ self._thread = None
+
+ def is_running(self):
+ return self._thread is not None
+
+ def _timer(self, interval):
+ # Avoid cancellation during execution of self._callable()
+ cancel = self._event.wait(interval)
+ if cancel:
+ return
+
+ self._handler()
+
+
+class TimerEventSender(Timer):
+ # timeout handler is called by timer thread context.
+ # So in order to actual execution context to application's event thread,
+ # post the event to the application
+ def __init__(self, app, ev_cls):
+ super(TimerEventSender, self).__init__(self._timeout)
+ self._app = app
+ self._ev_cls = ev_cls
+
+ def _timeout(self):
+ self._app.send_event(self._app.name, self._ev_cls())
+
+
+class VRRPParams(object):
+ def __init__(self, config):
+ self.config = config
+ self.master_adver_interval = None # In seconds
+
+ @property
+ def skew_time(self):
+ # In seconds
+ config = self.config
+ version = config.version
+ priority = config.priority
+ if config.version == vrrp.VRRP_VERSION_V2:
+ return (256.0 - priority) / 256.0
+ if config.version == vrrp.VRRP_VERSION_V3:
+ return (((256.0 - priority) * self.master_adver_interval) / 256.0)
+ raise ValueError('unknown vrrp version %d' % version)
+
+ @property
+ def master_down_interval(self):
+ # In seconds
+ return (3.0 * self.master_adver_interval) + self.skew_time
+
+
+@six.add_metaclass(abc.ABCMeta)
+class VRRPState(object):
+ def __init__(self, vrrp_router):
+ super(VRRPState, self).__init__()
+ self.vrrp_router = vrrp_router
+
+ @abc.abstractmethod
+ def master_down(self, ev):
+ pass
+
+ @abc.abstractmethod
+ def adver(self, ev):
+ pass
+
+ @abc.abstractmethod
+ def preempt_delay(self, ev):
+ pass
+
+ @abc.abstractmethod
+ def vrrp_received(self, ev):
+ pass
+
+ @abc.abstractmethod
+ def vrrp_shutdown_request(self, ev):
+ pass
+
+ @abc.abstractmethod
+ def vrrp_config_change_request(self, ev):
+ pass
+
+
+class VRRPRouter(app_manager.RyuApp):
+ _EVENTS = [vrrp_event.EventVRRPStateChanged]
+ _CONSTRUCTORS = {}
+ _STATE_MAP = {} # should be overrided by concrete class
+
+ @staticmethod
+ def register(version):
+ def _register(cls):
+ VRRPRouter._CONSTRUCTORS[version] = cls
+ return cls
+ return _register
+
+ @staticmethod
+ def factory(name, monitor_name, interface, config, statistics, *args,
+ **kwargs):
+ cls = VRRPRouter._CONSTRUCTORS[config.version]
+ app_mgr = app_manager.AppManager.get_instance()
+ kwargs = kwargs.copy()
+ kwargs['name'] = name
+ kwargs['monitor_name'] = monitor_name
+ kwargs['vrrp_interface'] = interface
+ kwargs['vrrp_config'] = config
+ kwargs['vrrp_statistics'] = statistics
+ return app_mgr.instantiate(cls, *args, **kwargs)
+
+ class _EventMasterDown(event.EventBase):
+ pass
+
+ class _EventAdver(event.EventBase):
+ pass
+
+ class _EventPreemptDelay(event.EventBase):
+ pass
+
+ class _EventStatisticsOut(event.EventBase):
+ pass
+
+ def __init__(self, *args, **kwargs):
+ super(VRRPRouter, self).__init__(*args, **kwargs)
+ self.name = kwargs['name']
+ self.monitor_name = kwargs['monitor_name']
+ self.interface = kwargs['vrrp_interface']
+ self.config = kwargs['vrrp_config']
+ self.statistics = kwargs['vrrp_statistics']
+ self.params = VRRPParams(self.config)
+ self.state = None
+ self.state_impl = None
+ self.vrrp = None
+
+ self.master_down_timer = TimerEventSender(self, self._EventMasterDown)
+ self.adver_timer = TimerEventSender(self, self._EventAdver)
+ self.preempt_delay_timer = TimerEventSender(self,
+ self._EventPreemptDelay)
+ self.register_observer(self._EventMasterDown, self.name)
+ self.register_observer(self._EventAdver, self.name)
+
+ self.stats_out_timer = TimerEventSender(self,
+ self._EventStatisticsOut)
+ self.register_observer(self._EventStatisticsOut, self.name)
+
+ def send_advertisement(self, release=False):
+ if self.vrrp is None:
+ config = self.config
+ max_adver_int = vrrp.vrrp.sec_to_max_adver_int(
+ config.version, config.advertisement_interval)
+ self.vrrp = vrrp.vrrp.create_version(
+ config.version, vrrp.VRRP_TYPE_ADVERTISEMENT, config.vrid,
+ config.priority, max_adver_int, config.ip_addresses)
+
+ vrrp_ = self.vrrp
+ if release:
+ vrrp_ = vrrp_.create(vrrp_.type, vrrp_.vrid,
+ vrrp.VRRP_PRIORITY_RELEASE_RESPONSIBILITY,
+ vrrp_.max_adver_int, vrrp_.ip_addresses)
+
+ if self.vrrp.priority == 0:
+ self.statistics.tx_vrrp_zero_prio_packets += 1
+ # create packet frame each time to generate new ip identity
+ interface = self.interface
+ packet_ = vrrp_.create_packet(interface.primary_ip_address,
+ interface.vlan_id)
+ packet_.serialize()
+ vrrp_api.vrrp_transmit(self, self.monitor_name, packet_.data)
+ self.statistics.tx_vrrp_packets += 1
+
+ def state_change(self, new_state):
+ old_state = self.state
+ self.state = new_state
+ self.state_impl = self._STATE_MAP[new_state](self)
+ state_changed = vrrp_event.EventVRRPStateChanged(
+ self.name, self.monitor_name, self.interface, self.config,
+ old_state, new_state)
+ self.send_event_to_observers(state_changed)
+
+ @handler.set_ev_handler(_EventMasterDown)
+ def master_down_handler(self, ev):
+ self.state_impl.master_down(ev)
+
+ @handler.set_ev_handler(_EventAdver)
+ def adver_handler(self, ev):
+ self.state_impl.adver(ev)
+
+ @handler.set_ev_handler(_EventPreemptDelay)
+ def preempt_delay_handler(self, ev):
+ self.state_impl.preempt_delay(ev)
+
+ @handler.set_ev_handler(vrrp_event.EventVRRPReceived)
+ def vrrp_received_handler(self, ev):
+ self.state_impl.vrrp_received(ev)
+
+ @handler.set_ev_handler(vrrp_event.EventVRRPShutdownRequest)
+ def vrrp_shutdown_request_handler(self, ev):
+ assert ev.instance_name == self.name
+ self.state_impl.vrrp_shutdown_request(ev)
+
+ @handler.set_ev_handler(vrrp_event.EventVRRPConfigChangeRequest)
+ def vrrp_config_change_request_handler(self, ev):
+ config = self.config
+ if ev.priority is not None:
+ config.priority = ev.priority
+ if ev.advertisement_interval is not None:
+ config.advertisement_interval = ev.advertisement_interval
+ if ev.preempt_mode is not None:
+ config.preempt_mode = ev.preempt_mode
+ if ev.preempt_delay is not None:
+ config.preempt_delay = ev.preempt_delay
+ if ev.accept_mode is not None:
+ config.accept_mode = ev.accept_mode
+
+ # force to recreate cached vrrp packet
+ self.vrrp = None
+
+ self.state_impl.vrrp_config_change_request(ev)
+
+ @handler.set_ev_handler(_EventStatisticsOut)
+ def statistics_handler(self, ev):
+ # sends stats to somewhere here
+ # print self.statistics.get_stats()
+ self.stats_out_timer.start(self.statistics.statistics_interval)
+
+# RFC defines that start timer, then change the state.
+# This causes the race between state change and event dispatching.
+# So our implementation does, state change, then start timer
+
+
+class VRRPV2StateInitialize(VRRPState):
+ # In theory this shouldn't be called.
+ def master_down(self, ev):
+ self.vrrp_router.logger.warning('%s master_down',
+ self.__class__.__name__)
+
+ def adver(self, ev):
+ self.vrrp_router.logger.warning('%s adver', self.__class__.__name__)
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+
+ def vrrp_received(self, ev):
+ self.vrrp_router.logger.warning('%s vrrp_received',
+ self.__class__.__name__)
+
+ def vrrp_shutdown_request(self, ev):
+ self.vrrp_router.logger.warning('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ def vrrp_config_change_request(self, ev):
+ self.vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+
+
+class VRRPV2StateMaster(VRRPState):
+ def master_down(self, ev):
+ # should not reach here.
+ # In fact this can be happned due to event scheduling
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s master_down %s %s',
+ self.__class__.__name__,
+ ev.__class__.__name__, vrrp_router.state)
+
+ def _adver(self):
+ vrrp_router = self.vrrp_router
+ vrrp_router.send_advertisement()
+ vrrp_router.adver_timer.start(
+ vrrp_router.config.advertisement_interval)
+
+ def adver(self, ev):
+ self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
+ self._adver()
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+
+ def vrrp_received(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
+
+ ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
+ config = vrrp_router.config
+ if vrrp_.priority == 0:
+ vrrp_router.send_advertisement()
+ vrrp_router.adver_timer.start(config.advertisement_interval)
+ else:
+ params = vrrp_router.params
+ if (config.priority < vrrp_.priority or
+ (config.priority == vrrp_.priority and
+ vrrp.ip_address_lt(vrrp_router.interface.primary_ip_address,
+ ip.src))):
+ vrrp_router.adver_timer.cancel()
+
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_BACKUP)
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+
+ def vrrp_shutdown_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ vrrp_router.adver_timer.cancel()
+ vrrp_router.send_advertisement(True)
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+
+ def vrrp_config_change_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+ if ev.priority is not None or ev.advertisement_interval is not None:
+ vrrp_router.adver_timer.cancel()
+ self._adver()
+
+
+class VRRPV2StateBackup(VRRPState):
+ def _master_down(self):
+ vrrp_router = self.vrrp_router
+ vrrp_router.send_advertisement()
+
+ # This action should be done router on
+ # EventVRRPStateChanged(VRRP_STATE_BACKUP->VRRP_STATE_MASTER)
+ #
+ # RFC3768 6.4.2 Backup
+ # o Broadcast a gratuitous ARP request containing the virtual
+ # router MAC address for each IP address associated with the
+ # virtual router
+
+ # RACE: actual router has the responsiblity to send garp.
+ # so due to thread scheduling there is a race between
+ # actual router sending GARP and VRRPRouter becoming
+ # master/backup
+
+ vrrp_router.preempt_delay_timer.cancel()
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_MASTER)
+ vrrp_router.adver_timer.start(
+ vrrp_router.config.advertisement_interval)
+
+ def master_down(self, ev):
+ self.vrrp_router.logger.debug('%s master_down',
+ self.__class__.__name__)
+ self._master_down()
+
+ def adver(self, ev):
+ # should not reach here
+ # In fact this can be happned due to event scheduling
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s adver %s %s',
+ self.__class__.__name__,
+ ev.__class__.__name__, vrrp_router.state)
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+ self._master_down()
+
+ def vrrp_received(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
+
+ _ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
+ if vrrp_.priority == 0:
+ vrrp_router.master_down_timer.start(vrrp_router.params.skew_time)
+ else:
+ config = vrrp_router.config
+ params = vrrp_router.params
+ if (not config.preempt_mode or config.priority <= vrrp_.priority):
+ vrrp_router.preempt_delay_timer.cancel()
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+ elif (config.preempt_mode and config.preempt_delay > 0 and
+ config.priority > vrrp_.priority):
+ if not vrrp_router.preempt_delay_timer.is_running():
+ vrrp_router.preempt_delay_timer.start(config.preempt_delay)
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+
+ def vrrp_shutdown_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ vrrp_router.master_down_timer.cancel()
+ vrrp_router.preempt_delay_timer.cancel()
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+
+ def vrrp_config_change_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+ if ev.priority is not None and vrrp_router.config.address_owner:
+ vrrp_router.master_down_timer.cancel()
+ self._master_down()
+ if ev.preempt_mode is not None or ev.preempt_delay is not None:
+ vrrp_router.preempt_delay_timer.cancel()
+
+
+@VRRPRouter.register(vrrp.VRRP_VERSION_V2)
+class VRRPRouterV2(VRRPRouter):
+ _STATE_MAP = {
+ vrrp_event.VRRP_STATE_INITIALIZE: VRRPV2StateInitialize,
+ vrrp_event.VRRP_STATE_MASTER: VRRPV2StateMaster,
+ vrrp_event.VRRP_STATE_BACKUP: VRRPV2StateBackup,
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(VRRPRouterV2, self).__init__(*args, **kwargs)
+
+ def start(self):
+ params = self.params
+ params.master_adver_interval = self.config.advertisement_interval
+ self.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+ if self.config.address_owner:
+ self.send_advertisement()
+
+ # This action should be done router on
+ # EventVRRPStateChanged(None->VRRP_STATE_MASTER)
+ #
+ # RFC3768 6.4.1
+ # o Broadcast a gratuitous ARP request containing the virtual
+ # router MAC address for each IP address associated with the
+ # virtual router.
+
+ self.state_change(vrrp_event.VRRP_STATE_MASTER)
+ self.adver_timer.start(self.config.advertisement_interval)
+ else:
+ self.state_change(vrrp_event.VRRP_STATE_BACKUP)
+ self.master_down_timer.start(params.master_down_interval)
+
+ super(VRRPRouterV2, self).start()
+
+
+class VRRPV3StateInitialize(VRRPState):
+ # In theory this shouldn't be called.
+ def master_down(self, ev):
+ self.vrrp_router.logger.debug('%s master_down',
+ self.__class__.__name__)
+
+ def adver(self, ev):
+ self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+
+ def vrrp_received(self, ev):
+ self.vrrp_router.logger.debug('%s vrrp_received',
+ self.__class__.__name__)
+
+ def vrrp_shutdown_request(self, ev):
+ self.vrrp_router.logger.debug('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ def vrrp_config_change_request(self, ev):
+ self.vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+
+
+class VRRPV3StateMaster(VRRPState):
+ def master_down(self, ev):
+ # should not reach here
+ # In fact this can be happned due to event scheduling
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s master_down %s %s',
+ self.__class__.__name__,
+ ev.__class__.__name__, vrrp_router.state)
+
+ def _adver(self):
+ vrrp_router = self.vrrp_router
+ vrrp_router.send_advertisement()
+ vrrp_router.adver_timer.start(
+ vrrp_router.config.advertisement_interval)
+
+ def adver(self, ev):
+ self.vrrp_router.logger.debug('%s adver', self.__class__.__name__)
+ self._adver()
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+
+ def vrrp_received(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
+
+ ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
+ config = vrrp_router.config
+ if vrrp_.priority == 0:
+ vrrp_router.send_advertisement()
+ vrrp_router.adver_timer.start(config.advertisement_interval)
+ else:
+ params = vrrp_router.params
+ if (config.priority < vrrp_.priority or
+ (config.priority == vrrp_.priority and
+ vrrp.ip_address_lt(vrrp_router.interface.primary_ip_address,
+ ip.src))):
+ vrrp_router.adver_timer.cancel()
+ params.master_adver_interval = vrrp_.max_adver_int_in_sec
+
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_BACKUP)
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+
+ def vrrp_shutdown_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ vrrp_router.adver_timer.cancel()
+ vrrp_router.send_advertisement(True)
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+
+ def vrrp_config_change_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+ if ev.priority is not None or ev.advertisement_interval is not None:
+ vrrp_router.adver_timer.cancel()
+ self._adver()
+
+
+class VRRPV3StateBackup(VRRPState):
+ def _master_down(self):
+ vrrp_router = self.vrrp_router
+ vrrp_router.send_advertisement()
+
+ # This action should be done by router on
+ # EventStateChange(VRRP_SATE_BACKUP -> VRRP_STATE_MASTER)
+ #
+ # RFC 5795 6.4.2
+ # (375) + If the protected IPvX address is an IPv4 address, then:
+ # (380) * Broadcast a gratuitous ARP request on that interface
+ # containing the virtual router MAC address for each IPv4
+ # address associated with the virtual router.
+ # (385) + else // ipv6
+ # (390) * Compute and join the Solicited-Node multicast
+ # address [RFC4291] for the IPv6 address(es) associated with
+ # the virtual router.
+ # (395) * For each IPv6 address associated with the virtual
+ # router, send an unsolicited ND Neighbor Advertisement with
+ # the Router Flag (R) set, the Solicited Flag (S) unset, the
+ # Override flag (O) set, the target address set to the IPv6
+ # address of the virtual router, and the target link-layer
+ # address set to the virtual router MAC address.
+
+ # RACE: actual router has the responsiblity to send garp.
+ # so due to thread scheduling there is a race between
+ # actual router sending GARP and VRRPRouter becoming
+ # master/backup
+
+ vrrp_router.preempt_delay_timer.cancel()
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_MASTER)
+ vrrp_router.adver_timer.start(
+ vrrp_router.config.advertisement_interval)
+
+ def master_down(self, ev):
+ self.vrrp_router.logger.debug('%s master_down',
+ self.__class__.__name__)
+ self._master_down()
+
+ def adver(self, ev):
+ # should not reach here
+ # In fact this can be happned due to event scheduling
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('adver %s %s %s',
+ self.__class__.__name__,
+ ev.__class__.__name__, vrrp_router.state)
+
+ def preempt_delay(self, ev):
+ self.vrrp_router.logger.warning('%s preempt_delay',
+ self.__class__.__name__)
+ self._master_down()
+
+ def vrrp_received(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_received', self.__class__.__name__)
+
+ _ip, vrrp_ = vrrp.vrrp.get_payload(ev.packet)
+ if vrrp_.priority == 0:
+ vrrp_router.master_down_timer.start(vrrp_router.params.skew_time)
+ else:
+ params = vrrp_router.params
+ config = vrrp_router.config
+ if (not config.preempt_mode or config.priority <= vrrp_.priority):
+ params.master_adver_interval = vrrp_.max_adver_int_in_sec
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+ elif (config.preempt_mode and config.preempt_delay > 0 and
+ config.priority > vrrp_.priority):
+ if not vrrp_router.preempt_delay_timer.is_running():
+ vrrp_router.preempt_delay_timer.start(config.preempt_delay)
+ vrrp_router.master_down_timer.start(
+ params.master_down_interval)
+
+ def vrrp_shutdown_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.debug('%s vrrp_shutdown_request',
+ self.__class__.__name__)
+
+ vrrp_router.preempt_delay_timer.cancel()
+ vrrp_router.master_down_timer.cancel()
+ vrrp_router.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+
+ def vrrp_config_change_request(self, ev):
+ vrrp_router = self.vrrp_router
+ vrrp_router.logger.warning('%s vrrp_config_change_request',
+ self.__class__.__name__)
+ if ev.priority is not None and vrrp_router.config.address_owner:
+ vrrp_router.master_down_timer.cancel()
+ self._master_down()
+ if ev.preempt_mode is not None or ev.preempt_delay is not None:
+ vrrp_router.preempt_delay_timer.cancel()
+
+
+@VRRPRouter.register(vrrp.VRRP_VERSION_V3)
+class VRRPRouterV3(VRRPRouter):
+ _STATE_MAP = {
+ vrrp_event.VRRP_STATE_INITIALIZE: VRRPV3StateInitialize,
+ vrrp_event.VRRP_STATE_MASTER: VRRPV3StateMaster,
+ vrrp_event.VRRP_STATE_BACKUP: VRRPV3StateBackup,
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(VRRPRouterV3, self).__init__(*args, **kwargs)
+
+ def start(self):
+ self.state_change(vrrp_event.VRRP_STATE_INITIALIZE)
+ # Check role here and change accordingly
+ # Check config.admin_state
+ if self.config.address_owner or self.config.admin_state == 'master':
+ self.send_advertisement()
+
+ # This action should be done router on
+ # EventVRRPStateChanged(None->VRRP_STATE_MASTER)
+ #
+ # RFC 5795 6.4.1
+ # (115) + If the protected IPvX address is an IPv4 address, then:
+ # (120) * Broadcast a gratuitous ARP request containing the
+ # virtual router MAC address for each IP address associated
+ # with the virtual router.
+ # (125) + else // IPv6
+ # (130) * For each IPv6 address associated with the virtual
+ # router, send an unsolicited ND Neighbor Advertisement with
+ # the Router Flag (R) set, the Solicited Flag (S) unset, the
+ # Override flag (O) set, the target address set to the IPv6
+ # address of the virtual router, and the target link-layer
+ # address set to the virtual router MAC address.
+
+ self.state_change(vrrp_event.VRRP_STATE_MASTER)
+ self.adver_timer.start(self.config.advertisement_interval)
+ else:
+ params = self.params
+ params.master_adver_interval = self.config.advertisement_interval
+ self.state_change(vrrp_event.VRRP_STATE_BACKUP)
+ self.master_down_timer.start(params.master_down_interval)
+
+ self.stats_out_timer.start(self.statistics.statistics_interval)
+ super(VRRPRouterV3, self).start()