summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2014-11-09 19:26:18 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2014-11-10 09:31:40 +0900
commitf4305289295477e4d1d35f71af45d3b97fe9115d (patch)
treeba4820db47595fce31bce9525493b03008ff015c
parent1352d0c66744516f2275c960e692be01ff80ee5a (diff)
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>
-rw-r--r--ryu/services/protocols/bgp/api/rtconf.py9
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py15
-rw-r--r--ryu/services/protocols/bgp/core.py15
-rw-r--r--ryu/services/protocols/bgp/peer.py28
-rw-r--r--ryu/services/protocols/bgp/rtconf/neighbors.py42
5 files changed, 98 insertions, 11 deletions
diff --git a/ryu/services/protocols/bgp/api/rtconf.py b/ryu/services/protocols/bgp/api/rtconf.py
index 079a2f52..74a12c95 100644
--- a/ryu/services/protocols/bgp/api/rtconf.py
+++ b/ryu/services/protocols/bgp/api/rtconf.py
@@ -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):
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 19ed528a..87fdfe54 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -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}
diff --git a/ryu/services/protocols/bgp/core.py b/ryu/services/protocols/bgp/core.py
index 8552a803..37007ecb 100644
--- a/ryu/services/protocols/bgp/core.py
+++ b/ryu/services/protocols/bgp/core.py
@@ -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
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 7c20f6d5..eac098c0 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -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
@@ -426,6 +427,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
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 if CONNECT_MODE is not PASSIVE
+ self._on_update_connect_mode(self._neigh_conf.connect_mode)
- # Tries actively to establish session.
- self._connect_loop(client_factory)
+ # Start sink processing
+ self._process_outgoing_msg_list()
def _send_outgoing_route_refresh_msg(self, rr_msg):
"""Sends given message `rr_msg` to peer.
diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py
index f5eb9b11..29a000f5 100644
--- a/ryu/services/protocols/bgp/rtconf/neighbors.py
+++ b/ryu/services/protocols/bgp/rtconf/neighbors.py
@@ -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."""