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