# 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()
