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