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