Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include <cmn/agent_cmn.h>
7 : #include <oper/vrf.h>
8 : #include <oper/route_common.h>
9 : #include <oper/peer.h>
10 : #include <oper/agent_route_walker.h>
11 : #include <oper/mirror_table.h>
12 :
13 : #include <controller/controller_route_walker.h>
14 : #include <controller/controller_peer.h>
15 : #include <controller/controller_vrf_export.h>
16 : #include <controller/controller_export.h>
17 : #include <controller/controller_init.h>
18 :
19 222 : Peer::Peer(Type type, const std::string &name, bool export_to_controller) :
20 222 : type_(type), name_(name), export_to_controller_(export_to_controller) {
21 222 : refcount_ = 0;
22 222 : sequence_number_ = 0;
23 222 : }
24 :
25 246 : Peer::~Peer() {
26 246 : }
27 :
28 379 : bool Peer::DeleteOnZeroRefcount() const {
29 379 : return false;
30 : }
31 :
32 25309 : void intrusive_ptr_add_ref(const Peer *p) {
33 25309 : p->refcount_++;
34 : // validate that reference is not taken while delete is in progress
35 25309 : assert(!p->IsDeleted());
36 25309 : }
37 :
38 25309 : void intrusive_ptr_release(const Peer *p) {
39 50618 : if (p->refcount_.fetch_sub(1) == 1 && p->DeleteOnZeroRefcount()) {
40 21 : delete p;
41 : }
42 25309 : }
43 :
44 21 : DynamicPeer::DynamicPeer(Agent *agent, Type type, const std::string &name,
45 21 : bool export_to_controller) :
46 21 : Peer(type, name, export_to_controller) {
47 42 : delete_timeout_timer_ = TimerManager::CreateTimer(
48 21 : *(agent->event_manager())->io_service(),
49 : "Dynamic Peer Delete Timer",
50 : agent->task_scheduler()->\
51 : GetTaskId("db::DBTable"), 0);
52 21 : deleted_ = false;
53 21 : skip_add_change_ = false;
54 21 : }
55 :
56 21 : DynamicPeer::~DynamicPeer() {
57 : // Dynamic Peer should be marked deleted and will free
58 : // automatically once all the references go away
59 21 : assert(deleted_);
60 21 : assert(refcount() == 0);
61 21 : TimerManager::DeleteTimer(delete_timeout_timer_);
62 21 : }
63 :
64 21 : void DynamicPeer::ProcessDelete(DynamicPeer *p) {
65 21 : p->StopRouteExports();
66 :
67 21 : if (p->deleted_.exchange(true)) {
68 0 : return;
69 : }
70 :
71 21 : if (p->refcount() != 0) {
72 : // still pending references are there start delete timeout timer
73 21 : p->delete_timeout_timer_->Start(kDeleteTimeout,
74 : boost::bind(&DynamicPeer::DeleteTimeout,
75 : p));
76 21 : return;
77 : }
78 :
79 : // no pending references delete the peer inline and return
80 0 : delete p;
81 : }
82 :
83 0 : bool DynamicPeer::DeleteTimeout() {
84 0 : assert(0);
85 : return false;
86 : }
87 :
88 21 : bool DynamicPeer::DeleteOnZeroRefcount() const {
89 21 : if (!deleted_) {
90 0 : return false;
91 : }
92 :
93 : // last reference has gone, cancel the timer and delete peer
94 21 : delete_timeout_timer_->Cancel();
95 :
96 21 : return true;
97 : }
98 :
99 134 : const Ip4Address *Peer::NexthopIp(Agent *agent, const AgentPath *path) const {
100 134 : return agent->router_ip_ptr();
101 : }
102 :
103 21 : BgpPeer::BgpPeer(AgentXmppChannel *channel, const Ip4Address &server_ip,
104 : const std::string &name, DBTableBase::ListenerId id,
105 21 : Peer::Type bgp_peer_type) :
106 : DynamicPeer(channel->agent(), bgp_peer_type, name, false),
107 42 : channel_(channel), server_ip_(server_ip), id_(id),
108 21 : delete_stale_walker_(NULL), route_walker_cb_(NULL),
109 42 : delete_stale_walker_cb_(NULL) {
110 21 : AllocPeerNotifyWalker();
111 21 : AllocDeleteStaleWalker();
112 21 : AllocDeletePeerWalker();
113 21 : setup_time_ = UTCTimestampUsec();
114 21 : }
115 :
116 42 : BgpPeer::~BgpPeer() {
117 21 : const Agent *agent = route_walker()->agent();
118 : // TODO verify if this unregister can be done in walkdone callback
119 : // for delpeer
120 21 : if ((id_ != -1) && agent->vrf_table()) {
121 21 : agent->vrf_table()->Unregister(id_);
122 : }
123 21 : ReleaseDeleteStaleWalker();
124 21 : ReleaseDeletePeerWalker();
125 21 : ReleasePeerNotifyWalker();
126 42 : }
127 :
128 : // Route notify walker routines
129 42 : void BgpPeer::AllocPeerNotifyWalker() {
130 42 : if (!route_walker()) {
131 42 : Agent *agent = channel_->agent();
132 84 : route_walker_ = new ControllerRouteWalker(server_ip_.to_string(),
133 42 : this);
134 : agent->oper_db()->agent_route_walk_manager()->
135 42 : RegisterWalker(static_cast<AgentRouteWalker *>
136 : (route_walker_.get()));
137 : }
138 42 : }
139 :
140 42 : void BgpPeer::ReleasePeerNotifyWalker() {
141 42 : if (!route_walker()) {
142 0 : return;
143 : }
144 :
145 42 : Agent *agent = Agent::GetInstance();
146 42 : agent->oper_db()->agent_route_walk_manager()->ReleaseWalker(route_walker());
147 42 : route_walker_.reset();
148 : }
149 :
150 0 : void BgpPeer::PeerNotifyRoutes(WalkDoneCb cb) {
151 0 : route_walker_cb_ = cb;
152 0 : route_walker()->Start(ControllerRouteWalker::NOTIFYALL, true,
153 0 : route_walker_cb_);
154 0 : }
155 :
156 21 : void BgpPeer::StopPeerNotifyRoutes() {
157 : //No implementation of stop, to stop a walk release walker. Re-allocate for
158 : //further use.
159 21 : ReleasePeerNotifyWalker();
160 21 : AllocPeerNotifyWalker();
161 21 : }
162 :
163 0 : void BgpPeer::PeerNotifyMulticastRoutes(bool associate) {
164 0 : route_walker()->Start(ControllerRouteWalker::NOTIFYMULTICAST, associate,
165 : NULL);
166 0 : }
167 :
168 : // Delete stale walker routines
169 42 : void BgpPeer::AllocDeleteStaleWalker() {
170 42 : if (!delete_stale_walker()) {
171 42 : Agent *agent = channel_->agent();
172 84 : delete_stale_walker_ = new ControllerRouteWalker(server_ip_.to_string(),
173 42 : this);
174 : agent->oper_db()->agent_route_walk_manager()->
175 42 : RegisterWalker(static_cast<AgentRouteWalker *>
176 : (delete_stale_walker_.get()));
177 : }
178 42 : }
179 :
180 63 : void BgpPeer::ReleaseDeleteStaleWalker() {
181 63 : if (!delete_stale_walker()) {
182 21 : return;
183 : }
184 :
185 42 : Agent *agent = Agent::GetInstance();
186 : agent->oper_db()->agent_route_walk_manager()->
187 42 : ReleaseWalker(delete_stale_walker());
188 42 : delete_stale_walker_.reset();
189 : }
190 :
191 : // Delete stale walker routines
192 21 : void BgpPeer::AllocDeletePeerWalker() {
193 21 : if (!delete_peer_walker()) {
194 21 : Agent *agent = channel_->agent();
195 42 : delete_peer_walker_ = new ControllerRouteWalker(server_ip_.to_string(),
196 21 : this);
197 : agent->oper_db()->agent_route_walk_manager()->
198 21 : RegisterWalker(static_cast<AgentRouteWalker *>
199 : (delete_peer_walker_.get()));
200 : }
201 21 : }
202 :
203 21 : void BgpPeer::ReleaseDeletePeerWalker() {
204 21 : if (!delete_peer_walker()) {
205 0 : return;
206 : }
207 :
208 21 : Agent *agent = Agent::GetInstance();
209 : agent->oper_db()->agent_route_walk_manager()->
210 21 : ReleaseWalker(delete_peer_walker());
211 21 : delete_peer_walker_.reset();
212 : }
213 :
214 21 : void BgpPeer::DelPeerRoutes(WalkDoneCb walk_done_cb,
215 : uint64_t sequence_number) {
216 : //Since peer is getting deleted no need of seperate walk to delete stale or
217 : //non stale paths.
218 21 : ReleaseDeleteStaleWalker();
219 21 : delete_peer_walker_cb_ = walk_done_cb;
220 21 : delete_peer_walker()->set_sequence_number(sequence_number);
221 42 : delete_peer_walker()->Start(ControllerRouteWalker::DELPEER, false,
222 21 : delete_peer_walker_cb_);
223 21 : }
224 :
225 40 : void BgpPeer::DeleteStale() {
226 : //If peer is marked for deletion skip. Deletion should take care of removing
227 : //routes.
228 40 : if (SkipAddChangeRequest())
229 21 : return;
230 :
231 19 : delete_stale_walker()->set_sequence_number(sequence_number());
232 38 : delete_stale_walker()->Start(ControllerRouteWalker::DELSTALE, false,
233 19 : delete_stale_walker_cb_);
234 : }
235 :
236 21 : void BgpPeer::StopDeleteStale() {
237 : //No implementation of stop, to stop a walk release walker. Re-allocate for
238 : //further use.
239 21 : ReleaseDeleteStaleWalker();
240 21 : AllocDeleteStaleWalker();
241 21 : }
242 :
243 163 : ControllerRouteWalker *BgpPeer::route_walker() const {
244 163 : return static_cast<ControllerRouteWalker *>(route_walker_.get());
245 : }
246 :
247 185 : ControllerRouteWalker *BgpPeer::delete_stale_walker() const {
248 185 : return static_cast<ControllerRouteWalker *>(delete_stale_walker_.get());
249 : }
250 :
251 105 : ControllerRouteWalker *BgpPeer::delete_peer_walker() const {
252 105 : return static_cast<ControllerRouteWalker *>(delete_peer_walker_.get());
253 : }
254 : /*
255 : * Get the VRF state and unregister from all route table using
256 : * rt_export listener id. This will be called for active and non active bgp
257 : * peers. In case of active bgp peers send unsubscribe to control node(request
258 : * came via vrf delete).
259 : */
260 2100 : void BgpPeer::DeleteVrfState(DBTablePartBase *partition,
261 : DBEntryBase *entry) {
262 2100 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
263 :
264 2100 : DBTableBase::ListenerId id = GetVrfExportListenerId();
265 : VrfExport::State *vrf_state = static_cast<VrfExport::State *>
266 2100 : (GetVrfExportState(partition, entry));
267 :
268 2100 : if (vrf_state == NULL)
269 2084 : return;
270 :
271 112 : for (uint8_t table_type = (Agent::INVALID + 1);
272 112 : table_type < Agent::ROUTE_TABLE_MAX; table_type++) {
273 96 : if (vrf_state->rt_export_[table_type])
274 96 : vrf_state->rt_export_[table_type]->Unregister();
275 : }
276 :
277 16 : if (vrf_state->exported_ == true) {
278 : // Check if the notification is for active bgp peer or not.
279 : // Send unsubscribe only for active bgp peer.
280 : // If skip_add_change is set for this dynamic peer, then dont export.
281 16 : if (SkipAddChangeRequest() == false) {
282 16 : AgentXmppChannel::ControllerSendSubscribe(GetAgentXmppChannel(),
283 : vrf,
284 : false);
285 : }
286 : }
287 :
288 16 : vrf->ClearState(partition->parent(), id);
289 16 : delete vrf_state;
290 :
291 16 : return;
292 : }
293 :
294 : // For given peer return the dbstate for given VRF and partition
295 2312 : DBState *BgpPeer::GetVrfExportState(DBTablePartBase *partition,
296 : DBEntryBase *entry) {
297 2312 : DBTableBase::ListenerId id = GetVrfExportListenerId();
298 2312 : VrfEntry *vrf = static_cast<VrfEntry *>(entry);
299 2312 : return (static_cast<VrfExport::State *>(vrf->GetState(partition->parent(),
300 2312 : id)));
301 : }
302 :
303 : // For given route return the dbstate for given partiton
304 87 : DBState *BgpPeer::GetRouteExportState(DBTablePartBase *partition,
305 : DBEntryBase *entry) {
306 87 : AgentRoute *route = static_cast<AgentRoute *>(entry);
307 87 : VrfEntry *vrf = route->vrf();
308 :
309 87 : DBTablePartBase *vrf_partition = agent()->vrf_table()->
310 87 : GetTablePartition(vrf);
311 :
312 : VrfExport::State *vs = static_cast<VrfExport::State *>
313 87 : (GetVrfExportState(vrf_partition, vrf));
314 :
315 87 : if (vs == NULL)
316 0 : return NULL;
317 :
318 87 : Agent::RouteTableType table_type = route->GetTableType();
319 87 : RouteExport::State *state = NULL;
320 87 : if (vs->rt_export_[table_type]) {
321 87 : state = static_cast<RouteExport::State *>(route->GetState(partition->
322 : parent(),
323 87 : vs->rt_export_[table_type]->GetListenerId()));
324 : }
325 87 : return state;
326 : }
327 :
328 203 : Agent *BgpPeer::agent() const {
329 203 : return channel_->agent();
330 : }
331 :
332 276 : AgentXmppChannel *BgpPeer::GetAgentXmppChannel() const {
333 276 : return channel_;
334 : }
335 :
336 0 : uint64_t BgpPeer::ChannelSequenceNumber() const {
337 0 : return GetAgentXmppChannel()->sequence_number();
338 : }
339 :
340 0 : void BgpPeer::set_route_walker_cb(WalkDoneCb cb) {
341 0 : route_walker_cb_ = cb;
342 0 : }
343 :
344 0 : void BgpPeer::set_delete_stale_walker_cb(WalkDoneCb cb) {
345 0 : delete_stale_walker_cb_ = cb;
346 0 : }
347 0 : void BgpPeer::set_delete_peer_walker_cb(WalkDoneCb cb) {
348 0 : delete_peer_walker_cb_ = cb;
349 0 : }
|