backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / core_managers / table_manager.py
1 import logging
2 from collections import OrderedDict
3
4 import netaddr
5
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
39
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
70
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
75
76
77 LOG = logging.getLogger('bgpspeaker.core_managers.table_mixin')
78
79
80 class TableCoreManager(object):
81     """Methods performing core operations on tables."""
82
83     def __init__(self, core_service, common_conf):
84
85         self._tables = {}
86         self._rt_mgr = core_service.rt_manager
87         self._signal_bus = core_service.signal_bus
88
89         # (VRF) Tables to which the routes with a given route target
90         # should be imported.
91         #
92         # Key: RouteTarget
93         # Value: List of tables.
94         self._tables_for_rt = {}
95
96         # Global/Default tables, keyed by RouteFamily.
97         self._global_tables = {}
98
99         self._core_service = core_service
100         self._signal_bus = self._core_service.signal_bus
101
102         # VPN label range
103         self._asbr_label_range = common_conf.label_range
104
105         self._next_vpnv4_label = int(self._asbr_label_range[0])
106
107         self._next_hop_label = {}
108
109     @property
110     def global_tables(self):
111         return self._global_tables
112
113     def remove_vrf_by_vrf_conf(self, vrf_conf):
114
115         route_family = vrf_conf.route_family
116         assert route_family in SUPPORTED_VRF_RF
117         table_id = (vrf_conf.route_dist, route_family)
118
119         vrf_table = self._tables.pop(table_id)
120
121         self._remove_links_to_vrf_table(vrf_table)
122
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,
129                                                    for_withdrawal=True)
130                 self.learn_path(vpn_clone)
131         LOG.debug('VRF with RD %s marked for removal', vrf_conf.route_dist)
132
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
141
142         Checks if we have any path RT common with VRF table's import RT.
143         """
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()
156         else:
157             raise ValueError('Invalid VRF table route family: %s' %
158                              vrf_table.route_family)
159
160         vrf_table.import_vpn_paths_from_table(vpn_table, import_rts)
161
162     def learn_path(self, path):
163         """Inserts `path` into correct global table.
164
165         Since known paths to `Destination` has changes, we queue it for further
166         processing.
167         """
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)
173
174     def remember_sent_route(self, sent_route):
175         """Records `sent_route` inside proper table.
176
177         Records of `sent_route` from Adj-RIB-out.
178         """
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)
182
183     def on_interesting_rts_change(self, new_global_rts, removed_global_rts):
184         """Update global tables as interested RTs changed.
185
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.
189         """
190         # We add new RT NLRI and request RR for other peers.
191         if new_global_rts:
192             LOG.debug(
193                 'Sending route_refresh to all neighbors that'
194                 ' did not negotiate RTC capability.'
195             )
196
197             pm = self._core_service.peer_manager
198             pm.schedule_rr_to_non_rtc_peers()
199         if removed_global_rts:
200             LOG.debug(
201                 'Cleaning up global tables as some interested RTs were removed'
202             )
203             self._clean_global_uninteresting_paths()
204
205     def get_global_table_by_route_family(self, route_family):
206         if route_family not in SUPPORTED_GLOBAL_RF:
207             raise ValueError(
208                 'Given route family: %s currently not supported' % route_family
209             )
210
211         global_table = None
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()
234
235         return global_table
236
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))
240
241     def get_vrf_tables(self, vrf_rf=None):
242         vrf_tables = {}
243         for (scope_id, table_id), table in self._tables.items():
244             if scope_id is None:
245                 continue
246             if vrf_rf is not None and table_id != vrf_rf:
247                 continue
248             vrf_tables[(scope_id, table_id)] = table
249         return vrf_tables
250
251     def get_ipv4_table(self):
252         """Returns global IPv4 table.
253
254         Creates the table if it does not exist.
255         """
256
257         vpn_table = self._global_tables.get(RF_IPv4_UC)
258         # Lazy initialize the table.
259         if not vpn_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
263
264         return vpn_table
265
266     def get_ipv6_table(self):
267         table = self._global_tables.get(RF_IPv6_UC)
268         if not table:
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
272         return table
273
274     def get_vpn6_table(self):
275         """Returns global VPNv6 table.
276
277         Creates the table if it does not exist.
278         """
279         vpn_table = self._global_tables.get(RF_IPv6_VPN)
280         # Lazy initialize the table.
281         if not vpn_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
285
286         return vpn_table
287
288     def get_vpn4_table(self):
289         """Returns global VPNv6 table.
290
291         Creates the table if it does not exist.
292         """
293         vpn_table = self._global_tables.get(RF_IPv4_VPN)
294         # Lazy initialize the table.
295         if not vpn_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
299
300         return vpn_table
301
302     def get_evpn_table(self):
303         """Returns global EVPN table.
304
305         Creates the table if it does not exist.
306         """
307         evpn_table = self._global_tables.get(RF_L2_EVPN)
308         # Lazy initialization of the table.
309         if not evpn_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
313
314         return evpn_table
315
316     def get_rtc_table(self):
317         """Returns global RTC table.
318
319         Creates the table if it does not exist.
320         """
321         rtc_table = self._global_tables.get(RF_RTC_UC)
322         # Lazy initialization of the table.
323         if not rtc_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
327         return rtc_table
328
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'
336             LOG.error(message)
337             raise ValueError(message)
338             # Increment label by 1 as next label.
339         self._next_vpnv4_label += 1
340         return lbl
341
342     def get_ipv4fs_table(self):
343         """Returns global IPv4 Flow Specification table.
344
345         Creates the table if it does not exist.
346         """
347         ipv4fs_table = self._global_tables.get(RF_IPv4_FLOWSPEC)
348         # Lazy initialization of the table.
349         if not ipv4fs_table:
350             ipv4fs_table = IPv4FlowSpecTable(self._core_service,
351                                              self._signal_bus)
352             self._global_tables[RF_IPv4_FLOWSPEC] = ipv4fs_table
353             self._tables[(None, RF_IPv4_FLOWSPEC)] = ipv4fs_table
354
355         return ipv4fs_table
356
357     def get_ipv6fs_table(self):
358         """Returns global IPv6 Flow Specification table.
359
360         Creates the table if it does not exist.
361         """
362         ipv6fs_table = self._global_tables.get(RF_IPv6_FLOWSPEC)
363         # Lazy initialization of the table.
364         if not ipv6fs_table:
365             ipv6fs_table = IPv6FlowSpecTable(self._core_service,
366                                              self._signal_bus)
367             self._global_tables[RF_IPv6_FLOWSPEC] = ipv6fs_table
368             self._tables[(None, RF_IPv6_FLOWSPEC)] = ipv6fs_table
369
370         return ipv6fs_table
371
372     def get_vpnv4fs_table(self):
373         """Returns global VPNv4 Flow Specification table.
374
375         Creates the table if it does not exist.
376         """
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,
381                                                self._signal_bus)
382             self._global_tables[RF_VPNv4_FLOWSPEC] = vpnv4fs_table
383             self._tables[(None, RF_VPNv4_FLOWSPEC)] = vpnv4fs_table
384
385         return vpnv4fs_table
386
387     def get_vpnv6fs_table(self):
388         """Returns global VPNv6 Flow Specification table.
389
390         Creates the table if it does not exist.
391         """
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,
396                                                self._signal_bus)
397             self._global_tables[RF_VPNv6_FLOWSPEC] = vpnv6fs_table
398             self._tables[(None, RF_VPNv6_FLOWSPEC)] = vpnv6fs_table
399
400         return vpnv6fs_table
401
402     def get_l2vpnfs_table(self):
403         """Returns global L2VPN Flow Specification table.
404
405         Creates the table if it does not exist.
406         """
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,
411                                                self._signal_bus)
412             self._global_tables[RF_L2VPN_FLOWSPEC] = l2vpnfs_table
413             self._tables[(None, RF_L2VPN_FLOWSPEC)] = l2vpnfs_table
414
415         return l2vpnfs_table
416
417     def get_nexthop_label(self, label_key):
418         return self._next_hop_label.get(label_key, None)
419
420     def set_nexthop_label(self, key, value):
421         self._next_hop_label[key] = value
422
423     def update_vrf_table_links(self, vrf_table, new_imp_rts,
424                                removed_imp_rts):
425         """Update mapping from RT to VRF table."""
426         assert vrf_table
427         if new_imp_rts:
428             self._link_vrf_table(vrf_table, new_imp_rts)
429         if removed_imp_rts:
430             self._remove_links_to_vrf_table_for_rts(vrf_table,
431                                                     removed_imp_rts)
432
433     def re_install_net_ctrl_paths(self, vrf_table):
434         """Re-installs paths from NC with current BGP policy.
435
436         Iterates over known paths from NC installed in `vrf4_table` and
437         adds new path with path attributes as per current VRF configuration.
438         """
439         assert vrf_table
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(
444                         nlri=path.nlri,
445                         next_hop=path.nexthop,
446                         gen_lbl=True
447                     )
448         LOG.debug('Re-installed NC paths with current policy for table %s.',
449                   vrf_table)
450
451     def _remove_links_to_vrf_table(self, vrf_table):
452         """Removes any links to given `vrf_table`."""
453         assert vrf_table
454         vrf_conf = vrf_table.vrf_conf
455         self._remove_links_to_vrf_table_for_rts(vrf_table,
456                                                 vrf_conf.import_rts)
457
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
462         for rt in rts:
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:
467                 try:
468                     rt_specific_tables.remove(vrf_table)
469                 except KeyError:
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)
474
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]
479
480     def create_and_link_vrf_table(self, vrf_conf):
481         """Factory method to create VRF table for given `vrf_conf`.
482
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.
487         """
488         route_family = vrf_conf.route_family
489
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
502         else:
503             raise ValueError('Unsupported route family for VRF: %s' %
504                              route_family)
505
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
509
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)
513
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.
517         if import_rts:
518             self._link_vrf_table(vrf_table, import_rts)
519
520         return vrf_table
521
522     def _link_vrf_table(self, vrf_table, rt_list):
523         route_family = vrf_table.route_family
524         for rt in rt_list:
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:
528                 table_set = set()
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',
532                       vrf_table, rt)
533
534     def _clean_global_uninteresting_paths(self):
535         """Marks paths that do not have any route targets of interest
536         for withdrawal.
537
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.
545         """
546         uninteresting_dest_count = 0
547         interested_rts = self._rt_mgr.global_interested_rts
548         LOG.debug('Cleaning uninteresting paths. Global interested RTs %s',
549                   interested_rts)
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:
554                 continue
555             table = self.get_global_table_by_route_family(route_family)
556             uninteresting_dest_count += \
557                 table.clean_uninteresting_paths(interested_rts)
558
559         LOG.debug('Found %s number of destinations had uninteresting paths.',
560                   uninteresting_dest_count)
561
562     def import_single_vpn_path_to_all_vrfs(self, vpn_path, path_rts=None):
563         """Imports *vpn_path* to qualifying VRF tables.
564
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.
567         """
568         LOG.debug('Importing path %s to qualifying VRFs', vpn_path)
569
570         # If this path has no RTs we are done.
571         if not path_rts:
572             LOG.info('Encountered a path with no RTs: %s', vpn_path)
573             return
574
575         # We match path RTs with all VRFs that are interested in them.
576         interested_tables = set()
577
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
591         else:
592             raise ValueError('Unsupported route family for VRF: %s' %
593                              vpn_path.route_family)
594
595         for rt in path_rts:
596             rt_rf_id = rt + ':' + str(route_family)
597             vrf_rt_tables = self._tables_for_rt.get(rt_rf_id)
598             if vrf_rt_tables:
599                 interested_tables.update(vrf_rt_tables)
600
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:
611                         self._signal_bus.\
612                             dest_changed(update_vrf_dest)
613         else:
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)
616
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`.
623
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
626         `next_hop`.
627         If `is_withdraw` is True, remove a BGP route from the VRF table
628         and the given `next_hop` is ignored.
629
630         If `route_family` is VRF_RF_L2_EVPN, `route_type` and `kwargs`
631         are required to construct EVPN NLRI and `prefix` is ignored.
632
633         ``redundancy_mode`` specifies a redundancy mode type.
634
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.
639
640         Returns assigned VPN label.
641         """
642         from ryu.services.protocols.bgp.core import BgpCoreError
643
644         assert route_dist
645
646         if is_withdraw:
647             gen_lbl = False
648             next_hop = None
649         else:
650             gen_lbl = True
651             if not (is_valid_ipv4(next_hop) or is_valid_ipv6(next_hop)):
652                 raise BgpCoreError(
653                     desc='Invalid IPv4/IPv6 nexthop: %s' % next_hop)
654
655         vrf_table = self._tables.get((route_dist, route_family))
656         if vrf_table is None:
657             raise BgpCoreError(
658                 desc='VRF table  does not exist: route_dist=%s, '
659                      'route_family=%s' % (route_dist, route_family))
660
661         vni = kwargs.get('vni', None)
662
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:
674             assert route_type
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)
685             if esi is not 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))
693             if vni is not None:
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]
700                 gen_lbl = False
701             prefix = subclass(**kwargs)
702         else:
703             raise BgpCoreError(
704                 desc='Unsupported route family %s' % route_family)
705
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)
713
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.
717
718         ``flowspec_family`` specifies one of the flowspec family name.
719
720         ``route_dist`` specifies a route distinguisher value.
721
722         ``rules`` specifies NLRIs of Flow Specification as
723         a dictionary type value.
724
725         `` actions`` specifies Traffic Filtering Actions of
726         Flow Specification as a dictionary type value.
727
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.
731         """
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,
737         )
738
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)
742             try:
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)
749             try:
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)
756             try:
757                 communities = create_l2vpnflowspec_actions(actions)
758             except ValueError as e:
759                 raise BgpCoreError(desc=str(e))
760         else:
761             raise BgpCoreError(
762                 desc='Unsupported flowspec_family %s' % flowspec_family)
763
764         if vrf_table is None:
765             raise BgpCoreError(
766                 desc='VRF table does not exist: route_dist=%s, '
767                      'flowspec_family=%s' % (route_dist, flowspec_family))
768
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)
774
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`.
778
779         If `is_withdraw` is False, which is the default, add a BGP route
780         to the Global table.
781         If `is_withdraw` is True, remove a BGP route from the Global table.
782         """
783         src_ver_num = 1
784         peer = None
785         # set mandatory path attributes
786         origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
787         aspath = BGPPathAttributeAsPath([[]])
788
789         pathattrs = OrderedDict()
790         pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
791         pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
792
793         net = netaddr.IPNetwork(prefix)
794         addr = str(net.ip)
795         masklen = net.prefixlen
796         if ip.valid_ipv4(addr):
797             _nlri = IPAddrPrefix(masklen, addr)
798             if next_hop is None:
799                 next_hop = '0.0.0.0'
800             p = Ipv4Path
801         else:
802             _nlri = IP6AddrPrefix(masklen, addr)
803             if next_hop is None:
804                 next_hop = '::'
805             p = Ipv6Path
806
807         new_path = p(peer, _nlri, src_ver_num,
808                      pattrs=pathattrs, nexthop=next_hop,
809                      is_withdraw=is_withdraw)
810
811         # add to global table and propagates to neighbors
812         self.learn_path(new_path)
813
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.
817
818         ``flowspec_family`` specifies one of the Flow Specification
819          family name.
820
821         ``rules`` specifies NLRIs of Flow Specification as
822         a dictionary type value.
823
824         `` actions`` specifies Traffic Filtering Actions of
825         Flow Specification as a dictionary type value.
826
827         If `is_withdraw` is False, which is the default, add a BGP route
828         to the Global table.
829         If `is_withdraw` is True, remove a BGP route from the Global table.
830         """
831
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,
837         )
838
839         src_ver_num = 1
840         peer = None
841
842         # set mandatory path attributes
843         origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
844         aspath = BGPPathAttributeAsPath([[]])
845
846         pathattrs = OrderedDict()
847         pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
848         pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
849
850         if flowspec_family == FLOWSPEC_FAMILY_IPV4:
851             _nlri = FlowSpecIPv4NLRI.from_user(**rules)
852             p = IPv4FlowSpecPath
853
854             try:
855                 communities = create_v4flowspec_actions(actions)
856             except ValueError as e:
857                 raise BgpCoreError(desc=str(e))
858
859             if communities:
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)
865             p = IPv6FlowSpecPath
866
867             try:
868                 communities = create_v6flowspec_actions(actions)
869             except ValueError as e:
870                 raise BgpCoreError(desc=str(e))
871
872             if communities:
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
879
880             try:
881                 communities = create_l2vpnflowspec_actions(actions)
882             except ValueError as e:
883                 raise BgpCoreError(desc=str(e))
884
885             if communities:
886                 pathattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = (
887                     BGPPathAttributeExtendedCommunities(
888                         communities=communities))
889         else:
890             raise BgpCoreError(
891                 desc='Unsupported flowspec family %s' % flowspec_family)
892
893         new_path = p(peer, _nlri, src_ver_num,
894                      pattrs=pathattrs, is_withdraw=is_withdraw)
895
896         # add to global table and propagates to neighbors
897         self.learn_path(new_path)
898
899     def clean_stale_routes(self, peer, route_family=None):
900         """Removes old routes from `peer` from `route_family` table.
901
902         Routes/paths version number is compared with `peer`s current version
903         number.
904         """
905
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.' %
909                                  route_family)
910
911             tables = [self._global_tables.get(route_family)]
912         else:
913             tables = self._global_tables.values()
914         for table in tables:
915             table.cleanup_paths_for_peer(peer)