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