|
1 /* |
|
2 * Copyright 2004-2006 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 # include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 #include <oasys/util/StringBuffer.h> |
|
22 |
|
23 #include "ContactManager.h" |
|
24 #include "Contact.h" |
|
25 #include "Link.h" |
|
26 #include "bundling/BundleDaemon.h" |
|
27 #include "bundling/BundleEvent.h" |
|
28 #include "conv_layers/ConvergenceLayer.h" |
|
29 |
|
30 namespace dtn { |
|
31 |
|
32 //---------------------------------------------------------------------- |
|
33 ContactManager::ContactManager() |
|
34 : BundleEventHandler("ContactManager", "/dtn/contact/manager"), |
|
35 opportunistic_cnt_(0) |
|
36 { |
|
37 links_ = new LinkSet(); |
|
38 } |
|
39 |
|
40 //---------------------------------------------------------------------- |
|
41 ContactManager::~ContactManager() |
|
42 { |
|
43 delete links_; |
|
44 } |
|
45 |
|
46 //---------------------------------------------------------------------- |
|
47 bool |
|
48 ContactManager::add_new_link(const LinkRef& link) |
|
49 { |
|
50 oasys::ScopeLock l(&lock_, "ContactManager::add_new_link"); |
|
51 |
|
52 ASSERT(link != NULL); |
|
53 ASSERT(!link->isdeleted()); |
|
54 |
|
55 log_debug("adding NEW link %s", link->name()); |
|
56 if (has_link(link->name())) { |
|
57 return false; |
|
58 } |
|
59 links_->insert(LinkRef(link.object(), "ContactManager")); |
|
60 |
|
61 if (!link->is_create_pending()) { |
|
62 log_debug("posting LinkCreatedEvent"); |
|
63 BundleDaemon::post(new LinkCreatedEvent(link)); |
|
64 } |
|
65 |
|
66 return true; |
|
67 } |
|
68 |
|
69 //---------------------------------------------------------------------- |
|
70 void |
|
71 ContactManager::del_link(const LinkRef& link, bool wait, |
|
72 ContactEvent::reason_t reason) |
|
73 { |
|
74 oasys::ScopeLock l(&lock_, "ContactManager::del_link"); |
|
75 ASSERT(link != NULL); |
|
76 |
|
77 if (!has_link(link)) { |
|
78 log_err("ContactManager::del_link: link %s does not exist", |
|
79 link->name()); |
|
80 return; |
|
81 } |
|
82 ASSERT(!link->isdeleted()); |
|
83 |
|
84 log_debug("ContactManager::del_link: deleting link %s", link->name()); |
|
85 |
|
86 if (!wait) |
|
87 link->delete_link(); |
|
88 |
|
89 // Close the link if it is open or in the process of being opened. |
|
90 if (link->isopen() || link->isopening()) { |
|
91 BundleDaemon::instance()->post( |
|
92 new LinkStateChangeRequest(link, Link::CLOSED, reason)); |
|
93 } |
|
94 |
|
95 // Cancel the link's availability timer (if one exists). |
|
96 AvailabilityTimerMap::iterator iter = availability_timers_.find(link); |
|
97 if (iter != availability_timers_.end()) { |
|
98 LinkAvailabilityTimer* timer = iter->second; |
|
99 availability_timers_.erase(link); |
|
100 |
|
101 // Attempt to cancel the timer, relying on the timer system to clean |
|
102 // up the timer state once it bubbles to the top of the timer queue. |
|
103 // If the timer is in the process of firing (i.e., race condition), |
|
104 // the timer should clean itself up in the timeout handler. |
|
105 if (!timer->cancel()) { |
|
106 log_warn("ContactManager::del_link: " |
|
107 "failed to cancel availability timer -- race condition"); |
|
108 } |
|
109 } |
|
110 |
|
111 links_->erase(link); |
|
112 |
|
113 if (wait) { |
|
114 l.unlock(); |
|
115 // If some parent calling del_link already locked the Contact Manager, |
|
116 // the lock will remain locked, and an event ahead of the |
|
117 // LinkDeletedEvent may wait for the lock, causing deadlock |
|
118 ASSERT(!lock()->is_locked_by_me()); |
|
119 oasys::Notifier notifier("ContactManager::del_link"); |
|
120 BundleDaemon::post_and_wait(new LinkDeletedEvent(link), ¬ifier); |
|
121 link->delete_link(); |
|
122 } else { |
|
123 BundleDaemon::post(new LinkDeletedEvent(link)); |
|
124 } |
|
125 } |
|
126 |
|
127 //---------------------------------------------------------------------- |
|
128 bool |
|
129 ContactManager::has_link(const LinkRef& link) |
|
130 { |
|
131 oasys::ScopeLock l(&lock_, "ContactManager::has_link"); |
|
132 ASSERT(link != NULL); |
|
133 |
|
134 LinkSet::iterator iter = links_->find(link); |
|
135 if (iter == links_->end()) |
|
136 return false; |
|
137 return true; |
|
138 } |
|
139 |
|
140 //---------------------------------------------------------------------- |
|
141 bool |
|
142 ContactManager::has_link(const char* name) |
|
143 { |
|
144 oasys::ScopeLock l(&lock_, "ContactManager::has_link"); |
|
145 ASSERT(name != NULL); |
|
146 |
|
147 LinkSet::iterator iter; |
|
148 for (iter = links_->begin(); iter != links_->end(); ++iter) { |
|
149 if (strcasecmp((*iter)->name(), name) == 0) |
|
150 return true; |
|
151 } |
|
152 return false; |
|
153 } |
|
154 |
|
155 //---------------------------------------------------------------------- |
|
156 LinkRef |
|
157 ContactManager::find_link(const char* name) |
|
158 { |
|
159 oasys::ScopeLock l(&lock_, "ContactManager::find_link"); |
|
160 |
|
161 LinkSet::iterator iter; |
|
162 LinkRef link("ContactManager::find_link: return value"); |
|
163 |
|
164 for (iter = links_->begin(); iter != links_->end(); ++iter) { |
|
165 if (strcasecmp((*iter)->name(), name) == 0) { |
|
166 link = *iter; |
|
167 ASSERT(!link->isdeleted()); |
|
168 return link; |
|
169 } |
|
170 } |
|
171 return link; |
|
172 } |
|
173 |
|
174 //---------------------------------------------------------------------- |
|
175 const LinkSet* |
|
176 ContactManager::links() |
|
177 { |
|
178 ASSERTF(lock_.is_locked_by_me(), |
|
179 "ContactManager::links must be called while holding lock"); |
|
180 return links_; |
|
181 } |
|
182 |
|
183 //---------------------------------------------------------------------- |
|
184 void |
|
185 ContactManager::LinkAvailabilityTimer::timeout(const struct timeval& now) |
|
186 { |
|
187 (void)now; |
|
188 cm_->reopen_link(link_); |
|
189 delete this; |
|
190 } |
|
191 |
|
192 //---------------------------------------------------------------------- |
|
193 void |
|
194 ContactManager::reopen_link(const LinkRef& link) |
|
195 { |
|
196 oasys::ScopeLock l(&lock_, "ContactManager::reopen_link"); |
|
197 ASSERT(link != NULL); |
|
198 |
|
199 log_debug("reopen link %s", link->name()); |
|
200 |
|
201 availability_timers_.erase(link); |
|
202 |
|
203 if (!has_link(link)) { |
|
204 log_warn("ContactManager::reopen_link: " |
|
205 "link %s does not exist", link->name()); |
|
206 return; |
|
207 } |
|
208 ASSERT(!link->isdeleted()); |
|
209 |
|
210 if (link->state() == Link::UNAVAILABLE) { |
|
211 BundleDaemon::post( |
|
212 new LinkStateChangeRequest(link, Link::OPEN, |
|
213 ContactEvent::RECONNECT)); |
|
214 } else { |
|
215 // state race (possibly due to user action) |
|
216 log_err("availability timer fired for link %s but state is %s", |
|
217 link->name(), Link::state_to_str(link->state())); |
|
218 } |
|
219 } |
|
220 |
|
221 //---------------------------------------------------------------------- |
|
222 void |
|
223 ContactManager::handle_link_created(LinkCreatedEvent* event) |
|
224 { |
|
225 oasys::ScopeLock l(&lock_, "ContactManager::handle_link_created"); |
|
226 |
|
227 LinkRef link = event->link_; |
|
228 ASSERT(link != NULL); |
|
229 |
|
230 if(link->isdeleted()) |
|
231 { |
|
232 log_warn("ContactManager::handle_link_created: " |
|
233 "link %s is being deleted", link->name()); |
|
234 return; |
|
235 } |
|
236 |
|
237 if (!has_link(link)) { |
|
238 log_err("ContactManager::handle_link_created: " |
|
239 "link %s does not exist", link->name()); |
|
240 return; |
|
241 } |
|
242 |
|
243 // Post initial state events; MOVED from Link::create_link(). |
|
244 link->set_initial_state(); |
|
245 } |
|
246 |
|
247 //---------------------------------------------------------------------- |
|
248 void |
|
249 ContactManager::handle_link_available(LinkAvailableEvent* event) |
|
250 { |
|
251 oasys::ScopeLock l(&lock_, "ContactManager::handle_link_available"); |
|
252 |
|
253 LinkRef link = event->link_; |
|
254 ASSERT(link != NULL); |
|
255 |
|
256 if(link->isdeleted()) |
|
257 { |
|
258 log_warn("ContactManager::handle_link_available: " |
|
259 "link %s is being deleted", link->name()); |
|
260 return; |
|
261 } |
|
262 |
|
263 if (!has_link(link)) { |
|
264 log_warn("ContactManager::handle_link_available: " |
|
265 "link %s does not exist", link->name()); |
|
266 return; |
|
267 } |
|
268 |
|
269 AvailabilityTimerMap::iterator iter; |
|
270 iter = availability_timers_.find(link); |
|
271 if (iter == availability_timers_.end()) { |
|
272 return; // no timer for this link |
|
273 } |
|
274 |
|
275 LinkAvailabilityTimer* timer = iter->second; |
|
276 availability_timers_.erase(link); |
|
277 |
|
278 // try to cancel the timer and rely on the timer system to clean |
|
279 // it up once it bubbles to the top of the queue... if there's a |
|
280 // race and the timer is in the process of firing, it should clean |
|
281 // itself up in the timeout handler. |
|
282 if (!timer->cancel()) { |
|
283 log_warn("ContactManager::handle_link_available: " |
|
284 "can't cancel availability timer: race condition"); |
|
285 } |
|
286 } |
|
287 |
|
288 //---------------------------------------------------------------------- |
|
289 void |
|
290 ContactManager::handle_link_unavailable(LinkUnavailableEvent* event) |
|
291 { |
|
292 oasys::ScopeLock l(&lock_, "ContactManager::handle_link_unavailable"); |
|
293 |
|
294 LinkRef link = event->link_; |
|
295 ASSERT(link != NULL); |
|
296 |
|
297 if (!has_link(link)) { |
|
298 log_warn("ContactManager::handle_link_unavailable: " |
|
299 "link %s does not exist", link->name()); |
|
300 return; |
|
301 } |
|
302 |
|
303 if(link->isdeleted()) |
|
304 { |
|
305 log_warn("ContactManager::handle_link_unavailable: " |
|
306 "link %s is being deleted", link->name()); |
|
307 return; |
|
308 } |
|
309 |
|
310 // don't do anything for links that aren't ondemand or alwayson |
|
311 if (link->type() != Link::ONDEMAND && link->type() != Link::ALWAYSON) { |
|
312 log_debug("ContactManager::handle_link_unavailable: " |
|
313 "ignoring link unavailable for link of type %s", |
|
314 link->type_str()); |
|
315 return; |
|
316 } |
|
317 |
|
318 // or if the link wasn't broken but instead was closed by user |
|
319 // action or by going idle |
|
320 if (event->reason_ == ContactEvent::USER || |
|
321 event->reason_ == ContactEvent::IDLE) |
|
322 { |
|
323 log_debug("ContactManager::handle_link_unavailable: " |
|
324 "ignoring link unavailable due to %s", |
|
325 event->reason_to_str(event->reason_)); |
|
326 return; |
|
327 } |
|
328 |
|
329 // adjust the retry interval in the link to handle backoff in case |
|
330 // it continuously fails to open, then schedule the timer. note |
|
331 // that if this is the first time the link is opened, the |
|
332 // retry_interval will be initialized to zero so we set it to the |
|
333 // minimum here. the retry interval is reset in the link open |
|
334 // event handler. |
|
335 if (link->retry_interval_ == 0) { |
|
336 link->retry_interval_ = link->params().min_retry_interval_; |
|
337 } |
|
338 |
|
339 int timeout = link->retry_interval_; |
|
340 link->retry_interval_ *= 2; |
|
341 if (link->retry_interval_ > link->params().max_retry_interval_) { |
|
342 link->retry_interval_ = link->params().max_retry_interval_; |
|
343 } |
|
344 |
|
345 LinkAvailabilityTimer* timer = new LinkAvailabilityTimer(this, link); |
|
346 |
|
347 AvailabilityTimerMap::value_type val(link, timer); |
|
348 if (availability_timers_.insert(val).second == false) { |
|
349 log_err("ContactManager::handle_link_unavailable: " |
|
350 "error inserting timer for link %s into table!", link->name()); |
|
351 delete timer; |
|
352 return; |
|
353 } |
|
354 |
|
355 log_debug("link %s unavailable (%s): scheduling retry timer in %d seconds", |
|
356 link->name(), event->reason_to_str(event->reason_), timeout); |
|
357 timer->schedule_in(timeout * 1000); |
|
358 } |
|
359 |
|
360 //---------------------------------------------------------------------- |
|
361 void |
|
362 ContactManager::handle_contact_up(ContactUpEvent* event) |
|
363 { |
|
364 oasys::ScopeLock l(&lock_, "ContactManager::handle_contact_up"); |
|
365 |
|
366 LinkRef link = event->contact_->link(); |
|
367 ASSERT(link != NULL); |
|
368 |
|
369 if(link->isdeleted()) |
|
370 { |
|
371 log_warn("ContactManager::handle_contact_up: " |
|
372 "link %s is being deleted, not marking its contact up", link->name()); |
|
373 return; |
|
374 } |
|
375 |
|
376 if (!has_link(link)) { |
|
377 log_warn("ContactManager::handle_contact_up: " |
|
378 "link %s does not exist", link->name()); |
|
379 return; |
|
380 } |
|
381 |
|
382 if (link->type() == Link::ONDEMAND || link->type() == Link::ALWAYSON) { |
|
383 log_debug("ContactManager::handle_contact_up: " |
|
384 "resetting retry interval for link %s: %d -> %d", |
|
385 link->name(), |
|
386 link->retry_interval_, |
|
387 link->params().min_retry_interval_); |
|
388 link->retry_interval_ = link->params().min_retry_interval_; |
|
389 } |
|
390 } |
|
391 |
|
392 //---------------------------------------------------------------------- |
|
393 LinkRef |
|
394 ContactManager::find_link_to(ConvergenceLayer* cl, |
|
395 const std::string& nexthop, |
|
396 const EndpointID& remote_eid, |
|
397 Link::link_type_t type, |
|
398 u_int states) |
|
399 { |
|
400 oasys::ScopeLock l(&lock_, "ContactManager::find_link_to"); |
|
401 |
|
402 LinkSet::iterator iter; |
|
403 LinkRef link("ContactManager::find_link_to: return value"); |
|
404 |
|
405 log_debug("find_link_to: cl %s nexthop %s remote_eid %s " |
|
406 "type %s states 0x%x", |
|
407 cl ? cl->name() : "ANY", |
|
408 nexthop.c_str(), remote_eid.c_str(), |
|
409 type == Link::LINK_INVALID ? "ANY" : Link::link_type_to_str(type), |
|
410 states); |
|
411 |
|
412 // make sure some sane criteria was specified |
|
413 ASSERT((cl != NULL) || |
|
414 (nexthop != "") || |
|
415 (remote_eid != EndpointID::NULL_EID()) || |
|
416 (type != Link::LINK_INVALID)); |
|
417 |
|
418 for (iter = links_->begin(); iter != links_->end(); ++iter) { |
|
419 if ( ((type == Link::LINK_INVALID) || (type == (*iter)->type())) && |
|
420 ((cl == NULL) || ((*iter)->clayer() == cl)) && |
|
421 ((nexthop == "") || (nexthop == (*iter)->nexthop())) && |
|
422 ((remote_eid == EndpointID::NULL_EID()) || |
|
423 (remote_eid == (*iter)->remote_eid())) && |
|
424 ((states & (*iter)->state()) != 0) ) |
|
425 { |
|
426 link = *iter; |
|
427 log_debug("ContactManager::find_link_to: " |
|
428 "matched link *%p", link.object()); |
|
429 ASSERT(!link->isdeleted()); |
|
430 return link; |
|
431 } |
|
432 } |
|
433 |
|
434 log_debug("ContactManager::find_link_to: no match"); |
|
435 return link; |
|
436 } |
|
437 |
|
438 //---------------------------------------------------------------------- |
|
439 LinkRef |
|
440 ContactManager::new_opportunistic_link(ConvergenceLayer* cl, |
|
441 const std::string& nexthop, |
|
442 const EndpointID& remote_eid, |
|
443 const std::string* link_name) |
|
444 { |
|
445 log_debug("new_opportunistic_link: cl %s nexthop %s remote_eid %s", |
|
446 cl->name(), nexthop.c_str(), remote_eid.c_str()); |
|
447 |
|
448 oasys::ScopeLock l(&lock_, "ContactManager::new_opportunistic_link"); |
|
449 |
|
450 // find a unique link name |
|
451 char name[64]; |
|
452 |
|
453 if (link_name) { |
|
454 strncpy(name, link_name->c_str(), sizeof(name)); |
|
455 |
|
456 while (find_link(name) != NULL) { |
|
457 snprintf(name, sizeof(name), "%s-%d", |
|
458 link_name->c_str(), opportunistic_cnt_); |
|
459 |
|
460 opportunistic_cnt_++; |
|
461 } |
|
462 } |
|
463 |
|
464 else { |
|
465 do { |
|
466 snprintf(name, sizeof(name), "link-%d", |
|
467 opportunistic_cnt_); |
|
468 opportunistic_cnt_++; |
|
469 } while (find_link(name) != NULL); |
|
470 } |
|
471 |
|
472 LinkRef link = Link::create_link(name, Link::OPPORTUNISTIC, cl, |
|
473 nexthop.c_str(), 0, NULL); |
|
474 if (link == NULL) { |
|
475 log_warn("ContactManager::new_opportunistic_link: " |
|
476 "unexpected error creating opportunistic link"); |
|
477 return link; |
|
478 } |
|
479 |
|
480 LinkRef new_link(link.object(), |
|
481 "ContactManager::new_opportunistic_link: return value"); |
|
482 |
|
483 new_link->set_remote_eid(remote_eid); |
|
484 |
|
485 if (!add_new_link(new_link)) { |
|
486 new_link->delete_link(); |
|
487 log_err("ContactManager::new_opportunistic_link: " |
|
488 "failed to add new opportunistic link %s", new_link->name()); |
|
489 new_link = NULL; |
|
490 } |
|
491 |
|
492 return new_link; |
|
493 } |
|
494 |
|
495 //---------------------------------------------------------------------- |
|
496 void |
|
497 ContactManager::dump(oasys::StringBuffer* buf) const |
|
498 { |
|
499 oasys::ScopeLock l(&lock_, "ContactManager::dump"); |
|
500 |
|
501 buf->append("Links:\n"); |
|
502 LinkSet::iterator iter; |
|
503 for (iter = links_->begin(); iter != links_->end(); ++iter) { |
|
504 buf->appendf("*%p\n", (*iter).object()); |
|
505 } |
|
506 } |
|
507 |
|
508 } // namespace dtn |