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