vrrp service

services.protocols.vrrp
    utils: util functions for vrrp service
    event: VRRP related events and classes
    monitor:  interface monitor
    router: VRRP router
    manager: a class that manages VRRP routers
    api: API for VRRP service
    dumper: vrrp event dumper (a sample application)

the directory structure (services.protocols.vrrp) was
suggested by FUJITA Tomonori.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
Isaku Yamahata 2013-11-22 16:46:01 +09:00 committed by FUJITA Tomonori
parent f03a9ceb6c
commit 3c10efe07f
14 changed files with 2612 additions and 0 deletions

15
ryu/services/__init__.py Normal file
View File

@ -0,0 +1,15 @@
# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
# Copyright (C) 2013 YAMAMOTO Takashi <yamamoto at valinux co 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.

View File

@ -0,0 +1,15 @@
# 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.

View File

@ -0,0 +1,15 @@
# 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.

View File

@ -0,0 +1,65 @@
# 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.
from ryu.services.protocols.vrrp import event as vrrp_event
def vrrp_config(app, interface, config):
"""create an instance.
returns EventVRRPConfigReply(instance.name, interface, config)
on success.
returns EventVRRPConfigReply(None, interface, config)
on failure.
"""
config_request = vrrp_event.EventVRRPConfigRequest(interface, config)
config_request.sync = True
return app.send_request(config_request)
def vrrp_shutdown(app, instance_name):
"""shutdown the instance.
"""
shutdown_request = vrrp_event.EventVRRPShutdownRequest(instance_name)
app.send_event(vrrp_event.VRRP_MANAGER_NAME, shutdown_request)
def vrrp_transmit(app, monitor_name, data):
"""transmit a packet from the switch. this is internal use only.
data is str-like, a packet to send.
"""
transmit_request = vrrp_event.EventVRRPTransmitRequest(data)
app.send_event(monitor_name, transmit_request)
def vrrp_list(app, instance_name=None):
"""list instances.
returns EventVRRPListReply([VRRPInstance]).
"""
list_request = vrrp_event.EventVRRPListRequest(instance_name)
list_request.dst = vrrp_event.VRRP_MANAGER_NAME
return app.send_request(list_request)
def vrrp_config_change(app, instance_name,
priority=None, advertisement_interval=None,
preempt_mode=None, accept_mode=None):
"""change configuration of an instance.
None means no change.
"""
config_change = vrrp_event.EventVRRPConfigChangeRequest(
instance_name, priority, advertisement_interval,
preempt_mode, accept_mode)
return app.send_event(vrrp_event.VRRP_MANAGER_NAME, config_change)

View File

@ -0,0 +1,154 @@
# 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 event dumper
This is also a template for router implementation that support VRRP
"""
from ryu.base import app_manager
from ryu.controller import handler
from ryu.services.protocols.vrrp import event as vrrp_event
class VRRPDumper(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
super(VRRPDumper, self).__init__(*args, **kwargs)
@handler.set_ev_cls(vrrp_event.EventVRRPStateChanged)
def vrrp_state_changed_handler(self, ev):
old_state = ev.old_state
new_state = ev.new_state
self.logger.info('state change %s: %s -> %s', ev.instance_name,
old_state, new_state)
if new_state == vrrp_event.VRRP_STATE_MASTER:
self.logger.info('becomes master')
if old_state is None:
# 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.
#
# or
#
# 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.
#
pass
elif old_state == vrrp_event.VRRP_STATE_BACKUP:
# RFC3768 6.4.2
# o Broadcast a gratuitous ARP request containing the virtual
# router MAC address for each IP address associated with the
# virtual router
#
# or
#
# 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.
pass
# RFC 3768 6.4.3
# - MUST respond to ARP requests for the IP address(es) associated
# with the virtual router.
# - MUST forward packets with a destination link layer MAC address
# equal to the virtual router MAC address.
# - MUST NOT accept packets addressed to the IP address(es)
# associated with the virtual router if it is not the IP address
# owner.
# - MUST accept packets addressed to the IP address(es) associated
# with the virtual router if it is the IP address owner.
#
# or
#
# RFC5798 6.4.3
#(605) - If the protected IPvX address is an IPv4 address, then:
# (610) + MUST respond to ARP requests for the IPv4 address(es)
# associated with the virtual router.
#(615) - else // ipv6
# (620) + MUST be a member of the Solicited-Node multicast
# address for the IPv6 address(es) associated with the virtual
# router.
# (625) + MUST respond to ND Neighbor Solicitation message for
# the IPv6 address(es) associated with the virtual router.
# (630) ++ MUST send ND Router Advertisements for the virtual
# router.
# (635) ++ If Accept_Mode is False: MUST NOT drop IPv6 Neighbor
# Solicitations and Neighbor Advertisements.
#(640) +-endif // ipv4?
#(645) - MUST forward packets with a destination link-layer MAC
#address equal to the virtual router MAC address.
#(650) - MUST accept packets addressed to the IPvX address(es)
#associated with the virtual router if it is the IPvX address owner
#or if Accept_Mode is True. Otherwise, MUST NOT accept these
#packets.
elif new_state == vrrp_event.VRRP_STATE_BACKUP:
self.logger.info('becomes backup')
# RFC 3768 6.4.2 Backup
# - MUST NOT respond to ARP requests for the IP address(s)
# associated with the virtual router.
# - MUST discard packets with a destination link layer MAC address
# equal to the virtual router MAC address.
# - MUST NOT accept packets addressed to the IP address(es)
# associated with the virtual router.
#
# or
#
# RFC 5798 6.4.2 Backup
#(305) - If the protected IPvX address is an IPv4 address, then:
# (310) + MUST NOT respond to ARP requests for the IPv4
# address(es) associated with the virtual router.
#(315) - else // protected addr is IPv6
# (320) + MUST NOT respond to ND Neighbor Solicitation messages
# for the IPv6 address(es) associated with the virtual router.
# (325) + MUST NOT send ND Router Advertisement messages for the
# virtual router.
#(330) -endif // was protected addr IPv4?
#(335) - MUST discard packets with a destination link-layer MAC
#address equal to the virtual router MAC address.
#(340) - MUST NOT accept packets addressed to the IPvX address(es)
#associated with the virtual router.
elif new_state == vrrp_event.VRRP_STATE_INITIALIZE:
if old_state is None:
self.logger.info('initialized')
else:
self.logger.info('shutdowned')
else:
raise ValueError('invalid vrrp state %s' % new_state)

View File

@ -0,0 +1,265 @@
# 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.
"""
Events for VRRP
"""
from ryu.controller import event
from ryu.lib import dpid as dpid_lib
from ryu.lib import mac as mac_lib
from ryu.lib.packet import vrrp
from ryu.lib import addrconv
# When an instance is created, state transition is None -> Initialize
VRRP_STATE_INITIALIZE = 'Initialize'
VRRP_STATE_MASTER = 'Master'
VRRP_STATE_BACKUP = 'Backup'
VRRP_MANAGER_NAME = 'VRRPManager'
class VRRPInterfaceBase(object):
"""
interface on which VRRP router works
vlan_id = None means no vlan.
NOTE: multiple virtual router can be configured on single port
See RFC 5798 4.2 Sample Configuration 2
"""
def __init__(self, mac_address, primary_ip_address, vlan_id=None):
super(VRRPInterfaceBase, self).__init__()
self.mac_address = mac_address
self.primary_ip_address = primary_ip_address
self.vlan_id = vlan_id
def __eq__(self, other):
return (self.__class__ == other.__class__ and
self.mac_address == other.mac_address and
self.primary_ip_address == other.primary_ip_address and
self.vlan_id == other.vlan_id)
def __hash__(self):
return hash((
addrconv.mac.text_to_bin(self.mac_address),
vrrp.ip_text_to_bin(self.primary_ip_address), self.vlan_id))
class VRRPInterfaceNetworkDevice(VRRPInterfaceBase):
def __init__(self, mac_address, primary_ip_address, vlan_id,
device_name):
super(VRRPInterfaceNetworkDevice, self).__init__(
mac_address, primary_ip_address, vlan_id)
self.device_name = device_name
def __str__(self):
return '%s<%s, %s, %s, %s>' % (
self.__class__.__name__,
self.mac_address,
self.primary_ip_address, self.vlan_id,
self.device_name)
def __eq__(self, other):
return (super(VRRPInterfaceNetworkDevice, self).__eq__(other) and
self.device_name == other.device_name)
def __hash__(self):
return hash((
addrconv.mac.text_to_bin(self.mac_address),
vrrp.ip_text_to_bin(self.primary_ip_address), self.vlan_id,
self.device_name))
class VRRPInterfaceOpenFlow(VRRPInterfaceBase):
def __init__(self, mac_address, primary_ip_address, vlan_id,
dpid, port_no):
super(VRRPInterfaceOpenFlow, self).__init__(
mac_address, primary_ip_address, vlan_id)
self.dpid = dpid
self.port_no = port_no
def __str__(self):
return '%s<%s, %s, %s, %s, %d>' % (
self.__class__.__name__,
self.mac_address,
self.primary_ip_address, self.vlan_id,
dpid_lib.dpid_to_str(self.dpid), self.port_no)
def __eq__(self, other):
return (super(VRRPInterfaceOpenFlow, self).__eq__(other) and
self.dpid == other.dpid and self.port_no == other.port_no)
def __hash__(self):
return hash((
addrconv.mac.text_to_bin(self.mac_address),
vrrp.ip_text_to_bin(self.primary_ip_address), self.vlan_id,
self.dpid, self.port_no))
class VRRPConfig(object):
"""
advertmisement_interval is in seconds as float. (Not in centiseconds)
"""
def __init__(self, version=vrrp.VRRP_VERSION_V3, vrid=None,
priority=vrrp.VRRP_PRIORITY_BACKUP_DEFAULT, ip_addresses=None,
advertisement_interval=vrrp.VRRP_MAX_ADVER_INT_DEFAULT_IN_SEC,
preempt_mode=True, preempt_delay=0, accept_mode=False):
# To allow version and priority default
assert vrid is not None
assert ip_addresses is not None
super(VRRPConfig, self).__init__()
self.version = version
self.vrid = vrid
self.priority = priority
self.ip_addresses = ip_addresses
self.advertisement_interval = advertisement_interval
self.preempt_mode = preempt_mode
self.preempt_delay = preempt_delay
self.accept_mode = accept_mode
self.is_ipv6 = vrrp.is_ipv6(ip_addresses[0])
@property
def address_owner(self):
return self.priority == vrrp.VRRP_PRIORITY_ADDRESS_OWNER
def __eq__(self, other):
return (self.version == other.version and
self.vrid == other.vrid and
self.priority == other.priority and
self.ip_addresses == other.ip_addresses and
self.advertisement_interval == other.advertisement_interval and
self.preempt_mode == other.preempt_mode and
self.preempt_delay == other.preempt_delay and
self.accept_mode == other.accept_mode and
self.is_ipv6 == other.is_ipv6)
def __hash__(self):
hash((self.version, self.vrid, self.priority,
map(vrrp.ip_text_to_bin, self.ip_addresses),
self.advertisement_interval, self.preempt_mode,
self.preempt_delay, self.accept_mode, self.is_ipv6))
class EventVRRPConfigRequest(event.EventRequestBase):
"""
Request from management layer to VRRP manager to initialize VRRP Router.
"""
def __init__(self, interface, config):
super(EventVRRPConfigRequest, self).__init__()
self.dst = VRRP_MANAGER_NAME
self.interface = interface
self.config = config
class EventVRRPConfigReply(event.EventReplyBase):
def __init__(self, instance_name, interface, config):
# dst = None. dst is filled by app_base.RyuApp.send_reply()
super(EventVRRPConfigReply, self).__init__(None)
self.instance_name = instance_name # None means failure
self.interface = interface
self.config = config
class EventVRRPShutdownRequest(event.EventRequestBase):
"""
Request from management layer to VRRP to shutdown VRRP Router.
"""
def __init__(self, instance_name):
super(EventVRRPShutdownRequest, self).__init__()
self.instance_name = instance_name
class EventVRRPStateChanged(event.EventBase):
"""
Event that this VRRP Router changed its state.
"""
def __init__(self, instance_name, monitor_name, interface, config,
old_state, new_state):
super(EventVRRPStateChanged, self).__init__()
self.instance_name = instance_name
self.monitor_name = monitor_name
self.interface = interface
self.config = config
self.old_state = old_state
self.new_state = new_state
class VRRPInstance(object):
def __init__(self, instance_name, monitor_name, config, interface, state):
super(VRRPInstance, self).__init__()
self.instance_name = instance_name
self.monitor_name = monitor_name
self.config = config
self.interface = interface
self.state = state
class EventVRRPListRequest(event.EventRequestBase):
"""
Event that requests list of configured VRRP router
instance_name=None means all instances.
"""
def __init__(self, instance_name=None):
super(EventVRRPListRequest, self).__init__()
self.instance_name = instance_name
class EventVRRPListReply(event.EventReplyBase):
def __init__(self, instance_list):
super(EventVRRPListReply, self).__init__(None)
self.instance_list = instance_list
class EventVRRPConfigChangeRequest(event.EventRequestBase):
"""
Event that requests to change configuration of a given VRRP router.
None means no-change.
"""
def __init__(self, instance_name, priority=None,
advertisement_interval=None, preempt_mode=None,
preempt_delay=None, accept_mode=None):
super(EventVRRPConfigChangeRequest, self).__init__()
self.instance_name = instance_name
self.priority = priority
self.advertisement_interval = advertisement_interval
self.preempt_mode = preempt_mode
self.preempt_delay = preempt_delay
self.accept_mode = accept_mode
# Following classes are internally used by VRRP
class EventVRRPReceived(event.EventBase):
"""
Event that port manager received valid VRRP packet.
Usually handed by VRRP Router.
"""
def __init__(self, interface, packet):
super(EventVRRPReceived, self).__init__()
self.interface = interface
self.packet = packet
class EventVRRPTransmitRequest(event.EventRequestBase):
"""
Request from VRRP router to port manager to transmit VRRP packet.
"""
def __init__(self, data):
super(EventVRRPTransmitRequest, self).__init__()
self.data = data

View File

@ -0,0 +1,156 @@
# 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 manager that manages VRRP router instances
VRRPManager creates/deletes VRRPRouter, VRRPInterfaceMonitor
dynamically as requested.
Usage example
PYTHONPATH=. ./bin/ryu-manager --verbose \
ryu.services.protocols.vrrp.manager \
ryu.services.protocols.vrrp.dumper
"""
from ryu.base import app_manager
from ryu.controller import handler
from ryu.lib import hub
from ryu.services.protocols.vrrp import event as vrrp_event
from ryu.services.protocols.vrrp import monitor as vrrp_monitor
from ryu.services.protocols.vrrp import router as vrrp_router
class VRRPInstance(object):
def __init__(self, name, monitor_name, config, interface):
super(VRRPInstance, self).__init__()
self.name = name # vrrp_router.name
self.monitor_name = monitor_name # interface_monitor.name
self.config = config
self.interface = interface
self.state = None
def state_changed(self, new_state):
self.state = new_state
class VRRPManager(app_manager.RyuApp):
@staticmethod
def _instance_name(interface, vrid, is_ipv6):
ip_version = 'ipv6' if is_ipv6 else 'ipv4'
return 'VRRP-Router-%s-%d-%s' % (str(interface), vrid, ip_version)
def __init__(self, *args, **kwargs):
super(VRRPManager, self).__init__(*args, **kwargs)
self._args = args
self._kwargs = kwargs
self.name = vrrp_event.VRRP_MANAGER_NAME
self._instances = {} # name -> VRRPInstance
self.shutdown = hub.Queue()
def start(self):
self.threads.append(hub.spawn(self._shutdown_loop))
super(VRRPManager, self).start()
@handler.set_ev_cls(vrrp_event.EventVRRPConfigRequest)
def config_request_handler(self, ev):
config = ev.config
interface = ev.interface
name = self._instance_name(interface, config.vrid, config.is_ipv6)
if name in self._instances:
rep = vrrp_event.EventVRRPConfigReply(None, interface, config)
self.reply_to_request(ev, rep)
return
monitor = vrrp_monitor.VRRPInterfaceMonitor.factory(
interface, config, name, *self._args, **self._kwargs)
router = vrrp_router.VRRPRouter.factory(
name, monitor.name, interface, config, *self._args, **self._kwargs)
# Event piping
# vrrp_router -> vrrp_manager
# EventVRRPStateChanged to vrrp_manager is handled by framework
# vrrp_manager -> vrrp_rouer
self.register_observer(vrrp_event.EventVRRPShutdownRequest,
router.name)
# vrrp_router -> vrrp_monitor
router.register_observer(vrrp_event.EventVRRPStateChanged,
monitor.name)
router.register_observer(vrrp_event.EventVRRPTransmitRequest,
monitor.name)
# vrrp_interface_monitor -> vrrp_router
monitor.register_observer(vrrp_event.EventVRRPReceived, router.name)
instance = VRRPInstance(name, monitor.name, config, interface)
self._instances[name] = instance
#self.logger.debug('report_bricks')
#app_manager.AppManager.get_instance().report_bricks() # debug
monitor.start()
router.start()
rep = vrrp_event.EventVRRPConfigReply(instance.name, interface, config)
self.reply_to_request(ev, rep)
def _proxy_event(self, ev):
name = ev.instance_name
instance = self._instances.get(name, None)
if not instance:
self.logger.info('unknown vrrp router %s', name)
return
self.send_event(instance.name, ev)
@handler.set_ev_cls(vrrp_event.EventVRRPShutdownRequest)
def shutdown_request_handler(self, ev):
self._proxy_event(ev)
@handler.set_ev_cls(vrrp_event.EventVRRPConfigChangeRequest)
def config_change_request_handler(self, ev):
self._proxy_event(ev)
@handler.set_ev_cls(vrrp_event.EventVRRPStateChanged)
def state_change_handler(self, ev):
instance = self._instances.get(ev.instance_name, None)
assert instance is not None
instance.state_changed(ev.new_state)
if ev.old_state and ev.new_state == vrrp_event.VRRP_STATE_INITIALIZE:
self.shutdown.put(instance)
def _shutdown_loop(self):
app_mgr = app_manager.AppManager.get_instance()
while self.is_active or not self.shutdown.empty():
instance = self.shutdown.get()
app_mgr.uninstantiate(instance.name)
app_mgr.uninstantiate(instance.monitor_name)
del self._instances[instance.name]
@handler.set_ev_cls(vrrp_event.EventVRRPListRequest)
def list_request_handler(self, ev):
instance_name = ev.instance_name
if instance_name is None:
instance_list = [vrrp_event.VRRPInstance(
instance.name, instance.monitor_name,
instance.config, instance.interface, instance.state)
for instance in self._instances.values()]
else:
instance = self._instances.get(instance_name, None)
if instance is None:
instance_list = []
else:
instance_list = [vrrp_event.VRRPInstance(
instance_name, instance.monitor_name,
instance.config, instance.interface, instance.state)]
vrrp_list = vrrp_event.EventVRRPListReply(instance_list)
self.reply_to_request(ev, vrrp_list)

View File

@ -0,0 +1,151 @@
# 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.
"""
Interface monitor.
Watching packet received on this interface and parse VRRP packet.
VRRPManager creates/deletes instances of interface monitor dynamically.
"""
from ryu.base import app_manager
from ryu.controller import handler
from ryu.lib.packet import packet
from ryu.lib.packet import vlan
from ryu.lib.packet import vrrp
from ryu.services.protocols.vrrp import event as vrrp_event
class VRRPInterfaceMonitor(app_manager.RyuApp):
# subclass of VRRPInterfaceBase -> subclass of VRRPInterfaceMonitor
_CONSTRUCTORS = {}
@staticmethod
def register(interface_cls):
def _register(cls):
VRRPInterfaceMonitor._CONSTRUCTORS[interface_cls] = cls
return cls
return _register
@staticmethod
def factory(interface, config, router_name, *args, **kwargs):
cls = VRRPInterfaceMonitor._CONSTRUCTORS[interface.__class__]
app_mgr = app_manager.AppManager.get_instance()
kwargs = kwargs.copy()
kwargs['router_name'] = router_name
kwargs['vrrp_config'] = config
kwargs['vrrp_interface'] = interface
app = app_mgr.instantiate(cls, *args, **kwargs)
return app
@classmethod
def instance_name(cls, interface, vrid):
return '%s-%s-%d' % (cls.__name__, str(interface), vrid)
def __init__(self, *args, **kwargs):
super(VRRPInterfaceMonitor, self).__init__(*args, **kwargs)
self.config = kwargs['vrrp_config']
self.interface = kwargs['vrrp_interface']
self.router_name = kwargs['router_name']
self.name = self.instance_name(self.interface, self.config.vrid)
def _send_vrrp_packet_received(self, packet_data):
# OF doesn't support VRRP packet matching, so we have to parse
# it ourselvs.
packet_ = packet.Packet(packet_data)
protocols = packet_.protocols
# we expect either of
# [ether, vlan, ip, vrrp{, padding}]
# or
# [ether, ip, vrrp{, padding}]
if len(protocols) < 2:
self.logger.debug('len(protocols) %d', len(protocols))
return
vlan_vid = self.interface.vlan_id
may_vlan = protocols[1]
if (vlan_vid is not None) != isinstance(may_vlan, vlan.vlan):
self.logger.debug('vlan_vid: %s %s', vlan_vid, type(may_vlan))
return
if vlan_vid is not None and vlan_vid != may_vlan.vid:
self.logger.debug('vlan_vid: %s vlan %s', vlan_vid, type(may_vlan))
return
# self.logger.debug('%s %s', packet_, packet_.protocols)
may_ip, may_vrrp = vrrp.vrrp.get_payload(packet_)
if not may_ip or not may_vrrp:
# self.logger.debug('may_ip %s may_vrrp %s', may_ip, may_vrrp)
return
if not vrrp.vrrp.is_valid_ttl(may_ip):
self.logger.debug('valid_ttl')
return
if may_vrrp.version != self.config.version:
self.logger.debug('vrrp version %d %d',
may_vrrp.version, self.config.version)
return
if not may_vrrp.is_valid():
self.logger.debug('valid vrrp')
return
offset = 0
for proto in packet_.protocols:
if proto == may_vrrp:
break
offset += len(proto)
if not may_vrrp.checksum_ok(
may_ip, packet_.data[offset:offset + len(may_vrrp)]):
self.logger.debug('bad checksum')
return
if may_vrrp.vrid != self.config.vrid:
self.logger.debug('vrid %d %d', may_vrrp.vrid, self.config.vrid)
return
if may_vrrp.is_ipv6 != self.config.is_ipv6:
self.logger.debug('is_ipv6 %s %s',
may_vrrp.is_ipv6, self.config.is_ipv6)
return
# TODO: Optional check rfc5798 7.1
# may_vrrp.ip_addresses equals to self.config.ip_addresses
vrrp_received = vrrp_event.EventVRRPReceived(self.interface, packet_)
self.send_event(self.router_name, vrrp_received)
@handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
def vrrp_transmit_request_handler(self, ev):
raise NotImplementedError()
def _initialize(self):
raise NotImplementedError()
def _shutdown(self):
raise NotImplementedError()
@handler.set_ev_handler(vrrp_event.EventVRRPStateChanged)
def vrrp_state_changed_handler(self, ev):
assert ev.interface == self.interface
if ev.new_state == vrrp_event.VRRP_STATE_INITIALIZE:
# add/del packet in rule
if ev.old_state:
self._shutdown()
else:
self._initialize()
elif ev.new_state in [vrrp_event.VRRP_STATE_BACKUP,
vrrp_event.VRRP_STATE_MASTER]:
pass
else:
raise RuntimeError('unknown vrrp state %s' % ev.new_state)

View File

@ -0,0 +1,234 @@
# 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.
# tested on 64bit linux.
# On other platform like 32bit Linux, the structure can be different
# due to alignment difference.
import contextlib
import fcntl
import socket
import struct
from ryu.controller import handler
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.lib import addrconv
from ryu.lib import hub
from ryu.lib.packet import arp
from ryu.lib.packet import vrrp
from ryu.services.protocols.vrrp import monitor
from ryu.services.protocols.vrrp import event as vrrp_event
from ryu.services.protocols.vrrp import utils
# Those are not defined in socket module
IFNAMSIZ = 16
SS_MAXSIZE = 128
SIOCGIFINDEX = 0x8933 # This is for Linux x64. May differ on other Linux
MCAST_JOIN_GROUP = 42
MCAST_LEAVE_GROUP = 45
PACKET_ADD_MEMBERSHIP = 1
PACKET_DROP_MEMBERSHIP = 2
PACKET_MR_MULTICAST = 0
SOL_PACKET = 263
def if_nametoindex(ifname):
# can the one defined in libc.so be used?
#
# IFNAMSIZE = 16
# struct ifreq {
# char ifr_name[IFNAMSIZ]; /* Interface name */
# union {
# struct sockaddr ifr_addr;
# struct sockaddr ifr_dstaddr;
# struct sockaddr ifr_broadaddr;
# struct sockaddr ifr_netmask;
# struct sockaddr ifr_hwaddr;
# short ifr_flags;
# int ifr_ifindex;
# int ifr_metric;
# int ifr_mtu;
# struct ifmap ifr_map;
# char ifr_slave[IFNAMSIZ];
# char ifr_newname[IFNAMSIZ];
# char *ifr_data;
# };
# };
PACK_STR = '16sI12x'
# get ip address of the given interface
with contextlib.closing(socket.socket(socket.AF_INET,
socket.SOCK_DGRAM, 0)) as udp_socket:
ifreq = struct.pack(PACK_STR, ifname, 0)
res = fcntl.ioctl(udp_socket, SIOCGIFINDEX, ifreq)
return struct.unpack(PACK_STR, res)[1]
@monitor.VRRPInterfaceMonitor.register(vrrp_event.VRRPInterfaceNetworkDevice)
class VRRPInterfaceMonitorNetworkDevice(monitor.VRRPInterfaceMonitor):
"""
This module uses raw socket so that privilege(CAP_NET_ADMIN capability)
is required.
"""
def __init__(self, *args, **kwargs):
super(VRRPInterfaceMonitorNetworkDevice, self).__init__(*args,
**kwargs)
self.__is_active = True
config = self.config
if config.is_ipv6:
family = socket.AF_INET6
ether_type = ether.ETH_TYPE_IPV6
mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
else:
family = socket.AF_INET
ether_type = ether.ETH_TYPE_IP
mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
# socket module doesn't define IPPROTO_VRRP
self.ip_socket = socket.socket(family, socket.SOCK_RAW,
inet.IPPROTO_VRRP)
self.packet_socket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(ether_type))
self.packet_socket.bind((self.interface.device_name, ether_type,
socket.PACKET_MULTICAST,
arp.ARP_HW_TYPE_ETHERNET,
addrconv.mac.text_to_bin(mac_address)))
self.ifindex = if_nametoindex(self.interface.device_name)
def start(self):
# discard received packets before joining multicast membership
packet_socket = self.packet_socket
packet_socket.setblocking(0)
with hub.Timeout(0.1, False):
while True:
try:
packet_socket.recv(1500)
except socket.error:
break
packet_socket.setblocking(1)
self._join_multicast_membership(True)
self._join_vrrp_group(True)
super(VRRPInterfaceMonitorNetworkDevice, self).start()
self.threads.append(hub.spawn(self._recv_loop))
def stop(self):
self.__is_active = False
super(VRRPInterfaceMonitorNetworkDevice, self).stop()
def _join_multicast_membership(self, join_leave):
config = self.config
if config.is_ipv6:
mac_address = vrrp.vrrp_ipv6_src_mac_address(config.vrid)
else:
mac_address = vrrp.vrrp_ipv4_src_mac_address(config.vrid)
if join_leave:
add_drop = PACKET_ADD_MEMBERSHIP
else:
add_drop = PACKET_DROP_MEMBERSHIP
packet_mreq = struct.pack('IHH8s', self.ifindex,
PACKET_MR_MULTICAST, 6,
addrconv.mac.text_to_bin(mac_address))
self.packet_socket.setsockopt(SOL_PACKET, add_drop, packet_mreq)
def _join_vrrp_group(self, join_leave):
if join_leave:
join_leave = MCAST_JOIN_GROUP
else:
join_leave = MCAST_LEAVE_GROUP
# struct group_req {
# __u32 gr_interface; /* interface index */
# struct __kernel_sockaddr_storage gr_group; /* group address */
# };
group_req = struct.pack('I', self.ifindex)
# padding to gr_group. This is environment dependent
group_req += '\x00' * (struct.calcsize('P') - struct.calcsize('I'))
if self.config.is_ipv6:
# struct sockaddr_in6 {
# sa_family_t sin6_family; /* AF_INET6 */
# in_port_t sin6_port; /* port number */
# uint32_t sin6_flowinfo; /* IPv6 flow information */
# struct in6_addr sin6_addr; /* IPv6 address */
# uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
# };
# struct in6_addr {
# unsigned char s6_addr[16]; /* IPv6 address */
# };
family = socket.IPPROTO_IPV6
sockaddr = struct.pack('H', socket.AF_INET6)
sockaddr += struct.pack('!H', 0)
sockaddr += struct.pack('!I', 0)
sockaddr += addrconv.ipv6.text_to_bin(vrrp.VRRP_IPV6_DST_ADDRESS)
sockaddr += struct.pack('I', 0)
else:
# #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
# struct sockaddr_in {
# __kernel_sa_family_t sin_family; /* Address family */
# __be16 sin_port; /* Port number */
# struct in_addr sin_addr; /* Internet address */
# /* Pad to size of `struct sockaddr'. */
# unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
# sizeof(unsigned short int) - sizeof(struct in_addr)];
# };
# struct in_addr {
# __be32 s_addr;
# };
family = socket.IPPROTO_IP
sockaddr = struct.pack('H', socket.AF_INET)
sockaddr += struct.pack('!H', 0)
sockaddr += addrconv.ipv4.text_to_bin(vrrp.VRRP_IPV4_DST_ADDRESS)
sockaddr += '\x00' * (SS_MAXSIZE - len(sockaddr))
group_req += sockaddr
self.ip_socket.setsockopt(family, join_leave, group_req)
return
def _recv_loop(self):
packet_socket = self.packet_socket
packet_socket.settimeout(1.3) # to check activeness periodically
try:
while self.__is_active:
try:
buf = packet_socket.recv(128)
except socket.timeout:
self.logger.debug('timeout')
continue
if len(buf) == 0:
self.__is_active = False
break
self.logger.debug('recv buf')
self._send_vrrp_packet_received(buf)
finally:
self._join_vrrp_group(False)
self._join_multicast_membership(False)
@handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
def vrrp_transmit_request_handler(self, ev):
self.logger.debug('send')
self.packet_socket.sendto(ev.data, (self.interface.device_name, 0))
def _initialize(self):
# nothing
pass
def _shutdown(self):
self.__is_active = False

View File

@ -0,0 +1,141 @@
# 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.
from ryu.controller import handler
from ryu.controller import ofp_event
from ryu.lib import dpid as dpid_lib
from ryu.lib.packet import vrrp
from ryu.ofproto import ether
from ryu.ofproto import inet
from ryu.ofproto import ofproto_v1_2
from ryu.ofproto import ofproto_v1_3
from ryu.services.protocols.vrrp import monitor
from ryu.services.protocols.vrrp import event as vrrp_event
from ryu.services.protocols.vrrp import utils
@monitor.VRRPInterfaceMonitor.register(vrrp_event.VRRPInterfaceOpenFlow)
class VRRPInterfaceMonitorOpenFlow(monitor.VRRPInterfaceMonitor):
# OF1.2
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION,
ofproto_v1_3.OFP_VERSION] # probably work with OF1.3
_TABLE = 0 # generate packet-in in this table
_PRIORITY = 0x8000 # default priority
def __init__(self, *args, **kwargs):
super(VRRPInterfaceMonitorOpenFlow, self).__init__(*args, **kwargs)
table = kwargs.get('vrrp_imof_table', None)
if table is not None:
self._TABLE = int(table)
priority = kwargs.get('vrrp_imof_priority', None)
if priority is not None:
self._PRIORITY = int(priority)
@handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
def packet_in_handler(self, ev):
self.logger.debug('packet_in_handler')
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
# TODO: subscribe only the designated datapath
dpid = datapath.id
if dpid != self.interface.dpid:
self.logger.debug('packet_in_handler dpid %s %s',
dpid_lib.dpid_to_str(dpid),
dpid_lib.dpid_to_str(self.interface.dpid))
return
in_port = None
for field in msg.match.fields:
if field.header == ofproto.OXM_OF_IN_PORT:
in_port = field.value
break
if in_port != self.interface.port_no:
self.logger.debug('packet_in_handler in_port %s %s',
in_port, self.interface.port_no)
return
self._send_vrrp_packet_received(msg.data)
def _get_dp(self):
return utils.get_dp(self, self.interface.dpid)
@handler.set_ev_handler(vrrp_event.EventVRRPTransmitRequest)
def vrrp_transmit_request_handler(self, ev):
dp = self._get_dp()
if not dp:
return
utils.dp_packet_out(dp, self.interface.port_no, ev.data)
def _ofp_match(self, ofproto_parser):
is_ipv6 = vrrp.is_ipv6(self.config.ip_addresses[0])
kwargs = {}
kwargs['in_port'] = self.interface.port_no
if is_ipv6:
kwargs['eth_dst'] = vrrp.VRRP_IPV6_DST_MAC_ADDRESS
kwargs['eth_src'] = \
vrrp.vrrp_ipv6_src_mac_address(self.config.vrid)
kwargs['eth_type'] = ether.ETH_TYPE_IPV6
kwargs['ipv6_dst'] = vrrp.VRRP_IPV6_DST_ADDRESS
else:
kwargs['eth_dst'] = vrrp.VRRP_IPV4_DST_MAC_ADDRESS
kwargs['eth_src'] = \
vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
kwargs['eth_type'] = ether.ETH_TYPE_IP
kwargs['ipv4_dst'] = vrrp.VRRP_IPV4_DST_ADDRESS
if self.interface.vlan_id is not None:
kwargs['vlan_vid'] = self.interface.vlan_id
kwargs['ip_proto'] = inet.IPPROTO_VRRP
# OF1.2 doesn't support TTL match.
# It needs to be checked by packet in handler
return ofproto_parser.OFPMatch(**kwargs)
def _initialize(self):
dp = self._get_dp()
if not dp:
return
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
match = self._ofp_match(ofproto_parser)
utils.dp_flow_mod(dp, self._TABLE, ofproto.OFPFC_DELETE_STRICT,
self._PRIORITY, match, [],
out_port=ofproto.OFPP_CONTROLLER)
match = self._ofp_match(ofproto_parser)
actions = [ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
instructions = [ofproto_parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
utils.dp_flow_mod(dp, self._TABLE, ofproto.OFPFC_ADD, self._PRIORITY,
match, instructions)
def _shutdown(self):
dp = self._get_dp()
if not dp:
return
ofproto = dp.ofproto
match = self._ofp_match(dp.ofproto_parser)
utils.dp_flow_mod(dp, self._TABLE, ofproto.OFPFC_DELETE_STRICT,
self._PRIORITY, match, [],
out_port=ofproto.OFPP_CONTROLLER)

View File

@ -0,0 +1,687 @@
# 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
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
class VRRPState(object):
__metaclass__ = abc.ABCMeta
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, *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
return app_mgr.instantiate(cls, *args, **kwargs)
class _EventMasterDown(event.EventBase):
pass
class _EventAdver(event.EventBase):
pass
class _EventPreemptDelay(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.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)
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)
# 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)
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)
# 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.warn('%s master_down', self.__class__.__name__)
def adver(self, ev):
self.vrrp_router.logger.warn('%s adver', self.__class__.__name__)
def preempt_delay(self, ev):
self.vrrp_router.logger.warn('%s preempt_delay',
self.__class__.__name__)
def vrrp_received(self, ev):
self.vrrp_router.logger.warn('%s vrrp_received',
self.__class__.__name__)
def vrrp_shutdown_request(self, ev):
self.vrrp_router.logger.warn('%s vrrp_shutdown_request',
self.__class__.__name__)
def vrrp_config_change_request(self, ev):
self.vrrp_router.logger.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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.warn('%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)
if self.config.address_owner:
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)
super(VRRPRouterV3, self).start()

View File

@ -0,0 +1,99 @@
# 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.
"""
sample router manager.
(un-)instantiate routers
Usage example:
PYTHONPATH=. ./bin/ryu-manager --verbose \
ryu.services.protocols.vrrp.manager \
ryu.services.protocols.vrrp.dumper \
ryu.services.protocols.vrrp.sample_manager
"""
from ryu.base import app_manager
from ryu.controller import handler
from ryu.services.protocols.vrrp import event as vrrp_event
from ryu.services.protocols.vrrp import sample_router
class RouterManager(app_manager.RyuApp):
_ROUTER_CLASSES = {
vrrp_event.VRRPInterfaceNetworkDevice: {
4: sample_router.RouterIPV4Linux,
6: sample_router.RouterIPV6Linux,
},
vrrp_event.VRRPInterfaceOpenFlow: {
4: sample_router.RouterIPV4OpenFlow,
6: sample_router.RouterIPV6OpenFlow,
},
}
def __init__(self, *args, **kwargs):
super(RouterManager, self).__init__(*args, **kwargs)
self._args = args
self._kwargs = kwargs
self.routers = {} # instance name -> router name
def _router_factory(self, instance_name, monitor_name, interface, config):
cls = None
for interface_cls, router_clses in self._ROUTER_CLASSES.items():
if isinstance(interface, interface_cls):
if config.is_ipv6:
cls = router_clses[6]
else:
cls = router_clses[4]
break
self.logger.debug('interface %s %s', type(interface), interface)
self.logger.debug('cls %s', cls)
if cls is None:
raise ValueError('Unknown interface type %s %s' % (type(interface),
interface))
kwargs = self._kwargs.copy()
kwargs.update({
'name': instance_name,
'monitor_name': monitor_name,
'config': config,
'interface': interface,
})
app_mgr = app_manager.AppManager.get_instance()
return app_mgr.instantiate(cls, *self._args, **kwargs)
@handler.set_ev_cls(vrrp_event.EventVRRPStateChanged)
def vrrp_state_changed_handler(self, ev):
if ev.new_state == vrrp_event.VRRP_STATE_INITIALIZE:
if ev.old_state:
self._shutdown(ev)
else:
self._initialize(ev)
return
router_name = self.routers.get(ev.instance_name)
self.send_event(router_name, ev)
def _initialize(self, ev):
router = self._router_factory(ev.instance_name, ev.monitor_name,
ev.interface, ev.config)
self.routers[ev.instance_name] = router.name
self.send_event(router.name, ev)
router.start()
def _shutdown(self, ev):
router_name = self.routers.pop(ev.instance_name)
self.send_event(router_name, ev)
app_mgr = app_manager.AppManager.get_instance()
app_mgr.uninstantiate(router_name)

View File

@ -0,0 +1,539 @@
# 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.
"""
router implementation base class
a template for router implementation that support VRRP
Those routers needs to be created by someone else.
sample_manager.routerManager is an example.
Usage example:
PYTHONPATH=. ./bin/ryu-manager --verbose \
ryu.services.protocols.vrrp.manager \
ryu.services.protocols.vrrp.dumper \
ryu.services.protocols.vrrp.sample_manager
"""
import contextlib
import greenlet
import socket
from ryu.base import app_manager
from ryu.controller import handler
from ryu.controller import ofp_event
from ryu.lib import hub
from ryu.lib import mac as mac_lib
from ryu.lib.packet import arp
from ryu.lib.packet import ethernet
from ryu.lib.packet import packet
from ryu.lib.packet import vlan
from ryu.lib.packet import vrrp
from ryu.ofproto import ether
from ryu.ofproto import ofproto_v1_2
from ryu.services.protocols.vrrp import api as vrrp_api
from ryu.services.protocols.vrrp import event as vrrp_event
from ryu.services.protocols.vrrp import utils
class RouterBase(app_manager.RyuApp):
def _router_name(self, config, interface):
ip_version = 'ipv6' if config.is_ipv6 else 'ipv4'
return '%s-%s-%d-%s' % (self.__class__.__name__,
str(interface), config.vrid, ip_version)
def __init__(self, *args, **kwargs):
super(RouterBase, self).__init__(*args, **kwargs)
self.instance_name = kwargs['name']
self.monitor_name = kwargs['monitor_name']
self.config = kwargs['config']
self.interface = kwargs['interface']
self.name = self._router_name(self.config, self.interface)
def _transmit(self, data):
vrrp_api.vrrp_transmit(self, self.monitor_name, data)
def _initialized(self):
self.logger.debug('initialized')
def _initialized_to_master(self):
self.logger.debug('initialized to 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.
#
# or
#
# 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.
def _become_master(self):
self.logger.debug('become master')
# RFC3768 6.4.2
# o Broadcast a gratuitous ARP request containing the virtual
# router MAC address for each IP address associated with the
# virtual router
#
# or
#
# 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.
def _become_backup(self):
self.logger.debug('become backup')
# RFC 3768 6.4.2 Backup
# - MUST NOT respond to ARP requests for the IP address(s)
# associated with the virtual router.
# - MUST discard packets with a destination link layer MAC address
# equal to the virtual router MAC address.
# - MUST NOT accept packets addressed to the IP address(es)
# associated with the virtual router.
#
# or
#
# RFC 5798 6.4.2 Backup
#(305) - If the protected IPvX address is an IPv4 address, then:
# (310) + MUST NOT respond to ARP requests for the IPv4
# address(es) associated with the virtual router.
#(315) - else // protected addr is IPv6
# (320) + MUST NOT respond to ND Neighbor Solicitation messages
# for the IPv6 address(es) associated with the virtual router.
# (325) + MUST NOT send ND Router Advertisement messages for the
# virtual router.
#(330) -endif // was protected addr IPv4?
#(335) - MUST discard packets with a destination link-layer MAC
#address equal to the virtual router MAC address.
#(340) - MUST NOT accept packets addressed to the IPvX address(es)
#associated with the virtual router.
def _shutdowned(self):
self.logger.debug('shutdowned')
@handler.set_ev_handler(vrrp_event.EventVRRPStateChanged)
def vrrp_state_changed_handler(self, ev):
old_state = ev.old_state
new_state = ev.new_state
self.logger.debug('sample router %s -> %s', old_state, new_state)
if new_state == vrrp_event.VRRP_STATE_MASTER:
if old_state == vrrp_event.VRRP_STATE_INITIALIZE:
self._initialized_to_master()
elif old_state == vrrp_event.VRRP_STATE_BACKUP:
self._become_master()
# RFC 3768 6.4.3
# - MUST respond to ARP requests for the IP address(es) associated
# with the virtual router.
# - MUST forward packets with a destination link layer MAC address
# equal to the virtual router MAC address.
# - MUST NOT accept packets addressed to the IP address(es)
# associated with the virtual router if it is not the IP address
# owner.
# - MUST accept packets addressed to the IP address(es) associated
# with the virtual router if it is the IP address owner.
#
# or
#
# RFC5798 6.4.3
#(605) - If the protected IPvX address is an IPv4 address, then:
# (610) + MUST respond to ARP requests for the IPv4 address(es)
# associated with the virtual router.
#(615) - else // ipv6
# (620) + MUST be a member of the Solicited-Node multicast
# address for the IPv6 address(es) associated with the virtual
# router.
# (625) + MUST respond to ND Neighbor Solicitation message for
# the IPv6 address(es) associated with the virtual router.
# (630) ++ MUST send ND Router Advertisements for the virtual
# router.
# (635) ++ If Accept_Mode is False: MUST NOT drop IPv6 Neighbor
# Solicitations and Neighbor Advertisements.
#(640) +-endif // ipv4?
#(645) - MUST forward packets with a destination link-layer MAC
#address equal to the virtual router MAC address.
#(650) - MUST accept packets addressed to the IPvX address(es)
#associated with the virtual router if it is the IPvX address owner
#or if Accept_Mode is True. Otherwise, MUST NOT accept these
#packets.
elif new_state == vrrp_event.VRRP_STATE_BACKUP:
self._become_backup()
elif new_state == vrrp_event.VRRP_STATE_INITIALIZE:
if old_state is None:
self._initialized()
else:
self._shutdowned()
else:
raise ValueError('invalid vrrp state %s' % new_state)
class RouterIPV4(RouterBase):
def _garp_packet(self, ip_address):
# prepare garp packet
src_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
e = ethernet.ethernet(mac_lib.BROADCAST_STR, src_mac,
ether.ETH_TYPE_ARP)
a = arp.arp_ip(arp.ARP_REQUEST, src_mac, ip_address,
mac_lib.DONTCARE_STR, ip_address)
p = packet.Packet()
p.add_protocol(e)
utils.may_add_vlan(p, self.interface.vlan_id)
p.add_protocol(a)
p.serialize()
return p
def __init__(self, *args, **kwargs):
super(RouterIPV4, self).__init__(*args, **kwargs)
assert not self.config.is_ipv6
self.garp_packets = [self._garp_packet(ip_address)
for ip_address in self.config.ip_addresses]
def _send_garp(self):
self.logger.debug('_send_garp')
for garp_packet in self.garp_packets:
self._transmit(garp_packet.data)
def _arp_reply_packet(self, arp_req_sha, arp_req_spa, arp_req_tpa):
if not (arp_req_tpa in self.config.ip_addresses or
arp_req_tpa == self.config.primary_ip_address):
return None
src_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
e = ethernet.ethernet(arp_req_sha, src_mac, ether.ETH_TYPE_ARP)
a = arp.arp_ip(arp.ARP_REPLY, src_mac, arp_req_tpa,
arp_req_sha, arp_req_spa)
p = packet.Packet()
p.add_protocol(e)
utils.may_add_vlan(p, self.interface.vlan_id)
p.add_protocol(a)
p.serialize()
self._transmit(p.data)
def _arp_process(self, data):
dst_mac = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
arp_sha = None
arp_spa = None
arp_tpa = None
p = packet.Packet(data)
for proto in p.protocols:
if isinstance(proto, ethernet.ethernet):
if proto.dst not in (mac_lib.BROADCAST_STR, dst_mac):
return None
ethertype = proto.ethertype
if not ((self.interface.vlan_id is None and
ethertype == ether.ETH_TYPE_ARP) or
(self.interface.vlan_id is not None and
ethertype == ether.ETH_TYPE_8021Q)):
return None
elif isinstance(proto, vlan.vlan):
if (proto.vid != self.interface.vlan_id or
proto.ethertype != ether.ETH_TYPE_ARP):
return None
elif isinstance(proto, arp.arp):
if (proto.hwtype != arp.ARP_HW_TYPE_ETHERNET or
proto.proto != ether.ETH_TYPE_IP or
proto.hlen != 6 or proto.plen != 4 or
proto.opcode != arp.ARP_REQUEST or
proto.dst_mac != dst_mac):
return None
arp_sha = proto.src_mac
arp_spa = proto.src_ip
arp_tpa = proto.dst_ip
break
if arp_sha is None or arp_spa is None or arp_tpa is None:
self.logger.debug('malformed arp request? arp_sha %s arp_spa %s',
arp_sha, arp_spa)
return None
self._arp_reply_packet(arp_sha, arp_spa, arp_tpa)
class RouterIPV4Linux(RouterIPV4):
def __init__(self, *args, **kwargs):
super(RouterIPV4Linux, self).__init__(*args, **kwargs)
assert isinstance(self.interface,
vrrp_event.VRRPInterfaceNetworkDevice)
self.__is_master = False
self._arp_thread = None
def start(self):
self._disable_router()
super(RouterIPV4Linux, self).start()
def _initialized_to_master(self):
self.logger.debug('initialized to master')
self._master()
def _become_master(self):
self.logger.debug('become master')
self._master()
def _master(self):
self.__is_master = True
self._enable_router()
self._send_garp()
def _become_backup(self):
self.logger.debug('become backup')
self.__is_master = False
self._disable_router()
def _shutdowned(self):
# When VRRP functionality is disabled, what to do?
# should we also exit? or continue to route packets?
self._disable_router()
def _arp_loop_socket(self, packet_socket):
while True:
try:
buf = packet_socket.recv(1500)
except socket.timeout:
continue
self._arp_process(buf)
def _arp_loop(self):
try:
with contextlib.closing(
socket.socket(
socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(ether.ETH_TYPE_ARP))) as packet_socket:
packet_socket.bind((self.interface.device_name,
socket.htons(ether.ETH_TYPE_ARP),
socket.PACKET_BROADCAST,
arp.ARP_HW_TYPE_ETHERNET,
mac_lib.BROADCAST))
self._arp_loop_socket(packet_socket)
except greenlet.GreenletExit:
# suppress thread.kill exception
pass
def _enable_router(self):
if self._arp_thread is None:
self._arp_thread = hub.spawn(self._arp_loop)
# TODO: implement real routing logic
self.logger.debug('TODO:_enable_router')
def _disable_router(self):
if self._arp_thread is not None:
self._arp_thread.kill()
hub.joinall([self._arp_thread])
self._arp_thread = None
# TODO: implement real routing logic
self.logger.debug('TODO:_disable_router')
class RouterIPV4OpenFlow(RouterIPV4):
OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION]
# it must be that
# _DROP_PRIORITY < monitor.VRRPInterfaceMonitorOpenFlow._PRIORITY or
# _DROP_TABLE > monitor.VRRPInterfaceMonitorOpenFlow._TABLE
# to gurantee that VRRP packets are send to controller
_DROP_TABLE = 0
_DROP_PRIORITY = 0x8000 / 2
# it must be that
# _ARP_PRIORITY < _DROP_PRIORITY or
# _ARP_TABLE > _DROP_TABLE
# to gurantee that responding arp can be disabled
_ARP_TABLE = 0
_ARP_PRIORITY = _DROP_PRIORITY / 2
# it must be that
# _ROUTEING_TABLE < _ARP_TABLE or
# _ROUTING_TABLE > _ARP_TABLE
# to gurantee that routing can be disabled
_ROUTING_TABLE = 0
_ROUTING_PRIORITY = _ARP_PRIORITY / 2
def __init__(self, *args, **kwargs):
super(RouterIPV4OpenFlow, self).__init__(*args, **kwargs)
assert isinstance(self.interface, vrrp_event.VRRPInterfaceOpenFlow)
def _get_dp(self):
return utils.get_dp(self, self.interface.dpid)
def start(self):
dp = self._get_dp()
assert dp
self._uninstall_route_rule(dp)
self._uninstall_arp_rule(dp)
self._uninstall_drop_rule(dp)
self._install_drop_rule(dp)
self._install_arp_rule(dp)
self._install_route_rule(dp)
super(RouterIPV4OpenFlow, self).start()
def _initialized_to_master(self):
self.logger.debug('initialized to master')
self._master()
def _become_master(self):
self.logger.debug('become master')
self._master()
def _master(self):
dp = self._get_dp()
if dp is None:
return
self._uninstall_drop_rule(dp)
self._send_garp(dp)
def _become_backup(self):
self.logger.debug('become backup')
dp = self._get_dp()
if dp is None:
return
self._install_drop_rule(dp)
def _shutdowned(self):
dp = self._get_dp()
if dp is None:
return
# When VRRP functionality is disabled, what to do?
# should we also exit? or continue to route packets?
self._uninstall_route_rule(dp)
self._uninstall_arp_rule(dp)
self._uninstall_drop_rule(dp)
@handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
# TODO: subscribe only the datapath that we route
dpid = datapath.dpid
if dpid != self.interface.dpid:
return
for field in msg.match.fields:
header = field.header
if header == ofproto.OXM_OF_IN_PORT:
if field.value != self.interface.port_no:
return
break
self._arp_process(msg.data)
def _drop_match(self, dp):
kwargs = {}
kwargs['in_port'] = self.interface.port_no
kwargs['eth_dst'] = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
if self.interface.vlan_id is not None:
kwargs['vlan_vid'] = self.interface.vlan_id
return dp.ofproto_parser.OFPMatch(**kwargs)
def _install_drop_rule(self, dp):
match = self._drop_match(dp)
utils.dp_flow_mod(dp, self._DROP_TABLE, dp.ofproto.OFPFC_ADD,
self._DROP_PRIORITY, match, [])
def _uninstall_drop_rule(self, dp):
match = self._drop_match(dp)
utils.dp_flow_mod(dp, self._DROP_TABLE, dp.ofproto.OFPFC_DELETE_STRICT,
self._DROP_PRIORITY, match, [])
def _arp_match(self, dp):
kwargs = {}
kwargs['in_port'] = self.interface.port_no
kwargs['eth_dst'] = mac_lib.BROADCAST_STR
kwargs['eth_type'] = ether.ETH_TYPE_ARP
if self.interface.vlan_id is not None:
kwargs['vlan_vid'] = self.interface.vlan_id
kwargs['arp_op'] = arp.ARP_REQUEST
kwargs['arp_tpa'] = vrrp.vrrp_ipv4_src_mac_address(self.config.vrid)
return dp.ofproto_parser.OFPMatch(**kwargs)
def _install_arp_rule(self, dp):
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
match = self._arp_match(dp)
actions = [ofproto_parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
instructions = [ofproto_parser.OFPInstructionActions(
ofproto.OFPIT_APPLY_ACTIONS, actions)]
utils.dp_flow_mod(dp, self._ARP_TABLE, dp.fproto.OFPFC_ADD,
self._ARP_PRIORITY, match, instructions)
def _uninstall_arp_rule(self, dp):
match = self._arp_match(dp)
utils.dp_flow_mod(dp, self._ARP_TABLE, dp.fproto.OFPFC_DELETE_STRICT,
self._ARP_PRIORITY, match, [])
def _install_route_rule(self, dp):
# TODO: implement real routing logic
self.logger.debug('TODO:_install_router_rule')
def _uninstall_route_rule(self, dp):
# TODO: implement real routing logic
self.logger.debug('TODO:_uninstall_router_rule')
class RouterIPV6(RouterBase):
def __init__(self, *args, **kwargs):
super(RouterIPV6, self).__init__(*args, **kwargs)
assert self.config.is_ipv6
class RouterIPV6Linux(RouterIPV6):
def __init__(self, *args, **kwargs):
super(RouterIPV6Linux, self).__init__(*args, **kwargs)
assert isinstance(self.interface,
vrrp_event.VRRPInterfaceNetworkDevice)
# TODO: reader's home work
pass
class RouterIPV6OpenFlow(RouterIPV6):
def __init__(self, *args, **kwargs):
super(RouterIPV6OpenFlow, self).__init__(*args, **kwargs)
assert isinstance(self.interface, vrrp_event.VRRPInterfaceOpenFlow)
# TODO: reader's home work
pass

View File

@ -0,0 +1,76 @@
# 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.
from ryu.lib.packet import ethernet
from ryu.lib.packet import vlan
from ryu.ofproto import ether
from ryu.topology import api as topo_api
def may_add_vlan(packet, vlan_id):
"""
:type packet: ryu.lib.packet.packet.Packet
:param packet:
:type vlan_id: int (0 <= vlan_id <= 4095) or None (= No VLAN)
:param vlan_id:
"""
if vlan_id is None:
return
e = packet.protocols[0]
assert isinstance(e, ethernet.ethernet)
v = vlan.vlan(0, 0, vlan_id, e.ethertype)
e.ethertype = ether.ETH_TYPE_8021Q
packet.add_protocol(v)
def get_dp(app, dpid):
"""
:type dpid: datapath id
:param dpid:
:rtype: ryu.controller.controller.Datapatyh
:returns: datapath corresponding to dpid
"""
switches = topo_api.get_switch(app, dpid)
if not switches:
return None
assert len(switches) == 1
return switches[0].dp
def dp_packet_out(dp, port_no, data):
# OF 1.2
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
actions = [ofproto_parser.OFPActionOutput(port_no,
ofproto.OFPCML_NO_BUFFER)]
packet_out = ofproto_parser.OFPPacketOut(
dp, 0xffffffff, ofproto.OFPP_CONTROLLER, actions, data)
dp.send_msg(packet_out)
def dp_flow_mod(dp, table, command, priority, match, instructions,
out_port=None):
# OF 1.2
ofproto = dp.ofproto
ofproto_parser = dp.ofproto_parser
if out_port is None:
out_port = ofproto.OFPP_ANY
flow_mod = ofproto_parser.OFPFlowMod(
dp, 0, 0, table, command, 0, 0,
priority, 0xffffffff, out_port, ofproto.OFPG_ANY,
ofproto.OFPFF_CHECK_OVERLAP, match, instructions)
dp.send_msg(flow_mod)