diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2014-11-09 19:26:18 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2014-11-10 09:31:40 +0900 |
commit | f4305289295477e4d1d35f71af45d3b97fe9115d (patch) | |
tree | ba4820db47595fce31bce9525493b03008ff015c | |
parent | 1352d0c66744516f2275c960e692be01ff80ee5a (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.py | 9 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgpspeaker.py | 15 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/core.py | 15 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/peer.py | 28 | ||||
-rw-r--r-- | 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 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.""" |