1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
17 This module provides a convenient application for using Ryu BGPSpeaker and for
18 writing your BGP application.
20 It reads a configuration file which includes settings for neighbors, routes
22 Please refer to ``ryu/services/protocols/bgp/bgp_sample_conf.py`` for the
27 $ ryu-manager ryu/services/protocols/bgp/application.py \\
28 --bgp-app-config-file ryu/services/protocols/bgp/bgp_sample_conf.py
33 You can also use the SSH console and see the RIB and do some operations from
35 The SSH port and username/password can be configured by the configuration file.
36 You can check the help by hitting '?' key in this interface.
40 $ ssh localhost -p 4990
42 Hello, this is Ryu BGP speaker (version 4.19).
45 clear - allows to reset BGP connections
47 quit - exit this session
48 set - set runtime settings
49 show - shows runtime state information
52 Status codes: * valid, > best
53 Origin codes: i - IGP, e - EGP, ? - incomplete
54 Network Labels Next Hop Reason Metric LocPrf Path
55 *> 10.10.1.0/24 None 0.0.0.0 Only Path i
58 Integration with Other Applications
59 ===================================
61 ``ryu.services.protocols.bgp.application.RyuBGPSpeaker`` will notifies the
62 following events to other Ryu applications.
64 - ``EventBestPathChanged``
65 - ``EventAdjRibInChanged``
69 To catch these events, specify ``@set_ev_cls()`` decorator to the event
70 handlers in the Ryu applications.
76 from ryu.base import app_manager
77 from ryu.controller.handler import set_ev_cls
78 from ryu.services.protocols.bgp import application as bgp_application
81 class MyBGPApp(app_manager.RyuApp):
83 'ryubgpspeaker': bgp_application.RyuBGPSpeaker,
86 def __init__(self, *args, **kwargs):
87 super(MyBGPApp, self).__init__(*args, **kwargs)
89 # Stores "ryu.services.protocols.bgp.application.RyuBGPSpeaker"
90 # instance in order to call the APIs of
91 # "ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker" via
93 # Please note at this time, "BGPSpeaker" is NOT instantiated yet.
94 self.app = kwargs['ryubgpspeaker']
96 @set_ev_cls(bgp_application.EventBestPathChanged)
97 def _best_patch_changed_handler(self, ev):
99 'Best path changed: is_withdraw=%s, path=%s',
100 ev.is_withdraw, ev.path)
104 $ ryu-manager my_bgp_app.py \\
105 --bgp-app-config-file ryu/services/protocols/bgp/bgp_sample_conf.py
109 For the APIs for ``ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker``,
110 please refer to :doc:`../library_bgp_speaker_ref`.
120 from ryu.lib import hub
121 from ryu.utils import load_source
122 from ryu.base.app_manager import RyuApp
123 from ryu.controller.event import EventBase
124 from ryu.services.protocols.bgp.base import add_bgp_error_metadata
125 from ryu.services.protocols.bgp.base import BGPSException
126 from ryu.services.protocols.bgp.base import BIN_ERROR
127 from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
128 from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER
129 from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_IP
130 from ryu.services.protocols.bgp.net_ctrl import NC_RPC_BIND_PORT
131 from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
132 from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
133 from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
134 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
135 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
138 LOG = logging.getLogger('bgpspeaker.application')
140 CONF = cfg.CONF['bgp-app']
143 @add_bgp_error_metadata(code=BIN_ERROR,
145 def_desc='Unknown bootstrap exception.')
146 class ApplicationException(BGPSException):
148 Specific Base exception related to `BSPSpeaker`.
153 def validate_rpc_host(ip):
155 Validates the given ip for use as RPC server address.
157 if not is_valid_ipv4(ip) and not is_valid_ipv6(ip):
158 raise ApplicationException(
159 desc='Invalid RPC ip address: %s' % ip)
163 def load_config(config_file):
165 Validates the given file for use as the settings file for BGPSpeaker
166 and loads the configuration from the given file as a module instance.
168 if not config_file or not os.path.isfile(config_file):
169 raise ApplicationException(
170 desc='Invalid configuration file: %s' % config_file)
172 # Loads the configuration from the given file, if available.
174 return load_source('bgpspeaker.application.settings', config_file)
175 except Exception as e:
176 raise ApplicationException(desc=str(e))
179 class EventBestPathChanged(EventBase):
181 Event called when any best remote path is changed due to UPDATE messages
182 or remote peer's down.
184 This event is the wrapper for ``best_path_change_handler`` of
185 ``bgpspeaker.BGPSpeaker``.
187 ``path`` attribute contains an instance of ``info_base.base.Path``
190 If ``is_withdraw`` attribute is ``True``, ``path`` attribute has the
191 information of the withdraw route.
194 def __init__(self, path, is_withdraw):
195 super(EventBestPathChanged, self).__init__()
197 self.is_withdraw = is_withdraw
200 class EventAdjRibInChanged(EventBase):
202 Event called when any adj-RIB-in path is changed due to UPDATE messages
203 or remote peer's down.
205 This event is the wrapper for ``adj_rib_in_change_handler`` of
206 ``bgpspeaker.BGPSpeaker``.
208 ``path`` attribute contains an instance of ``info_base.base.Path``
211 If ``is_withdraw`` attribute is ``True``, ``path`` attribute has the
212 information of the withdraw route.
214 ``peer_ip`` is the peer's IP address who sent this path.
216 ``peer_as`` is the peer's AS number who sent this path.
219 def __init__(self, path, is_withdraw, peer_ip, peer_as):
220 super(EventAdjRibInChanged, self).__init__()
222 self.is_withdraw = is_withdraw
223 self.peer_ip = peer_ip
224 self.peer_as = peer_as
227 class EventPeerDown(EventBase):
229 Event called when the session to the remote peer goes down.
231 This event is the wrapper for ``peer_down_handler`` of
232 ``bgpspeaker.BGPSpeaker``.
234 ``remote_ip`` attribute is the IP address of the remote peer.
236 ``remote_as`` attribute is the AS number of the remote peer.
239 def __init__(self, remote_ip, remote_as):
240 super(EventPeerDown, self).__init__()
241 self.remote_ip = remote_ip
242 self.remote_as = remote_as
245 class EventPeerUp(EventBase):
247 Event called when the session to the remote peer goes up.
249 This event is the wrapper for ``peer_up_handler`` of
250 ``bgpspeaker.BGPSpeaker``.
252 ``remote_ip`` attribute is the IP address of the remote peer.
254 ``remote_as`` attribute is the AS number of the remote peer.
257 def __init__(self, remote_ip, remote_as):
258 super(EventPeerUp, self).__init__()
259 self.remote_ip = remote_ip
260 self.remote_as = remote_as
263 class RyuBGPSpeaker(RyuApp):
265 Base application for implementing BGP applications.
268 EventBestPathChanged,
269 EventAdjRibInChanged,
274 def __init__(self, *args, **kwargs):
275 super(RyuBGPSpeaker, self).__init__(*args, **kwargs)
276 self.config_file = CONF.config_file
278 # BGPSpeaker instance (not instantiated yet)
282 super(RyuBGPSpeaker, self).start()
284 # If configuration file was provided and loaded successfully, we start
285 # BGPSpeaker using the given settings.
286 # If no configuration file is provided or if any minimum required
287 # setting is missing, BGPSpeaker will not be started.
289 LOG.debug('Loading config file %s...', self.config_file)
290 settings = load_config(self.config_file)
292 # Configure logging settings, if available.
293 if hasattr(settings, 'LOGGING'):
294 # Not implemented yet.
295 LOG.debug('Loading LOGGING settings... (NOT implemented yet)')
296 # from logging.config import dictConfig
297 # logging_settings = dictConfig(settings.LOGGING)
299 # Configure BGP settings, if available.
300 if hasattr(settings, 'BGP'):
301 LOG.debug('Loading BGP settings...')
302 self._start_speaker(settings.BGP)
304 # Configure SSH settings, if available.
305 if hasattr(settings, 'SSH'):
306 LOG.debug('Loading SSH settings...')
307 # Note: paramiko used in bgp.operator.ssh is the optional
308 # requirements, imports bgp.operator.ssh here.
309 from ryu.services.protocols.bgp.operator import ssh
310 hub.spawn(ssh.SSH_CLI_CONTROLLER.start, **settings.SSH)
312 # Start RPC server with the given RPC settings.
314 NC_RPC_BIND_PORT: CONF.rpc_port,
315 NC_RPC_BIND_IP: validate_rpc_host(CONF.rpc_host),
317 return hub.spawn(NET_CONTROLLER.start, **rpc_settings)
319 def _start_speaker(self, settings):
321 Starts BGPSpeaker using the given settings.
323 # Check required settings.
324 _required_settings = (
328 for required in _required_settings:
329 if required not in settings:
330 raise ApplicationException(
331 desc='Required BGP configuration missing: %s' % required)
333 # Set event notify handlers if no corresponding handler specified.
335 'best_path_change_handler', self._notify_best_path_changed_event)
337 'adj_rib_in_change_handler', self._notify_adj_rib_in_changed_event)
339 'peer_down_handler', self._notify_peer_down_event)
341 'peer_up_handler', self._notify_peer_up_event)
343 # Pop settings other than creating BGPSpeaker instance.
344 neighbors_settings = settings.pop('neighbors', [])
345 vrfs_settings = settings.pop('vrfs', [])
346 routes_settings = settings.pop('routes', [])
348 # Create BGPSpeaker instance.
349 LOG.debug('Starting BGPSpeaker...')
350 settings.setdefault('as_number', settings.pop(LOCAL_AS))
351 self.speaker = BGPSpeaker(**settings)
354 LOG.debug('Adding neighbors...')
355 self._add_neighbors(neighbors_settings)
358 LOG.debug('Adding VRFs...')
359 self._add_vrfs(vrfs_settings)
362 LOG.debug('Adding routes...')
363 self._add_routes(routes_settings)
365 def _notify_best_path_changed_event(self, ev):
366 ev = EventBestPathChanged(ev.path, ev.is_withdraw)
367 self.send_event_to_observers(ev)
369 def _notify_adj_rib_in_changed_event(self, ev, peer_ip, peer_as):
370 ev = EventAdjRibInChanged(ev.path, ev.is_withdraw, peer_ip, peer_as)
371 self.send_event_to_observers(ev)
373 def _notify_peer_down_event(self, remote_ip, remote_as):
374 ev = EventPeerDown(remote_ip, remote_as)
375 self.send_event_to_observers(ev)
377 def _notify_peer_up_event(self, remote_ip, remote_as):
378 ev = EventPeerUp(remote_ip, remote_as)
379 self.send_event_to_observers(ev)
381 def _add_neighbors(self, settings):
383 Add BGP neighbors from the given settings.
385 All valid neighbors are loaded.
386 Miss-configured neighbors are ignored and errors are logged.
388 for neighbor_settings in settings:
389 LOG.debug('Adding neighbor settings: %s', neighbor_settings)
391 self.speaker.neighbor_add(**neighbor_settings)
392 except RuntimeConfigError as e:
395 def _add_vrfs(self, settings):
397 Add BGP VRFs from the given settings.
399 All valid VRFs are loaded.
400 Miss-configured VRFs are ignored and errors are logged.
402 for vrf_settings in settings:
403 LOG.debug('Adding VRF settings: %s', vrf_settings)
405 self.speaker.vrf_add(**vrf_settings)
406 except RuntimeConfigError as e:
409 def _add_routes(self, settings):
411 Add BGP routes from given settings.
413 All valid routes are loaded.
414 Miss-configured routes are ignored and errors are logged.
416 for route_settings in settings:
417 if 'prefix' in route_settings:
418 prefix_add = self.speaker.prefix_add
419 elif 'route_type' in route_settings:
420 prefix_add = self.speaker.evpn_prefix_add
421 elif 'flowspec_family' in route_settings:
422 prefix_add = self.speaker.flowspec_prefix_add
424 LOG.debug('Skip invalid route settings: %s', route_settings)
427 LOG.debug('Adding route settings: %s', route_settings)
429 prefix_add(**route_settings)
430 except RuntimeConfigError as e: