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