diff options
author | ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp> | 2014-03-30 08:27:09 +0000 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2014-04-01 17:29:15 +0900 |
commit | 706cd11ce38093263e0e43792aef64ed60535395 (patch) | |
tree | 594baec965e339954d4994db47898638f706e4af | |
parent | f813ec4232e610df45519d0ae73f3f83852f99cc (diff) |
bgp: handle non-MPBGP UPDATE msg
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/base.py | 3 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/core_managers/table_manager.py | 24 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/info_base/ipv4.py | 71 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/peer.py | 151 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/base.py | 9 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/neighbors.py | 14 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/utils/bgp.py | 5 |
7 files changed, 267 insertions, 10 deletions
diff --git a/ryu/services/protocols/bgp/base.py b/ryu/services/protocols/bgp/base.py index d68f1ad4..489ba3af 100644 --- a/ryu/services/protocols/bgp/base.py +++ b/ryu/services/protocols/bgp/base.py @@ -42,7 +42,8 @@ OrderedDict = OrderedDict # Currently supported address families. -SUPPORTED_GLOBAL_RF = set([nlri.RF_IPv4_VPN, +SUPPORTED_GLOBAL_RF = set([nlri.RF_IPv4_UC, + nlri.RF_IPv4_VPN, nlri.RF_RTC_UC, nlri.RF_IPv6_VPN ]) diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py index 4aca4a8d..5554e4a1 100644 --- a/ryu/services/protocols/bgp/core_managers/table_manager.py +++ b/ryu/services/protocols/bgp/core_managers/table_manager.py @@ -2,6 +2,8 @@ import logging from ryu.services.protocols.bgp.base import SUPPORTED_GLOBAL_RF from ryu.services.protocols.bgp.info_base.rtc import RtcTable +from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path +from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Table from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Path from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Table from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path @@ -146,7 +148,10 @@ class TableCoreManager(object): ) global_table = None - if route_family == nlri.RF_IPv4_VPN: + if route_family == nlri.RF_IPv4_UC: + global_table = self.get_ipv4_table() + + elif route_family == nlri.RF_IPv4_VPN: global_table = self.get_vpn4_table() elif route_family == nlri.RF_IPv6_VPN: @@ -171,6 +176,23 @@ class TableCoreManager(object): vrf_tables[(scope_id, table_id)] = table return vrf_tables + def get_ipv4_table(self): + """Returns global IPv4 table. + + Creates the table if it does not exist. + """ + + vpn_table = self._global_tables.get(nlri.RF_IPv4_UC) + # Lazy initialize the table. + if not vpn_table: + vpn_table = Ipv4Table(self._core_service, self._signal_bus) + self._global_tables[nlri.RF_IPv4_UC] = vpn_table + self._tables[(None, nlri.RF_IPv4_UC)] = vpn_table + + return vpn_table + + + def get_vpn6_table(self): """Returns global VPNv6 table. diff --git a/ryu/services/protocols/bgp/info_base/ipv4.py b/ryu/services/protocols/bgp/info_base/ipv4.py new file mode 100644 index 00000000..0d8a68dc --- /dev/null +++ b/ryu/services/protocols/bgp/info_base/ipv4.py @@ -0,0 +1,71 @@ +# Copyright (c) NTT Multimedia Communications Laboratories, Inc. +# All rights reserved. + +""" + Defines data types and models required specifically for IPv4 support. +""" + +import logging + +from ryu.services.protocols.bgp.protocols.bgp.nlri import Ipv4 +from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_UC + +from ryu.services.protocols.bgp.info_base.base import Path +from ryu.services.protocols.bgp.info_base.base import Table +from ryu.services.protocols.bgp.info_base.base import Destination +from ryu.services.protocols.bgp.info_base.base import NonVrfPathProcessingMixin + +LOG = logging.getLogger('bgpspeaker.info_base.ipv4') + + +class IPv4Dest(Destination, NonVrfPathProcessingMixin): + """VPNv4 Destination + + Store IPv4 Paths. + """ + ROUTE_FAMILY = RF_IPv4_UC + + def _best_path_lost(self): + NonVrfPathProcessingMixin._best_path_lost(self) + + def _new_best_path(self, best_path): + NonVrfPathProcessingMixin._new_best_path(self, best_path) + +class Ipv4Table(Table): + """Global table to store IPv4 routing information. + + Uses `IPv4Dest` to store destination information for each known vpnv4 + paths. + """ + ROUTE_FAMILY = RF_IPv4_UC + VPN_DEST_CLASS = IPv4Dest + + def __init__(self, core_service, signal_bus): + super(Ipv4Table, self).__init__(None, core_service, signal_bus) + + def _table_key(self, nlri): + """Return a key that will uniquely identify this NLRI inside + this table. + """ + return nlri.prefix + + def _create_dest(self, nlri): + return self.VPN_DEST_CLASS(self, nlri) + + def __str__(self): + return '%s(scope_id: %s, rf: %s)' % ( + self.__class__.__name__, self.scope_id, self.route_family + ) + + + +class Ipv4Path(Path): + """Represents a way of reaching an VPNv4 destination.""" + ROUTE_FAMILY = RF_IPv4_UC + VRF_PATH_CLASS = None # defined in init - anti cyclic import hack + NLRI_CLASS = Ipv4 + + def __init__(self, *args, **kwargs): + super(Ipv4Path, self).__init__(*args, **kwargs) + from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Path + self.VRF_PATH_CLASS = Vrf4Path diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py index 87838b39..2b41b62c 100644 --- a/ryu/services/protocols/bgp/peer.py +++ b/ryu/services/protocols/bgp/peer.py @@ -888,10 +888,37 @@ class Peer(Source, Sink, NeighborConfListener, Activity): mp_unreach_attr = update_msg.get_path_attr( pathattr.MpUnreachNlri.ATTR_NAME ) + + # non-MPBGP Update msg. if not (mp_reach_attr or mp_unreach_attr): - LOG.error('Received UPDATE msg. with no MpReachNlri or ' + LOG.info('Received UPDATE msg. with no MpReachNlri or ' 'MpUnReachNlri attribute.') - raise exceptions.MalformedAttrList() + if not self.is_mpbgp_cap_valid(nlri.RF_IPv4_UC): + LOG.error('Got UPDATE message with un-available afi/safi %s' % nlri.RF_IPv4_UC) + + nlri_list = update_msg.nlri_list + if len(nlri_list) > 0: + # Check for missing well-known mandatory attributes. + aspath = update_msg.get_path_attr(pathattr.AsPath.ATTR_NAME) + if not aspath: + raise exceptions.MissingWellKnown(pathattr.AsPath.TYPE_CODE) + + # We do not have a setting to enable/disable first-as check. + # We by default do first-as check below. + if (self.is_ebgp_peer() and + not aspath.has_matching_leftmost(self.remote_as)): + LOG.error('First AS check fails. Raise appropriate exception.') + raise exceptions.MalformedAsPath() + + origin = update_msg.get_path_attr(pathattr.Origin.ATTR_NAME) + if not origin: + raise exceptions.MissingWellKnown(pathattr.Origin.TYPE_CODE) + + nexthop = update_msg.get_path_attr(pathattr.NextHop.ATTR_NAME) + if not nexthop: + raise exceptions.MissingWellKnown(pathattr.NextHop.TYPE_CODE) + + return True # Check if received MP_UNREACH path attribute is of available afi/safi if mp_unreach_attr: @@ -963,15 +990,127 @@ class Peer(Source, Sink, NeighborConfListener, Activity): mp_unreach_attr = update_msg.get_path_attr( pathattr.MpUnreachNlri.ATTR_NAME ) + + nlri_list = update_msg.nlri_list + withdraw_list = update_msg.withdraw_list + if mp_reach_attr: # Extract advertised paths from given message. - self._extract_and_handle_new_paths(update_msg) + self._extract_and_handle_mpbgp_new_paths(update_msg) if mp_unreach_attr: # Extract withdraws from given message. - self._extract_and_handle_withdraws(mp_unreach_attr) + self._extract_and_handle_mpbgp_withdraws(mp_unreach_attr) + + if nlri_list: + self._extract_and_handle_bgp4_new_paths(update_msg) + + if withdraw_list: + self._extract_and_handle_bgp4_withdraws(withdraw_list) + + def _extract_and_handle_bgp4_new_paths(self, update_msg): + """Extracts new paths advertised in the given update message's + *MpReachNlri* attribute. + + Assumes MPBGP capability is enabled and message was validated. + Parameters: + - update_msg: (Update) is assumed to be checked for all bgp + message errors. + - valid_rts: (iterable) current valid/configured RTs. + + Extracted paths are added to appropriate *Destination* for further + processing. + """ + umsg_pattrs = update_msg.pathattr_map + + msg_rf = nlri.RF_IPv4_UC + # Check if this route family is among supported route families. + if msg_rf not in SUPPORTED_GLOBAL_RF: + LOG.info(('Received route for route family %s which is' + ' not supported. Ignoring paths from this UPDATE: %s') % + (msg_rf, update_msg)) + return + + aspath = umsg_pattrs.get(pathattr.AsPath.ATTR_NAME) + # Check if AS_PATH has loops. + if aspath.has_local_as(self._common_conf.local_as): + LOG.error('Update message AS_PATH has loops. Ignoring this' + ' UPDATE. %s' % update_msg) + return + + next_hop = update_msg.get_path_attr(pathattr.NextHop.ATTR_NAME) + # Nothing to do if we do not have any new NLRIs in this message. + msg_nlri_list = update_msg.nlri_list + if not msg_nlri_list: + LOG.debug('Update message did not have any new MP_REACH_NLRIs.') + return + + # Create path instances for each NLRI from the update message. + for msg_nlri in msg_nlri_list: + LOG.debug('NLRI: %s' % msg_nlri) + new_path = bgp_utils.create_path( + self, + msg_nlri, + pattrs=umsg_pattrs, + nexthop=next_hop + ) + LOG.debug('Extracted paths from Update msg.: %s' % new_path) + # Update appropriate table with new paths. + tm = self._core_service.table_manager + tm.learn_path(new_path) + + # If update message had any qualifying new paths, do some book-keeping. + if msg_nlri_list: + # Update prefix statistics. + self.state.incr(PeerCounterNames.RECV_PREFIXES, + incr_by=len(msg_nlri_list)) + # Check if we exceed max. prefixes allowed for this neighbor. + if self._neigh_conf.exceeds_max_prefix_allowed( + self.state.get_count(PeerCounterNames.RECV_PREFIXES)): + LOG.error('Max. prefix allowed for this neighbor ' + 'exceeded.') + + def _extract_and_handle_bgp4_withdraws(self, withdraw_list): + """Extracts withdraws advertised in the given update message's + *MpUnReachNlri* attribute. + + Assumes MPBGP capability is enabled. + Parameters: + - update_msg: (Update) is assumed to be checked for all bgp + message errors. + + Extracted withdraws are added to appropriate *Destination* for further + processing. + """ + msg_rf = nlri.RF_IPv4_UC + # Check if this route family is among supported route families. + if msg_rf not in SUPPORTED_GLOBAL_RF: + LOG.info( + ( + 'Received route for route family %s which is' + ' not supported. Ignoring withdraws form this UPDATE.' + ) % msg_rf + ) + return + + w_nlris = withdraw_list + if not w_nlris: + # If this is EOR of some kind, handle it + self._handle_eor(msg_rf) + + for w_nlri in w_nlris: + w_path = bgp_utils.create_path( + self, + w_nlri, + is_withdraw=True + ) + # Update appropriate table with withdraws. + tm = self._core_service.table_manager + tm.learn_path(w_path) + + - def _extract_and_handle_new_paths(self, update_msg): + def _extract_and_handle_mpbgp_new_paths(self, update_msg): """Extracts new paths advertised in the given update message's *MpReachNlri* attribute. @@ -1066,7 +1205,7 @@ class Peer(Source, Sink, NeighborConfListener, Activity): LOG.error('Max. prefix allowed for this neighbor ' 'exceeded.') - def _extract_and_handle_withdraws(self, mp_unreach_attr): + def _extract_and_handle_mpbgp_withdraws(self, mp_unreach_attr): """Extracts withdraws advertised in the given update message's *MpUnReachNlri* attribute. diff --git a/ryu/services/protocols/bgp/rtconf/base.py b/ryu/services/protocols/bgp/rtconf/base.py index 95717f8c..2fe0aa85 100644 --- a/ryu/services/protocols/bgp/rtconf/base.py +++ b/ryu/services/protocols/bgp/rtconf/base.py @@ -40,6 +40,7 @@ LOG = logging.getLogger('bgpspeaker.rtconf.base') # CAP_REFRESH = 'cap_refresh' CAP_ENHANCED_REFRESH = 'cap_enhanced_refresh' +CAP_MBGP_IPV4 = 'cap_mbgp_ipv4' CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4' CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6' CAP_RTC = 'cap_rtc' @@ -587,6 +588,14 @@ def validate_cap_enhanced_refresh(cer): 'settings: %s boolean value expected' % cer) return cer +@validate(name=CAP_MBGP_IPV4) +def validate_cap_mbgp_ipv4(cmv4): + if cmv4 not in (True, False): + raise ConfigTypeError(desc='Invalid Enhanced Refresh capability ' + 'settings: %s boolean value expected' % cmv4) + + return cmv4 + @validate(name=CAP_MBGP_VPNV4) def validate_cap_mbgp_vpnv4(cmv4): diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py index 679a1cb3..9a4920bd 100644 --- a/ryu/services/protocols/bgp/rtconf/neighbors.py +++ b/ryu/services/protocols/bgp/rtconf/neighbors.py @@ -24,6 +24,7 @@ from ryu.services.protocols.bgp.rtconf.base import ADVERTISE_PEER_AS from ryu.services.protocols.bgp.rtconf.base import BaseConf from ryu.services.protocols.bgp.rtconf.base import BaseConfListener from ryu.services.protocols.bgp.rtconf.base import CAP_ENHANCED_REFRESH +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.base import CAP_REFRESH @@ -51,6 +52,7 @@ from ryu.services.protocols.bgp.protocols.bgp.capabilities import \ MultiprotocolExtentionCap from ryu.services.protocols.bgp.protocols.bgp.capabilities import \ RouteRefreshCap +from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_UC from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_VPN from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv6_VPN from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_RTC_UC @@ -71,6 +73,7 @@ LOCAL_PORT = 'local_port' DEFAULT_CAP_GR_NULL = True DEFAULT_CAP_REFRESH = True DEFAULT_CAP_ENHANCED_REFRESH = True +DEFAULT_CAP_MBGP_IPV4 = False DEFAULT_CAP_MBGP_VPNV4 = True DEFAULT_CAP_MBGP_VPNV6 = False DEFAULT_HOLD_TIME = 40 @@ -146,7 +149,7 @@ class NeighborConf(ConfWithId, ConfWithStats): REQUIRED_SETTINGS = frozenset([REMOTE_AS, IP_ADDRESS, LOCAL_ADDRESS, LOCAL_PORT]) OPTIONAL_SETTINGS = frozenset([CAP_REFRESH, - CAP_ENHANCED_REFRESH, CAP_MBGP_VPNV4, + CAP_ENHANCED_REFRESH, CAP_MBGP_VPNV4, CAP_MBGP_IPV4, CAP_MBGP_VPNV6, CAP_RTC, RTC_AS, HOLD_TIME, ENABLED, MULTI_EXIT_DISC, MAX_PREFIXES, ADVERTISE_PEER_AS, SITE_OF_ORIGINS]) @@ -159,6 +162,8 @@ class NeighborConf(ConfWithId, ConfWithStats): CAP_REFRESH, DEFAULT_CAP_REFRESH, **kwargs) self._settings[CAP_ENHANCED_REFRESH] = compute_optional_conf( CAP_ENHANCED_REFRESH, DEFAULT_CAP_ENHANCED_REFRESH, **kwargs) + self._settings[CAP_MBGP_IPV4] = compute_optional_conf( + CAP_MBGP_IPV4, DEFAULT_CAP_MBGP_IPV4, **kwargs) self._settings[CAP_MBGP_VPNV4] = compute_optional_conf( CAP_MBGP_VPNV4, DEFAULT_CAP_MBGP_VPNV4, **kwargs) self._settings[CAP_MBGP_VPNV6] = compute_optional_conf( @@ -255,6 +260,10 @@ class NeighborConf(ConfWithId, ConfWithStats): return self._settings[CAP_ENHANCED_REFRESH] @property + def cap_mbgp_ipv4(self): + return self._settings[CAP_MBGP_IPV4] + + @property def cap_mbgp_vpnv4(self): return self._settings[CAP_MBGP_VPNV4] @@ -323,6 +332,9 @@ class NeighborConf(ConfWithId, ConfWithStats): capabilities = OrderedDict() mbgp_caps = [] + if self.cap_mbgp_ipv4: + mbgp_caps.append(MultiprotocolExtentionCap(RF_IPv4_UC)) + if self.cap_mbgp_vpnv4: mbgp_caps.append(MultiprotocolExtentionCap(RF_IPv4_VPN)) diff --git a/ryu/services/protocols/bgp/utils/bgp.py b/ryu/services/protocols/bgp/utils/bgp.py index bee4d3b9..fe22c45b 100644 --- a/ryu/services/protocols/bgp/utils/bgp.py +++ b/ryu/services/protocols/bgp/utils/bgp.py @@ -20,6 +20,7 @@ import logging import socket from ryu.services.protocols.bgp.protocols.bgp.messages import Update +from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_UC from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv4_VPN from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_IPv6_VPN from ryu.services.protocols.bgp.protocols.bgp.nlri import RF_RTC_UC @@ -27,6 +28,7 @@ from ryu.services.protocols.bgp.protocols.bgp.nlri import RtNlri from ryu.services.protocols.bgp.protocols.bgp import pathattr from ryu.services.protocols.bgp.protocols.bgp.pathattr import Med from ryu.services.protocols.bgp.info_base.rtc import RtcPath +from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Path from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path @@ -34,7 +36,8 @@ from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path LOG = logging.getLogger('utils.bgp') # RouteFmaily to path sub-class mapping. -_ROUTE_FAMILY_TO_PATH_MAP = {RF_IPv4_VPN: Vpnv4Path, +_ROUTE_FAMILY_TO_PATH_MAP = {RF_IPv4_UC: Ipv4Path, + RF_IPv4_VPN: Vpnv4Path, RF_IPv6_VPN: Vpnv6Path, RF_RTC_UC: RtcPath} |