585 lines
23 KiB
Python
585 lines
23 KiB
Python
import logging
|
|
import netaddr
|
|
from collections import OrderedDict
|
|
|
|
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.ipv6 import Ipv6Path
|
|
from ryu.services.protocols.bgp.info_base.ipv6 import Ipv6Table
|
|
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
|
|
from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Table
|
|
from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Table
|
|
from ryu.services.protocols.bgp.info_base.vrf6 import Vrf6Table
|
|
from ryu.services.protocols.bgp.rtconf import vrfs
|
|
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
|
|
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6
|
|
|
|
from ryu.lib.packet.bgp import RF_IPv4_UC
|
|
from ryu.lib.packet.bgp import RF_IPv6_UC
|
|
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
|
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
|
from ryu.lib.packet.bgp import RF_RTC_UC
|
|
from ryu.lib.packet.bgp import BGPPathAttributeOrigin
|
|
from ryu.lib.packet.bgp import BGPPathAttributeAsPath
|
|
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
|
|
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
|
|
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
|
|
from ryu.lib.packet.bgp import IPAddrPrefix
|
|
from ryu.lib.packet.bgp import IP6AddrPrefix
|
|
|
|
from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
|
|
from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4_prefix
|
|
from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
|
|
from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6_prefix
|
|
|
|
|
|
LOG = logging.getLogger('bgpspeaker.core_managers.table_mixin')
|
|
|
|
|
|
class TableCoreManager(object):
|
|
"""Methods performing core operations on tables."""
|
|
|
|
def __init__(self, core_service, common_conf):
|
|
|
|
self._tables = {}
|
|
self._rt_mgr = core_service.rt_manager
|
|
self._signal_bus = core_service.signal_bus
|
|
|
|
# (VRF) Tables to which the routes with a given route target
|
|
# should be imported.
|
|
#
|
|
# Key: RouteTarget
|
|
# Value: List of tables.
|
|
self._tables_for_rt = {}
|
|
|
|
# Global/Default tables, keyed by RouteFamily.
|
|
self._global_tables = {}
|
|
|
|
self._core_service = core_service
|
|
self._signal_bus = self._core_service.signal_bus
|
|
|
|
# VPN label range
|
|
self._asbr_label_range = common_conf.label_range
|
|
|
|
self._next_vpnv4_label = int(self._asbr_label_range[0])
|
|
|
|
self._next_hop_label = {}
|
|
|
|
@property
|
|
def global_tables(self):
|
|
return self._global_tables
|
|
|
|
def remove_vrf_by_vrf_conf(self, vrf_conf):
|
|
|
|
route_family = vrf_conf.route_family
|
|
assert route_family in (vrfs.VRF_RF_IPV4, vrfs.VRF_RF_IPV6)
|
|
table_id = (vrf_conf.route_dist, route_family)
|
|
|
|
vrf_table = self._tables.pop(table_id)
|
|
|
|
self._remove_links_to_vrf_table(vrf_table)
|
|
|
|
# Withdraw the best-path whose source was NC since it may have been
|
|
# exported to VPN table.
|
|
for destination in vrf_table.values():
|
|
best_path = destination.best_path
|
|
if best_path and best_path.source is None:
|
|
vpn_clone = best_path.clone_to_vpn(vrf_conf.route_dist,
|
|
for_withdrawal=True)
|
|
self.learn_path(vpn_clone)
|
|
LOG.debug('VRF with RD %s marked for removal', vrf_conf.route_dist)
|
|
|
|
def import_all_vpn_paths_to_vrf(self, vrf_table, import_rts=None):
|
|
"""Imports Vpnv4/6 paths from Global/VPN table into given Vrfv4/6
|
|
table.
|
|
:param vrf_table: Vrf table to which we import
|
|
:type vrf_table: VrfTable
|
|
:param import_rts: import RTs to override default import_rts of
|
|
vrf table for this import
|
|
:type import_rts: set of strings
|
|
|
|
|
|
Checks if we have any path RT common with VRF table's import RT.
|
|
"""
|
|
rfs = (Vrf4Table.ROUTE_FAMILY, Vrf6Table.ROUTE_FAMILY)
|
|
assert vrf_table.route_family in rfs, 'Invalid VRF table.'
|
|
|
|
if vrf_table.route_family == Vrf4Table.ROUTE_FAMILY:
|
|
vpn_table = self.get_vpn4_table()
|
|
else:
|
|
vpn_table = self.get_vpn6_table()
|
|
|
|
vrf_table.import_vpn_paths_from_table(vpn_table, import_rts)
|
|
|
|
def learn_path(self, path):
|
|
"""Inserts `path` into correct global table.
|
|
|
|
Since known paths to `Destination` has changes, we queue it for further
|
|
processing.
|
|
"""
|
|
# Get VPN/Global table
|
|
table = self.get_global_table_by_route_family(path.route_family)
|
|
gpath_dest = table.insert(path)
|
|
# Since destination was updated, we enqueue it for processing.
|
|
self._signal_bus.dest_changed(gpath_dest)
|
|
|
|
def remember_sent_route(self, sent_route):
|
|
"""Records `sent_route` inside proper table.
|
|
|
|
Records of `sent_route` from Adj-RIB-out.
|
|
"""
|
|
route_family = sent_route.path.route_family
|
|
table = self.get_global_table_by_route_family(route_family)
|
|
table.insert_sent_route(sent_route)
|
|
|
|
def on_interesting_rts_change(self, new_global_rts, removed_global_rts):
|
|
"""Update global tables as interested RTs changed.
|
|
|
|
Adds `new_rts` and removes `removed_rts` rt nlris. Does not check if
|
|
`new_rts` or `removed_rts` are already present. Schedules refresh
|
|
request to peers that do not participate in RTC address-family.
|
|
"""
|
|
# We add new RT NLRI and request RR for other peers.
|
|
if new_global_rts:
|
|
LOG.debug(
|
|
'Sending route_refresh to all neighbors that'
|
|
' did not negotiate RTC capability.'
|
|
)
|
|
|
|
pm = self._core_service.peer_manager
|
|
pm.schedule_rr_to_non_rtc_peers()
|
|
if removed_global_rts:
|
|
LOG.debug(
|
|
'Cleaning up global tables as some interested RTs were removed'
|
|
)
|
|
self._clean_global_uninteresting_paths()
|
|
|
|
def get_global_table_by_route_family(self, route_family):
|
|
if route_family not in SUPPORTED_GLOBAL_RF:
|
|
raise ValueError(
|
|
'Given route family: %s currently not supported' % route_family
|
|
)
|
|
|
|
global_table = None
|
|
if route_family == RF_IPv4_UC:
|
|
global_table = self.get_ipv4_table()
|
|
elif route_family == RF_IPv6_UC:
|
|
global_table = self.get_ipv6_table()
|
|
elif route_family == RF_IPv4_VPN:
|
|
global_table = self.get_vpn4_table()
|
|
|
|
elif route_family == RF_IPv6_VPN:
|
|
global_table = self.get_vpn6_table()
|
|
|
|
elif route_family == RF_RTC_UC:
|
|
global_table = self.get_rtc_table()
|
|
|
|
return global_table
|
|
|
|
def get_vrf_table(self, vrf_rd, vrf_rf):
|
|
assert vrf_rd is not None
|
|
return self._tables.get((vrf_rd, vrf_rf))
|
|
|
|
def get_vrf_tables(self, vrf_rf=None):
|
|
vrf_tables = {}
|
|
for (scope_id, table_id), table in self._tables.items():
|
|
if scope_id is None:
|
|
continue
|
|
if vrf_rf is not None and table_id != vrf_rf:
|
|
continue
|
|
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(RF_IPv4_UC)
|
|
# Lazy initialize the table.
|
|
if not vpn_table:
|
|
vpn_table = Ipv4Table(self._core_service, self._signal_bus)
|
|
self._global_tables[RF_IPv4_UC] = vpn_table
|
|
self._tables[(None, RF_IPv4_UC)] = vpn_table
|
|
|
|
return vpn_table
|
|
|
|
def get_ipv6_table(self):
|
|
table = self._global_tables.get(RF_IPv6_UC)
|
|
if not table:
|
|
table = Ipv6Table(self._core_service, self._signal_bus)
|
|
self._global_tables[RF_IPv6_UC] = table
|
|
self._tables[(None, RF_IPv6_UC)] = table
|
|
return table
|
|
|
|
def get_vpn6_table(self):
|
|
"""Returns global VPNv6 table.
|
|
|
|
Creates the table if it does not exist.
|
|
"""
|
|
vpn_table = self._global_tables.get(RF_IPv6_VPN)
|
|
# Lazy initialize the table.
|
|
if not vpn_table:
|
|
vpn_table = Vpnv6Table(self._core_service, self._signal_bus)
|
|
self._global_tables[RF_IPv6_VPN] = vpn_table
|
|
self._tables[(None, RF_IPv6_VPN)] = vpn_table
|
|
|
|
return vpn_table
|
|
|
|
def get_vpn4_table(self):
|
|
"""Returns global VPNv6 table.
|
|
|
|
Creates the table if it does not exist.
|
|
"""
|
|
vpn_table = self._global_tables.get(RF_IPv4_VPN)
|
|
# Lazy initialize the table.
|
|
if not vpn_table:
|
|
vpn_table = Vpnv4Table(self._core_service, self._signal_bus)
|
|
self._global_tables[RF_IPv4_VPN] = vpn_table
|
|
self._tables[(None, RF_IPv4_VPN)] = vpn_table
|
|
|
|
return vpn_table
|
|
|
|
def get_rtc_table(self):
|
|
"""Returns global RTC table.
|
|
|
|
Creates the table if it does not exist.
|
|
"""
|
|
rtc_table = self._global_tables.get(RF_RTC_UC)
|
|
# Lazy initialization of the table.
|
|
if not rtc_table:
|
|
rtc_table = RtcTable(self._core_service, self._signal_bus)
|
|
self._global_tables[RF_RTC_UC] = rtc_table
|
|
self._tables[(None, RF_RTC_UC)] = rtc_table
|
|
return rtc_table
|
|
|
|
def get_next_vpnv4_label(self):
|
|
# Get next available label
|
|
lbl = self._next_vpnv4_label
|
|
# Check if label is within max. range allowed.
|
|
if lbl > int(self._asbr_label_range[1]):
|
|
# Currently we log error message if we exceed configured range.
|
|
message = 'Have reached max label range'
|
|
LOG.error(message)
|
|
raise ValueError(message)
|
|
# Increment label by 1 as next label.
|
|
self._next_vpnv4_label += 1
|
|
return lbl
|
|
|
|
def get_nexthop_label(self, label_key):
|
|
return self._next_hop_label.get(label_key, None)
|
|
|
|
def set_nexthop_label(self, key, value):
|
|
self._next_hop_label[key] = value
|
|
|
|
def update_vrf_table_links(self, vrf_table, new_imp_rts,
|
|
removed_imp_rts):
|
|
"""Update mapping from RT to VRF table."""
|
|
assert vrf_table
|
|
if new_imp_rts:
|
|
self._link_vrf_table(vrf_table, new_imp_rts)
|
|
if removed_imp_rts:
|
|
self._remove_links_to_vrf_table_for_rts(vrf_table,
|
|
removed_imp_rts)
|
|
|
|
def re_install_net_ctrl_paths(self, vrf_table):
|
|
"""Re-installs paths from NC with current BGP policy.
|
|
|
|
Iterates over known paths from NC installed in `vrf4_table` and
|
|
adds new path with path attributes as per current VRF configuration.
|
|
"""
|
|
assert vrf_table
|
|
for dest in vrf_table.values():
|
|
for path in dest.known_path_list:
|
|
if path.source is None:
|
|
vrf_table.insert_vrf_path(
|
|
path.nlri,
|
|
path.nexthop,
|
|
gen_lbl=True
|
|
)
|
|
LOG.debug('Re-installed NC paths with current policy for table %s.',
|
|
vrf_table)
|
|
|
|
def _remove_links_to_vrf_table(self, vrf_table):
|
|
"""Removes any links to given `vrf_table`."""
|
|
assert vrf_table
|
|
vrf_conf = vrf_table.vrf_conf
|
|
self._remove_links_to_vrf_table_for_rts(vrf_table,
|
|
vrf_conf.import_rts)
|
|
|
|
def _remove_links_to_vrf_table_for_rts(self, vrf_table, rts):
|
|
rts_with_no_table = set()
|
|
affected_tables = set()
|
|
route_family = vrf_table.route_family
|
|
for rt in rts:
|
|
rt_rf_id = rt + ':' + str(route_family)
|
|
rt_specific_tables = self._tables_for_rt.get(rt_rf_id)
|
|
affected_tables.update(rt_specific_tables)
|
|
if rt_specific_tables:
|
|
try:
|
|
rt_specific_tables.remove(vrf_table)
|
|
except KeyError:
|
|
LOG.debug('Did not find table listed as interested '
|
|
'for its import RT: %s', rt)
|
|
if len(rt_specific_tables) == 0:
|
|
rts_with_no_table.add(rt)
|
|
|
|
# Remove records of RT that have no tables associated with it.
|
|
for rt in rts_with_no_table:
|
|
rt_rf_id = rt + ':' + str(route_family)
|
|
del self._tables_for_rt[rt_rf_id]
|
|
|
|
def create_and_link_vrf_table(self, vrf_conf):
|
|
"""Factory method to create VRF table for given `vrf_conf`.
|
|
|
|
Adds mapping to this table with appropriate scope. Also, adds mapping
|
|
for import RT of this VRF to created table to facilitate
|
|
importing/installing of paths from global tables.
|
|
Returns created table.
|
|
"""
|
|
|
|
route_family = vrf_conf.route_family
|
|
assert route_family in (VRF_RF_IPV4, VRF_RF_IPV6)
|
|
vrf_table = None
|
|
if route_family == VRF_RF_IPV4:
|
|
vrf_table = Vrf4Table(
|
|
vrf_conf, self._core_service, self._signal_bus
|
|
)
|
|
table_id = (vrf_conf.route_dist, route_family)
|
|
self._tables[table_id] = vrf_table
|
|
|
|
elif route_family == VRF_RF_IPV6:
|
|
vrf_table = Vrf6Table(
|
|
vrf_conf, self._core_service, self._signal_bus
|
|
)
|
|
table_id = (vrf_conf.route_dist, route_family)
|
|
self._tables[table_id] = vrf_table
|
|
|
|
assert vrf_table is not None
|
|
LOG.debug('Added new VrfTable with rd: %s and add_fmly: %s',
|
|
vrf_conf.route_dist, route_family)
|
|
|
|
import_rts = vrf_conf.import_rts
|
|
# If VRF is configured with import RT, we put this table
|
|
# in a list corresponding to this RT for easy access.
|
|
if import_rts:
|
|
self._link_vrf_table(vrf_table, import_rts)
|
|
|
|
return vrf_table
|
|
|
|
def _link_vrf_table(self, vrf_table, rt_list):
|
|
route_family = vrf_table.route_family
|
|
for rt in rt_list:
|
|
rt_rf_id = rt + ':' + str(route_family)
|
|
table_set = self._tables_for_rt.get(rt_rf_id)
|
|
if table_set is None:
|
|
table_set = set()
|
|
self._tables_for_rt[rt_rf_id] = table_set
|
|
table_set.add(vrf_table)
|
|
LOG.debug('Added VrfTable %s to import RT table list: %s',
|
|
vrf_table, rt)
|
|
|
|
def _clean_global_uninteresting_paths(self):
|
|
"""Marks paths that do not have any route targets of interest
|
|
for withdrawal.
|
|
|
|
Since global tables can have paths with route targets that are not
|
|
interesting any more, we have to clean these paths so that appropriate
|
|
withdraw are sent out to NC and other peers. Interesting route targets
|
|
change as VRF are modified or some filter is that specify what route
|
|
targets are allowed are updated. This clean up should only be done when
|
|
a route target is no longer considered interesting and some paths with
|
|
that route target was installing in any of the global table.
|
|
"""
|
|
uninteresting_dest_count = 0
|
|
interested_rts = self._rt_mgr.global_interested_rts
|
|
LOG.debug('Cleaning uninteresting paths. Global interested RTs %s',
|
|
interested_rts)
|
|
for route_family in [RF_IPv4_VPN, RF_IPv6_VPN, RF_RTC_UC]:
|
|
# TODO(PH): We currently do not install RT_NLRI paths based on
|
|
# extended path attributes (RT)
|
|
if route_family == RF_RTC_UC:
|
|
continue
|
|
table = self.get_global_table_by_route_family(route_family)
|
|
uninteresting_dest_count += \
|
|
table.clean_uninteresting_paths(interested_rts)
|
|
|
|
LOG.debug('Found %s number of destinations had uninteresting paths.',
|
|
uninteresting_dest_count)
|
|
|
|
def import_single_vpn_path_to_all_vrfs(self, vpn_path, path_rts=None):
|
|
"""Imports *vpnv4_path* to qualifying VRF tables.
|
|
|
|
Import RTs of VRF table is matched with RTs from *vpn4_path* and if we
|
|
have any common RTs we import the path into VRF.
|
|
"""
|
|
assert (vpn_path.route_family in
|
|
(Vpnv4Path.ROUTE_FAMILY, Vpnv6Path.ROUTE_FAMILY))
|
|
LOG.debug('Importing path %s to qualifying VRFs', vpn_path)
|
|
|
|
# If this path has no RTs we are done.
|
|
if not path_rts:
|
|
LOG.info('Encountered a path with no RTs: %s', vpn_path)
|
|
return
|
|
|
|
# We match path RTs with all VRFs that are interested in them.
|
|
interested_tables = set()
|
|
|
|
# Get route family of VRF to when this VPN Path can be imported to
|
|
route_family = RF_IPv4_UC
|
|
if vpn_path.route_family != RF_IPv4_VPN:
|
|
route_family = RF_IPv6_UC
|
|
for rt in path_rts:
|
|
rt_rf_id = rt + ':' + str(route_family)
|
|
vrf_rt_tables = self._tables_for_rt.get(rt_rf_id)
|
|
if vrf_rt_tables:
|
|
interested_tables.update(vrf_rt_tables)
|
|
|
|
if interested_tables:
|
|
# We iterate over all VRF tables that are interested in the RT
|
|
# of the given path and import this path into them.
|
|
route_dist = vpn_path.nlri.route_dist
|
|
for vrf_table in interested_tables:
|
|
if (vpn_path.source is not None and
|
|
route_dist != vrf_table.vrf_conf.route_dist):
|
|
update_vrf_dest = vrf_table.import_vpn_path(vpn_path)
|
|
# Queue the destination for further processing.
|
|
if update_vrf_dest is not None:
|
|
self._signal_bus.\
|
|
dest_changed(update_vrf_dest)
|
|
else:
|
|
# If we do not have any VRF with import RT that match with path RT
|
|
LOG.debug('No VRF table found that imports RTs: %s', path_rts)
|
|
|
|
def add_to_vrf(self, route_dist, prefix, next_hop, route_family):
|
|
"""Adds `prefix` to VRF identified by `route_dist` with given
|
|
`next_hop`.
|
|
|
|
Returns assigned VPN label.
|
|
"""
|
|
from ryu.services.protocols.bgp.core import BgpCoreError
|
|
|
|
assert route_dist and prefix and next_hop
|
|
if route_family not in (VRF_RF_IPV4, VRF_RF_IPV6):
|
|
raise ValueError('Given route_family %s is not supported.' %
|
|
route_family)
|
|
|
|
vrf_table = None
|
|
table_id = (route_dist, route_family)
|
|
if route_family == VRF_RF_IPV4:
|
|
vrf_table = self._tables.get(table_id)
|
|
if vrf_table is None:
|
|
raise BgpCoreError(desc='VRF table for RD: %s does not '
|
|
'exist.' % route_dist)
|
|
if not is_valid_ipv4_prefix(prefix) or not is_valid_ipv4(next_hop):
|
|
raise BgpCoreError(desc='Invalid Ipv4 prefix or nexthop.')
|
|
ip, masklen = prefix.split('/')
|
|
prefix = IPAddrPrefix(int(masklen), ip)
|
|
elif route_family == VRF_RF_IPV6:
|
|
vrf_table = self._tables.get(table_id)
|
|
if vrf_table is None:
|
|
raise BgpCoreError(desc='VRF table for RD: %s does not '
|
|
'exist.' % route_dist)
|
|
if not is_valid_ipv6_prefix(prefix) or not is_valid_ipv6(next_hop):
|
|
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_global_table(self, prefix, nexthop=None,
|
|
is_withdraw=False):
|
|
src_ver_num = 1
|
|
peer = None
|
|
# set mandatory path attributes
|
|
origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
|
|
aspath = BGPPathAttributeAsPath([[]])
|
|
|
|
pathattrs = OrderedDict()
|
|
pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
|
|
pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
|
|
|
|
net = netaddr.IPNetwork(prefix)
|
|
ip = str(net.ip)
|
|
masklen = net.prefixlen
|
|
if netaddr.valid_ipv4(ip):
|
|
_nlri = IPAddrPrefix(masklen, ip)
|
|
if nexthop is None:
|
|
nexthop = '0.0.0.0'
|
|
p = Ipv4Path
|
|
else:
|
|
_nlri = IP6AddrPrefix(masklen, ip)
|
|
if nexthop is None:
|
|
nexthop = '::'
|
|
p = Ipv6Path
|
|
|
|
new_path = p(peer, _nlri, src_ver_num,
|
|
pattrs=pathattrs, nexthop=nexthop,
|
|
is_withdraw=is_withdraw)
|
|
|
|
# add to global ipv4 table and propagates to neighbors
|
|
self.learn_path(new_path)
|
|
|
|
def remove_from_vrf(self, route_dist, prefix, route_family):
|
|
"""Removes `prefix` from VRF identified by `route_dist`.
|
|
|
|
Returns assigned VPN label.
|
|
"""
|
|
from ryu.services.protocols.bgp.core import BgpCoreError
|
|
# Validate given
|
|
if route_family not in (VRF_RF_IPV4, VRF_RF_IPV6):
|
|
raise BgpCoreError(desc='Unsupported route family %s' %
|
|
route_family)
|
|
val_ipv4 = route_family == VRF_RF_IPV4\
|
|
and is_valid_ipv4_prefix(prefix)
|
|
val_ipv6 = route_family == VRF_RF_IPV6\
|
|
and is_valid_ipv6_prefix(prefix)
|
|
|
|
if not val_ipv4 and not val_ipv6:
|
|
raise BgpCoreError(desc='Invalid prefix or nexthop.')
|
|
|
|
table_id = (route_dist, route_family)
|
|
if route_family == VRF_RF_IPV4:
|
|
vrf_table = self._tables.get(table_id)
|
|
if not vrf_table:
|
|
raise BgpCoreError(desc='Vrf for route distinguisher %s does '
|
|
'not exist.' % route_dist)
|
|
ip, masklen = prefix.split('/')
|
|
prefix = IPAddrPrefix(int(masklen), ip)
|
|
else:
|
|
vrf_table = self._tables.get(table_id)
|
|
if not vrf_table:
|
|
raise BgpCoreError(desc='Vrf for route distinguisher %s does '
|
|
'not exist.' % route_dist)
|
|
ip6, masklen = prefix.split('/')
|
|
prefix = IP6AddrPrefix(int(masklen), ip6)
|
|
# We do not check if we have a path to given prefix, we issue
|
|
# withdrawal. Hence multiple withdrawals have not side effect.
|
|
return vrf_table.insert_vrf_path(prefix, is_withdraw=True)
|
|
|
|
def clean_stale_routes(self, peer, route_family=None):
|
|
"""Removes old routes from `peer` from `route_family` table.
|
|
|
|
Routes/paths version number is compared with `peer`s current version
|
|
number.
|
|
"""
|
|
|
|
if route_family is not None:
|
|
if route_family not in SUPPORTED_GLOBAL_RF:
|
|
raise ValueError('Given route family %s is not supported.' %
|
|
route_family)
|
|
|
|
tables = [self._global_tables.get(route_family)]
|
|
else:
|
|
tables = self._global_tables.values()
|
|
for table in tables:
|
|
table.cleanup_paths_for_peer(peer)
|