Line data Source code
1 : /*
2 : * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #ifndef SRC_BGP_BGP_EVPN_H_
7 : #define SRC_BGP_BGP_EVPN_H_
8 :
9 : #include <boost/ptr_container/ptr_map.hpp>
10 : #include <boost/scoped_ptr.hpp>
11 : #include <tbb/spin_rw_mutex.h>
12 :
13 : #include <list>
14 : #include <set>
15 : #include <vector>
16 : #include <atomic>
17 :
18 : #include "base/lifetime.h"
19 : #include "base/task_trigger.h"
20 : #include "base/address.h"
21 : #include "bgp/bgp_attr.h"
22 : #include "bgp/bgp_path.h"
23 : #include "db/db_entry.h"
24 :
25 : class BgpPath;
26 : class DBTablePartition;
27 : class DBTablePartBase;
28 : class EvpnRoute;
29 : class ErmVpnRoute;
30 : class EvpnState;
31 : class EvpnTable;
32 : class ErmVpnTable;
33 : class EvpnManagerPartition;
34 : class EvpnManager;
35 : class ShowEvpnTable;
36 : struct UpdateInfo;
37 :
38 : typedef boost::intrusive_ptr<EvpnState> EvpnStatePtr;
39 :
40 : /// @brief This is the base class for a multicast node in an EVPN instance.
41 : /// The node could either represent a local vRouter that's connected to a
42 : /// control node via XMPP or a remote vRouter/PE that's discovered via BGP.
43 : ///
44 : /// In normal operation, the EvpnMcastNodes corresponding to vRouters (local
45 : /// or remote) support edge replication, while those corresponding to EVPN PEs
46 : /// do not. However, for test purposes, it's possible to have vRouters that
47 : /// no not support edge replication.
48 : class EvpnMcastNode : public DBState {
49 : public:
50 : enum Type {
51 : LocalNode,
52 : RemoteNode
53 : };
54 : /// @brief Constructor for EvpnMcastNode. The type indicates whether this is a local
55 : /// or remote EvpnMcastNode.
56 : EvpnMcastNode(EvpnManagerPartition *partition, EvpnRoute *route,
57 : uint8_t type);
58 : EvpnMcastNode(EvpnManagerPartition *partition, EvpnRoute *route,
59 : uint8_t type, EvpnStatePtr state);
60 : ~EvpnMcastNode();
61 :
62 : /// @brief Update the label and attributes for a EvpnMcastNode.
63 : /// Return true if either of them changed.
64 : bool UpdateAttributes(EvpnRoute *route);
65 :
66 : /// @brief Handle update of EvpnLocalMcastNode.
67 : ///
68 : /// We delete and add the Inclusive Multicast route to ensure that all the
69 : /// attributes are updated. An in-place update is not always possible since
70 : /// the vRouter address is part of the key for the Inclusive Multicast route.
71 : virtual void TriggerUpdate() = 0;
72 :
73 8076 : EvpnStatePtr state() { return state_; };
74 8419 : void set_state(EvpnStatePtr state) { state_ = state; };
75 57968 : EvpnRoute *route() { return route_; }
76 29998 : uint8_t type() const { return type_; }
77 18289 : const BgpAttr *attr() const { return attr_.get(); }
78 18289 : uint32_t label() const { return label_; }
79 119509 : Ip4Address address() const { return address_; }
80 1648 : Ip4Address replicator_address() const { return replicator_address_; }
81 130170 : bool edge_replication_not_supported() const {
82 130170 : return edge_replication_not_supported_;
83 : }
84 20508 : bool assisted_replication_supported() const {
85 20508 : return assisted_replication_supported_;
86 : }
87 121343 : bool assisted_replication_leaf() const {
88 121343 : return assisted_replication_leaf_;
89 : }
90 :
91 : protected:
92 : EvpnManagerPartition *partition_;
93 : EvpnStatePtr state_;
94 : EvpnRoute *route_;
95 : uint8_t type_;
96 : BgpAttrPtr attr_;
97 : uint32_t label_;
98 : Ip4Address address_;
99 : Ip4Address replicator_address_;
100 : bool edge_replication_not_supported_;
101 : bool assisted_replication_supported_;
102 : bool assisted_replication_leaf_;
103 :
104 : private:
105 : DISALLOW_COPY_AND_ASSIGN(EvpnMcastNode);
106 : };
107 :
108 :
109 : /// @brief This class represents (in the context of an EVPN instance) a local
110 : /// vRouter that's connected to a control node via XMPP. An EvpnLocalMcastNode
111 : /// gets created and associated as DBState with the broadcast MAC route advertised
112 : /// by the vRouter.
113 : ///
114 : /// The EvpnLocalMcastNode mainly exists to translate the broadcast MAC route
115 : /// advertised by the vRouter into an EVPN Inclusive Multicast route. In the
116 : /// other direction, EvpnLocalMcastNode serves as the anchor point to build a
117 : /// vRouter specific ingress replication olist so that the vRouter can send
118 : /// multicast traffic to EVPN PEs (and possibly vRouters in test environment)
119 : /// that do not support edge replication.
120 : ///
121 : /// An Inclusive Multicast route is added for each EvpnLocalMcastNode. The
122 : /// attributes of the Inclusive Multicast route are based on the broadcast
123 : /// MAC route corresponding to the EvpnLocalMcastNode. The label for the
124 : /// broadcast MAC route is advertised as the label for ingress replication
125 : /// in the PmsiTunnel attribute.
126 : class EvpnLocalMcastNode : public EvpnMcastNode {
127 : public:
128 :
129 : /// @brief Constructor for EvpnLocalMcastNode.
130 : ///
131 : /// Add an Inclusive Multicast route corresponding to Broadcast MAC route.
132 : ///
133 : /// Need to Notify the Broadcast MAC route so that the table Export method
134 : /// can run and build the OList. OList is not built till EvpnLocalMcastNode
135 : /// has been created.
136 : EvpnLocalMcastNode(EvpnManagerPartition *partition, EvpnRoute *route);
137 : EvpnLocalMcastNode(EvpnManagerPartition *partition, EvpnRoute *route,
138 : EvpnStatePtr state);
139 : virtual ~EvpnLocalMcastNode();
140 :
141 : /// @brief Handle update of EvpnLocalMcastNode.
142 : ///
143 : /// We delete and add the Inclusive Multicast route to ensure that all the
144 : /// attributes are updated. An in-place update is not always possible since
145 : /// the vRouter address is part of the key for the Inclusive Multicast route.
146 : virtual void TriggerUpdate();
147 :
148 : UpdateInfo *GetUpdateInfo(EvpnRoute *route);
149 : EvpnRoute *inclusive_mcast_route() { return inclusive_mcast_route_; }
150 :
151 : private:
152 : /// @brief Add Inclusive Multicast route for this EvpnLocalMcastNode.
153 : /// The attributes are based on the Broadcast MAC route.
154 : void AddInclusiveMulticastRoute();
155 :
156 : /// @brief Delete Inclusive Multicast route for this EvpnLocalMcastNode.
157 : void DeleteInclusiveMulticastRoute();
158 :
159 : EvpnRoute *inclusive_mcast_route_;
160 :
161 : DISALLOW_COPY_AND_ASSIGN(EvpnLocalMcastNode);
162 : };
163 :
164 :
165 : /// @brief This class represents (in the context of an EVPN instance) a remote
166 : /// vRouter or PE discovered via BGP. An EvpnRemoteMcastNode is created and
167 : /// associated as DBState with the Inclusive Multicast route in question.
168 : ///
169 : /// An EvpnRemoteMcastNode also gets created for the Inclusive Multicast route
170 : /// that's added for each EvpnLocalMcastNode. This is required only to support
171 : /// test mode where vRouter acts like PE that doesn't support edge replication.
172 : class EvpnRemoteMcastNode : public EvpnMcastNode {
173 : public:
174 : EvpnRemoteMcastNode(EvpnManagerPartition *partition, EvpnRoute *route);
175 : EvpnRemoteMcastNode(EvpnManagerPartition *partition, EvpnRoute *route,
176 : EvpnStatePtr state);
177 : virtual ~EvpnRemoteMcastNode();
178 :
179 : /// @brief Handle update of EvpnRemoteMcastNode.
180 : virtual void TriggerUpdate();
181 :
182 : private:
183 : DISALLOW_COPY_AND_ASSIGN(EvpnRemoteMcastNode);
184 : };
185 :
186 :
187 : /// @brief This class represents a remote EVPN segment that has 2 or more PEs
188 : /// that are multi-homed to it. An EvpnSegment is created when we see a MAC
189 : /// route with a non-NULL ESI or when we see an AD route for the ESI in question.
190 : ///
191 : /// An EvpnSegment contains a vector of lists of MAC routes that are dependent
192 : /// on it. There's a list entry in the vector for each DB partition. All the
193 : /// MAC routes in a given partition that are associated with the EvpnSegment are
194 : /// in inserted in the list for that partition. The lists are updated as and
195 : /// when the EvpnSegment for MAC routes is updated.
196 : ///
197 : /// An EvpnSegment contains a list of Remote PEs that have advertised per-ESI
198 : /// AD routes for the EVPN segment in question. The list is updated when paths
199 : /// are added/deleted from the AD route. A change in the contents of the list
200 : /// triggers an update of all dependent MAC routes, so that their aliased paths
201 : /// can be updated. The single-active state of the EvpnSegment is also updated
202 : /// when the PE list is updated. The PE list is updated from the context of the
203 : /// bgp::EvpnSegment task.
204 : class EvpnSegment : public DBState {
205 : public:
206 : EvpnSegment(EvpnManager *evpn_manager, const EthernetSegmentId &esi);
207 : ~EvpnSegment();
208 :
209 : class RemotePe {
210 : public:
211 : RemotePe(const BgpPath *path);
212 :
213 : /// @brief Equality operator for EvpnSegment::RemotePe.
214 : /// Do not compare esi_valid and single_active fields since they are
215 : /// derived.
216 : bool operator==(const RemotePe &rhs) const;
217 :
218 : bool esi_valid;
219 : bool single_active;
220 : const IPeer *peer;
221 : BgpAttrPtr attr;
222 : uint32_t flags;
223 : BgpPath::PathSource src;
224 : };
225 :
226 : typedef std::list<RemotePe> RemotePeList;
227 : typedef RemotePeList::const_iterator const_iterator;
228 :
229 123 : const_iterator begin() const { return pe_list_.begin(); }
230 234 : const_iterator end() const { return pe_list_.end(); }
231 :
232 : /// @brief Add the given MAC route as a dependent of this EvpnSegment.
233 : void AddMacRoute(size_t part_id, EvpnRoute *route);
234 :
235 : /// @brief Delete the given MAC route as a dependent of this EvpnSegment.
236 : /// Trigger deletion of the EvpnSegment if there are no dependent
237 : /// routes in the partition.
238 : void DeleteMacRoute(size_t part_id, EvpnRoute *route);
239 :
240 : /// @brief Trigger an update of all dependent MAC routes for this EvpnSegment.
241 : /// Note that the bgp::EvpnSegment task is mutually exclusive with the
242 : /// db::DBTable task.
243 : void TriggerMacRouteUpdate();
244 :
245 : /// @brief Update the PE list for this EvpnSegment. This should be called when
246 : /// the AutoDisocvery route is updated.
247 : /// Return true if there's a change in the PE list i.e. if an entry is
248 : /// added, deleted or updated.
249 : bool UpdatePeList();
250 :
251 : /// @brief Return true if it's safe to delete this EvpnSegment.
252 : bool MayDelete() const;
253 :
254 68 : const EthernetSegmentId &esi() const { return esi_; }
255 56 : EvpnRoute *esi_ad_route() { return esi_ad_route_; }
256 22 : void set_esi_ad_route(EvpnRoute *esi_ad_route) {
257 22 : esi_ad_route_ = esi_ad_route;
258 22 : }
259 22 : void clear_esi_ad_route() { esi_ad_route_ = NULL; }
260 429 : bool single_active() const { return single_active_; }
261 :
262 : private:
263 : typedef std::set<EvpnRoute *> RouteList;
264 : typedef std::vector<RouteList> RouteListVector;
265 :
266 : EvpnManager *evpn_manager_;
267 : EthernetSegmentId esi_;
268 : EvpnRoute *esi_ad_route_;
269 : bool single_active_;
270 : RouteListVector route_lists_;
271 : RemotePeList pe_list_;
272 :
273 : DISALLOW_COPY_AND_ASSIGN(EvpnSegment);
274 : };
275 :
276 :
277 : /// @brief This class represents the EvpnManager state associated with a MAC route.
278 : ///
279 : /// In the steady state, a EvpnMacState should exist only for MAC routes that
280 : /// have a non-zero ESI. The segment_ field is a pointer to the EvpnSegment for
281 : /// the ESI in question. A EvpnMacState is created from the route listener when
282 : /// we see a MAC route with a non-zero ESI. It is deleted after processing the
283 : /// MAC route if the route has been deleted or if it has a zero ESI.
284 : //
285 : /// The AliasedPathList keeps track of the aliased BgpPaths that we've added.
286 : /// An aliased BgpPath is added for each remote PE for all-active EvpnSegment.
287 : /// The contents of the AliasedPathList are updated when the ESI for the MAC
288 : /// route changes or when the list of remote PEs for the EvpnSegment changes.
289 : class EvpnMacState : public DBState {
290 : public:
291 : EvpnMacState(EvpnManager *evpn_manager, EvpnRoute *route);
292 : ~EvpnMacState();
293 :
294 : /// @brief Update aliased BgpPaths for the EvpnRoute based on the remote PEs
295 : /// for the EvpnSegment.
296 : /// Return true if the list of aliased paths is modified.
297 : bool ProcessMacRouteAliasing();
298 :
299 216 : EvpnSegment *segment() { return segment_; }
300 : const EvpnSegment *segment() const { return segment_; }
301 54 : void set_segment(EvpnSegment *segment) { segment_ = segment; }
302 54 : void clear_segment() { segment_ = NULL; }
303 :
304 : private:
305 : typedef std::set<BgpPath *> AliasedPathList;
306 :
307 : /// @brief Add the BgpPath specified by the iterator to the aliased path list.
308 : /// Also inserts the BgpPath to the BgpRoute.
309 : void AddAliasedPath(AliasedPathList::const_iterator it);
310 :
311 : /// @brief Delete the BgpPath specified by the iterator from the aliased path list.
312 : /// Also deletes the BgpPath from the BgpRoute.
313 : void DeleteAliasedPath(AliasedPathList::const_iterator it);
314 :
315 : /// @brief Find or create the matching aliased BgpPath.
316 : BgpPath *LocateAliasedPath(const EvpnSegment::RemotePe *remote_pe,
317 : uint32_t label);
318 :
319 : EvpnManager *evpn_manager_;
320 : EvpnRoute *route_;
321 : EvpnSegment *segment_;
322 : AliasedPathList aliased_path_list_;
323 :
324 : DISALLOW_COPY_AND_ASSIGN(EvpnMacState);
325 : };
326 :
327 : /// @brief This class holds Evpn state for a particular <S,G> at any given time.
328 : ///
329 : /// In Evpn state machinery, different types of routes are sent and received at
330 : /// different phases of processing. This class holds all relevant information
331 : /// associated with an <S,G>.
332 : ///
333 : /// This is a refcounted class which is referred by DB States of different
334 : /// routes. When the refcount reaches 0, (last referring db state is deleted),
335 : /// this object is deleted from the container map and then destroyed.
336 : ///
337 : /// global_ermvpn_tree_rt_
338 : /// This is a reference to GlobalErmVpnRoute associated with the ErmVpnTree
339 : /// used in the data plane for this <S,G>. This route is created/updated
340 : /// when ErmVpn notifies changes to ermvpn routes.
341 : ///
342 : /// states_
343 : /// This is the parent map that holds 'this' EvpnState pointer as the value
344 : /// for the associated SG key. When the refcount reaches zero, it indicates
345 : /// that there is no reference to this state from of the DB States associated
346 : /// with any Evpn route. Hence at that time, this state is removed this map
347 : /// states_ and destroyed. This map actually sits inside the associated
348 : /// EvpnProjectManagerParitition object.
349 : class EvpnState {
350 : public:
351 : typedef std::set<EvpnRoute *> RoutesSet;
352 : typedef std::map<EvpnRoute *, BgpAttrPtr> RoutesMap;
353 :
354 : /// @brief Simple structure to hold <S,G>. Source as "0.0.0.0" can be used to encode
355 : /// <*,G> as well.
356 : struct SG {
357 : SG(const Ip4Address &source, const Ip4Address &group);
358 : SG(const IpAddress &source, const IpAddress &group);
359 : explicit SG(const ErmVpnRoute *route);
360 : explicit SG(const EvpnRoute *route);
361 : bool operator<(const SG &other) const;
362 :
363 : IpAddress source;
364 : IpAddress group;
365 : };
366 :
367 : typedef std::map<SG, EvpnState *> StatesMap;
368 :
369 : /// @brief A global MVPN state for a given <S.G> within a EvpnProjectManager.
370 : EvpnState(const SG &sg, StatesMap *states, EvpnManager* manager);
371 :
372 : virtual ~EvpnState();
373 : const SG &sg() const;
374 : ErmVpnRoute *global_ermvpn_tree_rt();
375 : const ErmVpnRoute *global_ermvpn_tree_rt() const;
376 : void set_global_ermvpn_tree_rt(ErmVpnRoute *global_ermvpn_tree_rt);
377 9107 : RoutesSet &smet_routes() { return smet_routes_; }
378 : const RoutesSet &smet_routes() const { return smet_routes_; }
379 : const StatesMap *states() const { return states_; }
380 10744 : StatesMap *states() { return states_; }
381 2687 : EvpnManager *manager() { return manager_; }
382 : const EvpnManager *manager() const { return manager_; }
383 : int refcount() const { return refcount_; }
384 :
385 : private:
386 : friend class EvpnMcastNode;
387 : friend class EvpnManagerPartition;
388 :
389 : /// @brief Increment refcont atomically.
390 : friend void intrusive_ptr_add_ref(EvpnState *evpn_state);
391 :
392 : /// @brief Decrement refcount of an evpn_state. If the refcount falls to 1, it implies
393 : /// that there is no more reference to this particular state from any other data
394 : /// structure. Hence, it can be deleted from the container map and destroyed as
395 : /// well.
396 : friend void intrusive_ptr_release(EvpnState *evpn_state);
397 :
398 : const ErmVpnTable *table() const;
399 :
400 : SG sg_;
401 : ErmVpnRoute *global_ermvpn_tree_rt_;
402 : RoutesSet smet_routes_;
403 : StatesMap *states_;
404 : EvpnManager *manager_;
405 : std::atomic<int> refcount_;
406 :
407 : DISALLOW_COPY_AND_ASSIGN(EvpnState);
408 : };
409 :
410 :
411 : /// @brief This class represents a partition in the EvpnManager.
412 : ///
413 : /// It is used to keep track of local and remote EvpnMcastNodes that belong to
414 : /// the partition. The partition is determined on the ethernet tag in the IM
415 : /// route.
416 : ///
417 : /// An EvpnManagerPartition contains a set of MAC routes whose alias paths need
418 : /// to be updated. Entries are added to the list using the TriggerMacRouteUpdate
419 : /// method.
420 : class EvpnManagerPartition {
421 : public:
422 : typedef EvpnState::SG SG;
423 : typedef std::map<SG, std::set<EvpnMcastNode *> > EvpnMcastNodeList;
424 :
425 : EvpnManagerPartition(EvpnManager *evpn_manager, size_t part_id);
426 : ~EvpnManagerPartition();
427 :
428 : /// @brief Get the DBTablePartition for the EvpnTable for our partition id.
429 : DBTablePartition *GetTablePartition();
430 :
431 : /// @brief Notify the Broadcast MAC route for the given EvpnMcastNode.
432 : void NotifyNodeRoute(EvpnMcastNode *node);
433 :
434 : /// @brief Go through all replicator EvpnMcastNodes and notify associated Broadcast
435 : /// MAC route.
436 : void NotifyReplicatorNodeRoutes();
437 :
438 : /// @brief Go through all ingress replication client EvpnMcastNodes and notify the
439 : /// associated Broadcast MAC route.
440 : void NotifyIrClientNodeRoutes(bool exclude_edge_replication_supported);
441 :
442 : /// @brief Add an EvpnMcastNode to the EvpnManagerPartition.
443 : void AddMcastNode(EvpnMcastNode *node, EvpnRoute *route);
444 :
445 : /// @brief Delete an EvpnMcastNode from the EvpnManagerPartition.
446 : void DeleteMcastNode(EvpnMcastNode *node, EvpnRoute *route);
447 :
448 : /// @brief Update an EvpnMcastNode in the EvpnManagerPartition.
449 : /// Need to remove/add EvpnMcastNode from the replicator, leaf and ir client
450 : /// lists as appropriate.
451 : void UpdateMcastNode(EvpnMcastNode *node, EvpnRoute *route);
452 :
453 : /// @brief Delete an EvpnMcastNode from the EvpnManagerPartition.
454 : bool RemoveMcastNodeFromList(EvpnState::SG &sg, EvpnMcastNode *node,
455 : EvpnMcastNodeList *list);
456 :
457 : /// @brief Add the given MAC route to the update list.
458 : /// This method gets called either when the MAC route itself changes or when
459 : /// the remote PE list for the EvpnSegment of the MAC route gets updated.
460 : void TriggerMacRouteUpdate(EvpnRoute *route);
461 :
462 : /// @brief Return true if the EvpnManagerPartition is empty i.e. it has no local
463 : /// or remote EvpnMcastNodes and no MAC routes that need to be updated.
464 : bool empty() const;
465 :
466 : const EvpnMcastNodeList &remote_mcast_node_list() const {
467 : return remote_mcast_node_list_;
468 : }
469 : const EvpnMcastNodeList &local_mcast_node_list() const {
470 : return local_mcast_node_list_;
471 : }
472 : const EvpnMcastNodeList &leaf_node_list() const {
473 : return leaf_node_list_;
474 : }
475 15874 : EvpnMcastNodeList *remote_mcast_node_list() {
476 15874 : return &remote_mcast_node_list_;
477 : }
478 850 : EvpnMcastNodeList *local_mcast_node_list() {
479 850 : return &local_mcast_node_list_;
480 : }
481 632 : EvpnMcastNodeList *leaf_node_list() {
482 632 : return &leaf_node_list_;
483 : }
484 :
485 : /// @brief Return the BgpServer for the EvpnManagerPartition.
486 : BgpServer *server();
487 :
488 : /// @brief Return the EvpnTable for the EvpnManagerPartition.
489 : const EvpnTable *table() const;
490 :
491 108 : size_t part_id() const { return part_id_; }
492 :
493 : private:
494 : friend class EvpnManager;
495 : friend class BgpEvpnManagerTest;
496 :
497 : typedef std::set<EvpnRoute *> EvpnRouteList;
498 :
499 : /// @brief Process the MAC route update list for this EvpnManagerPartition.
500 : bool ProcessMacUpdateList();
501 :
502 : /// @brief Disable processing of the update list.
503 : /// For testing only.
504 : void DisableMacUpdateProcessing();
505 :
506 : /// @brief Enable processing of the update list.
507 : /// For testing only.
508 : void EnableMacUpdateProcessing();
509 :
510 : EvpnStatePtr GetState(const SG &sg);
511 : EvpnStatePtr GetState(const SG &sg) const;
512 : EvpnStatePtr GetState(EvpnRoute *route);
513 : EvpnStatePtr LocateState(EvpnRoute *route);
514 : EvpnStatePtr LocateState(const SG &sg);
515 : EvpnStatePtr CreateState(const SG &sg);
516 170342 : const EvpnState::StatesMap &states() const { return states_; }
517 : EvpnState::StatesMap &states() { return states_; }
518 : bool GetForestNodeAddress(ErmVpnRoute *rt, Ip4Address *address) const;
519 : void NotifyForestNode(EvpnRoute *route, ErmVpnTable *table);
520 :
521 : EvpnManager *evpn_manager_;
522 : size_t part_id_;
523 : EvpnState::StatesMap states_;;
524 : DBTablePartition *table_partition_;
525 : EvpnMcastNodeList local_mcast_node_list_;
526 : EvpnMcastNodeList remote_mcast_node_list_;
527 : EvpnMcastNodeList replicator_node_list_;
528 : EvpnMcastNodeList leaf_node_list_;
529 : EvpnMcastNodeList regular_node_list_;
530 : EvpnMcastNodeList ir_client_node_list_;
531 : EvpnRouteList mac_update_list_;
532 : boost::scoped_ptr<TaskTrigger> mac_update_trigger_;
533 :
534 : DISALLOW_COPY_AND_ASSIGN(EvpnManagerPartition);
535 : };
536 :
537 :
538 : /// @brief This class represents the EVPN manager for an EvpnTable in a VRF.
539 : ///
540 : /// It is responsible for listening to route notifications on the associated
541 : /// EvpnTable and implementing glue logic to massage routes so that vRouters
542 : /// can communicate properly with EVPN PEs.
543 : ///
544 : /// It currently implements glue logic for multicast connectivity between the
545 : /// vRouters and EVPN PEs. This is achieved by keeping track of local/remote
546 : /// EvpnMcastNodes and constructing ingress replication OList for any given
547 : /// EvpnLocalMcastNode when requested.
548 : ///
549 : /// It also provides the EvpnTable class with an API to get the UpdateInfo for
550 : /// a route in EvpnTable. This is used by the table's Export method to build
551 : /// the RibOutAttr for the broadcast MAC routes. This is how we send ingress
552 : /// replication OList information for an EVPN instance to the XMPP peers.
553 : ///
554 : /// An EvpnManager keeps a vector of pointers to EvpnManagerPartitions. The
555 : /// number of partitions is the same as the DB partition count. A partition
556 : /// contains a subset of EvpnMcastNodes that are created based on EvpnRoutes
557 : /// in the EvpnTable.
558 : ///
559 : /// The concurrency model is that each EvpnManagerPartition can be updated and
560 : /// can build the ingress replication OLists independently of other partitions.
561 : ///
562 : /// The EvpnManager is also used to implement glue logic for EVPN aliasing when
563 : /// remote PEs have multi-homed segments.
564 : ///
565 : /// An EvpnManager maintains a map of EvpnSegments keyed by EthernetSegmentId.
566 : /// It also keeps sets of EvpnSegments that need to be updated or evaluated for
567 : /// deletion.
568 : ///
569 : /// An EvpnSegment gets added to the segment update list in the EvpnManager when
570 : /// there's a change in the AD route for the EvpnSegment. The update list gets
571 : /// processed in the context of the bgp::EvpnSegment task.
572 : ///
573 : /// An EvpnSegment gets added to the segment delete list in the EvpnManager when
574 : /// the PE list becomes empty (bgp::EvpnSegment task) or when the MAC route list
575 : /// for a given partition becomes empty (db::DBTable task). The actual call to
576 : /// MayDelete and subsequent destroy, if appropriate, happens in in the context
577 : /// of bgp::EvpnSegment task.
578 : ///
579 : /// The bgp::EvpnSegment task is mutually exclusive with the db::DBTable task.
580 : class EvpnManager {
581 : public:
582 : explicit EvpnManager(EvpnTable *table);
583 : virtual ~EvpnManager();
584 :
585 : /// @brief Initialize the EvpnManager. We allocate the EvpnManagerPartitions
586 : /// and register a DBListener for the EvpnTable.
587 : virtual void Initialize();
588 :
589 : /// @brief Terminate the EvpnManager. We free the EvpnManagerPartitions
590 : /// and unregister from the EvpnTable.
591 : virtual void Terminate();
592 :
593 : /// @brief Construct export state for the given EvpnRoute. Note that the route
594 : /// only needs to be exported to the IPeer from which it was learnt.
595 : virtual UpdateInfo *GetUpdateInfo(EvpnRoute *route);
596 :
597 : /// @brief Get the EvpnManagerPartition for the given partition id.
598 : EvpnManagerPartition *GetPartition(size_t part_id);
599 :
600 : /// @brief Get the DBTablePartition for the EvpnTable for given partition id.
601 : DBTablePartition *GetTablePartition(size_t part_id);
602 :
603 : /// @brief Fill information for introspect command.
604 : /// Note that all IM routes are always in partition 0.
605 : void FillShowInfo(ShowEvpnTable *sevt) const;
606 :
607 : BgpServer *server();
608 70738 : EvpnTable *table() { return table_; }
609 : const EvpnTable *table() const { return table_; }
610 34498 : ErmVpnTable *ermvpn_table() { return ermvpn_table_; }
611 25587 : const ErmVpnTable *ermvpn_table() const { return ermvpn_table_; }
612 133 : int listener_id() const { return listener_id_; }
613 15328 : int ermvpn_listener_id() const { return ermvpn_listener_id_; }
614 :
615 : /// @brief Find or create the EvpnSegment for the given EthernetSegmentId.
616 : EvpnSegment *LocateSegment(const EthernetSegmentId &esi);
617 :
618 : /// @brief Find the EvpnSegment for the given EthernetSegmentId.
619 : EvpnSegment *FindSegment(const EthernetSegmentId &esi);
620 :
621 : /// @brief Trigger deletion of the given EvpnSegment.
622 : /// The EvpnSegment is added to a set of EvpnSegments that can potentially
623 : /// be deleted. This method can be invoked from multiple db::DBTable tasks
624 : /// in parallel when a MAC routes are removed from the dependency list in an
625 : /// EvpnSegment. Hence we ensure exclusive access using a write lock.
626 : ///
627 : /// The list is processed from the context of bgp::EvpnSegment task which is
628 : /// mutually exclusive with db::DBTable task.
629 : void TriggerSegmentDelete(EvpnSegment *segment);
630 :
631 : /// @brief Trigger update of the given EvpnSegment.
632 : /// The EvpnSegment is added to a set of EvpnSegments for which updates
633 : /// need triggered. This method is called in the context of db::DBTable
634 : /// task and a task instance of 0 since all AutoDisocvery routes always
635 : /// get sharded to partition 0.
636 : ///
637 : /// The set is processed in the context of bgp::EvpnSegment task, which
638 : /// is mutually exclusive with db::DBTable task.
639 : void TriggerSegmentUpdate(EvpnSegment *segment);
640 :
641 : /// @brief Trigger deletion of the EvpnManager and propagate the delete to any
642 : /// dependents.
643 : void ManagedDelete();
644 :
645 : /// @brief Initiate shutdown for the EvpnManager.
646 : void Shutdown();
647 :
648 : /// @brief Trigger deletion of the EvpnManager and propagate the delete to any
649 : /// dependents.
650 : bool MayDelete() const;
651 :
652 : /// @brief Attempt to enqueue a delete for the EvpnManager.
653 : void RetryDelete();
654 :
655 : /// @brief Return the LifetimeActor for the EvpnManager.
656 : LifetimeActor *deleter();
657 :
658 : private:
659 : friend class BgpEvpnManagerTest;
660 : friend class BgpEvpnAliasingTest;
661 :
662 : class DeleteActor;
663 : typedef std::vector<EvpnManagerPartition *> PartitionList;
664 : typedef boost::ptr_map<const EthernetSegmentId, EvpnSegment> SegmentMap;
665 : typedef std::set<EvpnSegment *> SegmentSet;
666 :
667 : /// @brief Allocate the EvpnManagerPartitions.
668 : void AllocPartitions();
669 :
670 : /// @brief Free the EvpnManagerPartitions.
671 : void FreePartitions();
672 :
673 : /// @brief DBListener callback handler for AutoDisocvery routes in the EvpnTable.
674 : void AutoDiscoveryRouteListener(EvpnRoute *route);
675 :
676 : /// @brief DBListener callback handler for MacAdvertisement routes in the EvpnTable.
677 : void MacAdvertisementRouteListener(EvpnManagerPartition *partition,
678 : EvpnRoute *route);
679 :
680 : /// @brief DBListener callback handler for InclusiveMulticast routes in the EvpnTable.
681 : void InclusiveMulticastRouteListener(EvpnManagerPartition *partition,
682 : EvpnRoute *route);
683 :
684 : /// @brief DBListener callback handler for SelectiveMulticast routes in the EvpnTable.
685 : void SelectiveMulticastRouteListener(EvpnManagerPartition *partition,
686 : EvpnRoute *route);
687 :
688 : /// @brief DBListener callback handler for the EvpnTable.
689 : void RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry);
690 :
691 : /// @brief ErmVpnTable route listener callback function.
692 : ///
693 : /// Process changes (create/update/delete) to GlobalErmVpnRoute in vrf.ermvpn.0
694 : void ErmVpnRouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry);
695 :
696 : /// @brief Process the set of EvpnSegments that can potentially be deleted.
697 : /// Remove the EvpnSegment from the map and destroy if it's fine to
698 : /// to delete the EvpnSegment.
699 : bool ProcessSegmentDeleteSet();
700 :
701 : /// @brief Process the set of EvpnSegments that need to be updated.
702 : ///
703 : /// Go through each EvpnSegment and update it's PE list. Trigger updates
704 : /// of all it's dependent MAC routes if there's a change in the PE list.
705 : bool ProcessSegmentUpdateSet();
706 :
707 : /// @brief Check whether an ErmVpnRoute is locally originated GlobalTreeRoute.
708 : bool IsUsableGlobalTreeRootRoute(ErmVpnRoute *ermvpn_route) const;
709 :
710 : /// @brief Disable processing of the update list.
711 : /// For testing only.
712 : void DisableSegmentUpdateProcessing();
713 :
714 : /// @brief Enable processing of the update list.
715 : /// For testing only.
716 : void EnableSegmentUpdateProcessing();
717 :
718 : /// @brief Disable processing of the delete list.
719 : /// For testing only.
720 : void DisableSegmentDeleteProcessing();
721 :
722 : /// @brief Enable processing of the delete list.
723 : /// For testing only.
724 : void EnableSegmentDeleteProcessing();
725 :
726 : /// @brief Disable processing of the update lists in all partitions.
727 : /// For testing only.
728 : void DisableMacUpdateProcessing();
729 :
730 : /// @brief Enable processing of the update lists in all partitions.
731 : /// For testing only.
732 : void EnableMacUpdateProcessing();
733 :
734 : /// @brief Set DB State and update count.
735 : void SetDBState(EvpnRoute *route, EvpnMcastNode *dbstate);
736 :
737 : /// @brief Create DB State and update count. If there is no DB State associated in the
738 : /// table, resume table deletion if the deletion was pending.
739 : void ClearDBState(EvpnRoute *route);
740 :
741 : EvpnTable *table_;
742 : ErmVpnTable *ermvpn_table_;
743 : int listener_id_;
744 : int ermvpn_listener_id_;
745 : std::atomic<int> db_states_count_;
746 : PartitionList partitions_;
747 : tbb::spin_rw_mutex segment_rw_mutex_;
748 : SegmentMap segment_map_;
749 : SegmentSet segment_delete_set_;
750 : SegmentSet segment_update_set_;
751 : boost::scoped_ptr<TaskTrigger> segment_delete_trigger_;
752 : boost::scoped_ptr<TaskTrigger> segment_update_trigger_;
753 :
754 : boost::scoped_ptr<DeleteActor> deleter_;
755 : LifetimeRef<EvpnManager> table_delete_ref_;
756 :
757 : DISALLOW_COPY_AND_ASSIGN(EvpnManager);
758 : };
759 :
760 : /// @brief Increment refcont atomically.
761 23105 : inline void intrusive_ptr_add_ref(EvpnState *evpn_state) {
762 23105 : evpn_state->refcount_.fetch_add(1);
763 23105 : }
764 :
765 : /// @brief Decrement refcount of an evpn_state. If the refcount falls to 1, it implies
766 : /// that there is no more reference to this particular state from any other data
767 : /// structure. Hence, it can be deleted from the container map and destroyed as
768 : /// well.
769 23105 : inline void intrusive_ptr_release(EvpnState *evpn_state) {
770 23105 : int prev = evpn_state->refcount_.fetch_sub(1);
771 23105 : if (prev > 1)
772 20419 : return;
773 2686 : if (evpn_state->states()) {
774 : EvpnState::StatesMap::iterator iter =
775 2686 : evpn_state->states()->find(evpn_state->sg());
776 2686 : if (iter != evpn_state->states()->end()) {
777 2686 : assert(iter->second == evpn_state);
778 2686 : evpn_state->states()->erase(iter);
779 :
780 : // Attempt project manager deletion as it could be held up due to
781 : // this map being non-empty so far..
782 2686 : if (evpn_state->manager()->deleter()->IsDeleted())
783 1 : evpn_state->manager()->deleter()->RetryDelete();
784 : }
785 : }
786 2686 : delete evpn_state;
787 : }
788 :
789 : #define EVPN_RT_LOG(rt, ...) \
790 : RTINSTANCE_LOG(EvpnRoute, this->table()->routing_instance(), \
791 : SandeshLevel::UT_DEBUG, \
792 : RTINSTANCE_LOG_FLAG_ALL, \
793 : (rt)->GetPrefix().source().to_string(), \
794 : (rt)->GetPrefix().group().to_string(), \
795 : (rt)->ToString(), ##__VA_ARGS__)
796 :
797 : #define EVPN_ERMVPN_RT_LOG(rt, ...) \
798 : RTINSTANCE_LOG(EvpnErmVpnRoute, this->table()->routing_instance(), \
799 : SandeshLevel::UT_DEBUG, \
800 : RTINSTANCE_LOG_FLAG_ALL, \
801 : (rt)->GetPrefix().source().to_string(), \
802 : (rt)->GetPrefix().group().to_string(), \
803 : (rt)->ToString(), ##__VA_ARGS__)
804 :
805 : #define EVPN_TRACE(type, ...) \
806 : RTINSTANCE_LOG(type, this->table()->routing_instance(), \
807 : SandeshLevel::UT_DEBUG, RTINSTANCE_LOG_FLAG_ALL, ##__VA_ARGS__)
808 :
809 : #endif // SRC_BGP_BGP_EVPN_H_
|