backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / services / protocols / bgp / core_managers / peer_manager.py
1 import logging
2 import netaddr
3
4 from ryu.services.protocols.bgp.base import SUPPORTED_GLOBAL_RF
5 from ryu.services.protocols.bgp.model import OutgoingRoute
6 from ryu.services.protocols.bgp.peer import Peer
7 from ryu.lib.packet.bgp import BGPPathAttributeCommunities
8 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
9 from ryu.lib.packet.bgp import BGP_ATTR_TYPE_COMMUNITIES
10 from ryu.lib.packet.bgp import RF_RTC_UC
11 from ryu.lib.packet.bgp import RouteTargetMembershipNLRI
12 from ryu.services.protocols.bgp.utils.bgp \
13     import clone_path_and_update_med_for_target_neighbor
14 LOG = logging.getLogger('bgpspeaker.core_managers.peer_manager')
15
16
17 class PeerManager(object):
18     def __init__(
19             self, core_service, neighbors_conf,
20     ):
21         self._core_service = core_service
22         self._signal_bus = core_service.signal_bus
23         self._table_manager = core_service.table_manager
24         self._rt_manager = core_service.rt_manager
25         self._peers = {}
26
27         # Peer to RTFilter map
28         # Key: Peer instance
29         # Value: set of RTs that constitute RT filter for this peer
30         self._peer_to_rtfilter_map = {}
31         self._neighbors_conf = neighbors_conf
32
33     @property
34     def iterpeers(self):
35         return iter(self._peers.values())
36
37     def set_peer_to_rtfilter_map(self, new_map):
38         self._peer_to_rtfilter_map = new_map
39
40     def add_peer(self, neigh_conf, common_conf):
41         peer = Peer(common_conf, neigh_conf, self._core_service,
42                     self._signal_bus, self)
43         self._peers[neigh_conf.ip_address] = peer
44         self._core_service.on_peer_added(peer)
45
46     def remove_peer(self, neigh_conf):
47         neigh_ip_address = neigh_conf.ip_address
48         peer = self._peers.get(neigh_ip_address)
49         peer.stop()
50         del self._peers[neigh_ip_address]
51         self._core_service.on_peer_removed(peer)
52
53     def get_by_addr(self, addr):
54         return self._peers.get(str(netaddr.IPAddress(addr)))
55
56     def on_peer_down(self, peer):
57         """Peer down handler.
58
59         Cleans up the paths in global tables that was received from this peer.
60         """
61         LOG.debug('Cleaning obsolete paths whose source/version: %s/%s',
62                   peer.ip_address, peer.version_num)
63         # Launch clean-up for each global tables.
64         self._table_manager.clean_stale_routes(peer)
65
66     def _get_non_rtc_peers(self):
67         non_rtc_peer_list = set()
68         for peer in self._peers.values():
69             if (peer.in_established() and
70                     not peer.is_mpbgp_cap_valid(RF_RTC_UC)):
71                 non_rtc_peer_list.add(peer)
72         return non_rtc_peer_list
73
74     def curr_peer_rtfilter(self, peer):
75         return self._peer_to_rtfilter_map.get(peer)
76
77     def get_peers_in_established(self):
78         """Returns list of peers in established state."""
79         est_peers = []
80         for peer in self._peers.values():
81             if peer.in_established:
82                 est_peers.append(peer)
83         return est_peers
84
85     def resend_sent(self, route_family, peer):
86         """For given `peer` re-send sent paths.
87
88         Parameters:
89             - `route-family`: (RouteFamily) of the sent paths to re-send
90             - `peer`: (Peer) peer for which we need to re-send sent paths
91         """
92         if peer not in self._peers.values():
93             raise ValueError('Could not find given peer (%s)' % peer)
94
95         if route_family not in SUPPORTED_GLOBAL_RF:
96             raise ValueError(
97                 'Given route family (%s) is not supported.' % route_family
98             )
99
100         # Iterate over the global table for given afi, safi and enqueue
101         # out-going routes.
102         table = self._table_manager.get_global_table_by_route_family(
103             route_family
104         )
105
106         for destination in table.values():
107             # Check if this destination's sent - routes include this peer.
108             # i.e. check if this destinations was advertised and enqueue
109             # the path only if it was. If the current best-path has not been
110             # advertised before, it might already have a OutgoingRoute queued
111             # to be sent to the peer.
112             sent_routes = destination.sent_routes
113             if sent_routes is None or len(sent_routes) == 0:
114                 continue
115             for sent_route in sent_routes:
116                 if sent_route.sent_peer == peer:
117                     # update med - if previously med was set per neighbor or
118                     # wasn't set at all now it could have changed and we may
119                     # need to set new value there
120                     p = sent_route.path
121                     if p.med_set_by_target_neighbor or p.get_pattr(
122                             BGP_ATTR_TYPE_MULTI_EXIT_DISC) is None:
123                         sent_route.path = \
124                             clone_path_and_update_med_for_target_neighbor(
125                                 sent_route.path, peer.med
126                             )
127
128                     ogr = OutgoingRoute(sent_route.path,
129                                         for_route_refresh=True)
130                     peer.enque_outgoing_msg(ogr)
131
132     def req_rr_to_non_rtc_peers(self, route_family):
133         """Makes refresh request to all peers for given address family.
134
135         Skips making request to peer that have valid RTC capability.
136         """
137         assert route_family != RF_RTC_UC
138         for peer in self._peers.values():
139             # First check if peer is in established state
140             if (peer.in_established and
141                 # Check if peer has valid capability for given address
142                 # family
143                     peer.is_mbgp_cap_valid(route_family) and
144                 # Check if peer has valid capability for RTC
145                     not peer.is_mbgp_cap_valid(RF_RTC_UC)):
146                 peer.request_route_refresh(route_family)
147
148     def make_route_refresh_request(self, peer_ip, *route_families):
149         """Request route-refresh for peer with `peer_ip` for given
150         `route_families`.
151
152         Will make route-refresh request for a given `route_family` only if such
153         capability is supported and if peer is in ESTABLISHED state. Else, such
154         requests are ignored. Raises appropriate error in other cases. If
155         `peer_ip` is equal to 'all' makes refresh request to all valid peers.
156         """
157         LOG.debug('Route refresh requested for peer %s and route families %s',
158                   peer_ip, route_families)
159         if not SUPPORTED_GLOBAL_RF.intersection(route_families):
160             raise ValueError('Given route family(s) % is not supported.' %
161                              route_families)
162
163         peer_list = []
164         # If route-refresh is requested for all peers.
165         if peer_ip == 'all':
166             peer_list.extend(self.get_peers_in_established())
167         else:
168             given_peer = self._peers.get(peer_ip)
169             if not given_peer:
170                 raise ValueError('Invalid/unrecognized peer %s' % peer_ip)
171             if not given_peer.in_established:
172                 raise ValueError('Peer currently do not have established'
173                                  ' session.')
174             peer_list.append(given_peer)
175
176         # Make route refresh request to valid peers.
177         for peer in peer_list:
178             peer.request_route_refresh(*route_families)
179
180         return True
181
182     def comm_all_rt_nlris(self, peer):
183         """Shares/communicates current best rt_nlri paths with this peers.
184
185         Can be used to send initial updates after we have established session
186         with `peer` with which RTC capability is valid. Takes into account
187         peers RTC_AS setting and filters all RT NLRIs whose origin AS do not
188         match this setting.
189         """
190         # First check if for this peer mpbgp-rtc is valid.
191         if not peer.is_mbgp_cap_valid(RF_RTC_UC):
192             return
193
194         neigh_conf = self._neighbors_conf.get_neighbor_conf(peer.ip_address)
195         peer_rtc_as = neigh_conf.rtc_as
196         # Iterate over all RT_NLRI destination communicate qualifying RT_NLRIs
197         rtc_table = self._table_manager.get_rtc_table()
198         for dest in rtc_table.values():
199             best_path = dest.best_path
200             # Ignore a destination that currently does not have best path
201             if not best_path:
202                 continue
203
204             # If this is a local path
205             if best_path.source is None:
206                 # Check RT NLRI's origin AS matches peer RTC_AS setting
207                 origin_as = best_path.nlri.origin_as
208                 if origin_as == peer_rtc_as:
209                     peer.communicate_path(best_path)
210             else:
211                 # Communicate all remote RT NLRIs
212                 peer.communicate_path(best_path)
213
214         # Also communicate EOR as per RFC
215         peer.enque_end_of_rib(RF_RTC_UC)
216
217     def comm_all_best_paths(self, peer):
218         """Shares/communicates current best paths with this peers.
219
220         Can be used to send initial updates after we have established session
221         with `peer`.
222         """
223         LOG.debug('Communicating current best path for all afi/safi except'
224                   ' 1/132')
225         # We will enqueue best path from all global destination.
226         for route_family, table in self._table_manager.iter:
227             if route_family == RF_RTC_UC:
228                 continue
229             if peer.is_mbgp_cap_valid(route_family):
230                 for dest in table.values():
231                     if dest.best_path:
232                         peer.communicate_path(dest.best_path)
233
234     def comm_new_best_to_bgp_peers(self, new_best_path):
235         """Communicates/enqueues given best path to be sent to all qualifying
236         bgp peers.
237
238         If this path came from iBGP peers, it is not sent to other iBGP peers.
239         If this path has community-attribute, and if settings for recognize-
240         well-know attributes is set, we do as per [RFC1997], and queue outgoing
241         route only to qualifying BGP peers.
242         """
243         # Filter based on standard community
244         # If new best path has community attribute, it should be taken into
245         # account when sending UPDATE to peers.
246         comm_attr = new_best_path.get_pattr(BGP_ATTR_TYPE_COMMUNITIES)
247         if comm_attr:
248             comm_attr_na = comm_attr.has_comm_attr(
249                 BGPPathAttributeCommunities.NO_ADVERTISE
250             )
251             # If we have NO_ADVERTISE attribute is present, we do not send
252             # UPDATE to any peers
253             if comm_attr_na:
254                 LOG.debug('New best path has community attr. NO_ADVERTISE = %s'
255                           '. Hence not advertising to any peer', comm_attr_na)
256                 return
257
258         qualified_peers = self._collect_peers_of_interest(
259             new_best_path
260         )
261
262         # Distribute new best-path to qualified peers.
263         for peer in qualified_peers:
264             peer.communicate_path(new_best_path)
265
266     def _collect_peers_of_interest(self, new_best_path):
267         """Collect all peers that qualify for sharing a path with given RTs.
268         """
269         path_rts = new_best_path.get_rts()
270         qualified_peers = set(self._peers.values())
271
272         # Filter out peers based on RTC_AS setting if path is for RT_NLRI
273         qualified_peers = self._rt_manager.filter_by_origin_as(
274             new_best_path, qualified_peers
275         )
276
277         # We continue to filter out qualified peer based on path RTs
278         # If new best path has RTs, we need to share this UPDATE with
279         # qualifying peers
280         if path_rts:
281             # We add Default_RTC_NLRI to path RTs so that we can send it to
282             # peers that have expressed interest in all paths
283             path_rts.append(RouteTargetMembershipNLRI.DEFAULT_RT)
284             # All peers that do not have RTC capability qualify
285             qualified_peers = set(self._get_non_rtc_peers())
286             # Peers that have RTC capability and have common RT with the path
287             # also qualify
288             peer_to_rtfilter_map = self._peer_to_rtfilter_map
289             for peer, rt_filter in peer_to_rtfilter_map.items():
290                 # Ignore Network Controller (its not a BGP peer)
291                 if peer is None:
292                     continue
293
294                 if rt_filter is None:
295                     qualified_peers.add(peer)
296                 elif rt_filter.intersection(path_rts):
297                     qualified_peers.add(peer)
298
299         return qualified_peers
300
301     def schedule_rr_to_non_rtc_peers(self):
302         for route_family in SUPPORTED_GLOBAL_RF:
303             # Since we are dealing with peers that do not support RTC,
304             # ignore this address family
305             if route_family == RF_RTC_UC:
306                 continue
307
308             self.req_rr_to_non_rtc_peers(route_family)