2 from collections import OrderedDict
6 from ryu.services.protocols.bgp.base import SUPPORTED_GLOBAL_RF
7 from ryu.services.protocols.bgp.info_base.rtc import RtcTable
8 from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path
9 from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Table
10 from ryu.services.protocols.bgp.info_base.ipv6 import Ipv6Path
11 from ryu.services.protocols.bgp.info_base.ipv6 import Ipv6Table
12 from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Table
13 from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Table
14 from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Table
15 from ryu.services.protocols.bgp.info_base.vrf6 import Vrf6Table
16 from ryu.services.protocols.bgp.info_base.vrfevpn import VrfEvpnTable
17 from ryu.services.protocols.bgp.info_base.evpn import EvpnTable
18 from ryu.services.protocols.bgp.info_base.ipv4fs import IPv4FlowSpecPath
19 from ryu.services.protocols.bgp.info_base.ipv4fs import IPv4FlowSpecTable
20 from ryu.services.protocols.bgp.info_base.vpnv4fs import VPNv4FlowSpecTable
21 from ryu.services.protocols.bgp.info_base.vrf4fs import Vrf4FlowSpecTable
22 from ryu.services.protocols.bgp.info_base.ipv6fs import IPv6FlowSpecPath
23 from ryu.services.protocols.bgp.info_base.ipv6fs import IPv6FlowSpecTable
24 from ryu.services.protocols.bgp.info_base.vpnv6fs import VPNv6FlowSpecTable
25 from ryu.services.protocols.bgp.info_base.vrf6fs import Vrf6FlowSpecTable
26 from ryu.services.protocols.bgp.info_base.l2vpnfs import L2VPNFlowSpecTable
27 from ryu.services.protocols.bgp.info_base.vrfl2vpnfs import L2vpnFlowSpecPath
28 from ryu.services.protocols.bgp.info_base.vrfl2vpnfs import L2vpnFlowSpecTable
29 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
30 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6
31 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2_EVPN
32 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4_FLOWSPEC
33 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6_FLOWSPEC
34 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2VPN_FLOWSPEC
35 from ryu.services.protocols.bgp.rtconf.vrfs import SUPPORTED_VRF_RF
36 from ryu.services.protocols.bgp.utils.bgp import create_v4flowspec_actions
37 from ryu.services.protocols.bgp.utils.bgp import create_v6flowspec_actions
38 from ryu.services.protocols.bgp.utils.bgp import create_l2vpnflowspec_actions
40 from ryu.lib import type_desc
41 from ryu.lib import ip
42 from ryu.lib.packet.bgp import RF_IPv4_UC
43 from ryu.lib.packet.bgp import RF_IPv6_UC
44 from ryu.lib.packet.bgp import RF_IPv4_VPN
45 from ryu.lib.packet.bgp import RF_IPv6_VPN
46 from ryu.lib.packet.bgp import RF_L2_EVPN
47 from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC
48 from ryu.lib.packet.bgp import RF_IPv6_FLOWSPEC
49 from ryu.lib.packet.bgp import RF_VPNv4_FLOWSPEC
50 from ryu.lib.packet.bgp import RF_VPNv6_FLOWSPEC
51 from ryu.lib.packet.bgp import RF_L2VPN_FLOWSPEC
52 from ryu.lib.packet.bgp import RF_RTC_UC
53 from ryu.lib.packet.bgp import BGPPathAttributeOrigin
54 from ryu.lib.packet.bgp import BGPPathAttributeAsPath
55 from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
56 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
57 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
58 from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
59 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
60 from ryu.lib.packet.bgp import EvpnEsi
61 from ryu.lib.packet.bgp import EvpnArbitraryEsi
62 from ryu.lib.packet.bgp import EvpnNLRI
63 from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
64 from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
65 from ryu.lib.packet.bgp import IPAddrPrefix
66 from ryu.lib.packet.bgp import IP6AddrPrefix
67 from ryu.lib.packet.bgp import FlowSpecIPv4NLRI
68 from ryu.lib.packet.bgp import FlowSpecIPv6NLRI
69 from ryu.lib.packet.bgp import FlowSpecL2VPNNLRI
71 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
72 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4_prefix
73 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6
74 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv6_prefix
77 LOG = logging.getLogger('bgpspeaker.core_managers.table_mixin')
80 class TableCoreManager(object):
81 """Methods performing core operations on tables."""
83 def __init__(self, core_service, common_conf):
86 self._rt_mgr = core_service.rt_manager
87 self._signal_bus = core_service.signal_bus
89 # (VRF) Tables to which the routes with a given route target
93 # Value: List of tables.
94 self._tables_for_rt = {}
96 # Global/Default tables, keyed by RouteFamily.
97 self._global_tables = {}
99 self._core_service = core_service
100 self._signal_bus = self._core_service.signal_bus
103 self._asbr_label_range = common_conf.label_range
105 self._next_vpnv4_label = int(self._asbr_label_range[0])
107 self._next_hop_label = {}
110 def global_tables(self):
111 return self._global_tables
113 def remove_vrf_by_vrf_conf(self, vrf_conf):
115 route_family = vrf_conf.route_family
116 assert route_family in SUPPORTED_VRF_RF
117 table_id = (vrf_conf.route_dist, route_family)
119 vrf_table = self._tables.pop(table_id)
121 self._remove_links_to_vrf_table(vrf_table)
123 # Withdraw the best-path whose source was NC since it may have been
124 # exported to VPN table.
125 for destination in vrf_table.values():
126 best_path = destination.best_path
127 if best_path and best_path.source is None:
128 vpn_clone = best_path.clone_to_vpn(vrf_conf.route_dist,
130 self.learn_path(vpn_clone)
131 LOG.debug('VRF with RD %s marked for removal', vrf_conf.route_dist)
133 def import_all_vpn_paths_to_vrf(self, vrf_table, import_rts=None):
134 """Imports VPNv4/6 or EVPN paths from Global/VPN table into given
135 VRFv4/6 or VRFEVPN table.
136 :param vrf_table: Vrf table to which we import
137 :type vrf_table: VrfTable
138 :param import_rts: import RTs to override default import_rts of
139 vrf table for this import
140 :type import_rts: set of strings
142 Checks if we have any path RT common with VRF table's import RT.
144 if vrf_table.route_family == Vrf4Table.ROUTE_FAMILY:
145 vpn_table = self.get_vpn4_table()
146 elif vrf_table.route_family == Vrf6Table.ROUTE_FAMILY:
147 vpn_table = self.get_vpn6_table()
148 elif vrf_table.route_family == VrfEvpnTable.ROUTE_FAMILY:
149 vpn_table = self.get_evpn_table()
150 elif vrf_table.route_family == Vrf4FlowSpecTable.ROUTE_FAMILY:
151 vpn_table = self.get_vpnv4fs_table()
152 elif vrf_table.route_family == Vrf6FlowSpecTable.ROUTE_FAMILY:
153 vpn_table = self.get_vpnv6fs_table()
154 elif vrf_table.route_family == L2vpnFlowSpecTable.ROUTE_FAMILY:
155 vpn_table = self.get_l2vpnfs_table()
157 raise ValueError('Invalid VRF table route family: %s' %
158 vrf_table.route_family)
160 vrf_table.import_vpn_paths_from_table(vpn_table, import_rts)
162 def learn_path(self, path):
163 """Inserts `path` into correct global table.
165 Since known paths to `Destination` has changes, we queue it for further
168 # Get VPN/Global table
169 table = self.get_global_table_by_route_family(path.route_family)
170 gpath_dest = table.insert(path)
171 # Since destination was updated, we enqueue it for processing.
172 self._signal_bus.dest_changed(gpath_dest)
174 def remember_sent_route(self, sent_route):
175 """Records `sent_route` inside proper table.
177 Records of `sent_route` from Adj-RIB-out.
179 route_family = sent_route.path.route_family
180 table = self.get_global_table_by_route_family(route_family)
181 table.insert_sent_route(sent_route)
183 def on_interesting_rts_change(self, new_global_rts, removed_global_rts):
184 """Update global tables as interested RTs changed.
186 Adds `new_rts` and removes `removed_rts` rt nlris. Does not check if
187 `new_rts` or `removed_rts` are already present. Schedules refresh
188 request to peers that do not participate in RTC address-family.
190 # We add new RT NLRI and request RR for other peers.
193 'Sending route_refresh to all neighbors that'
194 ' did not negotiate RTC capability.'
197 pm = self._core_service.peer_manager
198 pm.schedule_rr_to_non_rtc_peers()
199 if removed_global_rts:
201 'Cleaning up global tables as some interested RTs were removed'
203 self._clean_global_uninteresting_paths()
205 def get_global_table_by_route_family(self, route_family):
206 if route_family not in SUPPORTED_GLOBAL_RF:
208 'Given route family: %s currently not supported' % route_family
212 if route_family == RF_IPv4_UC:
213 global_table = self.get_ipv4_table()
214 elif route_family == RF_IPv6_UC:
215 global_table = self.get_ipv6_table()
216 elif route_family == RF_IPv4_VPN:
217 global_table = self.get_vpn4_table()
218 elif route_family == RF_IPv6_VPN:
219 global_table = self.get_vpn6_table()
220 elif route_family == RF_L2_EVPN:
221 global_table = self.get_evpn_table()
222 elif route_family == RF_IPv4_FLOWSPEC:
223 global_table = self.get_ipv4fs_table()
224 elif route_family == RF_IPv6_FLOWSPEC:
225 global_table = self.get_ipv6fs_table()
226 elif route_family == RF_VPNv4_FLOWSPEC:
227 global_table = self.get_vpnv4fs_table()
228 elif route_family == RF_VPNv6_FLOWSPEC:
229 global_table = self.get_vpnv6fs_table()
230 elif route_family == RF_L2VPN_FLOWSPEC:
231 global_table = self.get_l2vpnfs_table()
232 elif route_family == RF_RTC_UC:
233 global_table = self.get_rtc_table()
237 def get_vrf_table(self, vrf_rd, vrf_rf):
238 assert vrf_rd is not None
239 return self._tables.get((vrf_rd, vrf_rf))
241 def get_vrf_tables(self, vrf_rf=None):
243 for (scope_id, table_id), table in self._tables.items():
246 if vrf_rf is not None and table_id != vrf_rf:
248 vrf_tables[(scope_id, table_id)] = table
251 def get_ipv4_table(self):
252 """Returns global IPv4 table.
254 Creates the table if it does not exist.
257 vpn_table = self._global_tables.get(RF_IPv4_UC)
258 # Lazy initialize the table.
260 vpn_table = Ipv4Table(self._core_service, self._signal_bus)
261 self._global_tables[RF_IPv4_UC] = vpn_table
262 self._tables[(None, RF_IPv4_UC)] = vpn_table
266 def get_ipv6_table(self):
267 table = self._global_tables.get(RF_IPv6_UC)
269 table = Ipv6Table(self._core_service, self._signal_bus)
270 self._global_tables[RF_IPv6_UC] = table
271 self._tables[(None, RF_IPv6_UC)] = table
274 def get_vpn6_table(self):
275 """Returns global VPNv6 table.
277 Creates the table if it does not exist.
279 vpn_table = self._global_tables.get(RF_IPv6_VPN)
280 # Lazy initialize the table.
282 vpn_table = Vpnv6Table(self._core_service, self._signal_bus)
283 self._global_tables[RF_IPv6_VPN] = vpn_table
284 self._tables[(None, RF_IPv6_VPN)] = vpn_table
288 def get_vpn4_table(self):
289 """Returns global VPNv6 table.
291 Creates the table if it does not exist.
293 vpn_table = self._global_tables.get(RF_IPv4_VPN)
294 # Lazy initialize the table.
296 vpn_table = Vpnv4Table(self._core_service, self._signal_bus)
297 self._global_tables[RF_IPv4_VPN] = vpn_table
298 self._tables[(None, RF_IPv4_VPN)] = vpn_table
302 def get_evpn_table(self):
303 """Returns global EVPN table.
305 Creates the table if it does not exist.
307 evpn_table = self._global_tables.get(RF_L2_EVPN)
308 # Lazy initialization of the table.
310 evpn_table = EvpnTable(self._core_service, self._signal_bus)
311 self._global_tables[RF_L2_EVPN] = evpn_table
312 self._tables[(None, RF_L2_EVPN)] = evpn_table
316 def get_rtc_table(self):
317 """Returns global RTC table.
319 Creates the table if it does not exist.
321 rtc_table = self._global_tables.get(RF_RTC_UC)
322 # Lazy initialization of the table.
324 rtc_table = RtcTable(self._core_service, self._signal_bus)
325 self._global_tables[RF_RTC_UC] = rtc_table
326 self._tables[(None, RF_RTC_UC)] = rtc_table
329 def get_next_vpnv4_label(self):
330 # Get next available label
331 lbl = self._next_vpnv4_label
332 # Check if label is within max. range allowed.
333 if lbl > int(self._asbr_label_range[1]):
334 # Currently we log error message if we exceed configured range.
335 message = 'Have reached max label range'
337 raise ValueError(message)
338 # Increment label by 1 as next label.
339 self._next_vpnv4_label += 1
342 def get_ipv4fs_table(self):
343 """Returns global IPv4 Flow Specification table.
345 Creates the table if it does not exist.
347 ipv4fs_table = self._global_tables.get(RF_IPv4_FLOWSPEC)
348 # Lazy initialization of the table.
350 ipv4fs_table = IPv4FlowSpecTable(self._core_service,
352 self._global_tables[RF_IPv4_FLOWSPEC] = ipv4fs_table
353 self._tables[(None, RF_IPv4_FLOWSPEC)] = ipv4fs_table
357 def get_ipv6fs_table(self):
358 """Returns global IPv6 Flow Specification table.
360 Creates the table if it does not exist.
362 ipv6fs_table = self._global_tables.get(RF_IPv6_FLOWSPEC)
363 # Lazy initialization of the table.
365 ipv6fs_table = IPv6FlowSpecTable(self._core_service,
367 self._global_tables[RF_IPv6_FLOWSPEC] = ipv6fs_table
368 self._tables[(None, RF_IPv6_FLOWSPEC)] = ipv6fs_table
372 def get_vpnv4fs_table(self):
373 """Returns global VPNv4 Flow Specification table.
375 Creates the table if it does not exist.
377 vpnv4fs_table = self._global_tables.get(RF_VPNv4_FLOWSPEC)
378 # Lazy initialization of the table.
379 if not vpnv4fs_table:
380 vpnv4fs_table = VPNv4FlowSpecTable(self._core_service,
382 self._global_tables[RF_VPNv4_FLOWSPEC] = vpnv4fs_table
383 self._tables[(None, RF_VPNv4_FLOWSPEC)] = vpnv4fs_table
387 def get_vpnv6fs_table(self):
388 """Returns global VPNv6 Flow Specification table.
390 Creates the table if it does not exist.
392 vpnv6fs_table = self._global_tables.get(RF_VPNv6_FLOWSPEC)
393 # Lazy initialization of the table.
394 if not vpnv6fs_table:
395 vpnv6fs_table = VPNv6FlowSpecTable(self._core_service,
397 self._global_tables[RF_VPNv6_FLOWSPEC] = vpnv6fs_table
398 self._tables[(None, RF_VPNv6_FLOWSPEC)] = vpnv6fs_table
402 def get_l2vpnfs_table(self):
403 """Returns global L2VPN Flow Specification table.
405 Creates the table if it does not exist.
407 l2vpnfs_table = self._global_tables.get(RF_L2VPN_FLOWSPEC)
408 # Lazy initialization of the table.
409 if not l2vpnfs_table:
410 l2vpnfs_table = L2VPNFlowSpecTable(self._core_service,
412 self._global_tables[RF_L2VPN_FLOWSPEC] = l2vpnfs_table
413 self._tables[(None, RF_L2VPN_FLOWSPEC)] = l2vpnfs_table
417 def get_nexthop_label(self, label_key):
418 return self._next_hop_label.get(label_key, None)
420 def set_nexthop_label(self, key, value):
421 self._next_hop_label[key] = value
423 def update_vrf_table_links(self, vrf_table, new_imp_rts,
425 """Update mapping from RT to VRF table."""
428 self._link_vrf_table(vrf_table, new_imp_rts)
430 self._remove_links_to_vrf_table_for_rts(vrf_table,
433 def re_install_net_ctrl_paths(self, vrf_table):
434 """Re-installs paths from NC with current BGP policy.
436 Iterates over known paths from NC installed in `vrf4_table` and
437 adds new path with path attributes as per current VRF configuration.
440 for dest in vrf_table.values():
441 for path in dest.known_path_list:
442 if path.source is None:
443 vrf_table.insert_vrf_path(
445 next_hop=path.nexthop,
448 LOG.debug('Re-installed NC paths with current policy for table %s.',
451 def _remove_links_to_vrf_table(self, vrf_table):
452 """Removes any links to given `vrf_table`."""
454 vrf_conf = vrf_table.vrf_conf
455 self._remove_links_to_vrf_table_for_rts(vrf_table,
458 def _remove_links_to_vrf_table_for_rts(self, vrf_table, rts):
459 rts_with_no_table = set()
460 affected_tables = set()
461 route_family = vrf_table.route_family
463 rt_rf_id = rt + ':' + str(route_family)
464 rt_specific_tables = self._tables_for_rt.get(rt_rf_id)
465 affected_tables.update(rt_specific_tables)
466 if rt_specific_tables:
468 rt_specific_tables.remove(vrf_table)
470 LOG.debug('Did not find table listed as interested '
471 'for its import RT: %s', rt)
472 if len(rt_specific_tables) == 0:
473 rts_with_no_table.add(rt)
475 # Remove records of RT that have no tables associated with it.
476 for rt in rts_with_no_table:
477 rt_rf_id = rt + ':' + str(route_family)
478 del self._tables_for_rt[rt_rf_id]
480 def create_and_link_vrf_table(self, vrf_conf):
481 """Factory method to create VRF table for given `vrf_conf`.
483 Adds mapping to this table with appropriate scope. Also, adds mapping
484 for import RT of this VRF to created table to facilitate
485 importing/installing of paths from global tables.
486 Returns created table.
488 route_family = vrf_conf.route_family
490 if route_family == VRF_RF_IPV4:
491 vrf_table = Vrf4Table
492 elif route_family == VRF_RF_IPV6:
493 vrf_table = Vrf6Table
494 elif route_family == VRF_RF_L2_EVPN:
495 vrf_table = VrfEvpnTable
496 elif route_family == VRF_RF_IPV4_FLOWSPEC:
497 vrf_table = Vrf4FlowSpecTable
498 elif route_family == VRF_RF_IPV6_FLOWSPEC:
499 vrf_table = Vrf6FlowSpecTable
500 elif route_family == VRF_RF_L2VPN_FLOWSPEC:
501 vrf_table = L2vpnFlowSpecTable
503 raise ValueError('Unsupported route family for VRF: %s' %
506 vrf_table = vrf_table(vrf_conf, self._core_service, self._signal_bus)
507 table_id = (vrf_conf.route_dist, route_family)
508 self._tables[table_id] = vrf_table
510 assert vrf_table is not None
511 LOG.debug('Added new VrfTable with route_dist:%s and route_family:%s',
512 vrf_conf.route_dist, route_family)
514 import_rts = vrf_conf.import_rts
515 # If VRF is configured with import RT, we put this table
516 # in a list corresponding to this RT for easy access.
518 self._link_vrf_table(vrf_table, import_rts)
522 def _link_vrf_table(self, vrf_table, rt_list):
523 route_family = vrf_table.route_family
525 rt_rf_id = rt + ':' + str(route_family)
526 table_set = self._tables_for_rt.get(rt_rf_id)
527 if table_set is None:
529 self._tables_for_rt[rt_rf_id] = table_set
530 table_set.add(vrf_table)
531 LOG.debug('Added VrfTable %s to import RT table list: %s',
534 def _clean_global_uninteresting_paths(self):
535 """Marks paths that do not have any route targets of interest
538 Since global tables can have paths with route targets that are not
539 interesting any more, we have to clean these paths so that appropriate
540 withdraw are sent out to NC and other peers. Interesting route targets
541 change as VRF are modified or some filter is that specify what route
542 targets are allowed are updated. This clean up should only be done when
543 a route target is no longer considered interesting and some paths with
544 that route target was installing in any of the global table.
546 uninteresting_dest_count = 0
547 interested_rts = self._rt_mgr.global_interested_rts
548 LOG.debug('Cleaning uninteresting paths. Global interested RTs %s',
550 for route_family in [RF_IPv4_VPN, RF_IPv6_VPN, RF_RTC_UC]:
551 # TODO(PH): We currently do not install RT_NLRI paths based on
552 # extended path attributes (RT)
553 if route_family == RF_RTC_UC:
555 table = self.get_global_table_by_route_family(route_family)
556 uninteresting_dest_count += \
557 table.clean_uninteresting_paths(interested_rts)
559 LOG.debug('Found %s number of destinations had uninteresting paths.',
560 uninteresting_dest_count)
562 def import_single_vpn_path_to_all_vrfs(self, vpn_path, path_rts=None):
563 """Imports *vpn_path* to qualifying VRF tables.
565 Import RTs of VRF table is matched with RTs from *vpn4_path* and if we
566 have any common RTs we import the path into VRF.
568 LOG.debug('Importing path %s to qualifying VRFs', vpn_path)
570 # If this path has no RTs we are done.
572 LOG.info('Encountered a path with no RTs: %s', vpn_path)
575 # We match path RTs with all VRFs that are interested in them.
576 interested_tables = set()
578 # Get route family of VRF to when this VPN Path can be imported to
579 if vpn_path.route_family == RF_IPv4_VPN:
580 route_family = RF_IPv4_UC
581 elif vpn_path.route_family == RF_IPv6_VPN:
582 route_family = RF_IPv6_UC
583 elif vpn_path.route_family == RF_L2_EVPN:
584 route_family = RF_L2_EVPN
585 elif vpn_path.route_family == RF_VPNv4_FLOWSPEC:
586 route_family = RF_IPv4_FLOWSPEC
587 elif vpn_path.route_family == RF_VPNv6_FLOWSPEC:
588 route_family = RF_IPv6_FLOWSPEC
589 elif vpn_path.route_family == RF_L2VPN_FLOWSPEC:
590 route_family = RF_L2VPN_FLOWSPEC
592 raise ValueError('Unsupported route family for VRF: %s' %
593 vpn_path.route_family)
596 rt_rf_id = rt + ':' + str(route_family)
597 vrf_rt_tables = self._tables_for_rt.get(rt_rf_id)
599 interested_tables.update(vrf_rt_tables)
601 if interested_tables:
602 # We iterate over all VRF tables that are interested in the RT
603 # of the given path and import this path into them.
604 route_dist = vpn_path.nlri.route_dist
605 for vrf_table in interested_tables:
606 if (vpn_path.source is not None or
607 route_dist != vrf_table.vrf_conf.route_dist):
608 update_vrf_dest = vrf_table.import_vpn_path(vpn_path)
609 # Queue the destination for further processing.
610 if update_vrf_dest is not None:
612 dest_changed(update_vrf_dest)
614 # If we do not have any VRF with import RT that match with path RT
615 LOG.debug('No VRF table found that imports RTs: %s', path_rts)
617 def update_vrf_table(self, route_dist, prefix=None, next_hop=None,
618 route_family=None, route_type=None, tunnel_type=None,
619 is_withdraw=False, redundancy_mode=None,
620 pmsi_tunnel_type=None, **kwargs):
621 """Update a BGP route in the VRF table identified by `route_dist`
622 with the given `next_hop`.
624 If `is_withdraw` is False, which is the default, add a BGP route
625 to the VRF table identified by `route_dist` with the given
627 If `is_withdraw` is True, remove a BGP route from the VRF table
628 and the given `next_hop` is ignored.
630 If `route_family` is VRF_RF_L2_EVPN, `route_type` and `kwargs`
631 are required to construct EVPN NLRI and `prefix` is ignored.
633 ``redundancy_mode`` specifies a redundancy mode type.
635 ` `pmsi_tunnel_type` specifies the type of the PMSI tunnel attribute
636 used to encode the multicast tunnel identifier.
637 This field is advertised only if route_type is
638 EVPN_MULTICAST_ETAG_ROUTE.
640 Returns assigned VPN label.
642 from ryu.services.protocols.bgp.core import BgpCoreError
651 if not (is_valid_ipv4(next_hop) or is_valid_ipv6(next_hop)):
653 desc='Invalid IPv4/IPv6 nexthop: %s' % next_hop)
655 vrf_table = self._tables.get((route_dist, route_family))
656 if vrf_table is None:
658 desc='VRF table does not exist: route_dist=%s, '
659 'route_family=%s' % (route_dist, route_family))
661 vni = kwargs.get('vni', None)
663 if route_family == VRF_RF_IPV4:
664 if not is_valid_ipv4_prefix(prefix):
665 raise BgpCoreError(desc='Invalid IPv4 prefix: %s' % prefix)
666 ip, masklen = prefix.split('/')
667 prefix = IPAddrPrefix(int(masklen), ip)
668 elif route_family == VRF_RF_IPV6:
669 if not is_valid_ipv6_prefix(prefix):
670 raise BgpCoreError(desc='Invalid IPv6 prefix: %s' % prefix)
671 ip6, masklen = prefix.split('/')
672 prefix = IP6AddrPrefix(int(masklen), ip6)
673 elif route_family == VRF_RF_L2_EVPN:
675 if route_type == EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME:
676 # MPLS labels will be assigned automatically
677 kwargs['mpls_labels'] = []
678 if route_type == EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME:
679 # Inclusive Multicast Ethernet Tag Route does not have "vni",
680 # omit "vni" from "kwargs" here.
681 vni = kwargs.pop('vni', None)
682 subclass = EvpnNLRI._lookup_type_name(route_type)
683 kwargs['route_dist'] = route_dist
684 esi = kwargs.get('esi', None)
686 if isinstance(esi, dict):
687 esi_type = esi.get('type', 0)
688 esi_class = EvpnEsi._lookup_type(esi_type)
689 kwargs['esi'] = esi_class.from_jsondict(esi)
690 else: # isinstance(esi, numbers.Integral)
691 kwargs['esi'] = EvpnArbitraryEsi(
692 type_desc.Int9.from_user(esi))
694 # Disable to generate MPLS labels,
695 # because encapsulation type is not MPLS.
696 from ryu.services.protocols.bgp.api.prefix import (
697 TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE)
698 assert tunnel_type in [
699 None, TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]
701 prefix = subclass(**kwargs)
704 desc='Unsupported route family %s' % route_family)
706 # We do not check if we have a path to given prefix, we issue
707 # withdrawal. Hence multiple withdrawals have not side effect.
708 return vrf_table.insert_vrf_path(
709 nlri=prefix, next_hop=next_hop, gen_lbl=gen_lbl,
710 is_withdraw=is_withdraw, redundancy_mode=redundancy_mode,
711 vni=vni, tunnel_type=tunnel_type,
712 pmsi_tunnel_type=pmsi_tunnel_type)
714 def update_flowspec_vrf_table(self, flowspec_family, route_dist, rules,
715 actions=None, is_withdraw=False):
716 """Update a BGP route in the VRF table for Flow Specification.
718 ``flowspec_family`` specifies one of the flowspec family name.
720 ``route_dist`` specifies a route distinguisher value.
722 ``rules`` specifies NLRIs of Flow Specification as
723 a dictionary type value.
725 `` actions`` specifies Traffic Filtering Actions of
726 Flow Specification as a dictionary type value.
728 If `is_withdraw` is False, which is the default, add a BGP route
729 to the VRF table identified by `route_dist`.
730 If `is_withdraw` is True, remove a BGP route from the VRF table.
732 from ryu.services.protocols.bgp.core import BgpCoreError
733 from ryu.services.protocols.bgp.api.prefix import (
734 FLOWSPEC_FAMILY_VPNV4,
735 FLOWSPEC_FAMILY_VPNV6,
736 FLOWSPEC_FAMILY_L2VPN,
739 if flowspec_family == FLOWSPEC_FAMILY_VPNV4:
740 vrf_table = self._tables.get((route_dist, VRF_RF_IPV4_FLOWSPEC))
741 prefix = FlowSpecIPv4NLRI.from_user(**rules)
743 communities = create_v4flowspec_actions(actions)
744 except ValueError as e:
745 raise BgpCoreError(desc=str(e))
746 elif flowspec_family == FLOWSPEC_FAMILY_VPNV6:
747 vrf_table = self._tables.get((route_dist, VRF_RF_IPV6_FLOWSPEC))
748 prefix = FlowSpecIPv6NLRI.from_user(**rules)
750 communities = create_v6flowspec_actions(actions)
751 except ValueError as e:
752 raise BgpCoreError(desc=str(e))
753 elif flowspec_family == FLOWSPEC_FAMILY_L2VPN:
754 vrf_table = self._tables.get((route_dist, VRF_RF_L2VPN_FLOWSPEC))
755 prefix = FlowSpecL2VPNNLRI.from_user(route_dist, **rules)
757 communities = create_l2vpnflowspec_actions(actions)
758 except ValueError as e:
759 raise BgpCoreError(desc=str(e))
762 desc='Unsupported flowspec_family %s' % flowspec_family)
764 if vrf_table is None:
766 desc='VRF table does not exist: route_dist=%s, '
767 'flowspec_family=%s' % (route_dist, flowspec_family))
769 # We do not check if we have a path to given prefix, we issue
770 # withdrawal. Hence multiple withdrawals have not side effect.
771 vrf_table.insert_vrffs_path(
772 nlri=prefix, communities=communities,
773 is_withdraw=is_withdraw)
775 def update_global_table(self, prefix, next_hop=None, is_withdraw=False):
776 """Update a BGP route in the Global table for the given `prefix`
777 with the given `next_hop`.
779 If `is_withdraw` is False, which is the default, add a BGP route
781 If `is_withdraw` is True, remove a BGP route from the Global table.
785 # set mandatory path attributes
786 origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
787 aspath = BGPPathAttributeAsPath([[]])
789 pathattrs = OrderedDict()
790 pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
791 pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
793 net = netaddr.IPNetwork(prefix)
795 masklen = net.prefixlen
796 if ip.valid_ipv4(addr):
797 _nlri = IPAddrPrefix(masklen, addr)
802 _nlri = IP6AddrPrefix(masklen, addr)
807 new_path = p(peer, _nlri, src_ver_num,
808 pattrs=pathattrs, nexthop=next_hop,
809 is_withdraw=is_withdraw)
811 # add to global table and propagates to neighbors
812 self.learn_path(new_path)
814 def update_flowspec_global_table(self, flowspec_family, rules,
815 actions=None, is_withdraw=False):
816 """Update a BGP route in the Global table for Flow Specification.
818 ``flowspec_family`` specifies one of the Flow Specification
821 ``rules`` specifies NLRIs of Flow Specification as
822 a dictionary type value.
824 `` actions`` specifies Traffic Filtering Actions of
825 Flow Specification as a dictionary type value.
827 If `is_withdraw` is False, which is the default, add a BGP route
829 If `is_withdraw` is True, remove a BGP route from the Global table.
832 from ryu.services.protocols.bgp.core import BgpCoreError
833 from ryu.services.protocols.bgp.api.prefix import (
834 FLOWSPEC_FAMILY_IPV4,
835 FLOWSPEC_FAMILY_IPV6,
836 FLOWSPEC_FAMILY_L2VPN,
842 # set mandatory path attributes
843 origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
844 aspath = BGPPathAttributeAsPath([[]])
846 pathattrs = OrderedDict()
847 pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
848 pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
850 if flowspec_family == FLOWSPEC_FAMILY_IPV4:
851 _nlri = FlowSpecIPv4NLRI.from_user(**rules)
855 communities = create_v4flowspec_actions(actions)
856 except ValueError as e:
857 raise BgpCoreError(desc=str(e))
860 pathattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = (
861 BGPPathAttributeExtendedCommunities(
862 communities=communities))
863 elif flowspec_family == FLOWSPEC_FAMILY_IPV6:
864 _nlri = FlowSpecIPv6NLRI.from_user(**rules)
868 communities = create_v6flowspec_actions(actions)
869 except ValueError as e:
870 raise BgpCoreError(desc=str(e))
873 pathattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = (
874 BGPPathAttributeExtendedCommunities(
875 communities=communities))
876 elif flowspec_family == FLOWSPEC_FAMILY_L2VPN:
877 _nlri = FlowSpecL2VPNNLRI.from_user(**rules)
878 p = L2vpnFlowSpecPath
881 communities = create_l2vpnflowspec_actions(actions)
882 except ValueError as e:
883 raise BgpCoreError(desc=str(e))
886 pathattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = (
887 BGPPathAttributeExtendedCommunities(
888 communities=communities))
891 desc='Unsupported flowspec family %s' % flowspec_family)
893 new_path = p(peer, _nlri, src_ver_num,
894 pattrs=pathattrs, is_withdraw=is_withdraw)
896 # add to global table and propagates to neighbors
897 self.learn_path(new_path)
899 def clean_stale_routes(self, peer, route_family=None):
900 """Removes old routes from `peer` from `route_family` table.
902 Routes/paths version number is compared with `peer`s current version
906 if route_family is not None:
907 if route_family not in SUPPORTED_GLOBAL_RF:
908 raise ValueError('Given route family %s is not supported.' %
911 tables = [self._global_tables.get(route_family)]
913 tables = self._global_tables.values()
915 table.cleanup_paths_for_peer(peer)