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.wat...@lab.ntt.co.jp>
---
 ryu/services/protocols/bgp/api/rtconf.py       |    9 +++++
 ryu/services/protocols/bgp/bgpspeaker.py       |   15 +++++++--
 ryu/services/protocols/bgp/core.py             |   15 +++++++--
 ryu/services/protocols/bgp/peer.py             |   28 +++++++++++++---
 ryu/services/protocols/bgp/rtconf/neighbors.py |   42 ++++++++++++++++++++++--
 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 079a2f5..74a12c9 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 19ed528..87fdfe5 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 8552a80..37007ec 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 5f78b3a..7d452b7 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
@@ -417,6 +418,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
 
@@ -529,6 +534,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
@@ -615,11 +633,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 f5eb9b1..29a000f 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."""
-- 
1.7.10.4


------------------------------------------------------------------------------
_______________________________________________
Ryu-devel mailing list
Ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to