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