mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-05 12:26:11 +02:00
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:
parent
1352d0c667
commit
f430528929
@ -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):
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user