]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_select.cc
Bug 5069: Keep listening after getsockname() error (#1713)
[thirdparty/squid.git] / src / peer_select.cc
CommitLineData
062e2281 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
062e2281 7 */
8
bbc27441
AJ
9/* DEBUG: section 44 Peer Selection Algorithm */
10
582c2af2
FC
11#include "squid.h"
12#include "acl/FilledChecklist.h"
9865de72 13#include "base/AsyncCbdataCalls.h"
6043e368 14#include "base/InstanceId.h"
8b082ed9 15#include "base/TypeTraits.h"
a011edee 16#include "CachePeer.h"
2e24d0bf 17#include "CachePeers.h"
21c22f04 18#include "carp.h"
582c2af2 19#include "client_side.h"
4a3b98d7 20#include "dns/LookupDetails.h"
a37fdd8a 21#include "errorpage.h"
a553a5a3 22#include "event.h"
eb13c21e 23#include "FwdState.h"
af69c635 24#include "globals.h"
bbaf2685 25#include "hier_code.h"
924f73bc 26#include "htcp.h"
d3dddfb5 27#include "http/Stream.h"
582c2af2 28#include "HttpRequest.h"
9b5c4a9a 29#include "icmp/net_db.h"
582c2af2 30#include "ICP.h"
c7a52e6d 31#include "ip/tools.h"
602d9612 32#include "ipcache.h"
f0ba2534 33#include "neighbors.h"
f795b373 34#include "peer_sourcehash.h"
37236ba1 35#include "peer_userhash.h"
582c2af2 36#include "PeerSelectState.h"
4d5904f7 37#include "SquidConfig.h"
582c2af2 38#include "Store.h"
98cacedb 39#include "time/gadgets.h"
062e2281 40
6043e368
AR
41/**
42 * A CachePeer which has been selected as a possible destination.
43 * Listed as pointers here so as to prevent duplicates being added but will
44 * be converted to a set of IP address path options before handing back out
45 * to the caller.
46 *
47 * Certain connection flags and outgoing settings will also be looked up and
48 * set based on the received request and CachePeer settings before handing back.
49 */
50class FwdServer
51{
52 MEMPROXY_CLASS(FwdServer);
53
54public:
55 FwdServer(CachePeer *p, hier_code c) :
56 _peer(p),
57 code(c),
58 next(nullptr)
59 {}
60
61 CbcPointer<CachePeer> _peer; /* NULL --> origin server */
62 hier_code code;
63 FwdServer *next;
64};
65
26ac0430 66static struct {
75e88d56 67 int timeouts;
2fadd50d 68} PeerStats;
062e2281 69
26ac0430
AJ
70static const char *DirectStr[] = {
71 "DIRECT_UNKNOWN",
72 "DIRECT_NO",
73 "DIRECT_MAYBE",
74 "DIRECT_YES"
75};
75e88d56 76
e7b08eec
AR
77/// a helper class to report a selected destination (for debugging)
78class PeerSelectionDumper
79{
80public:
cb835136
AR
81 PeerSelectionDumper(const PeerSelector * const aSelector, const CachePeer * const aPeer, const hier_code aCode):
82 selector(aSelector), peer(aPeer), code(aCode) {}
e7b08eec 83
cb835136 84 const PeerSelector * const selector; ///< selection parameters
e7b08eec
AR
85 const CachePeer * const peer; ///< successful selection info
86 const hier_code code; ///< selection algorithm
87};
88
cb835136 89CBDATA_CLASS_INIT(PeerSelector);
aa839030 90
e7b08eec
AR
91/// prints PeerSelectionDumper (for debugging)
92static std::ostream &
93operator <<(std::ostream &os, const PeerSelectionDumper &fsd)
94{
95 os << hier_code_str[fsd.code];
96
97 if (fsd.peer)
a555a85b 98 os << '/' << *fsd.peer;
cb835136
AR
99 else if (fsd.selector) // useful for DIRECT and gone PINNED destinations
100 os << '#' << fsd.selector->request->url.host();
e7b08eec
AR
101
102 return os;
103}
104
9865de72
EB
105/// An ICP ping timeout service.
106/// Protects event.cc (which is designed to handle a few unrelated timeouts)
107/// from exposure to thousands of ping-related timeouts on busy proxies.
108class PeerSelectorPingMonitor
109{
110public:
111 /// registers the given selector to be notified about the IPC ping timeout
112 void monitor(PeerSelector *);
113
114 /// removes a PeerSelector from the waiting list
115 void forget(PeerSelector *);
116
117 /// \returns a (nil) registration of a non-waiting peer selector
118 WaitingPeerSelectorPosition npos() { return selectors.end(); }
119
120private:
121 static void NoteWaitOver(void *monitor);
122
123 void startWaiting();
124 void abortWaiting();
125 void noteWaitOver();
126
127 WaitingPeerSelectors selectors; ///< \see WaitingPeerSelectors
128};
129
130/// monitors all PeerSelector ICP ping timeouts
131static PeerSelectorPingMonitor &
132PingMonitor()
133{
134 static const auto Instance = new PeerSelectorPingMonitor();
135 return *Instance;
136}
137
138/* PeerSelectorPingMonitor */
139
140/// PeerSelectorPingMonitor::noteWaitOver() wrapper
141void
142PeerSelectorPingMonitor::NoteWaitOver(void *raw)
143{
144 assert(raw);
145 static_cast<PeerSelectorPingMonitor*>(raw)->noteWaitOver();
146}
147
148/// schedules a single event to represent all waiting selectors
149void
150PeerSelectorPingMonitor::startWaiting()
151{
152 assert(!selectors.empty());
153 const auto interval = tvSubDsec(current_time, selectors.begin()->first);
154 eventAdd("PeerSelectorPingMonitor::NoteWaitOver", &PeerSelectorPingMonitor::NoteWaitOver, this, interval, 0, false);
155}
156
157/// undoes an earlier startWaiting() call
158void
159PeerSelectorPingMonitor::abortWaiting()
160{
161 // our event may be already in the AsyncCallQueue but that is OK:
162 // such queued calls cannot accumulate, and we ignore any stale ones
163 eventDelete(&PeerSelectorPingMonitor::NoteWaitOver, nullptr);
164}
165
166/// calls back all ready PeerSelectors and continues to wait for others
167void
168PeerSelectorPingMonitor::noteWaitOver()
169{
170 while (!selectors.empty() && current_time >= selectors.begin()->first) {
171 const auto selector = selectors.begin()->second;
172 CallBack(selector->al, [selector,this] {
173 selector->ping.monitorRegistration = npos();
174 AsyncCall::Pointer callback = asyncCall(44, 4, "PeerSelector::HandlePingTimeout",
b6388dfd 175 cbdataDialer(PeerSelector::HandlePingTimeout, selector));
9865de72
EB
176 ScheduleCallHere(callback);
177 });
178 selectors.erase(selectors.begin());
179 }
180
181 if (!selectors.empty()) {
182 // Since abortWaiting() is unreliable, we may have been awakened by a
183 // stale event A after event B has been scheduled. Now we are going to
184 // schedule event C. Prevent event accumulation by deleting B (if any).
185 abortWaiting();
186
187 startWaiting();
188 }
189}
190
191void
192PeerSelectorPingMonitor::monitor(PeerSelector *selector)
193{
194 assert(selector);
195
196 const auto deadline = selector->ping.deadline();
197 const auto position = selectors.emplace(deadline, selector);
198 selector->ping.monitorRegistration = position;
199
200 if (position == selectors.begin()) {
201 if (selectors.size() > 1)
202 abortWaiting(); // remove the previously scheduled earlier event
203 startWaiting();
204 } // else the already scheduled event is still the earliest one
205}
206
207void
208PeerSelectorPingMonitor::forget(PeerSelector *selector)
209{
210 assert(selector);
211
212 if (selector->ping.monitorRegistration == npos())
213 return; // already forgotten
214
215 const auto wasFirst = selector->ping.monitorRegistration == selectors.begin();
216 selectors.erase(selector->ping.monitorRegistration);
217 selector->ping.monitorRegistration = npos();
218
219 if (wasFirst) {
220 // do not reschedule if there are still elements with the same deadline
221 if (!selectors.empty() && selectors.begin()->first == selector->ping.deadline())
222 return;
223 abortWaiting();
224 if (!selectors.empty())
225 startWaiting();
226 } // else do nothing since the old scheduled event is still the earliest one
227}
228
229/* PeerSelector */
230
cb835136 231PeerSelector::~PeerSelector()
348b2031 232{
36339742
AJ
233 while (servers) {
234 FwdServer *next = servers->next;
3c670b50 235 delete servers;
36339742
AJ
236 servers = next;
237 }
238
9865de72
EB
239 cancelPingTimeoutMonitoring();
240
029c8349
AJ
241 if (entry) {
242 debugs(44, 3, entry->url());
029c8349 243 entry->ping_status = PING_DONE;
5ae21d99
AJ
244 }
245
029c8349 246 if (acl_checklist) {
d816f28d 247 debugs(44, DBG_IMPORTANT, "ERROR: Squid BUG: peer selector gone while waiting for a slow ACL");
029c8349 248 delete acl_checklist;
348b2031 249 }
62e76326 250
029c8349 251 HTTPMSGUNLOCK(request);
62e76326 252
029c8349
AJ
253 if (entry) {
254 assert(entry->ping_status != PING_WAITING);
1f90fd4a 255 entry->unlock("peerSelect");
aee3523a 256 entry = nullptr;
73a201f8 257 }
62e76326 258
029c8349 259 delete lastError;
348b2031 260}
062e2281 261
9865de72
EB
262void
263PeerSelector::startPingWaiting()
264{
265 assert(entry);
266 assert(entry->ping_status != PING_WAITING);
267 PingMonitor().monitor(this);
268 entry->ping_status = PING_WAITING;
269}
270
271void
272PeerSelector::cancelPingTimeoutMonitoring()
273{
274 PingMonitor().forget(this);
275}
276
2d72d4fd 277static int
cb365059 278peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry * entry)
062e2281 279{
cb365059
EB
280 assert(ps);
281 HttpRequest *request = ps->request;
282
7b665aeb 283 int n;
db1cd23c 284 assert(entry);
285 assert(entry->ping_status == PING_NONE);
9bd6a36d 286 assert(direct != DIRECT_YES);
cb835136 287 debugs(44, 3, entry->url());
62e76326 288
45e5102d 289 if (!request->flags.hierarchical && direct != DIRECT_NO)
62e76326 290 return 0;
291
d46a87a8 292 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
62e76326 293 if (direct != DIRECT_NO)
294 return 0;
295
cb365059 296 n = neighborsCount(ps);
62e76326 297
cb835136 298 debugs(44, 3, "counted " << n << " neighbors");
62e76326 299
7b665aeb 300 return n;
062e2281 301}
302
6043e368
AR
303static void
304peerSelect(PeerSelectionInitiator *initiator,
cfd66529 305 HttpRequest * request,
d4806c91 306 AccessLogEntry::Pointer const &al,
6043e368 307 StoreEntry * entry)
75e88d56 308{
86b389fc 309 if (entry)
7f06a3d8 310 debugs(44, 3, *entry << ' ' << entry->url());
86b389fc 311 else
7f06a3d8 312 debugs(44, 3, request->method);
62e76326 313
cb835136 314 const auto selector = new PeerSelector(initiator);
62e76326 315
cb835136
AR
316 selector->request = request;
317 HTTPMSGLOCK(selector->request);
318 selector->al = al;
62e76326 319
cb835136 320 selector->entry = entry;
62e76326 321
6cfa8966 322#if USE_CACHE_DIGESTS
62e76326 323
39edba21 324 request->hier.peer_select_start = current_time;
62e76326 325
39edba21 326#endif
62e76326 327
cb835136
AR
328 if (selector->entry)
329 selector->entry->lock("peerSelect");
62e76326 330
cb835136 331 selector->selectMore();
75e88d56 332}
333
6043e368
AR
334void
335PeerSelectionInitiator::startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry)
336{
337 subscribed = true;
338 peerSelect(this, request, ale, entry);
339 // and wait for noteDestination() and/or noteDestinationsEnd() calls
340}
341
cb835136 342void
329c128c 343PeerSelector::checkNeverDirectDone(const Acl::Answer answer)
75e88d56 344{
cb835136
AR
345 acl_checklist = nullptr;
346 debugs(44, 3, answer);
347 never_direct = answer;
3985b3f8 348 switch (answer) {
ae026ec6 349 case ACCESS_ALLOWED:
f365d297 350 /** if never_direct says YES, do that. */
cb835136
AR
351 direct = DIRECT_NO;
352 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)");
ae026ec6
AJ
353 break;
354 case ACCESS_DENIED: // not relevant.
1f585949 355 case ACCESS_DUNNO: // not relevant.
ae026ec6 356 break;
1f585949 357 case ACCESS_AUTH_REQUIRED:
ae026ec6 358 debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
1f585949 359 break;
ae026ec6 360 }
cb835136 361 selectMore();
75e88d56 362}
363
cb835136 364void
329c128c 365PeerSelector::CheckNeverDirectDone(Acl::Answer answer, void *data)
75e88d56 366{
cb835136
AR
367 static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
368}
369
370void
329c128c 371PeerSelector::checkAlwaysDirectDone(const Acl::Answer answer)
cb835136
AR
372{
373 acl_checklist = nullptr;
374 debugs(44, 3, answer);
375 always_direct = answer;
3985b3f8 376 switch (answer) {
ae026ec6
AJ
377 case ACCESS_ALLOWED:
378 /** if always_direct says YES, do that. */
cb835136
AR
379 direct = DIRECT_YES;
380 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)");
ae026ec6
AJ
381 break;
382 case ACCESS_DENIED: // not relevant.
1f585949 383 case ACCESS_DUNNO: // not relevant.
ae026ec6 384 break;
1f585949 385 case ACCESS_AUTH_REQUIRED:
ae026ec6 386 debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
1f585949 387 break;
ae026ec6 388 }
cb835136
AR
389 selectMore();
390}
391
392void
329c128c 393PeerSelector::CheckAlwaysDirectDone(Acl::Answer answer, void *data)
cb835136
AR
394{
395 static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
75e88d56 396}
397
cb835136 398/// \returns true (after destroying "this") if the peer initiator is gone
6043e368 399/// \returns false (without side effects) otherwise
cb835136
AR
400bool
401PeerSelector::selectionAborted()
6043e368 402{
cb835136 403 if (interestedInitiator())
6043e368
AR
404 return false;
405
406 debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
cb835136 407 delete this;
6043e368
AR
408 return true;
409}
410
cb835136 411/// A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
cfd66529 412void
cb835136 413PeerSelector::resolveSelected()
cfd66529 414{
cb835136 415 if (selectionAborted())
36339742 416 return;
6043e368 417
cb835136 418 FwdServer *fs = servers;
36339742 419
7177edfb
AJ
420 // Bug 3243: CVE 2009-0801
421 // Bypass of browser same-origin access control in intercepted communication
422 // To resolve this we must use only the original client destination when going DIRECT
423 // on intercepted traffic which failed Host verification
cb835136 424 const HttpRequest *req = request;
45e5102d 425 const bool isIntercepted = !req->flags.redirected &&
0d901ef4 426 (req->flags.intercepted || req->flags.interceptTproxy);
45e5102d 427 const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
7177edfb
AJ
428 const bool choseDirect = fs && fs->code == HIER_DIRECT;
429 if (isIntercepted && useOriginalDst && choseDirect) {
d9371d7b
AJ
430 // check the client is still around before using any of its details
431 if (req->clientConnectionManager.valid()) {
432 // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
433 Comm::ConnectionPointer p = new Comm::Connection();
434 p->remote = req->clientConnectionManager->clientConnection->local;
6043e368 435 fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
cb835136 436 handlePath(p, *fs);
d9371d7b 437 }
7177edfb
AJ
438
439 // clear the used fs and continue
cb835136 440 servers = fs->next;
3c670b50 441 delete fs;
cb835136 442 resolveSelected();
7177edfb
AJ
443 return;
444 }
445
3dde9e52
CT
446 if (fs && fs->code == PINNED) {
447 // Nil path signals a PINNED destination selection. Our initiator should
448 // borrow and use clientConnectionManager's pinned connection object
449 // (regardless of that connection destination).
450 handlePath(nullptr, *fs);
451 servers = fs->next;
452 delete fs;
453 resolveSelected();
454 return;
455 }
456
cfd66529 457 // convert the list of FwdServer destinations into destinations IP addresses
cb835136 458 if (fs && wantsMoreDestinations()) {
cfd66529 459 // send the next one off for DNS lookup.
cb835136
AR
460 const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host();
461 debugs(44, 2, "Find IP destination for: " << url() << "' via " << host);
462 Dns::nbgethostbyname(host, this);
cfd66529
AJ
463 return;
464 }
465
c2f4e9cc
AJ
466 // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
467 // due to the allocation method of fs, we must deallocate each manually.
468 // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
6043e368 469 if (fs) {
cb835136 470 assert(fs == servers);
9bc68187 471 while (fs) {
cb835136 472 servers = fs->next;
3c670b50 473 delete fs;
cb835136 474 fs = servers;
c2f4e9cc
AJ
475 }
476 }
477
cfd66529 478 // done with DNS lookups. pass back to caller
62e76326 479
cb835136
AR
480 debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url());
481 debugs(44, 2, " always_direct = " << always_direct);
482 debugs(44, 2, " never_direct = " << never_direct);
483 debugs(44, 2, " timedout = " << ping.timedout);
5ae21d99 484
cb835136
AR
485 ping.stop = current_time;
486 request->hier.ping = ping; // final result
3dfe8371 487
cb835136 488 if (lastError && foundPaths) {
6043e368
AR
489 // nobody cares about errors if we found destinations despite them
490 debugs(44, 3, "forgetting the last error");
cb835136
AR
491 delete lastError;
492 lastError = nullptr;
db1cd23c 493 }
62e76326 494
cb835136
AR
495 if (const auto initiator = interestedInitiator())
496 initiator->noteDestinationsEnd(lastError);
497 lastError = nullptr; // initiator owns the ErrorState object now
498 delete this;
93775f90 499}
500
fd9c47d1 501void
cb835136 502PeerSelector::noteLookup(const Dns::LookupDetails &details)
cfd66529 503{
fd9c47d1 504 /* ignore lookup delays that occurred after the initiator moved on */
cfd66529 505
cb835136 506 if (selectionAborted())
fd9c47d1 507 return;
cfd66529 508
fd9c47d1
AR
509 if (!wantsMoreDestinations())
510 return;
cfd66529 511
fd9c47d1
AR
512 request->recordLookup(details);
513}
5229395c 514
fd9c47d1 515void
cb835136 516PeerSelector::noteIp(const Ip::Address &ip)
fd9c47d1 517{
cb835136 518 if (selectionAborted())
fd9c47d1 519 return;
cfd66529 520
fd9c47d1
AR
521 if (!wantsMoreDestinations())
522 return;
27d1f0a0 523
fd9c47d1 524 const auto peer = servers->_peer.valid();
cfd66529 525
fd9c47d1
AR
526 // for TPROXY spoofing, we must skip unusable addresses
527 if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) {
528 if (ip.isIPv4() != request->client_addr.isIPv4())
529 return; // cannot spoof the client address on this link
530 }
c7a52e6d 531
fd9c47d1
AR
532 Comm::ConnectionPointer p = new Comm::Connection();
533 p->remote = ip;
380b09ae
AR
534 // XXX: We return a (non-peer) destination with a zero port if the selection
535 // initiator supplied a request target without a port. If there are no valid
536 // use cases for this behavior, stop _selecting_ such destinations.
537 p->remote.port(peer ? peer->http_port : request->url.port().value_or(0));
fd9c47d1
AR
538 handlePath(p, *servers);
539}
c7a52e6d 540
fd9c47d1 541void
cb835136 542PeerSelector::noteIps(const Dns::CachedIps *ia, const Dns::LookupDetails &details)
fd9c47d1 543{
cb835136 544 if (selectionAborted())
fd9c47d1 545 return;
cfd66529 546
fd9c47d1
AR
547 FwdServer *fs = servers;
548 if (!ia) {
549 debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host()));
a37fdd8a 550 // discard any previous error.
fd9c47d1 551 delete lastError;
aee3523a 552 lastError = nullptr;
a37fdd8a 553 if (fs->code == HIER_DIRECT) {
7e6eabbc 554 lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, request, al);
fd9c47d1 555 lastError->dnsError = details.error;
a37fdd8a 556 }
cfd66529 557 }
fd9c47d1 558 // else noteIp() calls have already processed all IPs in *ia
cfd66529 559
fd9c47d1 560 servers = fs->next;
3c670b50 561 delete fs;
cfd66529 562
cb835136
AR
563 // continue resolving selected peers
564 resolveSelected();
cfd66529
AJ
565}
566
cb835136
AR
567int
568PeerSelector::checkNetdbDirect()
b3264694 569{
9b5c4a9a 570#if USE_ICMP
a3c6762c 571 CachePeer *p;
b3264694 572 int myrtt;
573 int myhops;
62e76326 574
cb835136 575 if (direct == DIRECT_NO)
62e76326 576 return 0;
577
9b5c4a9a
AJ
578 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
579
cb835136 580 myrtt = netdbHostRtt(request->url.host());
5c51bffb
AJ
581 debugs(44, 3, "MY RTT = " << myrtt << " msec");
582 debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
62e76326 583
5f84d830 584 if (myrtt && myrtt <= Config.minDirectRtt)
62e76326 585 return 1;
586
cb835136 587 myhops = netdbHostHops(request->url.host());
62e76326 588
cb835136
AR
589 debugs(44, 3, "MY hops = " << myhops);
590 debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops);
62e76326 591
b3264694 592 if (myhops && myhops <= Config.minDirectHops)
62e76326 593 return 1;
594
cb835136 595 p = whichPeer(closest_parent_miss);
62e76326 596
aee3523a 597 if (p == nullptr)
62e76326 598 return 0;
599
cb835136 600 debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec");
62e76326 601
cb835136 602 if (myrtt && myrtt <= ping.p_rtt)
62e76326 603 return 1;
604
9b5c4a9a
AJ
605#endif /* USE_ICMP */
606
b3264694 607 return 0;
608}
609
cb835136
AR
610void
611PeerSelector::selectMore()
062e2281 612{
cb835136 613 if (selectionAborted())
36339742 614 return;
36339742 615
5c51bffb 616 debugs(44, 3, request->method << ' ' << request->url.host());
62e76326 617
45e5102d 618 /** If we don't know whether DIRECT is permitted ... */
cb835136
AR
619 if (direct == DIRECT_UNKNOWN) {
620 if (always_direct == ACCESS_DUNNO) {
621 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)");
45e5102d 622 /** check always_direct; */
aee3523a 623 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, nullptr);
cb835136
AR
624 ch->al = al;
625 acl_checklist = ch;
cb365059 626 acl_checklist->syncAle(request, nullptr);
cb835136 627 acl_checklist->nonBlockingCheck(CheckAlwaysDirectDone, this);
62e76326 628 return;
cb835136
AR
629 } else if (never_direct == ACCESS_DUNNO) {
630 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)");
45e5102d 631 /** check never_direct; */
aee3523a 632 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, nullptr);
cb835136
AR
633 ch->al = al;
634 acl_checklist = ch;
cb365059 635 acl_checklist->syncAle(request, nullptr);
cb835136 636 acl_checklist->nonBlockingCheck(CheckNeverDirectDone, this);
62e76326 637 return;
450fe1cb 638 } else if (request->flags.noDirect) {
45e5102d 639 /** if we are accelerating, direct is not an option. */
cb835136
AR
640 direct = DIRECT_NO;
641 debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
450fe1cb 642 } else if (request->flags.loopDetected) {
45e5102d 643 /** if we are in a forwarding-loop, direct is not an option. */
cb835136
AR
644 direct = DIRECT_YES;
645 debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
646 } else if (checkNetdbDirect()) {
647 direct = DIRECT_YES;
648 debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)");
62e76326 649 } else {
cb835136
AR
650 direct = DIRECT_MAYBE;
651 debugs(44, 3, "direct = " << DirectStr[direct] << " (default)");
62e76326 652 }
653
cb835136 654 debugs(44, 3, "direct = " << DirectStr[direct]);
db1cd23c 655 }
62e76326 656
d67acb4e 657 if (!entry || entry->ping_status == PING_NONE)
cb835136 658 selectPinned();
aee3523a 659 if (entry == nullptr) {
62e76326 660 (void) 0;
df503d6c 661 } else if (entry->ping_status == PING_NONE) {
cb835136 662 selectSomeNeighbor();
62e76326 663
664 if (entry->ping_status == PING_WAITING)
665 return;
db1cd23c 666 } else if (entry->ping_status == PING_WAITING) {
cb835136 667 selectSomeNeighborReplies();
9865de72 668 cancelPingTimeoutMonitoring();
62e76326 669 entry->ping_status = PING_DONE;
db1cd23c 670 }
62e76326 671
cb835136 672 switch (direct) {
62e76326 673
168dfda9 674 case DIRECT_YES:
cb835136 675 selectSomeDirect();
62e76326 676 break;
677
168dfda9 678 case DIRECT_NO:
cb835136
AR
679 selectSomeParent();
680 selectAllParents();
62e76326 681 break;
682
168dfda9 683 default:
62e76326 684
685 if (Config.onoff.prefer_direct)
cb835136 686 selectSomeDirect();
62e76326 687
45e5102d 688 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) {
cb835136
AR
689 selectSomeParent();
690 selectAllParents();
8b9a89c9 691 }
62e76326 692
693 if (!Config.onoff.prefer_direct)
cb835136 694 selectSomeDirect();
62e76326 695
696 break;
168dfda9 697 }
62e76326 698
cb835136
AR
699 // end peer selection; start resolving selected peers
700 resolveSelected();
db1cd23c 701}
702
cb365059 703bool peerAllowedToUse(const CachePeer *, PeerSelector*);
cfd66529 704
cb835136
AR
705/// Selects a pinned connection if it exists, is valid, and is allowed.
706void
707PeerSelector::selectPinned()
d67acb4e 708{
cb835136 709 // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
d67acb4e
AJ
710 if (!request->pinnedConnection())
711 return;
3dde9e52 712
daf80700
CT
713 const auto peer = request->pinnedConnection()->pinnedPeer();
714 const auto usePinned = peer ? peerAllowedToUse(peer, this) : (direct != DIRECT_NO);
715 // If the pinned connection is prohibited (for this request) then
cb835136 716 // the initiator must decide whether it is OK to open a new one instead.
daf80700
CT
717 request->pinnedConnection()->pinning.peerAccessDenied = !usePinned;
718
719 addSelection(peer, PINNED);
720 if (entry)
721 entry->ping_status = PING_DONE; // skip ICP
d67acb4e
AJ
722}
723
27774cee 724/**
db1cd23c 725 * Selects a neighbor (parent or sibling) based on one of the
726 * following methods:
727 * Cache Digests
728 * CARP
9b5c4a9a 729 * ICMP Netdb RTT estimates
db1cd23c 730 * ICP/HTCP queries
731 */
cb835136
AR
732void
733PeerSelector::selectSomeNeighbor()
db1cd23c 734{
a3c6762c 735 CachePeer *p;
db1cd23c 736 hier_code code = HIER_NONE;
737 assert(entry->ping_status == PING_NONE);
62e76326 738
cb835136 739 if (direct == DIRECT_YES) {
62e76326 740 entry->ping_status = PING_DONE;
741 return;
124511e5 742 }
62e76326 743
6cfa8966 744#if USE_CACHE_DIGESTS
cb365059 745 if ((p = neighborsDigestSelect(this))) {
5c51bffb 746 if (neighborType(p, request->url) == PEER_PARENT)
62e76326 747 code = CD_PARENT_HIT;
748 else
749 code = CD_SIBLING_HIT;
db1cd23c 750 } else
c127134a 751#endif
cb365059 752 if ((p = netdbClosestParent(this))) {
62e76326 753 code = CLOSEST_PARENT;
cb365059 754 } else if (peerSelectIcpPing(this, direct, entry)) {
cb835136
AR
755 debugs(44, 3, "Doing ICP pings");
756 ping.start = current_time;
757 ping.n_sent = neighborsUdpPing(request,
758 entry,
759 HandlePingReply,
760 this,
761 &ping.n_replies_expected,
762 &ping.timeout);
9865de72
EB
763 // TODO: Refactor neighborsUdpPing() to guarantee positive timeouts.
764 if (ping.timeout < 0)
765 ping.timeout = 0;
cb835136
AR
766
767 if (ping.n_sent == 0)
fa84c01d 768 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
cb835136
AR
769 debugs(44, 3, ping.n_replies_expected <<
770 " ICP replies expected, RTT " << ping.timeout <<
26ac0430 771 " msec");
62e76326 772
cb835136 773 if (ping.n_replies_expected > 0) {
9865de72 774 startPingWaiting();
62e76326 775 return;
776 }
777 }
778
db1cd23c 779 if (code != HIER_NONE) {
62e76326 780 assert(p);
cb835136 781 addSelection(p, code);
db1cd23c 782 }
62e76326 783
db1cd23c 784 entry->ping_status = PING_DONE;
785}
786
cb835136
AR
787/// Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
788void
789PeerSelector::selectSomeNeighborReplies()
db1cd23c 790{
aee3523a 791 CachePeer *p = nullptr;
db1cd23c 792 hier_code code = HIER_NONE;
cb835136
AR
793 assert(entry->ping_status == PING_WAITING);
794 assert(direct != DIRECT_YES);
62e76326 795
cb835136 796 if (checkNetdbDirect()) {
62e76326 797 code = CLOSEST_DIRECT;
cb835136 798 addSelection(nullptr, code);
62e76326 799 return;
db1cd23c 800 }
62e76326 801
cb835136
AR
802 if ((p = hit)) {
803 code = hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
26ac0430 804 } else {
cb835136
AR
805 if (!closest_parent_miss.isAnyAddr()) {
806 p = whichPeer(closest_parent_miss);
cc192b50 807 code = CLOSEST_PARENT_MISS;
cb835136
AR
808 } else if (!first_parent_miss.isAnyAddr()) {
809 p = whichPeer(first_parent_miss);
cc192b50 810 code = FIRST_PARENT_MISS;
811 }
812 }
db1cd23c 813 if (p && code != HIER_NONE) {
cb835136 814 addSelection(p, code);
db1cd23c 815 }
816}
817
cb835136
AR
818/// Adds a "direct" entry if the request can be forwarded to the origin server.
819void
820PeerSelector::selectSomeDirect()
db1cd23c 821{
cb835136 822 if (direct == DIRECT_NO)
62e76326 823 return;
824
db80e881 825 /* WAIS is not implemented natively */
cb835136 826 if (request->url.getScheme() == AnyP::PROTO_WAIS)
26ac0430 827 return;
db80e881 828
cb835136 829 addSelection(nullptr, HIER_DIRECT);
db1cd23c 830}
831
cb835136
AR
832void
833PeerSelector::selectSomeParent()
db1cd23c 834{
a3c6762c 835 CachePeer *p;
db1cd23c 836 hier_code code = HIER_NONE;
5c51bffb 837 debugs(44, 3, request->method << ' ' << request->url.host());
62e76326 838
cb835136 839 if (direct == DIRECT_YES)
62e76326 840 return;
841
cb365059 842 if ((p = peerSourceHashSelectParent(this))) {
b4ab8666 843 code = SOURCEHASH_PARENT;
2f1431ea 844#if USE_AUTH
cb365059 845 } else if ((p = peerUserHashSelectParent(this))) {
f7e1d9ce 846 code = USERHASH_PARENT;
2f1431ea 847#endif
cb365059 848 } else if ((p = carpSelectParent(this))) {
62e76326 849 code = CARP;
cb365059 850 } else if ((p = getRoundRobinParent(this))) {
62e76326 851 code = ROUNDROBIN_PARENT;
cb365059 852 } else if ((p = getWeightedRoundRobinParent(this))) {
62e76326 853 code = ROUNDROBIN_PARENT;
cb365059 854 } else if ((p = getFirstUpParent(this))) {
62e76326 855 code = FIRSTUP_PARENT;
cb365059 856 } else if ((p = getDefaultParent(this))) {
b4ab8666 857 code = DEFAULT_PARENT;
db1cd23c 858 }
62e76326 859
db1cd23c 860 if (code != HIER_NONE) {
cb835136 861 addSelection(p, code);
062e2281 862 }
863}
864
cb835136
AR
865/// Adds alive parents. Used as a last resort for never_direct.
866void
867PeerSelector::selectAllParents()
168dfda9 868{
168dfda9 869 /* Add all alive parents */
62e76326 870
2e24d0bf
EB
871 for (const auto &peer: CurrentCachePeers()) {
872 const auto p = peer.get();
62e76326 873 /* XXX: neighbors.c lacks a public interface for enumerating
874 * parents to a request so we have to dig some here..
875 */
876
5c51bffb 877 if (neighborType(p, request->url) != PEER_PARENT)
62e76326 878 continue;
879
cb365059 880 if (!peerHTTPOkay(p, this))
62e76326 881 continue;
882
cb835136 883 addSelection(p, ANY_OLD_PARENT);
168dfda9 884 }
62e76326 885
168dfda9 886 /* XXX: should add dead parents here, but it is currently
887 * not possible to find out which parents are dead or which
888 * simply are not configured to handle the request.
889 */
890 /* Add default parent as a last resort */
2e24d0bf 891 if (const auto p = getDefaultParent(this)) {
cb835136 892 addSelection(p, DEFAULT_PARENT);
168dfda9 893 }
894}
895
cb835136
AR
896void
897PeerSelector::handlePingTimeout()
062e2281 898{
cb835136 899 debugs(44, 3, url());
62e76326 900
9865de72
EB
901 // do nothing if ping reply came while handlePingTimeout() was queued
902 if (!entry || entry->ping_status != PING_WAITING)
903 return;
904
905 entry->ping_status = PING_DONE;
6043e368 906
cb835136 907 if (selectionAborted())
62e76326 908 return;
62e76326 909
5db6bf73 910 ++PeerStats.timeouts;
cb835136
AR
911 ping.timedout = 1;
912 selectMore();
913}
914
915void
9865de72 916PeerSelector::HandlePingTimeout(PeerSelector *selector)
cb835136 917{
9865de72 918 selector->handlePingTimeout();
85034133 919}
920
921void
922peerSelectInit(void)
923{
75e88d56 924 memset(&PeerStats, '\0', sizeof(PeerStats));
062e2281 925}
93775f90 926
cb835136
AR
927void
928PeerSelector::handleIcpParentMiss(CachePeer *p, icp_common_t *header)
b3264694 929{
930 int rtt;
62e76326 931
9b5c4a9a 932#if USE_ICMP
b3264694 933 if (Config.onoff.query_icmp) {
62e76326 934 if (header->flags & ICP_FLAG_SRC_RTT) {
935 rtt = header->pad & 0xFFFF;
9b5c4a9a 936 int hops = (header->pad >> 16) & 0xFFFF;
62e76326 937
938 if (rtt > 0 && rtt < 0xFFFF)
cb835136 939 netdbUpdatePeer(request->url, p, rtt, hops);
62e76326 940
cb835136
AR
941 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
942 closest_parent_miss = p->in_addr;
943 ping.p_rtt = rtt;
62e76326 944 }
945 }
b3264694 946 }
8b082ed9
FC
947#else
948 (void)header;
9b5c4a9a 949#endif /* USE_ICMP */
62e76326 950
18ec72b2 951 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 952 if (p->options.closest_only)
62e76326 953 return;
954
85223cd7 955 /* set FIRST_MISS if there is no CLOSEST parent */
cb835136 956 if (!closest_parent_miss.isAnyAddr())
62e76326 957 return;
958
cb835136 959 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
62e76326 960
d1b63fc8 961 if (rtt < 1)
62e76326 962 rtt = 1;
963
cb835136
AR
964 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
965 first_parent_miss = p->in_addr;
966 ping.w_rtt = rtt;
b3264694 967 }
968}
93775f90 969
cb835136
AR
970void
971PeerSelector::handleIcpReply(CachePeer *p, const peer_t type, icp_common_t *header)
93775f90 972{
e6ccf245 973 icp_opcode op = header->getOpCode();
cb835136 974 debugs(44, 3, icp_opcode_str[op] << ' ' << url());
69c95dd3 975#if USE_CACHE_DIGESTS && 0
26b164ac 976 /* do cd lookup to count false misses */
62e76326 977
3ab66981 978 if (p && request)
62e76326 979 peerNoteDigestLookup(request, p,
cb365059 980 peerDigestLookup(p, this));
62e76326 981
26b164ac 982#endif
62e76326 983
cb835136 984 ++ping.n_recv;
62e76326 985
27cd7235 986 if (op == ICP_MISS || op == ICP_DECHO) {
62e76326 987 if (type == PEER_PARENT)
cb835136 988 handleIcpParentMiss(p, header);
a7c05555 989 } else if (op == ICP_HIT) {
cb835136
AR
990 hit = p;
991 hit_type = type;
992 selectMore();
62e76326 993 return;
db1cd23c 994 }
62e76326 995
cb835136 996 if (ping.n_recv < ping.n_replies_expected)
62e76326 997 return;
998
cb835136 999 selectMore();
93775f90 1000}
86aebcda 1001
1002#if USE_HTCP
cb835136
AR
1003void
1004PeerSelector::handleHtcpReply(CachePeer *p, const peer_t type, HtcpReplyData *htcp)
86aebcda 1005{
cb835136
AR
1006 debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url());
1007 ++ping.n_recv;
62e76326 1008
44e237d0 1009 if (htcp->hit) {
cb835136
AR
1010 hit = p;
1011 hit_type = type;
1012 selectMore();
62e76326 1013 return;
44e237d0 1014 }
62e76326 1015
44e237d0 1016 if (type == PEER_PARENT)
cb835136 1017 handleHtcpParentMiss(p, htcp);
62e76326 1018
cb835136 1019 if (ping.n_recv < ping.n_replies_expected)
62e76326 1020 return;
1021
cb835136 1022 selectMore();
44e237d0 1023}
1024
cb835136
AR
1025void
1026PeerSelector::handleHtcpParentMiss(CachePeer *p, HtcpReplyData *htcp)
44e237d0 1027{
1028 int rtt;
62e76326 1029
9b5c4a9a 1030#if USE_ICMP
44e237d0 1031 if (Config.onoff.query_icmp) {
62e76326 1032 if (htcp->cto.rtt > 0) {
1033 rtt = (int) htcp->cto.rtt * 1000;
9b5c4a9a 1034 int hops = (int) htcp->cto.hops * 1000;
cb835136 1035 netdbUpdatePeer(request->url, p, rtt, hops);
62e76326 1036
cb835136
AR
1037 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
1038 closest_parent_miss = p->in_addr;
1039 ping.p_rtt = rtt;
62e76326 1040 }
1041 }
44e237d0 1042 }
8b082ed9
FC
1043#else
1044 (void)htcp;
9b5c4a9a 1045#endif /* USE_ICMP */
62e76326 1046
44e237d0 1047 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 1048 if (p->options.closest_only)
62e76326 1049 return;
1050
44e237d0 1051 /* set FIRST_MISS if there is no CLOSEST parent */
cb835136 1052 if (!closest_parent_miss.isAnyAddr())
62e76326 1053 return;
1054
cb835136 1055 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
62e76326 1056
d1b63fc8 1057 if (rtt < 1)
62e76326 1058 rtt = 1;
1059
cb835136
AR
1060 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
1061 first_parent_miss = p->in_addr;
1062 ping.w_rtt = rtt;
44e237d0 1063 }
86aebcda 1064}
62e76326 1065
86aebcda 1066#endif
1067
cb835136
AR
1068void
1069PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
86aebcda 1070{
0c3d3f65 1071 if (proto == AnyP::PROTO_ICP)
cb835136 1072 static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata));
62e76326 1073
86aebcda 1074#if USE_HTCP
62e76326 1075
0c3d3f65 1076 else if (proto == AnyP::PROTO_HTCP)
cb835136 1077 static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata));
62e76326 1078
86aebcda 1079#endif
62e76326 1080
86aebcda 1081 else
cb835136 1082 debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto);
86aebcda 1083}
db1cd23c 1084
cb835136
AR
1085void
1086PeerSelector::addSelection(CachePeer *peer, const hier_code code)
db1cd23c 1087{
e7b08eec 1088 // Find the end of the servers list. Bail on a duplicate destination.
cb835136
AR
1089 auto **serversTail = &servers;
1090 while (const auto server = *serversTail) {
e7b08eec
AR
1091 // There can be at most one PINNED destination.
1092 // Non-PINNED destinations are uniquely identified by their CachePeer
1093 // (even though a DIRECT destination might match a cache_peer address).
9b7992d9
EB
1094 // TODO: We may still add duplicates because the same peer could have
1095 // been removed from `servers` already (and given to the requestor).
e7b08eec 1096 const bool duplicate = (server->code == PINNED) ?
cb835136 1097 (code == PINNED) : (server->_peer == peer);
e7b08eec 1098 if (duplicate) {
cb835136
AR
1099 debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) <<
1100 "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code));
e7b08eec
AR
1101 return;
1102 }
cb835136 1103 serversTail = &server->next;
e7b08eec 1104 }
62e76326 1105
cb835136
AR
1106 debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code));
1107 *serversTail = new FwdServer(peer, code);
db1cd23c 1108}
b24880fe 1109
cb835136 1110PeerSelector::PeerSelector(PeerSelectionInitiator *initiator):
6043e368 1111 request(nullptr),
aee3523a 1112 entry (nullptr),
f53969cc
SM
1113 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
1114 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
1115 direct(DIRECT_UNKNOWN),
aee3523a
AR
1116 lastError(nullptr),
1117 servers (nullptr),
f53969cc
SM
1118 first_parent_miss(),
1119 closest_parent_miss(),
aee3523a 1120 hit(nullptr),
f53969cc 1121 hit_type(PEER_NONE),
aee3523a 1122 acl_checklist (nullptr),
6043e368 1123 initiator_(initiator)
b24880fe 1124{
cc192b50 1125 ; // no local defaults.
b24880fe 1126}
1127
851feda6 1128const SBuf
cb835136 1129PeerSelector::url() const
769c64cc
AJ
1130{
1131 if (entry)
851feda6 1132 return SBuf(entry->url());
769c64cc
AJ
1133
1134 if (request)
851feda6 1135 return request->effectiveRequestUri();
769c64cc 1136
851feda6
AJ
1137 static const SBuf noUrl("[no URL]");
1138 return noUrl;
769c64cc
AJ
1139}
1140
6043e368
AR
1141/// \returns valid/interested peer initiator or nil
1142PeerSelectionInitiator *
cb835136 1143PeerSelector::interestedInitiator()
6043e368
AR
1144{
1145 const auto initiator = initiator_.valid();
1146
1147 if (!initiator) {
1148 debugs(44, 3, id << " initiator gone");
1149 return nullptr;
1150 }
1151
1152 if (!initiator->subscribed) {
1153 debugs(44, 3, id << " initiator lost interest");
1154 return nullptr;
1155 }
1156
fd9c47d1 1157 debugs(44, 7, id);
6043e368
AR
1158 return initiator;
1159}
1160
1161bool
cb835136 1162PeerSelector::wantsMoreDestinations() const {
6043e368 1163 const auto maxCount = Config.forward_max_tries;
8b082ed9 1164 return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount);
6043e368
AR
1165}
1166
1167void
3dde9e52 1168PeerSelector::handlePath(const Comm::ConnectionPointer &path, FwdServer &fs)
6043e368
AR
1169{
1170 ++foundPaths;
1171
3dde9e52
CT
1172 if (path) {
1173 path->peerType = fs.code;
1174 path->setPeer(fs._peer.get());
6043e368 1175
3dde9e52
CT
1176 // check for a configured outgoing address for this destination...
1177 getOutgoingAddress(request, path);
1178 debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url());
1179 } else
1180 debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url());
6043e368
AR
1181
1182 request->hier.ping = ping; // may be updated later
1183
6043e368
AR
1184 debugs(44, 2, " always_direct = " << always_direct);
1185 debugs(44, 2, " never_direct = " << never_direct);
1186 debugs(44, 2, " timedout = " << ping.timedout);
1187
1188 if (const auto initiator = interestedInitiator())
1189 initiator->noteDestination(path);
1190}
1191
cb835136 1192InstanceIdDefinitions(PeerSelector, "PeerSelector");
6043e368 1193
b24880fe 1194ping_data::ping_data() :
f53969cc
SM
1195 n_sent(0),
1196 n_recv(0),
1197 n_replies_expected(0),
1198 timeout(0),
1199 timedout(0),
1200 w_rtt(0),
9865de72
EB
1201 p_rtt(0),
1202 monitorRegistration(PingMonitor().npos())
b24880fe 1203{
1204 start.tv_sec = 0;
1205 start.tv_usec = 0;
1206 stop.tv_sec = 0;
1207 stop.tv_usec = 0;
1208}
f53969cc 1209
9865de72
EB
1210timeval
1211ping_data::deadline() const
1212{
1213 timeval timeInterval;
1214 timeInterval.tv_sec = timeout / 1000;
1215 timeInterval.tv_usec = (timeout % 1000) * 1000;
1216
1217 timeval result;
1218 tvAdd(result, start, timeInterval);
1219 return result;
1220}
1221