Line data Source code
1 : /*
2 : * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include <boost/shared_ptr.hpp>
6 :
7 : #include <sandesh/sandesh_trace.h>
8 :
9 : #include <cmn/agent_cmn.h>
10 : #include <oper/route_common.h>
11 : #include <oper/agent_route_walker.h>
12 : #include <oper/peer.h>
13 : #include <oper/vrf.h>
14 : #include <oper/mirror_table.h>
15 : #include <oper/agent_sandesh.h>
16 :
17 : #include "controller/controller_route_walker.h"
18 : #include "controller/controller_init.h"
19 : #include "controller/controller_types.h"
20 : #include "controller/controller_vrf_export.h"
21 : #include "controller/controller_export.h"
22 : #include "controller/controller_route_path.h"
23 :
24 20 : ControllerRouteWalker::ControllerRouteWalker(const std::string &name,
25 20 : Peer *peer) :
26 20 : AgentRouteWalker(name, dynamic_cast<BgpPeer *>(peer)->agent()),
27 40 : peer_(peer), associate_(false), type_(NOTIFYALL), sequence_number_(0) {
28 20 : }
29 :
30 : // Takes action based on context of walk. These walks are not parallel.
31 : // At a time peer can be only in one state.
32 4 : bool ControllerRouteWalker::VrfWalkNotify(DBTablePartBase *partition,
33 : DBEntryBase *entry) {
34 4 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
35 : // Notification from deleted VRF should have taken care of all operations
36 : // w.r.t. peer, see VrfExport::Notify
37 : // Exception is DelPeer walk. Reason being that this walk will start when
38 : // peer goes down in agent xmpp channel. When it happens bgp peer in channel
39 : // is reset. Any add/delete notification on said VRF checks if xmpp channel
40 : // is active which internally checks for bgp peer. In current case it will
41 : // be NULL and will in-turn ignore notification for delete. State will
42 : // not be deleted for that peer in vrf. To delete state, delpeer walk will
43 : // have to traverse deleted VRF as well.
44 4 : if (vrf->IsDeleted() && (type_ != DELPEER))
45 0 : return true;
46 :
47 4 : switch (type_) {
48 0 : case NOTIFYALL:
49 0 : return VrfNotifyAll(partition, entry);
50 0 : case NOTIFYMULTICAST:
51 0 : return VrfNotifyMulticast(partition, entry);
52 2 : case DELPEER:
53 2 : return VrfDelPeer(partition, entry);
54 2 : case DELSTALE:
55 2 : return VrfDelStale(partition, entry);
56 0 : default:
57 0 : return false;
58 : }
59 : return false;
60 : }
61 :
62 : /*
63 : * Notification for vrf entry - Creates states (VRF and route) and
64 : * send subscription to control node
65 : * This will be called for active bgp peer only.
66 : */
67 0 : bool ControllerRouteWalker::VrfNotifyAll(DBTablePartBase *partition,
68 : DBEntryBase *entry) {
69 0 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
70 0 : if (peer_->GetType() == Peer::BGP_PEER) {
71 0 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
72 :
73 0 : DBTableBase::ListenerId id = bgp_peer->GetVrfExportListenerId();
74 : VrfExport::State *state =
75 0 : static_cast<VrfExport::State *>(vrf->GetState(partition->parent(),
76 : id));
77 0 : if (state) {
78 : /* state for __default__ instance will not be created if the
79 : * xmpp channel is up the first time as export code registers to
80 : * vrf-table after entry for __default__ instance is created */
81 0 : state->force_chg_ = true;
82 : }
83 :
84 : //Pass this object pointer so that VrfExport::Notify can start the route
85 : //walk if required on this VRF. Also it adds state if none is found.
86 0 : VrfExport::Notify(agent(), bgp_peer->GetAgentXmppChannel(),
87 : partition, entry);
88 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Vrf Notify all", vrf->GetName(),
89 : bgp_peer->GetName());
90 0 : return true;
91 : }
92 0 : return false;
93 : }
94 :
95 : /*
96 : * Delete peer notifications for VRF.
97 : */
98 2 : bool ControllerRouteWalker::VrfDelPeer(DBTablePartBase *partition,
99 : DBEntryBase *entry) {
100 2 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
101 2 : if (peer_->GetType() == Peer::BGP_PEER) {
102 2 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
103 : VrfExport::State *state = static_cast<VrfExport::State *>
104 2 : (bgp_peer->GetVrfExportState(partition, vrf));
105 : // State can be NULL, when vrf delete has happened before peer goes down
106 : // and VRF is not deleted before delpeer walker reaches the entry.
107 : // Controller Notify would have deleted state before delpeer was invoked.
108 2 : if (!state)
109 2 : return true;
110 : // skip starting walk on route tables if all the the route tables
111 : // are already delete, this also safe-gaurds that StartRouteWalk
112 : // will not be the first reference on the deleted VRF which will
113 : // end up taking reference and setting refcount state on deleted
114 : // VRF which can cause Bug - 1495824
115 0 : if (vrf->AllRouteTableDeleted()) return true;
116 : // Register Callback for deletion of VRF state on completion of route
117 : // walks
118 0 : if (type_ == ControllerRouteWalker::DELPEER) {
119 0 : RouteWalkDoneForVrfCallback(boost::bind(
120 : &ControllerRouteWalker::RouteWalkDoneForVrf,
121 : this, _1));
122 : }
123 0 : StartRouteWalk(vrf, associate_, type_);
124 0 : return true;
125 : }
126 0 : return false;
127 : }
128 :
129 : /*
130 : * Delete stale notifications for VRF.
131 : */
132 2 : bool ControllerRouteWalker::VrfDelStale(DBTablePartBase *partition,
133 : DBEntryBase *entry) {
134 2 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
135 2 : if (peer_->GetType() == Peer::BGP_PEER) {
136 : // skip starting walk on route tables if all the the route tables
137 : // are already delete, this also safe-gaurds that StartRouteWalk
138 : // will not be the first reference on the deleted VRF which will
139 : // end up taking reference and setting refcount state on deleted
140 : // VRF which can cause Bug - 1495824
141 2 : if (vrf->AllRouteTableDeleted()) return true;
142 : // Register Callback for deletion of VRF state on completion of route
143 : // walks
144 2 : if (type_ == ControllerRouteWalker::DELSTALE) {
145 2 : RouteWalkDoneForVrfCallback(boost::bind(
146 : &ControllerRouteWalker::RouteWalkDoneForVrf,
147 : this, _1));
148 : }
149 2 : StartRouteWalk(vrf, associate_, type_);
150 2 : return true;
151 : }
152 0 : return false;
153 : }
154 :
155 0 : bool ControllerRouteWalker::VrfNotifyMulticast(DBTablePartBase *partition,
156 : DBEntryBase *entry) {
157 0 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
158 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Vrf Multicast", vrf->GetName(),
159 : peer_->GetName());
160 0 : return VrfNotifyInternal(partition, entry);
161 : }
162 :
163 : //Common routeine if basic vrf and peer check is required for the walk
164 0 : bool ControllerRouteWalker::VrfNotifyInternal(DBTablePartBase *partition,
165 : DBEntryBase *entry) {
166 0 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
167 0 : if (peer_->GetType() == Peer::BGP_PEER) {
168 0 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
169 0 : if (IgnoreNotify()) {
170 0 : return false;
171 : }
172 :
173 0 : DBTableBase::ListenerId id = bgp_peer->GetVrfExportListenerId();
174 : VrfExport::State *state =
175 0 : static_cast<VrfExport::State *>(vrf->GetState(partition->parent(),
176 : id));
177 0 : if (state) {
178 0 : StartRouteWalk(vrf, associate_, type_);
179 :
180 0 : if (Agent::GetInstance()->mulitcast_builder() ==
181 0 : bgp_peer->GetAgentXmppChannel()) {
182 0 : state->mcast_exported_ = associate_;
183 : }
184 : }
185 :
186 0 : return true;
187 : }
188 0 : return false;
189 : }
190 :
191 : // Takes action based on context of walk. These walks are not parallel.
192 : // At a time peer can be only in one state.
193 38 : bool ControllerRouteWalker::RouteWalkNotify(DBTablePartBase *partition,
194 : DBEntryBase *entry) {
195 38 : switch (type_) {
196 24 : case NOTIFYALL:
197 24 : return RouteNotifyAll(partition, entry);
198 0 : case NOTIFYMULTICAST:
199 0 : return RouteNotifyMulticast(partition, entry);
200 0 : case DELPEER:
201 0 : return RouteDelPeer(partition, entry);
202 14 : case DELSTALE:
203 14 : return RouteDelStale(partition, entry);
204 0 : default:
205 0 : return false;
206 : }
207 : return false;
208 : }
209 :
210 24 : bool ControllerRouteWalker::RouteNotifyInternal(DBTablePartBase *partition,
211 : DBEntryBase *entry) {
212 24 : AgentRoute *route = static_cast<AgentRoute *>(entry);
213 :
214 24 : if ((type_ == NOTIFYMULTICAST) && !route->is_multicast())
215 0 : return true;
216 :
217 24 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
218 24 : if (IgnoreNotify()) {
219 0 : return false;
220 : }
221 :
222 24 : Agent::RouteTableType table_type = route->GetTableType();
223 :
224 : //Get the route state
225 : RouteExport::State *route_state = static_cast<RouteExport::State *>
226 24 : (bgp_peer->GetRouteExportState(partition, entry));
227 24 : if (route_state) {
228 : // Forcibly send the notification to peer.
229 0 : route_state->force_chg_ = true;
230 : }
231 :
232 24 : VrfEntry *vrf = route->vrf();
233 24 : DBTablePartBase *vrf_partition = agent()->vrf_table()->
234 24 : GetTablePartition(vrf);
235 : VrfExport::State *vs = static_cast<VrfExport::State *>
236 24 : (bgp_peer->GetVrfExportState(vrf_partition, vrf));
237 :
238 24 : if (vs) {
239 24 : vs->rt_export_[table_type]->
240 24 : Notify(agent(), bgp_peer->GetAgentXmppChannel(), associate_,
241 : table_type, partition, entry);
242 : }
243 24 : return true;
244 : }
245 :
246 24 : bool ControllerRouteWalker::RouteNotifyAll(DBTablePartBase *partition,
247 : DBEntryBase *entry) {
248 24 : AgentRoute *route = static_cast<AgentRoute *>(entry);
249 24 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Route NotifyAll", route->ToString(),
250 : peer_->GetName());
251 24 : return RouteNotifyInternal(partition, entry);
252 : }
253 :
254 0 : bool ControllerRouteWalker::RouteNotifyMulticast(DBTablePartBase *partition,
255 : DBEntryBase *entry) {
256 0 : AgentRoute *route = static_cast<AgentRoute *>(entry);
257 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Route Multicast Notify", route->ToString(),
258 : peer_->GetName());
259 0 : return RouteNotifyInternal(partition, entry);
260 : }
261 :
262 14 : bool ControllerRouteWalker::RouteDelStale(DBTablePartBase *partition,
263 : DBEntryBase *entry) {
264 14 : AgentRoute *route = static_cast<AgentRoute *>(entry);
265 14 : if (!route)
266 0 : return true;
267 :
268 : //Enqueue path delete.
269 : AgentRouteKey *key = (static_cast<AgentRouteKey *>(route->
270 14 : GetDBRequestKey().get()))->Clone();
271 14 : key->set_peer(peer_);
272 14 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
273 14 : req.key.reset(key);
274 14 : req.data.reset(new StalePathData(sequence_number_));
275 14 : AgentRouteTable *table = static_cast<AgentRouteTable *>(route->get_table());
276 14 : table->Process(req);
277 14 : return true;
278 14 : }
279 :
280 : // Deletes the peer and corresponding state in route
281 0 : bool ControllerRouteWalker::RouteDelPeer(DBTablePartBase *partition,
282 : DBEntryBase *entry) {
283 0 : AgentRoute *route = static_cast<AgentRoute *>(entry);
284 0 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
285 :
286 0 : if (!route)
287 0 : return true;
288 :
289 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Route Delpeer", route->ToString(),
290 : peer_->GetName());
291 :
292 0 : VrfEntry *vrf = route->vrf();
293 0 : DBTablePartBase *vrf_partition = agent()->vrf_table()->
294 0 : GetTablePartition(vrf);
295 : VrfExport::State *vrf_state = static_cast<VrfExport::State *>
296 0 : (bgp_peer->GetVrfExportState(vrf_partition, vrf));
297 : RouteExport::State *state = static_cast<RouteExport::State *>
298 0 : (bgp_peer->GetRouteExportState(partition, entry));
299 0 : if (vrf_state && state && vrf_state->rt_export_[route->GetTableType()]) {
300 0 : route->ClearState(partition->parent(), vrf_state->rt_export_[route->
301 0 : GetTableType()]->GetListenerId());
302 0 : delete state;
303 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "DelPeer route walk, delete state",
304 : route->ToString(), peer_->GetName());
305 : }
306 :
307 : //Enqueue path delete.
308 : AgentRouteKey *key = (static_cast<AgentRouteKey *>(route->
309 0 : GetDBRequestKey().get()))->Clone();
310 0 : key->set_peer(peer_);
311 0 : DBRequest req(DBRequest::DB_ENTRY_DELETE);
312 0 : req.key.reset(key);
313 0 : req.data.reset();
314 0 : AgentRouteTable *table = static_cast<AgentRouteTable *>(route->get_table());
315 0 : table->Process(req);
316 0 : return true;
317 0 : }
318 :
319 : // Called when for a VRF all route table walks are complete.
320 : // Deletes the VRF state of that peer.
321 2 : void ControllerRouteWalker::RouteWalkDoneForVrf(VrfEntry *vrf) {
322 : // Currently used only for delete peer handling
323 : // Deletes the state and release the listener id
324 2 : if (type_ != DELPEER)
325 2 : return;
326 :
327 0 : CONTROLLER_ROUTE_WALKER_TRACE(Walker, "Route Walk done", vrf->GetName(),
328 : peer_->GetName());
329 0 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
330 0 : DBEntryBase *entry = static_cast<DBEntryBase *>(vrf);
331 0 : DBTablePartBase *partition = agent()->vrf_table()->GetTablePartition(vrf);
332 0 : bgp_peer->DeleteVrfState(partition, entry);
333 : }
334 :
335 : // walk_done_cb - Called back when all walk i.e. VRF and route are done.
336 5 : void ControllerRouteWalker::Start(Type type, bool associate,
337 : AgentRouteWalker::WalkDone walk_done_cb) {
338 5 : associate_ = associate;
339 5 : type_ = type;
340 5 : WalkDoneCallback(walk_done_cb);
341 :
342 5 : StartVrfWalk();
343 5 : }
344 :
345 8 : void ControllerRouteWalker::StartRouteWalk(VrfEntry *vrf, bool associate,
346 : Type type) {
347 8 : associate_ = associate;
348 8 : type_ = type;
349 8 : AgentRouteWalker::StartRouteWalk(vrf);
350 8 : }
351 :
352 0 : bool ControllerRouteWalker::IsDeleteWalk() const {
353 0 : return ((type_ == DELSTALE) || (type_ == DELPEER));
354 : }
355 :
356 24 : bool ControllerRouteWalker::IgnoreNotify() {
357 24 : BgpPeer *bgp_peer = static_cast<BgpPeer *>(peer_);
358 24 : return bgp_peer->SkipAddChangeRequest();
359 : }
|