bgp: support connect modes to choose how to connect to the neighbors

three connect modes are supported

    CONNECT_MODE_ACTIVE: try to connect from us. don't listen
    CONNECT_MODE_PASSIVE: just listen
    CONNECT_MODE_BOTH: try both methods

dynamic change of connect modes is also supported

Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
ISHIDA Wataru 2014-11-09 19:26:18 +09:00 committed by FUJITA Tomonori
parent 1352d0c667
commit f430528929
5 changed files with 98 additions and 11 deletions

View File

@ -79,6 +79,9 @@ def update_neighbor(neigh_ip_address, changes):
if k == neighbors.ENABLED:
rets.append(update_neighbor_enabled(neigh_ip_address, v))
if k == neighbors.CONNECT_MODE:
rets.append(_update_connect_mode(neigh_ip_address, v))
return all(rets)
@ -89,6 +92,12 @@ def _update_med(neigh_ip_address, value):
return True
def _update_connect_mode(neigh_ip_address, value):
neigh_conf = _get_neighbor_conf(neigh_ip_address)
neigh_conf.connect_mode = value
return True
@RegisterWithArgChecks(name='neighbor.delete',
req_args=[neighbors.IP_ADDRESS])
def delete_neighbor(neigh_ip_address):

View File

@ -52,12 +52,14 @@ from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_IPV4
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV4
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV6
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CONNECT_MODE
from ryu.services.protocols.bgp.rtconf.neighbors import PEER_NEXT_HOP
from ryu.services.protocols.bgp.rtconf.neighbors import PASSWORD
from ryu.services.protocols.bgp.rtconf.neighbors import IN_FILTER
from ryu.services.protocols.bgp.rtconf.neighbors import OUT_FILTER
from ryu.services.protocols.bgp.rtconf.neighbors import IS_ROUTE_SERVER_CLIENT
from ryu.services.protocols.bgp.rtconf.neighbors import IS_NEXT_HOP_SELF
from ryu.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE
from ryu.services.protocols.bgp.rtconf.neighbors import LOCAL_ADDRESS
from ryu.services.protocols.bgp.rtconf.neighbors import LOCAL_PORT
from ryu.services.protocols.bgp.info_base.base import Filter
@ -205,7 +207,7 @@ class BGPSpeaker(object):
next_hop=None, password=None, multi_exit_disc=None,
site_of_origins=None, is_route_server_client=False,
is_next_hop_self=False, local_address=None,
local_port=None):
local_port=None, connect_mode=DEFAULT_CONNECT_MODE):
""" This method registers a new neighbor. The BGP speaker tries to
establish a bgp session with the peer (accepts a connection
from the peer and also tries to connect to it).
@ -245,6 +247,12 @@ class BGPSpeaker(object):
``is_next_hop_self`` specifies whether the BGP speaker announces
its own ip address to iBGP neighbor or not as path's next_hop address.
``connect_mode`` specifies how to connect to this neighbor.
CONNECT_MODE_ACTIVE tries to connect from us.
CONNECT_MODE_PASSIVE just listens and wait for the connection.
CONNECT_MODE_BOTH use both methods.
The default is CONNECT_MODE_BOTH
``local_address`` specifies Loopback interface address for
iBGP peering.
@ -258,6 +266,7 @@ class BGPSpeaker(object):
bgp_neighbor[PASSWORD] = password
bgp_neighbor[IS_ROUTE_SERVER_CLIENT] = is_route_server_client
bgp_neighbor[IS_NEXT_HOP_SELF] = is_next_hop_self
bgp_neighbor[CONNECT_MODE] = connect_mode
# v6 advertizement is available with only v6 peering
if netaddr.valid_ipv4(address):
bgp_neighbor[CAP_MBGP_IPV4] = enable_ipv4
@ -321,12 +330,14 @@ class BGPSpeaker(object):
"""
assert conf_type == NEIGHBOR_CONF_MED
assert conf_type == NEIGHBOR_CONF_MED or conf_type == CONNECT_MODE
func_name = 'neighbor.update'
attribute_param = {}
if conf_type == NEIGHBOR_CONF_MED:
attribute_param = {neighbors.MULTI_EXIT_DISC: conf_value}
elif conf_type == CONNECT_MODE:
attribute_param = {neighbors.CONNECT_MODE: conf_value}
param = {neighbors.IP_ADDRESS: address,
neighbors.CHANGES: attribute_param}

View File

@ -40,6 +40,7 @@ from ryu.services.protocols.bgp.protocol import Factory
from ryu.services.protocols.bgp.signals.emit import BgpSignalBus
from ryu.services.protocols.bgp.speaker import BgpProtocol
from ryu.services.protocols.bgp.utils.rtfilter import RouteTargetManager
from ryu.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE
from ryu.services.protocols.bgp.utils import stats
from ryu.services.protocols.bgp.bmp import BMPClient
from ryu.lib import sockopt
@ -408,6 +409,7 @@ class CoreService(Factory, Activity):
assert socket
# Check if its a reactive connection or pro-active connection
_, remote_port = self.get_remotename(socket)
remote_port = int(remote_port)
is_reactive_conn = True
if remote_port == STD_BGP_SERVER_PORT_NUM:
is_reactive_conn = False
@ -435,8 +437,17 @@ class CoreService(Factory, Activity):
# configuration.
# 2) If this neighbor is not enabled according to configuration.
if not peer or not peer.enabled:
LOG.debug('Closed connection to %s:%s as it is not a recognized'
' peer.' % (peer_addr, peer_port))
LOG.debug('Closed connection %s %s:%s as it is not a recognized'
' peer.' % ('from' if bgp_proto.is_reactive else 'to',
peer_addr, peer_port))
# Send connection rejected notification as per RFC
code = BGP_ERROR_CEASE
subcode = BGP_ERROR_SUB_CONNECTION_RESET
bgp_proto.send_notification(code, subcode)
elif bgp_proto.is_reactive and \
peer.connect_mode is CONNECT_MODE_ACTIVE:
LOG.debug('Closed connection from %s:%s as connect_mode is'
' configured ACTIVE.' % (peer_addr, peer_port))
# Send connection rejected notification as per RFC
code = BGP_ERROR_CEASE
subcode = BGP_ERROR_SUB_CONNECTION_RESET

View File

@ -34,6 +34,7 @@ from ryu.services.protocols.bgp.info_base.base import AttributeMap
from ryu.services.protocols.bgp.model import ReceivedRoute
from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER
from ryu.services.protocols.bgp.rtconf.neighbors import NeighborConfListener
from ryu.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_PASSIVE
from ryu.services.protocols.bgp.signals.emit import BgpSignalBus
from ryu.services.protocols.bgp.speaker import BgpProtocol
from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path
@ -425,6 +426,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
def check_first_as(self):
return self._neigh_conf.check_first_as
@property
def connect_mode(self):
return self._neigh_conf.connect_mode
@property
def attribute_maps(self):
return self._attribute_maps
@ -538,6 +543,19 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
for af in negotiated_afs:
self._fire_route_refresh(af)
def _on_update_connect_mode(self, mode):
if mode is not CONNECT_MODE_PASSIVE and \
'peer.connect_loop' not in self._child_thread_map:
LOG.debug("start connect loop. (mode: %s)" % mode)
self._spawn('peer.connect_loop', self._connect_loop,
self._client_factory)
elif mode is CONNECT_MODE_PASSIVE:
LOG.debug("stop connect loop. (mode: %s)" % mode)
self._stop_child_threads('peer.connect_loop')
def on_update_connect_mode(self, conf_evt):
self._on_update_connect_mode(conf_evt.value)
def _apply_filter(self, filters, path):
block = False
blocked_cause = None
@ -624,11 +642,13 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
def _run(self, client_factory):
LOG.debug('Started peer %s' % self)
# Start sink processing in a separate thread
self._spawn('peer.process_outgoing', self._process_outgoing_msg_list)
self._client_factory = client_factory
# Tries actively to establish session.
self._connect_loop(client_factory)
# Tries actively to establish session if CONNECT_MODE is not PASSIVE
self._on_update_connect_mode(self._neigh_conf.connect_mode)
# Start sink processing
self._process_outgoing_msg_list()
def _send_outgoing_route_refresh_msg(self, rr_msg):
"""Sends given message `rr_msg` to peer.

View File

@ -82,6 +82,10 @@ IS_ROUTE_SERVER_CLIENT = 'is_route_server_client'
CHECK_FIRST_AS = 'check_first_as'
ATTRIBUTE_MAP = 'attribute_map'
IS_NEXT_HOP_SELF = 'is_next_hop_self'
CONNECT_MODE = 'connect_mode'
CONNECT_MODE_ACTIVE = 'active'
CONNECT_MODE_PASSIVE = 'passive'
CONNECT_MODE_BOTH = 'both'
# Default value constants.
DEFAULT_CAP_GR_NULL = True
@ -99,6 +103,7 @@ DEFAULT_OUT_FILTER = []
DEFAULT_IS_ROUTE_SERVER_CLIENT = False
DEFAULT_CHECK_FIRST_AS = False
DEFAULT_IS_NEXT_HOP_SELF = False
DEFAULT_CONNECT_MODE = CONNECT_MODE_BOTH
# Default value for *MAX_PREFIXES* setting is set to 0.
DEFAULT_MAX_PREFIXES = 0
@ -116,13 +121,15 @@ def validate_enabled(enabled):
@validate(name=CHANGES)
def validate_changes(changes):
for k, v in changes.iteritems():
if k not in (MULTI_EXIT_DISC, ENABLED):
if k not in (MULTI_EXIT_DISC, ENABLED, CONNECT_MODE):
raise ConfigValueError(desc="Unknown field to change: %s" % k)
if k == MULTI_EXIT_DISC:
validate_med(v)
elif k == ENABLED:
validate_enabled(v)
elif k == CONNECT_MODE:
validate_connect_mode(v)
return changes
@ -266,13 +273,24 @@ def validate_is_next_hop_self(is_next_hop_self):
return is_next_hop_self
@validate(name=CONNECT_MODE)
def validate_connect_mode(mode):
if mode not in (CONNECT_MODE_ACTIVE,
CONNECT_MODE_PASSIVE,
CONNECT_MODE_BOTH):
raise ConfigValueError(desc='Invalid connect_mode(%s)' % mode)
return mode
class NeighborConf(ConfWithId, ConfWithStats):
"""Class that encapsulates one neighbors' configuration."""
UPDATE_ENABLED_EVT = 'update_enabled_evt'
UPDATE_MED_EVT = 'update_med_evt'
UPDATE_CONNECT_MODE_EVT = 'update_connect_mode_evt'
VALID_EVT = frozenset([UPDATE_ENABLED_EVT, UPDATE_MED_EVT])
VALID_EVT = frozenset([UPDATE_ENABLED_EVT, UPDATE_MED_EVT,
UPDATE_CONNECT_MODE_EVT])
REQUIRED_SETTINGS = frozenset([REMOTE_AS, IP_ADDRESS])
OPTIONAL_SETTINGS = frozenset([CAP_REFRESH,
CAP_ENHANCED_REFRESH,
@ -285,7 +303,7 @@ class NeighborConf(ConfWithId, ConfWithStats):
PEER_NEXT_HOP, PASSWORD,
IN_FILTER, OUT_FILTER,
IS_ROUTE_SERVER_CLIENT, CHECK_FIRST_AS,
IS_NEXT_HOP_SELF])
IS_NEXT_HOP_SELF, CONNECT_MODE])
def __init__(self, **kwargs):
super(NeighborConf, self).__init__(**kwargs)
@ -323,6 +341,8 @@ class NeighborConf(ConfWithId, ConfWithStats):
self._settings[IS_NEXT_HOP_SELF] = compute_optional_conf(
IS_NEXT_HOP_SELF,
DEFAULT_IS_NEXT_HOP_SELF, **kwargs)
self._settings[CONNECT_MODE] = compute_optional_conf(
CONNECT_MODE, DEFAULT_CONNECT_MODE, **kwargs)
# We do not have valid default MED value.
# If no MED attribute is provided then we do not have to use MED.
@ -509,6 +529,15 @@ class NeighborConf(ConfWithId, ConfWithStats):
def is_next_hop_self(self):
return self._settings[IS_NEXT_HOP_SELF]
@property
def connect_mode(self):
return self._settings[CONNECT_MODE]
@connect_mode.setter
def connect_mode(self, mode):
self._settings[CONNECT_MODE] = mode
self._notify_listeners(NeighborConf.UPDATE_CONNECT_MODE_EVT, mode)
def exceeds_max_prefix_allowed(self, prefix_count):
allowed_max = self._settings[MAX_PREFIXES]
does_exceed = False
@ -652,14 +681,21 @@ class NeighborConfListener(ConfWithIdListener, ConfWithStatsListener):
self.on_update_enabled)
neigh_conf.add_listener(NeighborConf.UPDATE_MED_EVT,
self.on_update_med)
neigh_conf.add_listener(NeighborConf.UPDATE_CONNECT_MODE_EVT,
self.on_update_connect_mode)
@abstractmethod
def on_update_enabled(self, evt):
raise NotImplementedError('This method should be overridden.')
@abstractmethod
def on_update_med(self, evt):
raise NotImplementedError('This method should be overridden.')
@abstractmethod
def on_update_connect_mode(self, evt):
raise NotImplementedError('This method should be overridden.')
class NeighborsConfListener(BaseConfListener):
"""Base listener for change events to neighbor configuration container."""