Commit | Line | Data |
---|---|---|
91fcc471 TJ |
1 | /* |
2 | The software in this package is distributed under the GNU General | |
3 | Public License version 2 (with a special exception described below). | |
4 | ||
5 | A copy of GNU General Public License (GPL) is included in this distribution, | |
6 | in the file COPYING.GPL. | |
7 | ||
8 | As a special exception, if other files instantiate templates or use macros | |
9 | or inline functions from this file, or you compile this file and link it | |
10 | with other works to produce a work based on this file, this file | |
11 | does not by itself cause the resulting work to be covered | |
12 | by the GNU General Public License. | |
13 | ||
14 | However the source code for this file must still be made available | |
15 | in accordance with section (3) of the GNU General Public License. | |
16 | ||
17 | This exception does not invalidate any other reasons why a work based | |
18 | on this file might be covered by the GNU General Public License. | |
19 | */ | |
8f66f529 | 20 | #include "host/pingscheduler.h" |
9c55ecd3 | 21 | |
0d46491b | 22 | #include <iostream> |
1309d0e4 | 23 | #include <limits> |
0d46491b | 24 | |
9c55ecd3 | 25 | #include <boost/bind.hpp> |
26b0f687 | 26 | #include <boost/foreach.hpp> |
0d46491b | 27 | |
301610ca GMF |
28 | #include <logfunc.hpp> |
29 | ||
780b0bca | 30 | #include "boost_assert_handler.h" |
086e2cc0 | 31 | #include "host/pingerfactory.h" |
26b0f687 | 32 | #include "dns/dnsmaster.h" |
51cbc790 | 33 | #include "icmp/icmppinger.h" |
72e54d1c | 34 | #include "link/linkstatus.h" |
ced28dc7 | 35 | |
a7c2eb51 | 36 | using namespace std; |
2bf8720f GMF |
37 | using boost::asio::io_service; |
38 | using boost::bind; | |
101be5ce | 39 | using boost::date_time::time_resolution_traits_adapted64_impl; |
2bf8720f GMF |
40 | using boost::posix_time::microsec_clock; |
41 | using boost::posix_time::ptime; | |
42 | using boost::posix_time::seconds; | |
89f153af | 43 | using boost::posix_time::milliseconds; |
e58d7507 | 44 | using boost::shared_ptr; |
301610ca | 45 | using I2n::Logger::GlobalLogger; |
a7c2eb51 | 46 | |
87758553 | 47 | |
4c2a5ab5 | 48 | //----------------------------------------------------------------------------- |
4bb97b45 | 49 | // PingScheduler |
4c2a5ab5 GMF |
50 | //----------------------------------------------------------------------------- |
51 | ||
086e2cc0 GMF |
52 | /** |
53 | * @brief Parameterized constructor. | |
54 | * | |
ab2cb1ef | 55 | * @param io_serv The one @c io_serv object that controls async processing |
c1abff61 | 56 | * @param network_interface The name of the network interface sending the pings. |
086e2cc0 GMF |
57 | * @param destination_address The remote address to ping. |
58 | * @param destination_port The remote port to ping. | |
59 | * @param ping_protocol_list A list of protocols to use. | |
60 | * @param ping_interval_in_sec Amount of time between each ping. | |
61 | * @param ping_fail_percentage_limit Maximum amount of pings that can fail. | |
a7b15639 CH |
62 | * @param ping_congestion_percentage_limit Amount of pings indication congested |
63 | * line | |
64 | * @param ping_congestion_duration_thresh Duration in seconds that indicates a | |
65 | * congested line | |
91aa83f9 | 66 | * @param ping_reply_timeout Max amount time to wait for ping to finish |
086e2cc0 | 67 | * @param link_analyzer The object to monitor the link status. |
365036be | 68 | * @param first_delay Delay in seconds from start_pinging to first ping attempt |
91aa83f9 | 69 | * @param n_parallel_pings: Number of pingers to ping the same IP in parallel |
086e2cc0 | 70 | */ |
4bb97b45 | 71 | PingScheduler::PingScheduler( |
ab2cb1ef | 72 | const IoServiceItem io_serv, |
2bf8720f GMF |
73 | const string &network_interface, |
74 | const string &destination_address, | |
238da857 | 75 | const uint16_t destination_port, |
fe6a2f80 | 76 | const PingProtocolList &ping_protocol_list, |
c15a722d | 77 | const long ping_interval_in_sec, |
a341119a | 78 | const int ping_fail_percentage_limit, |
a7b15639 | 79 | const int ping_congestion_percentage_limit, |
941b5e25 | 80 | const int congest_caused_by_fail_percentage_limit, |
a7b15639 | 81 | const int ping_congestion_duration_thresh, |
079d19ab | 82 | const int ping_reply_timeout, |
59733431 | 83 | LinkStatusItem link_analyzer, |
91aa83f9 | 84 | const int first_delay, |
89f153af | 85 | const int n_parallel_pings, |
87758553 CH |
86 | const int parallel_ping_delay, |
87 | const int ping_timeout_factor | |
e39cc3da | 88 | ) : |
23f51766 CH |
89 | IoService( io_serv ), |
90 | NetworkInterfaceName( network_interface ), | |
23f51766 CH |
91 | DestinationAddress( destination_address ), |
92 | DestinationPort( destination_port ), | |
26b0f687 CH |
93 | Protocols( ping_protocol_list ), |
94 | ProtocolIter(), | |
95 | PingIntervalInSec( ping_interval_in_sec ), | |
242e5fb3 | 96 | NPingers( n_parallel_pings ), |
26b0f687 | 97 | FirstDelay( first_delay ), |
ab2cb1ef | 98 | NextPingTimer( *io_serv ), |
e39cc3da | 99 | TimeSentLastPing( microsec_clock::universal_time() ), |
26b0f687 | 100 | PingReplyTimeout( ping_reply_timeout ), |
87758553 | 101 | PingReplyTimeoutOrig( ping_reply_timeout ), |
c1abff61 | 102 | HostAnalyzer( destination_address, ping_fail_percentage_limit, |
a7b15639 | 103 | ping_congestion_percentage_limit, |
941b5e25 | 104 | congest_caused_by_fail_percentage_limit, |
a7b15639 CH |
105 | ping_congestion_duration_thresh, n_parallel_pings, |
106 | link_analyzer ), | |
26b0f687 | 107 | Resolver(), |
91aa83f9 | 108 | Pingers(), |
91aa83f9 CH |
109 | NPingersDone( 0 ), |
110 | ParallelPingDelay( parallel_ping_delay ), | |
89f153af | 111 | DelayedPingTimer( *io_serv ), |
26b0f687 CH |
112 | WantToPing( false ), |
113 | LogPrefix(), | |
87758553 CH |
114 | ContinueOnOutdatedIps( false ), |
115 | PingTimeoutFactor( ping_timeout_factor ) | |
ced28dc7 | 116 | { |
475ad07c GMF |
117 | BOOST_ASSERT( !network_interface.empty() ); |
118 | BOOST_ASSERT( !destination_address.empty() ); | |
23f51766 CH |
119 | BOOST_ASSERT( ( 0 < destination_port ) && |
120 | ( destination_port < numeric_limits<uint16_t>::max() ) ); | |
f71cb7e1 | 121 | BOOST_ASSERT( 0 < ping_interval_in_sec ); |
23f51766 CH |
122 | BOOST_ASSERT( (0 <= ping_fail_percentage_limit) && |
123 | ( ping_fail_percentage_limit <= 100) ); | |
124 | ||
26b0f687 | 125 | update_log_prefix(); |
23f51766 CH |
126 | |
127 | init_ping_protocol(); | |
242e5fb3 CH |
128 | |
129 | // start resolving already so we are prepared to ping | |
130 | update_dns_resolver(); | |
131 | ||
2d591235 | 132 | } |
ced28dc7 | 133 | |
086e2cc0 GMF |
134 | /** |
135 | * @brief Destructor. | |
136 | */ | |
4bb97b45 | 137 | PingScheduler::~PingScheduler() |
2d591235 | 138 | { |
ced28dc7 GMF |
139 | } |
140 | ||
c1d776ba | 141 | void PingScheduler::stop_pinging() |
ced28dc7 | 142 | { |
c1abff61 CH |
143 | // stop pinger and resolver |
144 | GlobalLogger.debug() << LogPrefix << "scheduler: stop pinging"; | |
91aa83f9 | 145 | clear_pingers(); |
72be9e7d | 146 | cancel_resolve(true); |
f076f8d4 | 147 | |
c1abff61 CH |
148 | // now cancel the own timer in case that pinger cancelation called callback |
149 | GlobalLogger.debug() << LogPrefix << "scheduler: cancel timer"; | |
f076f8d4 | 150 | NextPingTimer.cancel(); |
c1d776ba CH |
151 | } |
152 | ||
153 | /** | |
91aa83f9 CH |
154 | * @brief stop all pingers and remove them from Pingers variable which will |
155 | * proboably cause their destruction | |
d10644b9 | 156 | * |
91aa83f9 CH |
157 | * Pingers is empty afterwards |
158 | */ | |
159 | void PingScheduler::clear_pingers() | |
160 | { | |
161 | PingerItem pinger; | |
89f153af | 162 | while(!Pingers.empty()) |
91aa83f9 | 163 | { |
89f153af | 164 | pinger = Pingers.back(); |
91aa83f9 | 165 | pinger->stop_pinging(); |
89f153af | 166 | Pingers.pop_back(); |
91aa83f9 CH |
167 | } |
168 | } | |
169 | ||
170 | /** | |
23f51766 | 171 | * @brief Start into infinite loop of calls to ping |
cad0b08d CH |
172 | * |
173 | * Does not start yet but set NextPingTimer (possibly to 0), so action starts | |
174 | * when io_service is started | |
c1d776ba CH |
175 | */ |
176 | void PingScheduler::start_pinging() | |
177 | { | |
c1d776ba | 178 | if ( FirstDelay > 0 ) |
c1abff61 CH |
179 | GlobalLogger.info() << LogPrefix << "Delaying first ping by " |
180 | << FirstDelay << "s"; | |
59733431 | 181 | else |
c1abff61 | 182 | GlobalLogger.info() << LogPrefix << "Schedule ping as soon as possible"; |
cad0b08d CH |
183 | |
184 | (void) NextPingTimer.expires_from_now( seconds( FirstDelay ) ); | |
23f51766 | 185 | NextPingTimer.async_wait( bind( &PingScheduler::ping, this, |
cad0b08d | 186 | boost::asio::placeholders::error ) ); |
09de3c4b GMF |
187 | } |
188 | ||
4e91c69a | 189 | |
c1d776ba | 190 | /** |
23f51766 | 191 | * @brief call Ping::ping and schedule a call to ping_done_handler when finished |
c1d776ba | 192 | */ |
23f51766 | 193 | void PingScheduler::ping(const boost::system::error_code &error) |
823623d9 | 194 | { |
d26dce11 | 195 | if ( error ) |
f076f8d4 | 196 | { // get here, e.g. by NextPingTimer.cancel in stop_pinging |
d26dce11 | 197 | if ( error == boost::asio::error::operation_aborted ) |
c1abff61 CH |
198 | GlobalLogger.error() << LogPrefix << "Timer for ping was cancelled!" |
199 | << " --> Stopping"; | |
d26dce11 | 200 | else |
c1abff61 CH |
201 | GlobalLogger.error() << LogPrefix << "Received error " << error |
202 | << " waiting for ping! Stopping"; | |
d26dce11 CH |
203 | return; |
204 | } | |
205 | ||
23f51766 CH |
206 | // ping as soon as dns is ready |
207 | WantToPing = true; | |
8d26221d | 208 | ping_when_ready(); |
23f51766 CH |
209 | } |
210 | ||
c1d776ba | 211 | |
8d26221d | 212 | void PingScheduler::ping_when_ready() |
23f51766 CH |
213 | { |
214 | if ( !WantToPing ) | |
823623d9 | 215 | { |
2a4dde8b | 216 | GlobalLogger.info() << LogPrefix << "waiting for ping request " |
91aa83f9 | 217 | << "(should take no more than " << PingIntervalInSec << "s)"; |
23f51766 | 218 | return; |
823623d9 | 219 | } |
23f51766 CH |
220 | else if ( Resolver && Resolver->is_resolving() ) |
221 | { | |
fd62d09f | 222 | GlobalLogger.info() << LogPrefix << "waiting for DNS to finish"; |
23f51766 CH |
223 | return; |
224 | } | |
225 | else if ( !Resolver ) | |
242e5fb3 | 226 | { |
23f51766 CH |
227 | // should not happen, but check anyway |
228 | GlobalLogger.warning() << LogPrefix << "Have no resolver!"; | |
242e5fb3 CH |
229 | return; |
230 | } | |
09de3c4b | 231 | |
c1abff61 | 232 | GlobalLogger.info() << LogPrefix << "start ping"; |
23f51766 | 233 | WantToPing = false; |
09de3c4b | 234 | |
fd62d09f CH |
235 | // try to get an up-to-date IP (ContinueOnOutdatedIps may only be set |
236 | // because a CNAME was out of date -- IPs may still be current) | |
26b0f687 | 237 | HostAddress ip = Resolver->get_next_ip(); |
8d26221d | 238 | |
fd62d09f CH |
239 | if ( !ip.is_valid() ) |
240 | { // this can happen in 2 cases: if ContinueOnOutdatedIps==true | |
241 | // or when ip went out of date between resolve and now | |
242 | // --> try to use outdated IP | |
26b0f687 CH |
243 | GlobalLogger.info() << LogPrefix << "Checking for outdated IPs"; |
244 | bool check_up_to_date = false; | |
245 | ip = Resolver->get_next_ip(check_up_to_date); | |
246 | } | |
fd62d09f | 247 | if ( !ip.is_valid() ) |
b44a5f96 CH |
248 | { // Do not even have an outdated IP! |
249 | // This happens if have no cached IPs and resolve failed | |
250 | GlobalLogger.info() << LogPrefix << "Not even outdated IP to ping " | |
251 | << "-- treat like a failed ping."; | |
fd62d09f | 252 | |
ffa5cfe2 CH |
253 | // skip the ping and directly call ping_done_handler the appropriate |
254 | // number of times | |
255 | for (int count=0; count<NPingers; ++count) | |
256 | ping_done_handler(PingStatus_FailureNoIP, 0); | |
fd62d09f CH |
257 | } |
258 | else | |
259 | { | |
242e5fb3 CH |
260 | // create new pingers |
261 | for (int count=0; count<NPingers; ++count) | |
262 | Pingers.push_back( | |
263 | PingerFactory::createPinger(*ProtocolIter, IoService, | |
264 | NetworkInterfaceName, | |
265 | PingReplyTimeout) ); | |
266 | ||
267 | // remember when pinging started | |
268 | TimeSentLastPing = microsec_clock::universal_time(); | |
269 | ||
270 | // get actual IP | |
91aa83f9 CH |
271 | boost::asio::ip::address actual_ip = ip.get_ip(); |
272 | GlobalLogger.info() << LogPrefix << "pinging IP " << actual_ip | |
273 | << " with TTL " << ip.get_ttl().get_updated_value() << "s"; | |
242e5fb3 | 274 | delayed_ping(boost::system::error_code(), actual_ip, 0); |
26b0f687 | 275 | } |
3f6ba924 CH |
276 | } |
277 | ||
89f153af | 278 | void PingScheduler::delayed_ping( const boost::system::error_code &error, |
242e5fb3 | 279 | const boost::asio::ip::address ip, |
89f153af | 280 | const int pinger_index ) |
91aa83f9 | 281 | { |
89f153af CH |
282 | if (error) |
283 | { | |
284 | GlobalLogger.info() << LogPrefix << "delayed ping received an error: " | |
285 | << error; | |
286 | return; | |
287 | } | |
89f153af CH |
288 | |
289 | GlobalLogger.debug() << LogPrefix << "starting delayed ping index " | |
290 | << pinger_index; | |
291 | Pingers[pinger_index]->ping(ip, | |
292 | DestinationPort, | |
293 | boost::bind(&PingScheduler::ping_done_handler, | |
294 | this, _1, _2) ); | |
242e5fb3 CH |
295 | if (pinger_index >= NPingers-1) |
296 | GlobalLogger.debug() << LogPrefix << "started all delayed pings"; | |
297 | else | |
298 | { | |
299 | DelayedPingTimer.expires_from_now( milliseconds(ParallelPingDelay) ); | |
300 | DelayedPingTimer.async_wait( bind( &PingScheduler::delayed_ping, | |
89f153af | 301 | this, boost::asio::placeholders::error, ip, pinger_index+1) ); |
242e5fb3 | 302 | } |
91aa83f9 CH |
303 | } |
304 | ||
166fd9e9 | 305 | |
23f51766 | 306 | //------------------------------------------------------------------------------ |
91aa83f9 | 307 | // Post Processing of Ping result and Preparation for next ping |
23f51766 | 308 | //------------------------------------------------------------------------------ |
e58d7507 | 309 | |
c1d776ba CH |
310 | /** |
311 | * @brief called when Ping::ping is done; calls functions to update | |
312 | * statistics, ping interval and elapsed time; | |
23f51766 | 313 | * schedules a call to ping, thereby closing the loop |
c1d776ba | 314 | */ |
9c0dcf33 CH |
315 | void PingScheduler::ping_done_handler( const PingStatus &result, |
316 | const long ping_duration_us ) | |
502b6af0 | 317 | { |
96c4e7a4 CH |
318 | PingStatus edited_result = result; |
319 | if (result == PingStatus_SuccessReply && ContinueOnOutdatedIps) | |
320 | { | |
321 | edited_result = PingStatus_SuccessOutdatedIP; | |
322 | ||
323 | // reset ContinueOnOutdatedIps | |
324 | ContinueOnOutdatedIps = false; | |
325 | update_log_prefix(); | |
326 | } | |
327 | ||
91aa83f9 CH |
328 | ++NPingersDone; |
329 | GlobalLogger.info() << LogPrefix << "Ping " << NPingersDone << " of " | |
330 | << NPingers << " done with result " << to_string(edited_result); | |
f8918bd5 | 331 | |
c1d776ba | 332 | // post-processing |
9c0dcf33 | 333 | HostAnalyzer.update_ping_statistics( edited_result, ping_duration_us ); |
91aa83f9 CH |
334 | |
335 | // prepare next ping only after all pingers are done | |
336 | if (NPingersDone == NPingers) | |
242e5fb3 CH |
337 | { |
338 | // stop and destruct all pingers | |
339 | clear_pingers(); | |
340 | ||
341 | GlobalLogger.debug() << LogPrefix | |
342 | << "--------------------------------------------------------------"; | |
343 | ||
344 | // update variables for next ping: number of pings, delay, protocol | |
d10644b9 | 345 | // do this only after call to update_ping_statistics! |
242e5fb3 CH |
346 | update_ping_protocol(); |
347 | update_ping_interval(); | |
348 | update_ping_number(); | |
349 | ||
91aa83f9 | 350 | prepare_next_ping(); |
242e5fb3 | 351 | } |
91aa83f9 CH |
352 | } |
353 | ||
354 | ||
355 | void PingScheduler::prepare_next_ping() | |
356 | { | |
242e5fb3 CH |
357 | // start DNS resolve if necessary |
358 | update_dns_resolver(); | |
23f51766 | 359 | |
ffa5cfe2 CH |
360 | NPingersDone = 0; |
361 | ||
c1d776ba | 362 | // schedule next ping |
91aa83f9 CH |
363 | int seconds_since_last_ping = (microsec_clock::universal_time() |
364 | - TimeSentLastPing).total_seconds(); | |
365 | if ( seconds_since_last_ping > PingIntervalInSec ) | |
366 | { | |
367 | GlobalLogger.info() << "We are late for next ping!"; | |
368 | seconds_since_last_ping = PingIntervalInSec; | |
369 | (void) NextPingTimer.expires_from_now( seconds(0) ); | |
370 | } | |
371 | else | |
372 | (void) NextPingTimer.expires_from_now( seconds( PingIntervalInSec | |
373 | - seconds_since_last_ping ) ); | |
23f51766 | 374 | NextPingTimer.async_wait( bind( &PingScheduler::ping, this, |
d26dce11 | 375 | boost::asio::placeholders::error ) ); |
d8a91bd6 GMF |
376 | } |
377 | ||
378 | void PingScheduler::update_ping_interval() | |
379 | { | |
c1d776ba | 380 | // have to ping more often? |
fb469ffa | 381 | if ( HostAnalyzer.exceeded_ping_failed_limit() ) |
d8a91bd6 GMF |
382 | { |
383 | PingIntervalInSec.speed_up(); | |
384 | ||
c1abff61 CH |
385 | GlobalLogger.debug() << LogPrefix << "- Speeding up ping interval to: " |
386 | << PingIntervalInSec << "s"; | |
d8a91bd6 GMF |
387 | } |
388 | else | |
389 | { | |
390 | PingIntervalInSec.back_to_original(); | |
391 | ||
c1abff61 CH |
392 | GlobalLogger.debug() << LogPrefix << "- Stick to the original ping " |
393 | << "interval: " << PingIntervalInSec << "s"; | |
d8a91bd6 | 394 | } |
ced28dc7 GMF |
395 | } |
396 | ||
242e5fb3 CH |
397 | /** in case of congested line, increase number of pings |
398 | * | |
d10644b9 CH |
399 | * also increases ping timeout if line is congested |
400 | * | |
242e5fb3 CH |
401 | * CAUTION! Only call this after clear_pingers !!! |
402 | * */ | |
403 | void PingScheduler::update_ping_number() | |
404 | { | |
405 | // make sure we do not loose track of pingers here | |
406 | if ( NPingersDone != NPingers || !Pingers.empty() ) | |
407 | { | |
408 | GlobalLogger.warning() << LogPrefix << "Should only change number of " | |
409 | << "pingers when all are finished and deleted! Have " << NPingers | |
410 | << " pingers, " << NPingersDone << " of which are done and " | |
411 | << Pingers.size() << " in listDone! Will not change NPingers."; | |
412 | return; | |
413 | } | |
414 | ||
415 | if ( HostAnalyzer.exceeded_ping_congestion_limit() ) | |
416 | { | |
417 | NPingers.increase(); | |
418 | ||
d10644b9 CH |
419 | GlobalLogger.notice() << LogPrefix << "No reply from host, " |
420 | << "switching to burst ping mode with longer timeouts"; | |
242e5fb3 CH |
421 | GlobalLogger.debug() << LogPrefix << "- Increasing ping number to: " |
422 | << NPingers; | |
d10644b9 CH |
423 | |
424 | PingReplyTimeout = PingReplyTimeoutOrig * PingTimeoutFactor; | |
425 | GlobalLogger.debug() << LogPrefix << "- Increase ping timeout to " | |
426 | << PingReplyTimeout << "s"; | |
242e5fb3 CH |
427 | } |
428 | else | |
429 | { | |
430 | NPingers.back_to_original(); | |
431 | ||
432 | GlobalLogger.debug() << LogPrefix << "- Stick to the original ping " | |
433 | << "number: " << NPingers; | |
d10644b9 CH |
434 | |
435 | PingReplyTimeout = PingReplyTimeoutOrig; | |
436 | GlobalLogger.debug() << LogPrefix << "- Reset ping timeout to " | |
437 | << PingReplyTimeout << "s"; | |
242e5fb3 CH |
438 | } |
439 | ||
d10644b9 | 440 | // tell host analyzer so it expects the correct number of ping results |
242e5fb3 CH |
441 | HostAnalyzer.set_n_parallel_pings(NPingers); |
442 | } | |
443 | ||
23f51766 CH |
444 | //------------------------------------------------------------------------------ |
445 | // Ping Protocol Rotation | |
446 | //------------------------------------------------------------------------------ | |
447 | ||
26b0f687 | 448 | void PingScheduler::init_ping_protocol() |
23f51766 | 449 | { |
26b0f687 | 450 | ProtocolIter = Protocols.end(); |
23f51766 CH |
451 | get_next_ping_protocol(); |
452 | } | |
453 | ||
26b0f687 | 454 | void PingScheduler::update_ping_protocol() |
23f51766 CH |
455 | { |
456 | if ( can_change_ping_protocol() ) | |
457 | { | |
458 | get_next_ping_protocol(); | |
459 | } | |
460 | } | |
461 | ||
26b0f687 | 462 | void PingScheduler::get_next_ping_protocol() |
23f51766 | 463 | { |
26b0f687 CH |
464 | ++ProtocolIter; |
465 | if (ProtocolIter == Protocols.end()) | |
466 | ProtocolIter = Protocols.begin(); | |
23f51766 CH |
467 | } |
468 | ||
26b0f687 | 469 | bool PingScheduler::can_change_ping_protocol() const |
23f51766 | 470 | { |
c1abff61 CH |
471 | // TODO can_change_ping_protocol() and get_next_ping_protocol() may be |
472 | // implemented in a Algorithm class that can be exchanged in this class to | |
473 | // provide an algorithm neutral class | |
23f51766 CH |
474 | return true; |
475 | } | |
476 | ||
477 | //------------------------------------------------------------------------------ | |
478 | // DNS host name resolution | |
479 | //------------------------------------------------------------------------------ | |
26b0f687 CH |
480 | |
481 | // show "!" after host name if running on outdated IPs | |
8d26221d | 482 | void PingScheduler::update_log_prefix() |
26b0f687 CH |
483 | { |
484 | std::stringstream temp; | |
72be9e7d | 485 | temp << "Sched(" << DestinationAddress; |
26b0f687 CH |
486 | if (ContinueOnOutdatedIps) |
487 | temp << "!"; | |
488 | temp << "): "; | |
489 | LogPrefix = temp.str(); | |
490 | } | |
491 | ||
242e5fb3 | 492 | void PingScheduler::update_dns_resolver() |
23f51766 CH |
493 | { |
494 | if (Resolver && Resolver->is_resolving()) | |
72be9e7d CH |
495 | cancel_resolve(false); |
496 | ||
497 | if (ContinueOnOutdatedIps) | |
23f51766 | 498 | { |
72be9e7d CH |
499 | ContinueOnOutdatedIps = false; |
500 | update_log_prefix(); | |
23f51766 CH |
501 | } |
502 | ||
503 | // DNS master caches created resolvers and resolved IPs, so this will | |
504 | // probably just return an existing resolver with already resolved IPs for | |
505 | // requested protocol ( ICMP/TCP is ignored, only IPv4/v6 is important) | |
506 | Resolver = DnsMaster::get_instance()->get_resolver_for(DestinationAddress, | |
242e5fb3 | 507 | *ProtocolIter); |
fd62d09f CH |
508 | |
509 | // get number of up-to-date IPs | |
2a4dde8b | 510 | // TODO should check here, if they will be up to date in PingIntervalInSec |
fd62d09f CH |
511 | bool check_up_to_date = true; |
512 | int ip_count = Resolver->get_resolved_ip_count(check_up_to_date); | |
513 | if (ip_count > 0) | |
23f51766 | 514 | { |
fd62d09f | 515 | GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to " |
f8918bd5 | 516 | << ip_count << " (IPs may be outdated=" << !check_up_to_date << ")"; |
fd62d09f CH |
517 | HostAnalyzer.set_resolved_ip_count( ip_count ); |
518 | ||
72be9e7d | 519 | if (Resolver->is_resolving()) |
c1abff61 | 520 | GlobalLogger.warning() << LogPrefix << "have up to date IPs but " |
23f51766 CH |
521 | << "resolver seems to be resolving all the same... " |
522 | << "Start pinging anyway!"; | |
8d26221d | 523 | ping_when_ready(); |
23f51766 CH |
524 | } |
525 | else | |
2a4dde8b CH |
526 | { |
527 | GlobalLogger.info() << LogPrefix | |
528 | << "No up-to-date IPs --> start resolve"; | |
23f51766 | 529 | start_resolving_ping_address(); |
2a4dde8b CH |
530 | // set resolved_ip_count will be called in resolve callback |
531 | } | |
23f51766 CH |
532 | } |
533 | ||
26b0f687 | 534 | void PingScheduler::start_resolving_ping_address() |
23f51766 | 535 | { |
26b0f687 | 536 | Resolver->async_resolve( boost::bind(&PingScheduler::dns_resolve_callback, |
23f51766 CH |
537 | this, _1, _2) ); |
538 | } | |
539 | ||
26b0f687 | 540 | void PingScheduler::dns_resolve_callback(const bool was_success, |
cd71d095 | 541 | const int recursion_count) |
23f51766 | 542 | { |
c1abff61 | 543 | GlobalLogger.info() << LogPrefix << "dns resolution finished " |
23f51766 | 544 | << "with success = " << was_success << " " |
cd71d095 | 545 | << "after " << recursion_count << " recursions"; |
23f51766 | 546 | |
26b0f687 CH |
547 | if ( was_success ) |
548 | { | |
fd62d09f CH |
549 | // trust that a successfull DNS resolve means we have an IP with TTL>0 |
550 | int ip_count = Resolver->get_resolved_ip_count(!ContinueOnOutdatedIps); | |
551 | if (ip_count == 0) | |
ffa5cfe2 | 552 | { |
fd62d09f CH |
553 | GlobalLogger.warning() << LogPrefix |
554 | << "Should not have reached this case: resolve was " | |
555 | << "successfull but still have no IPs (up-to-date=" | |
556 | << !ContinueOnOutdatedIps << ")!"; | |
557 | if (DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() > 0) | |
558 | GlobalLogger.warning() << LogPrefix << "This probably happened " | |
559 | << "because you specified a TTL threshold > 0 but resolving" | |
560 | << " had no effect on TTLs since external cache is only " | |
561 | << "updated when TTL=0 is reached."; | |
562 | } | |
563 | else | |
564 | { | |
565 | GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to " | |
f8918bd5 CH |
566 | << ip_count << " (IPs may be outdated=" |
567 | << ContinueOnOutdatedIps << ") --> could ping now"; | |
fd62d09f | 568 | } |
ffa5cfe2 | 569 | HostAnalyzer.set_resolved_ip_count( ip_count ); |
8d26221d | 570 | ping_when_ready(); |
26b0f687 CH |
571 | } |
572 | else | |
573 | { // host name resolution failed; try again bypassing first outdated CNAME | |
8d26221d | 574 | // or using cached IP |
26b0f687 | 575 | std::string skip_host = Resolver->get_skip_cname(); |
23f51766 CH |
576 | |
577 | if (skip_host.empty()) | |
838e0acf | 578 | { // try to continue with cached IPs |
fd62d09f | 579 | int ip_count = Resolver->get_resolved_ip_count(false); |
838e0acf CH |
580 | |
581 | if (ip_count == 0) | |
582 | GlobalLogger.notice() << LogPrefix << "DNS failed " | |
583 | << "and have no cached IPs either --> cannot ping"; | |
584 | // ping_when_ready will deal with this case | |
585 | else | |
586 | { | |
587 | ContinueOnOutdatedIps = true; | |
588 | update_log_prefix(); | |
589 | ||
590 | GlobalLogger.notice() << LogPrefix << "DNS failed, " | |
591 | << "try anyway with cached data"; | |
592 | } | |
593 | ||
fd62d09f | 594 | GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to " |
f8918bd5 | 595 | << ip_count << " (IPs may be outdated=" << true << ")"; |
fd62d09f CH |
596 | HostAnalyzer.set_resolved_ip_count( ip_count ); |
597 | ||
8d26221d | 598 | ping_when_ready(); |
23f51766 CH |
599 | } |
600 | else | |
8d26221d | 601 | { // have CNAME to continue |
838e0acf CH |
602 | ContinueOnOutdatedIps = true; |
603 | update_log_prefix(); | |
23f51766 CH |
604 | GlobalLogger.notice() << LogPrefix << "DNS failed, " |
605 | << "try again skipping a CNAME and resolving " | |
606 | << skip_host << " directly"; | |
72be9e7d CH |
607 | |
608 | cancel_resolve(false); | |
609 | ||
610 | // now create new resolver | |
23f51766 | 611 | Resolver = DnsMaster::get_instance() |
26b0f687 | 612 | ->get_resolver_for(skip_host, *ProtocolIter); |
23f51766 CH |
613 | start_resolving_ping_address(); |
614 | } | |
615 | } | |
23f51766 | 616 | } |
72be9e7d CH |
617 | |
618 | /** | |
619 | * cancel resolver if force_cancel or if it is not resolving DestinationAddress | |
620 | * | |
621 | * Resolvers have a life on their own: they are cached by DnsMaster so never go | |
622 | * out of scope and even after calling callbacks, there might still be a | |
623 | * longterm timer active to re-try resolving. | |
624 | * We want to cancel that long-term timer only if the Resolver is not for our | |
625 | * real, original DestinationAddress but a CNAME, which can happen when trying | |
626 | * to skip cnames and working on out-dated IPs | |
627 | */ | |
628 | void PingScheduler::cancel_resolve(const bool force_cancel) | |
629 | { | |
630 | if (force_cancel) | |
631 | { | |
632 | GlobalLogger.info() << "Cancelling resolver (forced)"; | |
633 | Resolver->cancel_resolve(); | |
634 | } | |
635 | else if ( Resolver->get_hostname() == DestinationAddress ) | |
636 | GlobalLogger.info() << LogPrefix | |
637 | << "Leave original resolver active in background"; | |
638 | else | |
639 | { | |
640 | GlobalLogger.info() << LogPrefix << "Cancel resolver for " | |
641 | << Resolver->get_hostname() << " since is not the original " | |
642 | << DestinationAddress; | |
643 | Resolver->cancel_resolve(); | |
644 | } | |
645 | } | |
646 |