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')
17 class PeerManager(object):
19 self, core_service, neighbors_conf,
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
27 # Peer to RTFilter map
29 # Value: set of RTs that constitute RT filter for this peer
30 self._peer_to_rtfilter_map = {}
31 self._neighbors_conf = neighbors_conf
35 return iter(self._peers.values())
37 def set_peer_to_rtfilter_map(self, new_map):
38 self._peer_to_rtfilter_map = new_map
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)
46 def remove_peer(self, neigh_conf):
47 neigh_ip_address = neigh_conf.ip_address
48 peer = self._peers.get(neigh_ip_address)
50 del self._peers[neigh_ip_address]
51 self._core_service.on_peer_removed(peer)
53 def get_by_addr(self, addr):
54 return self._peers.get(str(netaddr.IPAddress(addr)))
56 def on_peer_down(self, peer):
59 Cleans up the paths in global tables that was received from this peer.
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)
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
74 def curr_peer_rtfilter(self, peer):
75 return self._peer_to_rtfilter_map.get(peer)
77 def get_peers_in_established(self):
78 """Returns list of peers in established state."""
80 for peer in self._peers.values():
81 if peer.in_established:
82 est_peers.append(peer)
85 def resend_sent(self, route_family, peer):
86 """For given `peer` re-send sent paths.
89 - `route-family`: (RouteFamily) of the sent paths to re-send
90 - `peer`: (Peer) peer for which we need to re-send sent paths
92 if peer not in self._peers.values():
93 raise ValueError('Could not find given peer (%s)' % peer)
95 if route_family not in SUPPORTED_GLOBAL_RF:
97 'Given route family (%s) is not supported.' % route_family
100 # Iterate over the global table for given afi, safi and enqueue
102 table = self._table_manager.get_global_table_by_route_family(
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:
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
121 if p.med_set_by_target_neighbor or p.get_pattr(
122 BGP_ATTR_TYPE_MULTI_EXIT_DISC) is None:
124 clone_path_and_update_med_for_target_neighbor(
125 sent_route.path, peer.med
128 ogr = OutgoingRoute(sent_route.path,
129 for_route_refresh=True)
130 peer.enque_outgoing_msg(ogr)
132 def req_rr_to_non_rtc_peers(self, route_family):
133 """Makes refresh request to all peers for given address family.
135 Skips making request to peer that have valid RTC capability.
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
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)
148 def make_route_refresh_request(self, peer_ip, *route_families):
149 """Request route-refresh for peer with `peer_ip` for given
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.
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.' %
164 # If route-refresh is requested for all peers.
166 peer_list.extend(self.get_peers_in_established())
168 given_peer = self._peers.get(peer_ip)
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'
174 peer_list.append(given_peer)
176 # Make route refresh request to valid peers.
177 for peer in peer_list:
178 peer.request_route_refresh(*route_families)
182 def comm_all_rt_nlris(self, peer):
183 """Shares/communicates current best rt_nlri paths with this peers.
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
190 # First check if for this peer mpbgp-rtc is valid.
191 if not peer.is_mbgp_cap_valid(RF_RTC_UC):
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
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)
211 # Communicate all remote RT NLRIs
212 peer.communicate_path(best_path)
214 # Also communicate EOR as per RFC
215 peer.enque_end_of_rib(RF_RTC_UC)
217 def comm_all_best_paths(self, peer):
218 """Shares/communicates current best paths with this peers.
220 Can be used to send initial updates after we have established session
223 LOG.debug('Communicating current best path for all afi/safi except'
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:
229 if peer.is_mbgp_cap_valid(route_family):
230 for dest in table.values():
232 peer.communicate_path(dest.best_path)
234 def comm_new_best_to_bgp_peers(self, new_best_path):
235 """Communicates/enqueues given best path to be sent to all qualifying
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.
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)
248 comm_attr_na = comm_attr.has_comm_attr(
249 BGPPathAttributeCommunities.NO_ADVERTISE
251 # If we have NO_ADVERTISE attribute is present, we do not send
252 # UPDATE to any peers
254 LOG.debug('New best path has community attr. NO_ADVERTISE = %s'
255 '. Hence not advertising to any peer', comm_attr_na)
258 qualified_peers = self._collect_peers_of_interest(
262 # Distribute new best-path to qualified peers.
263 for peer in qualified_peers:
264 peer.communicate_path(new_best_path)
266 def _collect_peers_of_interest(self, new_best_path):
267 """Collect all peers that qualify for sharing a path with given RTs.
269 path_rts = new_best_path.get_rts()
270 qualified_peers = set(self._peers.values())
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
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
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
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)
294 if rt_filter is None:
295 qualified_peers.add(peer)
296 elif rt_filter.intersection(path_rts):
297 qualified_peers.add(peer)
299 return qualified_peers
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:
308 self.req_rr_to_non_rtc_peers(route_family)