summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/source/library.rst2
-rw-r--r--doc/source/library_bgp_speaker.rst44
-rw-r--r--doc/source/library_bgp_speaker_ref.rst12
-rw-r--r--ryu/services/protocols/bgp/api/rtconf.py7
-rw-r--r--ryu/services/protocols/bgp/application.py10
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py287
-rw-r--r--ryu/services/protocols/bgp/core_managers/table_manager.py6
-rw-r--r--ryu/services/protocols/bgp/info_base/ipv4.py2
-rw-r--r--ryu/services/protocols/bgp/peer.py12
-rw-r--r--ryu/services/protocols/bgp/signals/emit.py6
10 files changed, 375 insertions, 13 deletions
diff --git a/doc/source/library.rst b/doc/source/library.rst
index eee6877a..38cc3872 100644
--- a/doc/source/library.rst
+++ b/doc/source/library.rst
@@ -10,3 +10,5 @@ Ryu provides some useful library for your network applications.
library_packet.rst
library_packet_ref.rst
library_of_config.rst
+ library_bgp_speaker.rst
+ library_bgp_speaker_ref.rst
diff --git a/doc/source/library_bgp_speaker.rst b/doc/source/library_bgp_speaker.rst
new file mode 100644
index 00000000..443242cf
--- /dev/null
+++ b/doc/source/library_bgp_speaker.rst
@@ -0,0 +1,44 @@
+*******************
+BGP speaker library
+*******************
+
+Introduction
+============
+
+Ryu BGP speaker library helps you to enable your code to speak BGP
+protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
+families.
+
+Example
+=======
+
+The following simple code creates a BGP instance with AS number 64512
+and Router ID 10.0.0.1. It tries to establish a bgp session with a
+peer (its IP is 192.168.177.32 and the AS number is 64513). The
+instance advertizes some prefixes.
+
+.. code-block:: python
+
+ import eventlet
+ from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker
+
+ def dump_remote_best_path_change(event):
+ print 'the best path changed:', event.remote_as, event.prefix,\
+ event.nexthop, event.is_withdraw
+
+ if __name__ == "__main__":
+ speaker = BGPSpeaker(as_number=64512, router_id='10.0.0.1',
+ best_path_change_handler=dump_remote_best_path_change)
+
+ speaker.neighbor_add('192.168.177.32', 64513)
+
+ count = 1
+ while True:
+ eventlet.sleep(30)
+ prefix = '10.20.' + str(count) + '.0/24'
+ print "add a new prefix", prefix
+ speaker.prefix_add(prefix)
+ count += 1
+ if count == 4:
+ speaker.shutdown()
+ break
diff --git a/doc/source/library_bgp_speaker_ref.rst b/doc/source/library_bgp_speaker_ref.rst
new file mode 100644
index 00000000..493f322e
--- /dev/null
+++ b/doc/source/library_bgp_speaker_ref.rst
@@ -0,0 +1,12 @@
+*********************************
+BGP speaker library API Reference
+*********************************
+
+BGPSpeaker class
+================
+
+.. autoclass:: ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker
+ :members:
+
+.. autoclass:: ryu.services.protocols.bgp.bgpspeaker.EventPrefix
+ :members:
diff --git a/ryu/services/protocols/bgp/api/rtconf.py b/ryu/services/protocols/bgp/api/rtconf.py
index bca6cc78..2869da5e 100644
--- a/ryu/services/protocols/bgp/api/rtconf.py
+++ b/ryu/services/protocols/bgp/api/rtconf.py
@@ -178,3 +178,10 @@ def add_network(prefix):
tm = CORE_MANAGER.get_core_service().table_manager
tm.add_to_ipv4_global_table(prefix)
return True
+
+
+@register(name='network.del')
+def del_network(prefix):
+ tm = CORE_MANAGER.get_core_service().table_manager
+ tm.add_to_ipv4_global_table(prefix, is_withdraw=True)
+ return True
diff --git a/ryu/services/protocols/bgp/application.py b/ryu/services/protocols/bgp/application.py
index 163e75f4..b803d369 100644
--- a/ryu/services/protocols/bgp/application.py
+++ b/ryu/services/protocols/bgp/application.py
@@ -67,12 +67,12 @@ class ApplicationException(BGPSException):
pass
-class BGPSpeaker(RyuApp):
+class RyuBGPSpeaker(RyuApp):
def __init__(self, *args, **kwargs):
- self.bind_ip = BGPSpeaker.validate_rpc_ip(CONF.bind_ip)
- self.bind_port = BGPSpeaker.validate_rpc_port(CONF.bind_port)
+ self.bind_ip = RyuBGPSpeaker.validate_rpc_ip(CONF.bind_ip)
+ self.bind_port = RyuBGPSpeaker.validate_rpc_port(CONF.bind_port)
self.config_file = CONF.bgp_config_file
- super(BGPSpeaker, self).__init__(*args, **kwargs)
+ super(RyuBGPSpeaker, self).__init__(*args, **kwargs)
def start(self):
# Only two main green threads are required for APGW bgp-agent.
@@ -98,7 +98,7 @@ class BGPSpeaker(RyuApp):
net_ctrl.NC_RPC_BIND_PORT: self.bind_port})
LOG.debug('Started Network Controller')
- super(BGPSpeaker, self).start()
+ super(RyuBGPSpeaker, self).start()
return t
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
new file mode 100644
index 00000000..b0726004
--- /dev/null
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -0,0 +1,287 @@
+# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""This module offers a class to enable your code to speak BGP protocol.
+
+"""
+
+from ryu.lib import hub
+from ryu.services.protocols.bgp.core_manager import CORE_MANAGER
+from ryu.services.protocols.bgp.signals.emit import BgpSignalBus
+from ryu.services.protocols.bgp.api.base import call
+from ryu.services.protocols.bgp.api.base import PREFIX
+from ryu.services.protocols.bgp.api.base import NEXT_HOP
+from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
+from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
+from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
+from ryu.services.protocols.bgp.rtconf.common import BGP_SERVER_PORT
+from ryu.services.protocols.bgp.rtconf.common import DEFAULT_BGP_SERVER_PORT
+from ryu.services.protocols.bgp.rtconf.common \
+ import DEFAULT_REFRESH_MAX_EOR_TIME
+from ryu.services.protocols.bgp.rtconf.common \
+ import DEFAULT_REFRESH_STALEPATH_TIME
+from ryu.services.protocols.bgp.rtconf.common \
+ import DEFAULT_BGP_CONN_RETRY_TIME
+from ryu.services.protocols.bgp.rtconf.common import DEFAULT_LABEL_RANGE
+from ryu.services.protocols.bgp.rtconf.common import REFRESH_MAX_EOR_TIME
+from ryu.services.protocols.bgp.rtconf.common import REFRESH_STALEPATH_TIME
+from ryu.services.protocols.bgp.rtconf.common import LABEL_RANGE
+from ryu.services.protocols.bgp.rtconf import neighbors
+from ryu.services.protocols.bgp.rtconf import vrfs
+from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4
+from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4
+from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6
+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.application import RyuBGPSpeaker
+
+
+class EventPrefix(object):
+ """
+ Used to pass an update on any best remote path to
+ best_path_change_handler.
+
+ ================ ======================================================
+ Attribute Description
+ ================ ======================================================
+ remote_as The AS number of a peer that caused this change
+ route_dist None in the case of ipv4 or ipv6 family
+ prefix A prefix was changed
+ nexthop The nexthop of the changed prefix
+ is_withdraw True if this prefix has gone otherwise False
+ ================ ======================================================
+
+ """
+
+ def __init__(self, remote_as, route_dist, prefix, nexthop, is_withdraw):
+ self.remote_as = remote_as
+ self.route_dist = route_dist
+ self.prefix = prefix
+ self.nexthop = nexthop
+ self.is_withdraw = is_withdraw
+
+
+class BGPSpeaker(object):
+ def __init__(self, as_number, router_id,
+ bgp_server_port=DEFAULT_BGP_SERVER_PORT,
+ refresh_stalepath_time=DEFAULT_REFRESH_STALEPATH_TIME,
+ refresh_max_eor_time=DEFAULT_REFRESH_MAX_EOR_TIME,
+ best_path_change_handler=None):
+ """Create a new BGPSpeaker object with as_number and router_id to
+ listen on bgp_server_port.
+
+ ``as_number`` specifies an Autonomous Number. It must be an integer
+ between 1 and 65535.
+
+ ``router_id`` specifies BGP router identifier. It must be the
+ string representation of an IPv4 address (e.g. 10.0.0.1).
+
+ ``bgp_server_port`` specifies TCP listen port number. 179 is
+ used if not specified.
+
+ ``refresh_stalepath_time`` causes the BGP speaker to remove
+ stale routes from the BGP table after the timer expires, even
+ if the speaker does not receive a Router-Refresh End-of-RIB
+ message. This feature is disabled (not implemented yet).
+
+ ``refresh_max_eor_time`` causes the BGP speaker to generate a
+ Route-Refresh End-of-RIB message if it was not able to
+ generate one due to route flapping. This feature is disabled
+ (not implemented yet).
+
+ ``best_path_change_handler``, if specified, is called when any
+ best remote path is changed due to an update message or remote
+ peer down. The handler is supposed to take one argument, the
+ instance of an EventPrefix class instance.
+
+ """
+ super(BGPSpeaker, self).__init__()
+ self.speaker = RyuBGPSpeaker()
+
+ settings = {}
+ settings[LOCAL_AS] = as_number
+ settings[ROUTER_ID] = router_id
+ settings[BGP_SERVER_PORT] = bgp_server_port
+ settings[REFRESH_STALEPATH_TIME] = refresh_stalepath_time
+ settings[REFRESH_MAX_EOR_TIME] = refresh_max_eor_time
+ self._core_start(settings)
+ self._init_signal_listeners()
+ self._best_path_change_handler = best_path_change_handler
+
+ def _notify_best_path_changed(self, path):
+ if not path.source:
+ # ours
+ return
+ ev = EventPrefix(remote_as=path.source.remote_as,
+ route_dist=None,
+ prefix=path.nlri.addr + '/' + str(path.nlri.length),
+ nexthop=path.nexthop, is_withdraw=path.is_withdraw)
+ if self._best_path_change_handler:
+ self._best_path_change_handler(ev)
+
+ def _init_signal_listeners(self):
+ CORE_MANAGER.get_core_service()._signal_bus.register_listener(
+ BgpSignalBus.BGP_BEST_PATH_CHANGED,
+ lambda _, dest: self._notify_best_path_changed(dest)
+ )
+
+ def _core_start(self, settings):
+ waiter = hub.Event()
+ call('core.start', waiter=waiter, **settings)
+ waiter.wait()
+
+ def _serve_forever(self):
+ pass
+
+ def shutdown(self):
+ """ Shutdown BGP speaker
+
+ """
+ call('core.stop')
+
+ def neighbor_add(self, address, remote_as,
+ enable_ipv4=DEFAULT_CAP_MBGP_IPV4,
+ enable_vpnv4=DEFAULT_CAP_MBGP_VPNV4,
+ enable_vpnv6=DEFAULT_CAP_MBGP_VPNV6):
+ """ 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).
+
+ ``address`` specifies the IP address of the peer. It must be
+ the string representation of an IP address. Only IP v4 is
+ supported now.
+
+ ``remote_as`` specifies the AS number of the peer. It must be
+ an integer between 1 and 65535.
+
+ ``enable_ipv4`` enables IPv4 address family for this
+ neighbor. The default is True.
+
+ ``enable_vpnv4`` enables VPNv4 address family for this
+ neighbor. The default is False.
+
+ ``enable_vpnv6`` enables VPNv6 address family for this
+ neighbor. The default is False.
+
+ """
+ bgp_neighbor = {}
+ bgp_neighbor[neighbors.IP_ADDRESS] = address
+ bgp_neighbor[neighbors.REMOTE_AS] = remote_as
+ bgp_neighbor[CAP_MBGP_IPV4] = enable_ipv4
+ bgp_neighbor[CAP_MBGP_VPNV4] = enable_vpnv4
+ bgp_neighbor[CAP_MBGP_VPNV6] = enable_vpnv6
+ call('neighbor.create', **bgp_neighbor)
+
+ def neighbor_del(self, address):
+ """ This method unregister the registered neighbor. If a session with
+ the peer exists, the session will be closed.
+
+ ``address`` specifies the IP address of the peer. It must be
+ the string representation of an IP address.
+
+ """
+ bgp_neighbor = {}
+ bgp_neighbor[neighbors.IP_ADDRESS] = address
+ call('neighbor.delete', **bgp_neighbor)
+
+ def prefix_add(self, prefix, next_hop=None, route_dist=None,
+ route_family=None):
+ """ This method adds a new prefix to be advertized.
+
+ ``prefix`` must be the string representation of an IP network
+ (e.g., 10.1.1.0/24).
+
+ ``next_hop`` specifies the next hop address for this
+ prefix. This parameter is necessary for only VPNv4 and VPNv6
+ address families.
+
+ ``route_dist`` specifies a route distinguisher value. This
+ parameter is necessary for only VPNv4 and VPNv6 address
+ families.
+
+ """
+ func_name = 'network.add'
+ networks = {}
+ networks[PREFIX] = prefix
+ if next_hop:
+ networks[NEXT_HOP] = next_hop
+ if route_dist:
+ func_name = 'prefix.add_local'
+ networks[ROUTE_DISTINGUISHER] = route_dist
+ call(func_name, **networks)
+
+ def prefix_del(self, prefix, route_dist=None, route_family=None):
+ """ This method deletes a advertized prefix.
+
+ ``prefix`` must be the string representation of an IP network
+ (e.g., 10.1.1.0/24).
+
+ ``route_dist`` specifies a route distinguisher value. This
+ parameter is necessary for only VPNv4 and VPNv6 address
+ families.
+
+ """
+ func_name = 'network.del'
+ networks = {}
+ networks[PREFIX] = prefix
+ if route_dist:
+ func_name = 'prefix.delete_local'
+ networks[ROUTE_DISTINGUISHER] = route_dist
+ call(func_name, **networks)
+
+ def vrf_add(self, route_dist, import_rts, export_rts, site_of_origins=None,
+ multi_exit_disc=None):
+ """ This method adds a new vrf used for VPN.
+
+ ``route_dist`` specifies a route distinguisher value.
+
+ ``import_rts`` specifies route targets to be imported.
+
+ ``export_rts`` specifies route targets to be exported.
+
+ """
+
+ vrf = {}
+ vrf[vrfs.ROUTE_DISTINGUISHER] = route_dist
+ vrf[vrfs.IMPORT_RTS] = import_rts
+ vrf[vrfs.EXPORT_RTS] = export_rts
+ call('vrf.create', **vrf)
+
+ def vrf_del(self, route_dist):
+ """ This method deletes the existing vrf.
+
+ ``route_dist`` specifies a route distinguisher value.
+
+ """
+
+ vrf = {}
+ vrf[vrfs.ROUTE_DISTINGUISHER] = route_dist
+ call('vrf.delete', **vrf)
+
+ def vrfs_get(self):
+ show = {}
+ show['params'] = ['vrf', 'routes', 'all']
+ return call('operator.show', **show)
+
+ def rib_get(self, family='ipv4'):
+ """ This method returns the BGP routing information in a json
+ format. This will be improved soon.
+
+ ``family`` specifies the address family of the RIB.
+
+ """
+ show = {}
+ show['params'] = ['rib', family]
+ return call('operator.show', **show)
diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py
index d6535b81..f8265dd7 100644
--- a/ryu/services/protocols/bgp/core_managers/table_manager.py
+++ b/ryu/services/protocols/bgp/core_managers/table_manager.py
@@ -477,12 +477,13 @@ class TableCoreManager(object):
raise BgpCoreError(desc='Invalid Ipv6 prefix or nexthop.')
ip6, masklen = prefix.split('/')
prefix = IP6AddrPrefix(int(masklen), ip6)
+
return vrf_table.insert_vrf_path(
prefix, next_hop=next_hop,
gen_lbl=True
)
- def add_to_ipv4_global_table(self, prefix):
+ def add_to_ipv4_global_table(self, prefix, is_withdraw=False):
ip, masklen = prefix.split('/')
_nlri = IPAddrPrefix(int(masklen), ip)
src_ver_num = 1
@@ -497,7 +498,8 @@ class TableCoreManager(object):
pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
new_path = Ipv4Path(peer, _nlri, src_ver_num,
- pattrs=pathattrs, nexthop=nexthop)
+ pattrs=pathattrs, nexthop=nexthop,
+ is_withdraw=is_withdraw)
# add to global ipv4 table and propagates to neighbors
self.learn_path(new_path)
diff --git a/ryu/services/protocols/bgp/info_base/ipv4.py b/ryu/services/protocols/bgp/info_base/ipv4.py
index 2e4db3fb..de0d4c77 100644
--- a/ryu/services/protocols/bgp/info_base/ipv4.py
+++ b/ryu/services/protocols/bgp/info_base/ipv4.py
@@ -38,9 +38,11 @@ class IPv4Dest(Destination, NonVrfPathProcessingMixin):
def _best_path_lost(self):
NonVrfPathProcessingMixin._best_path_lost(self)
+ self._core_service._signal_bus.best_path_changed(self)
def _new_best_path(self, best_path):
NonVrfPathProcessingMixin._new_best_path(self, best_path)
+ self._core_service._signal_bus.best_path_changed(best_path)
class Ipv4Table(Table):
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 95c223c5..c56b2941 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -773,12 +773,12 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
if unkown_opttrans_attrs:
new_pathattr.extend(unkown_opttrans_attrs.values())
- if isinstance(path, Ipv4Path):
- update = BGPUpdate(path_attributes=new_pathattr,
- nlri=nlri_list)
- else:
- update = BGPUpdate(path_attributes=new_pathattr)
- return update
+ if isinstance(path, Ipv4Path):
+ update = BGPUpdate(path_attributes=new_pathattr,
+ nlri=nlri_list)
+ else:
+ update = BGPUpdate(path_attributes=new_pathattr)
+ return update
def _connect_loop(self, client_factory):
"""In the current greeenlet we try to establish connection with peer.
diff --git a/ryu/services/protocols/bgp/signals/emit.py b/ryu/services/protocols/bgp/signals/emit.py
index 7f41c93f..18a12871 100644
--- a/ryu/services/protocols/bgp/signals/emit.py
+++ b/ryu/services/protocols/bgp/signals/emit.py
@@ -11,6 +11,7 @@ class BgpSignalBus(SignalBus):
BGP_VRF_STATS_CONFIG_CHANGED = (
'core', 'vrf', 'config', 'stats', 'changed'
)
+ BGP_BEST_PATH_CHANGED = ('core', 'best', 'changed')
def bgp_error(self, peer, code, subcode, reason):
return self.emit_signal(
@@ -53,3 +54,8 @@ class BgpSignalBus(SignalBus):
self.BGP_VRF_STATS_CONFIG_CHANGED,
vrf_conf
)
+
+ def best_path_changed(self, best_path):
+ return self.emit_signal(
+ self.BGP_BEST_PATH_CHANGED,
+ best_path)