backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / utils / rtfilter.py
1 # Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """
17  Module for RT Filter related functionality.
18 """
19 import logging
20
21 from ryu.lib.packet.bgp import RF_RTC_UC
22 from ryu.lib.packet.bgp import RouteTargetMembershipNLRI
23 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
24 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
25 from ryu.lib.packet.bgp import BGPPathAttributeAsPath
26 from ryu.lib.packet.bgp import BGPPathAttributeOrigin
27 from ryu.services.protocols.bgp.base import OrderedDict
28 from ryu.services.protocols.bgp.info_base.rtc import RtcPath
29
30 LOG = logging.getLogger('bgpspeaker.util.rtfilter')
31
32
33 class RouteTargetManager(object):
34     def __init__(self, core_service, neighbors_conf, vrfs_conf):
35         self._core_service = core_service
36         # TODO(PH): Consider extending VrfsConfListener and
37         # NeighborsConfListener
38         self._neighbors_conf = neighbors_conf
39         self._vrfs_conf = vrfs_conf
40
41         # Peer to its current RT filter map
42         # <key>/value = <peer_ip>/<rt filter set>
43         self._peer_to_rtfilter_map = {}
44
45         # Collection of import RTs of all configured VRFs
46         self._all_vrfs_import_rts_set = set()
47
48         # Collection of current RTC AS for all configured Neighbors
49         self._all_rtc_as_set = set()
50         # Interested RTs according to current entries in RTC global table
51         self._global_interested_rts = set()
52
53     @property
54     def peer_to_rtfilter_map(self):
55         return self._peer_to_rtfilter_map.copy()
56
57     @peer_to_rtfilter_map.setter
58     def peer_to_rtfilter_map(self, new_map):
59         self._peer_to_rtfilter_map = new_map.copy()
60
61     @property
62     def global_interested_rts(self):
63         return set(self._global_interested_rts)
64
65     def add_rt_nlri(self, route_target, is_withdraw=False):
66         assert route_target
67         # Since we allow RTC AS setting for each neighbor, we collect all we
68         # collect allRTC AS settings and add a RT NLRI using each AS number
69         rtc_as_set = set()
70         # Add RT NLRI with local AS
71         rtc_as_set.add(self._core_service.asn)
72         # Collect RTC AS from neighbor settings
73         rtc_as_set.update(self._neighbors_conf.rtc_as_set)
74         # Add RT NLRI path (withdraw) for each RTC AS
75         for rtc_as in rtc_as_set:
76             self._add_rt_nlri_for_as(rtc_as, route_target, is_withdraw)
77
78     def _add_rt_nlri_for_as(self, rtc_as, route_target, is_withdraw=False):
79         from ryu.services.protocols.bgp.core import EXPECTED_ORIGIN
80         rt_nlri = RouteTargetMembershipNLRI(rtc_as, route_target)
81         # Create a dictionary for path-attrs.
82         pattrs = OrderedDict()
83         if not is_withdraw:
84             # MpReachNlri and/or MpUnReachNlri attribute info. is contained
85             # in the path. Hence we do not add these attributes here.
86             pattrs[BGP_ATTR_TYPE_ORIGIN] = BGPPathAttributeOrigin(
87                 EXPECTED_ORIGIN)
88             pattrs[BGP_ATTR_TYPE_AS_PATH] = BGPPathAttributeAsPath([])
89
90         # Create Path instance and initialize appropriately.
91         path = RtcPath(None, rt_nlri, 0, is_withdraw=is_withdraw,
92                        pattrs=pattrs)
93         tm = self._core_service.table_manager
94         tm.learn_path(path)
95
96     def update_rtc_as_set(self):
97         """Syncs RT NLRIs for new and removed RTC_ASes.
98
99         This method should be called when a neighbor is added or removed.
100         """
101         # Compute the diffs in RTC_ASes
102         curr_rtc_as_set = self._neighbors_conf.rtc_as_set
103         # Always add local AS to RTC AS set
104         curr_rtc_as_set.add(self._core_service.asn)
105         removed_rtc_as_set = self._all_rtc_as_set - curr_rtc_as_set
106         new_rtc_as_set = curr_rtc_as_set - self._all_rtc_as_set
107
108         # Update to new RTC_AS set
109         self._all_rtc_as_set = curr_rtc_as_set
110
111         # Sync RT NLRI by adding/withdrawing as appropriate
112         for new_rtc_as in new_rtc_as_set:
113             for import_rt in self._all_vrfs_import_rts_set:
114                 self._add_rt_nlri_for_as(new_rtc_as, import_rt)
115         for removed_rtc_as in removed_rtc_as_set:
116             for import_rt in self._all_vrfs_import_rts_set:
117                 self._add_rt_nlri_for_as(removed_rtc_as, import_rt,
118                                          is_withdraw=True)
119
120     def update_local_rt_nlris(self):
121         """Does book-keeping of local RT NLRIs based on all configured VRFs.
122
123         Syncs all import RTs and RT NLRIs.
124         The method should be called when any VRFs are added/removed/changed.
125         """
126         current_conf_import_rts = set()
127         for vrf in self._vrfs_conf.vrf_confs:
128             current_conf_import_rts.update(vrf.import_rts)
129
130         removed_rts = self._all_vrfs_import_rts_set - current_conf_import_rts
131         new_rts = current_conf_import_rts - self._all_vrfs_import_rts_set
132         self._all_vrfs_import_rts_set = current_conf_import_rts
133
134         # Add new and withdraw removed local RtNlris
135         for new_rt in new_rts:
136             self.add_rt_nlri(new_rt)
137         for removed_rt in removed_rts:
138             self.add_rt_nlri(removed_rt, is_withdraw=True)
139
140     def on_rt_filter_chg_sync_peer(self, peer, new_rts, old_rts, table):
141         LOG.debug('RT Filter changed for peer %s, new_rts %s, old_rts %s ',
142                   peer, new_rts, old_rts)
143         for dest in table.values():
144             # If this destination does not have best path, we ignore it
145             if not dest.best_path:
146                 continue
147
148             desired_rts = set(dest.best_path.get_rts())
149
150             # If this path was sent to peer and if all path RTs are now not of
151             # interest, we need to send withdraw for this path to this peer
152             if dest.was_sent_to(peer):
153                 if not (desired_rts - old_rts):
154                     dest.withdraw_if_sent_to(peer)
155             else:
156                 # New RT could be Default RT, which means we need to share this
157                 # path
158                 desired_rts.add(RouteTargetMembershipNLRI.DEFAULT_RT)
159                 # If we have RT filter has new RTs that are common with path
160                 # RTs, then we send this path to peer
161                 if desired_rts.intersection(new_rts):
162                     peer.communicate_path(dest.best_path)
163
164     def _compute_global_interested_rts(self):
165         """Computes current global interested RTs for global tables.
166
167         Computes interested RTs based on current RT filters for peers. This
168         filter should be used to check if for RTs on a path that is installed
169         in any global table (expect RT Table).
170         """
171         interested_rts = set()
172         for rtfilter in self._peer_to_rtfilter_map.values():
173             interested_rts.update(rtfilter)
174
175         interested_rts.update(self._vrfs_conf.vrf_interested_rts)
176         # Remove default RT as it is not a valid RT for paths
177         # TODO(PH): Check if we have better alternative than add and remove
178         interested_rts.add(RouteTargetMembershipNLRI.DEFAULT_RT)
179         interested_rts.remove(RouteTargetMembershipNLRI.DEFAULT_RT)
180         return interested_rts
181
182     def update_interested_rts(self):
183         """Updates interested RT list.
184
185         Check if interested RTs have changes from previous check.
186         Takes appropriate action for new interesting RTs and removal of un-
187         interesting RTs.
188         """
189         prev_global_rts = self._global_interested_rts
190         curr_global_rts = self._compute_global_interested_rts()
191
192         new_global_rts = curr_global_rts - prev_global_rts
193         removed_global_rts = prev_global_rts - curr_global_rts
194
195         # Update current interested RTs for next iteration
196         self._global_interested_rts = curr_global_rts
197
198         LOG.debug('Global Interested RT changed, new RTs %s, removed RTs %s',
199                   new_global_rts, removed_global_rts)
200         tm = self._core_service.table_manager
201         tm.on_interesting_rts_change(new_global_rts, removed_global_rts)
202
203     def filter_by_origin_as(self, new_best_path, qualified_peers):
204         path_rf = new_best_path.route_family
205
206         # We need not filter any peer if this is not a RT NLRI path or if
207         # source of this path is remote peer (i.e. if this is not a local path)
208         if path_rf != RF_RTC_UC or new_best_path.source is not None:
209             return qualified_peers
210         else:
211             filtered_qualified_peers = []
212             rt_origin_as = new_best_path.nlri.origin_as
213             # Collect peers whose RTC_AS setting match paths RT Origin AS
214             for qualified_peer in qualified_peers:
215                 neigh_conf = \
216                     self._neighbors_conf.get_neighbor_conf(
217                         qualified_peer.ip_address)
218                 if neigh_conf.rtc_as == rt_origin_as:
219                     filtered_qualified_peers.append(qualified_peer)
220
221             # Update qualified peers to include only filtered peers
222             return filtered_qualified_peers