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