Line data Source code
1 : /*
2 : * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #ifndef SRC_BGP_BGP_MVPN_H_
6 : #define SRC_BGP_BGP_MVPN_H_
7 :
8 : #include <map>
9 : #include <set>
10 : #include <sstream>
11 : #include <string>
12 : #include <vector>
13 : #include <atomic>
14 : #include <shared_mutex>
15 : #include <boost/scoped_ptr.hpp>
16 :
17 : #include "base/lifetime.h"
18 : #include "base/address.h"
19 : #include "bgp/bgp_attr.h"
20 : #include "bgp/bgp_table.h"
21 : #include "db/db_entry.h"
22 :
23 : class BgpPath;
24 : class BgpRoute;
25 : class BgpServer;
26 : class BgpTable;
27 : class ErmVpnRoute;
28 : class ErmVpnTable;
29 : struct MvpnDBState;
30 : class MvpnManager;
31 : class MvpnManagerPartition;
32 : class MvpnPrefix;
33 : class MvpnProjectManager;
34 : class MvpnProjectManagerPartition;
35 : class MvpnRoute;
36 : class MvpnState;
37 : class MvpnTable;
38 : class PathResolver;
39 : class RoutingInstance;
40 : class UpdateInfo;
41 :
42 : typedef boost::intrusive_ptr<MvpnState> MvpnStatePtr;
43 :
44 : // This struct represents a MVPN Neighbor discovered using BGP or using auto
45 : // exported routes from one routing-instance into another.
46 : //
47 : // Each received Type1 Intra AS Auto-Discovery ad Type2 Inter AS Auto Discovery
48 : // routes from MVPN BGP Peers is maintained as MVPN neighbors inside a map in
49 : // each MvpnManager object. RouteDistinguisher is used as the key to this map.
50 : //
51 : // Typically neighbor information is used in mvpn when the Type I and Type II
52 : // routes also carry PMSI tunnel information to send multicast traffic to all
53 : // intended receivers. In Phase I, PMSI information is not sent encoded inside
54 : // Type I/Type II AD routes. Also, in phase I, PMSI information if received in
55 : // Type I/Type II routes from remote neighbors is not processed.
56 : struct MvpnNeighbor {
57 : public:
58 : MvpnNeighbor();
59 : MvpnNeighbor(const RouteDistinguisher &rd, const IpAddress &originator);
60 : std::string ToString() const;
61 : const RouteDistinguisher &rd() const;
62 : const IpAddress &originator() const;
63 : uint32_t source_as() const;
64 : bool operator==(const MvpnNeighbor &rhs) const;
65 :
66 : private:
67 : friend class MvpnManagerPartition;
68 :
69 : RouteDistinguisher rd_;
70 : IpAddress originator_;
71 : uint32_t source_as_;
72 : };
73 :
74 : // This class manages Mvpn routes with in a partition of an MvpnTable.
75 : //
76 : // It holds a back pointer to the parent MvpnManager class along with the
77 : // partition id this object belongs to.
78 : //
79 : // Upon route change notification, based on the route-type for which a route
80 : // notification has been received, different set of actions are undertaken in
81 : // this class. This class only handles routes in which customer 'Group' info is
82 : // encoded, such as Type3 S-PMSI routes.
83 : //
84 : // Notification for a Type7 SourceTreeJoinRoute route add/change/delete
85 : // When this route is successfully imported into a vrf, new Type3 S-PMSI
86 : // route is originated/updated into that vrf.mvpn.0 if there is at least
87 : // one associated Join route from the agent with flags marked as "Sender"
88 : // or "SenderAndReciver".
89 : //
90 : // Notification for a Type3 S-PMSI route add/change/delete
91 : // When sender indicates that it has a sender for a particular <S,G> entry,
92 : // (With Leaf-Information required in the PMSI tunnel attribute), then this
93 : // class originates/updates a new Type4 LeafAD route into vrf.mvpn.0 table.
94 : // LeafAD route however is originated only if a usuable GlobalErmVpnRoute
95 : // is avaiable for stitching. On the other hand, if such a route was already
96 : // originated before and now is no longer feasible, it is deleted instead.
97 : //
98 : // Notification for a Type4 Leaf AD route add/change/delete
99 : // When leaf-ad routes are imported into a vrf and notified, the routes are
100 : // stored inside a map using route as the key and best path attributes as
101 : // values in the associated MvpnState. If the route is deleted, then it is
102 : // deleted from the map as well. If a usable Type5 source active route is
103 : // present in the vrf, then it is notified so that sender agent can be
104 : // updated with the right set of path attributes (PMSI) in order to be able
105 : // to replicate multicast traffic in the data plane.
106 : class MvpnManagerPartition {
107 : public:
108 : MvpnManagerPartition(MvpnManager *manager, int part_id);
109 : virtual ~MvpnManagerPartition();
110 : MvpnProjectManagerPartition *GetProjectManagerPartition();
111 : const MvpnProjectManagerPartition *GetProjectManagerPartition() const;
112 :
113 : private:
114 : friend class MvpnManager;
115 :
116 : MvpnTable *table();
117 : int listener_id() const;
118 :
119 : void ProcessType3SPMSIRoute(MvpnRoute *spmsi_rt);
120 : void ProcessType4LeafADRoute(MvpnRoute *leaf_ad);
121 : void ProcessType5SourceActiveRoute(MvpnRoute *join_rt);
122 : void ProcessType7SourceTreeJoinRoute(MvpnRoute *join_rt);
123 :
124 : MvpnStatePtr GetState(MvpnRoute *route);
125 : MvpnStatePtr GetState(MvpnRoute *route) const;
126 : MvpnStatePtr LocateState(MvpnRoute *route);
127 : void NotifyForestNode(const Ip4Address &source, const Ip4Address &group);
128 : bool GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label,
129 : Ip4Address *address, std::vector<std::string> *encap) const;
130 :
131 : MvpnManager *manager_;
132 : int part_id_;
133 :
134 : DISALLOW_COPY_AND_ASSIGN(MvpnManagerPartition);
135 : };
136 :
137 : // This class manages MVPN routes for a given vrf.mvpn.0 table.
138 : //
139 : // In each *.mvpn.0 table, an instance of this class is created when ever the
140 : // mvpn table itself is created (which happens when its parent routing-instance
141 : // gets created.
142 : //
143 : // This class allocated one instance of MvpnManagerPartition for each DB
144 : // partition. While all <S,G> specific operations are essentially manages inside
145 : // MvpnManagerPartition object (in order to get necessary protection from
146 : // concurrency across different db partitions), all other operations which are
147 : // <S,G> agnostic are mainly handled in this class.
148 : //
149 : // Specifically, all MVPN BGP neighbors are maintained in std::map NeighborMap.
150 : // Neighbors are created or updated when Type1/Type2 paths are received and are
151 : // deleted when those routes are deleted. All access to this map is protected
152 : // by a mutex because even though the map itself may be created, updated, or
153 : // deleted always serially from with in the same db task, map will get accessed
154 : // (read) concurrently from task running of different DB partitions.
155 : //
156 : // This class also provides DeleteActor and maintains a LifetimeRef to parent
157 : // MvpnTable object in order to ensure orderly cleanup during table deletion.
158 : class MvpnManager {
159 : public:
160 : typedef std::vector<MvpnManagerPartition *> PartitionList;
161 : typedef PartitionList::const_iterator const_iterator;
162 : typedef std::map<RouteDistinguisher, MvpnNeighbor> NeighborMap;
163 :
164 : MvpnManager(MvpnTable *table, ErmVpnTable *ermvpn_table);
165 : virtual ~MvpnManager();
166 : bool FindNeighbor(const RouteDistinguisher &rd, MvpnNeighbor *nbr) const;
167 : MvpnProjectManager *GetProjectManager();
168 : void ManagedDelete();
169 : BgpRoute *RouteReplicate(BgpServer *server, BgpTable *src_table,
170 : BgpRoute *source_rt, const BgpPath *src_path, ExtCommunityPtr comm);
171 : void ResolvePath(RoutingInstance *rtinstance, BgpRoute *rt, BgpPath *path);
172 : MvpnTable *table();
173 : const MvpnTable *table() const;
174 : int listener_id() const;
175 : virtual void Terminate();
176 : virtual void Initialize();
177 : size_t neighbors_count() const;
178 : const NeighborMap &neighbors() const;
179 : void ReOriginateType1Route(const Ip4Address &old_identifier);
180 : void OriginateType1Route();
181 : bool MayDelete() const;
182 : const LifetimeActor *deleter() const;
183 : bool deleted() const;
184 : LifetimeActor *deleter();
185 : virtual void UpdateSecondaryTablesForReplication(MvpnRoute *rt,
186 : BgpTable::TableSet *secondary_tables) const;
187 2 : std::shared_mutex &neighbors_mutex() { return neighbors_mutex_; }
188 :
189 : private:
190 : friend class MvpnManagerPartition;
191 : class DeleteActor;
192 :
193 : void AllocPartitions();
194 : void FreePartitions();
195 : void ProcessType1ADRoute(MvpnRoute *route);
196 : void RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry);
197 : bool FindResolvedNeighbor(const BgpPath *path,
198 : MvpnNeighbor *neighbor) const;
199 : void SetDBState(MvpnRoute *route, MvpnDBState *mvpn_dbstate);
200 : void ClearDBState(MvpnRoute *route);
201 :
202 : MvpnTable *table_;
203 : ErmVpnTable *ermvpn_table_;
204 : int listener_id_;
205 : int identifier_listener_id_;
206 : std::atomic<int> db_states_count_;
207 : PartitionList partitions_;
208 :
209 : NeighborMap neighbors_;
210 : mutable std::shared_mutex neighbors_mutex_;
211 :
212 : boost::scoped_ptr<DeleteActor> deleter_;
213 : LifetimeRef<MvpnManager> table_delete_ref_;
214 : LifetimeRef<MvpnManager> ermvpn_table_delete_ref_;
215 :
216 : DISALLOW_COPY_AND_ASSIGN(MvpnManager);
217 : };
218 :
219 : // This class holds Mvpn state for a particular <S,G> at any given time.
220 : //
221 : // In MVPN state machinery, different types of routes are sent and received at
222 : // different phases of processing. This class holds all relevant information
223 : // associated with an <S,G>.
224 : //
225 : // This is a refcounted class which is referred by DB States of different
226 : // routes. When the refcount reaches 0, (last referring db state is deleted),
227 : // this object is deleted from the container map and then destroyed.
228 : //
229 : // global_ermvpn_tree_rt_
230 : // This is a reference to GlobalErmVpnRoute associated with the ErmVpnTree
231 : // used in the data plane for this <S,G>. This route is created/updated
232 : // when ErmVpn notifies changes to ermvpn routes.
233 : //
234 : // spmsi_rt_
235 : // This is the 'only' Type3 SPMSI sender route originated for this S,G.
236 : // When an agent indicates that it has an active sender for a particular
237 : // <S,G> via Join route, then this route is originated (if there is atleast
238 : // one active receiver)
239 : //
240 : // spmsi_routes_received_
241 : // This is a set of all Type3 spmsi routes received for this <S-G>. It is
242 : // possible that when these routes are received, then there is no ermvpn
243 : // tree route to use for forwarding in the data plane. In such a case, later
244 : // when global_ermvpn_tree_rt_ does get updated, all leaf ad routes in this
245 : // set are notified and re-evaluated.
246 : //
247 : // leafad_routes_attr_received_
248 : // This is a map of all type 4 leaf ad routes originated (in response to
249 : // received/imported type-3 spmsi routes. For each route, associated path
250 : // attributes of the best path are stored as value inside the map. Whenever
251 : // this map changes or when a new type-5 source active route is received,
252 : // the correspnding sender agent is notified with the olist that contains
253 : // the PMSI attributes as received in leafad routes path attributes.
254 : //
255 : // states_
256 : // This is the parent map that holds 'this' MvpnState pointer as the value
257 : // for the associated SG key. When the refcount reaches zero, it indicates
258 : // that there is no reference to this state from of the DB States associated
259 : // with any Mvpn route. Hence at that time, this state is removed this map
260 : // states_ and destroyed. This map actually sits inside the associated
261 : // MvpnProjectManagerParitition object.
262 : class MvpnState {
263 : public:
264 : typedef std::set<MvpnRoute *> RoutesSet;
265 : typedef std::map<MvpnRoute *, BgpAttrPtr> RoutesMap;
266 :
267 : // Simple structure to hold <S,G>. Source as "0.0.0.0" can be used to encode
268 : // <*,G> as well.
269 : struct SG {
270 : SG(const Ip4Address &source, const Ip4Address &group);
271 : SG(const IpAddress &source, const IpAddress &group);
272 : explicit SG(const ErmVpnRoute *route);
273 : explicit SG(const MvpnRoute *route);
274 : bool operator<(const SG &other) const;
275 :
276 : IpAddress source;
277 : IpAddress group;
278 : };
279 :
280 : typedef std::map<SG, MvpnState *> StatesMap;
281 : MvpnState(const SG &sg, StatesMap *states, MvpnProjectManager *pm);
282 :
283 : virtual ~MvpnState();
284 : const SG &sg() const;
285 : ErmVpnRoute *global_ermvpn_tree_rt();
286 : const ErmVpnRoute *global_ermvpn_tree_rt() const;
287 : MvpnRoute *spmsi_rt();
288 : const MvpnRoute *spmsi_rt() const;
289 : void set_global_ermvpn_tree_rt(ErmVpnRoute *global_ermvpn_tree_rt);
290 : void set_spmsi_rt(MvpnRoute *spmsi_rt);
291 : RoutesSet &spmsi_routes_received();
292 : RoutesMap &leafad_routes_attr_received();
293 : const RoutesSet &spmsi_routes_received() const;
294 : const RoutesMap &leafad_routes_attr_received() const;
295 1 : const StatesMap *states() const { return states_; }
296 32268 : StatesMap *states() { return states_; }
297 : MvpnRoute *source_active_rt();
298 : const MvpnRoute *source_active_rt() const;
299 : void set_source_active_rt(MvpnRoute *source_active_rt);
300 12477 : MvpnProjectManager *project_manager() { return project_manager_; }
301 1 : const MvpnProjectManager *project_manager() const {
302 1 : return project_manager_;
303 : }
304 1 : int refcount() const { return refcount_; }
305 :
306 : private:
307 : friend class MvpnDBState;
308 : friend class MvpnManagerPartition;
309 : friend class MvpnProjectManagerPartition;
310 : friend void intrusive_ptr_add_ref(MvpnState *mvpn_state);
311 : friend void intrusive_ptr_release(MvpnState *mvpn_state);
312 :
313 : const ErmVpnTable *table() const;
314 :
315 : SG sg_;
316 : ErmVpnRoute *global_ermvpn_tree_rt_;
317 : MvpnRoute *spmsi_rt_;
318 : MvpnRoute *source_active_rt_;
319 : RoutesSet spmsi_routes_received_;
320 : RoutesMap leafad_routes_attr_received_;
321 : StatesMap *states_;
322 : MvpnProjectManager *project_manager_;
323 : std::atomic<int> refcount_;
324 :
325 : DISALLOW_COPY_AND_ASSIGN(MvpnState);
326 : };
327 :
328 : // This class holds a reference to MvpnState along with associated route
329 : // pointer. This is stored as DBState inside the table along with the
330 : // associated route.
331 : //
332 : // Note: Routes are never deleted until the DB state is deleted. MvpnState which
333 : // is refcounted is also deleted only when there is no MvpnDBState that refers
334 : // to it.
335 : struct MvpnDBState : public DBState {
336 : explicit MvpnDBState(MvpnStatePtr state);
337 : ~MvpnDBState();
338 : MvpnStatePtr state();
339 : MvpnRoute *route();
340 : void set_state(MvpnStatePtr state);
341 : void set_route(MvpnRoute *route);
342 :
343 : private:
344 : MvpnStatePtr state_;
345 : MvpnRoute *route_;
346 :
347 : DISALLOW_COPY_AND_ASSIGN(MvpnDBState);
348 : };
349 :
350 : // This class glues mvpn and ermvpn modules, inside a particular DB partition.
351 : //
352 : // Each MVPN is associated with a parent MvpnProjectManager virtual-network via
353 : // configuration. This parent MvpnProjectManager's ermvpn tree is the one used
354 : // for all multicast packets replication in the data plane for the given MVPN.
355 : //
356 : // Inside each RoutingInstance object, name of this parent manager virtual
357 : // network is stored in mvpn_project_manager_network_ string. When ever this
358 : // information is set/modified/cleared in the routing instance, all associated
359 : // Type3 S-PMSI MPVN received routes should be notified for re-evaluation.
360 : //
361 : // MvpnState::StatesMap states_
362 : // A Map of <<S,G>, MvpnState> is maintained to hold MvpnState for all
363 : // <S,G>s that fall into a specific DB partition.
364 : //
365 : // This provides APIs to create/update/delete MvpnState as required. MvpnState
366 : // is refcounted. When the refcount reaches 1, it is deleted from the StatesMap
367 : // and destroyed.
368 : class MvpnProjectManagerPartition {
369 : public:
370 : typedef MvpnState::SG SG;
371 :
372 : MvpnProjectManagerPartition(MvpnProjectManager*manager, int part_id);
373 : virtual ~MvpnProjectManagerPartition();
374 : MvpnStatePtr GetState(const SG &sg);
375 : MvpnStatePtr GetState(const SG &sg) const;
376 : MvpnStatePtr LocateState(const SG &sg);
377 : MvpnStatePtr CreateState(const SG &sg);
378 27936 : const MvpnState::StatesMap &states() const { return states_; }
379 191 : MvpnState::StatesMap &states() { return states_; }
380 :
381 : private:
382 : friend class MvpnProjectManager;
383 : friend class MvpnManagerPartition;
384 :
385 : ErmVpnRoute *GetGlobalTreeRootRoute(ErmVpnRoute *rt) const;
386 : ErmVpnTable *table();
387 : const ErmVpnTable *table() const;
388 : void RouteListener(DBEntryBase *db_entry);
389 : int listener_id() const;
390 : void NotifyForestNode(const Ip4Address &source, const Ip4Address &group);
391 : bool GetForestNodePMSI(ErmVpnRoute *rt, uint32_t *label,
392 : Ip4Address *address, std::vector<std::string> *encap) const;
393 : bool IsUsableGlobalTreeRootRoute(ErmVpnRoute *ermvpn_route) const;
394 :
395 : // Back pointer to the parent MvpnProjectManager
396 : MvpnProjectManager *manager_;
397 :
398 : // Partition id of the manged DB partition.
399 : int part_id_;
400 : MvpnState::StatesMap states_;;
401 :
402 : DISALLOW_COPY_AND_ASSIGN(MvpnProjectManagerPartition);
403 : };
404 :
405 : // This class glues mvpn and ermvpn modules
406 : //
407 : // It maintains a list of MvpnProjectManagerPartition objects, one for each DB
408 : // partition.
409 : //
410 : // It listens to changes to ErmVpn table and for any applicable change to
411 : // GlobalErmVpnRoute, it notifies all applicable received SPMSI routes so that
412 : // those routes can be replicated/deleted based on the current state of the
413 : // GlobalErmVpnRoute associated with a given <S,G>.
414 : //
415 : // This class also provides DeleteActor and maintains a LifetimeRef to parent
416 : // MvpnTable object in order to ensure orderly cleanup during table deletion.
417 : class MvpnProjectManager {
418 : public:
419 : class DeleteActor;
420 : typedef std::vector<MvpnProjectManagerPartition *> PartitionList;
421 : typedef PartitionList::const_iterator const_iterator;
422 :
423 : explicit MvpnProjectManager(ErmVpnTable *table);
424 : virtual ~MvpnProjectManager();
425 : MvpnProjectManagerPartition *GetPartition(int part_id);
426 : const MvpnProjectManagerPartition *GetPartition(int part_id) const;
427 : void ManagedDelete();
428 : virtual void Terminate();
429 : ErmVpnTable *table();
430 : const ErmVpnTable *table() const;
431 : int listener_id() const;
432 : const LifetimeActor *deleter() const;
433 : LifetimeActor *deleter();
434 : bool deleted() const;
435 : virtual void Initialize();
436 : MvpnStatePtr GetState(MvpnRoute *route) const;
437 : MvpnStatePtr GetState(MvpnRoute *route);
438 : MvpnStatePtr GetState(ErmVpnRoute *route) const;
439 : UpdateInfo *GetUpdateInfo(MvpnRoute *route);
440 190 : const PartitionList &partitions() const { return partitions_; }
441 : bool MayDelete() const;
442 : void GetMvpnSourceAddress(ErmVpnRoute *ermvpn_route,
443 : Ip4Address *address) const;
444 :
445 : private:
446 : void AllocPartitions();
447 : void FreePartitions();
448 : void RouteListener(DBTablePartBase *tpart, DBEntryBase *db_entry);
449 : UpdateInfo *GetType7UpdateInfo(MvpnRoute *route);
450 :
451 : // Parent ErmVpn table.
452 : ErmVpnTable *table_;
453 : int listener_id_;
454 : PartitionList partitions_;
455 :
456 : boost::scoped_ptr<DeleteActor> deleter_;
457 : LifetimeRef<MvpnProjectManager> table_delete_ref_;
458 :
459 : DISALLOW_COPY_AND_ASSIGN(MvpnProjectManager);
460 : };
461 :
462 : // Increment refcont atomically.
463 95972 : inline void intrusive_ptr_add_ref(MvpnState *mvpn_state) {
464 95972 : mvpn_state->refcount_.fetch_add(1);
465 95972 : }
466 :
467 : // Decrement refcount of an mvpn_state. If the refcount falls to 1, it implies
468 : // that there is no more reference to this particular state from any other data
469 : // structure. Hence, it can be deleted from the container map and destroyed as
470 : // well.
471 95972 : inline void intrusive_ptr_release(MvpnState *mvpn_state) {
472 95972 : int prev = mvpn_state->refcount_.fetch_sub(1);
473 95972 : if (prev > 1)
474 87905 : return;
475 8067 : if (mvpn_state->states()) {
476 : MvpnState::StatesMap::iterator iter =
477 8067 : mvpn_state->states()->find(mvpn_state->sg());
478 8067 : if (iter != mvpn_state->states()->end()) {
479 8067 : assert(iter->second == mvpn_state);
480 8067 : mvpn_state->states()->erase(iter);
481 :
482 : // Attempt project manager deletion as it could be held up due to
483 : // this map being non-empty so far..
484 8067 : if (mvpn_state->project_manager()->deleter()->IsDeleted())
485 4410 : mvpn_state->project_manager()->deleter()->RetryDelete();
486 : }
487 : }
488 8067 : delete mvpn_state;
489 : }
490 :
491 : #define MVPN_RT_LOG(rt, ...) \
492 : RTINSTANCE_LOG(MvpnRoute, this->table()->routing_instance(), \
493 : SandeshLevel::UT_DEBUG, \
494 : RTINSTANCE_LOG_FLAG_ALL, \
495 : (rt)->GetPrefix().source().to_string(), \
496 : (rt)->GetPrefix().group().to_string(), \
497 : (rt)->GetType(), (rt)->ToString(), ##__VA_ARGS__)
498 :
499 : #define MVPN_ERMVPN_RT_LOG(rt, ...) \
500 : RTINSTANCE_LOG(MvpnErmVpnRoute, this->table()->routing_instance(), \
501 : SandeshLevel::UT_DEBUG, \
502 : RTINSTANCE_LOG_FLAG_ALL, \
503 : (rt)->GetPrefix().source().to_string(), \
504 : (rt)->GetPrefix().group().to_string(), \
505 : (rt)->GetType(), (rt)->ToString(), ##__VA_ARGS__)
506 :
507 : #define MVPN_LOG(type, ...) \
508 : RTINSTANCE_LOG(type, this->table()->routing_instance(), \
509 : SandeshLevel::SYS_DEBUG, RTINSTANCE_LOG_FLAG_ALL, ##__VA_ARGS__)
510 :
511 : #define MVPN_TRACE(type, ...) \
512 : RTINSTANCE_LOG(type, this->table()->routing_instance(), \
513 : SandeshLevel::UT_DEBUG, RTINSTANCE_LOG_FLAG_ALL, ##__VA_ARGS__)
514 :
515 : #endif // SRC_BGP_BGP_MVPN_H_
|