]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_select.cc
MemBuf implements Packable interface
[thirdparty/squid.git] / src / peer_select.cc
CommitLineData
062e2281 1/*
bde978a6 2 * Copyright (C) 1996-2015 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
AJ
255 // send the next one off for DNS lookup.
256 const char *host = fs->_peer ? fs->_peer->host : psstate->request->GetHost();
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()) {
c7a52e6d
AJ
350 const char *host = (fs->_peer ? fs->_peer->host : psstate->request->GetHost());
351 ipcacheMarkBadAddr(host, p->remote);
352 continue;
353 }
354
cb443714 355 if (fs->_peer)
4dd643d5 356 p->remote.port(fs->_peer->http_port);
cb443714 357 else
4dd643d5 358 p->remote.port(psstate->request->port);
5229395c 359 p->peerType = fs->code;
ce63af7f 360 p->setPeer(fs->_peer);
cfd66529
AJ
361
362 // check for a configured outgoing address for this destination...
363 getOutgoingAddress(psstate->request, p);
07f889c1 364 psstate->paths->push_back(p);
cfd66529
AJ
365 }
366 } else {
ceddefdd 367 debugs(44, 3, HERE << "Unknown host: " << (fs->_peer ? fs->_peer->host : psstate->request->GetHost()));
a37fdd8a
AJ
368 // discard any previous error.
369 delete psstate->lastError;
370 psstate->lastError = NULL;
371 if (fs->code == HIER_DIRECT) {
955394ce 372 psstate->lastError = new ErrorState(ERR_DNS_FAIL, Http::scServiceUnavailable, psstate->request);
a37fdd8a
AJ
373 psstate->lastError->dnsError = details.error;
374 }
cfd66529
AJ
375 }
376
377 psstate->servers = fs->next;
378 cbdataReferenceDone(fs->_peer);
379 memFree(fs, MEM_FWD_SERVER);
380
381 // see if more paths can be found
382 peerSelectDnsPaths(psstate);
383}
384
b3264694 385static int
386peerCheckNetdbDirect(ps_state * psstate)
387{
9b5c4a9a 388#if USE_ICMP
a3c6762c 389 CachePeer *p;
b3264694 390 int myrtt;
391 int myhops;
62e76326 392
0886a797 393 if (psstate->direct == DIRECT_NO)
62e76326 394 return 0;
395
9b5c4a9a
AJ
396 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
397
cc192b50 398 myrtt = netdbHostRtt(psstate->request->GetHost());
62e76326 399
bf8fe701 400 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
401 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
62e76326 402
5f84d830 403 if (myrtt && myrtt <= Config.minDirectRtt)
62e76326 404 return 1;
405
cc192b50 406 myhops = netdbHostHops(psstate->request->GetHost());
62e76326 407
bf8fe701 408 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
409 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
62e76326 410
b3264694 411 if (myhops && myhops <= Config.minDirectHops)
62e76326 412 return 1;
413
cc192b50 414 p = whichPeer(psstate->closest_parent_miss);
62e76326 415
5f84d830 416 if (p == NULL)
62e76326 417 return 0;
418
bf8fe701 419 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
62e76326 420
5f84d830 421 if (myrtt && myrtt <= psstate->ping.p_rtt)
62e76326 422 return 1;
423
9b5c4a9a
AJ
424#endif /* USE_ICMP */
425
b3264694 426 return 0;
427}
428
93775f90 429static void
511f47bb 430peerSelectFoo(ps_state * ps)
062e2281 431{
36339742
AJ
432 if (!cbdataReferenceValid(ps->callback_data)) {
433 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
434 delete ps;
435 return;
436 }
437
511f47bb 438 StoreEntry *entry = ps->entry;
190154cf 439 HttpRequest *request = ps->request;
7f06a3d8 440 debugs(44, 3, request->method << ' ' << request->GetHost());
62e76326 441
45e5102d 442 /** If we don't know whether DIRECT is permitted ... */
511f47bb 443 if (ps->direct == DIRECT_UNKNOWN) {
ae026ec6
AJ
444 if (ps->always_direct == ACCESS_DUNNO) {
445 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)");
45e5102d 446 /** check always_direct; */
d4806c91
CT
447 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.AlwaysDirect, request, NULL);
448 ch->al = ps->al;
449 ps->acl_checklist = ch;
b50e327b 450 ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone, ps);
62e76326 451 return;
ae026ec6
AJ
452 } else if (ps->never_direct == ACCESS_DUNNO) {
453 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)");
45e5102d 454 /** check never_direct; */
d4806c91
CT
455 ACLFilledChecklist *ch = new ACLFilledChecklist(Config.accessList.NeverDirect, request, NULL);
456 ch->al = ps->al;
457 ps->acl_checklist = ch;
ae026ec6 458 ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone, ps);
62e76326 459 return;
450fe1cb 460 } else if (request->flags.noDirect) {
45e5102d 461 /** if we are accelerating, direct is not an option. */
62e76326 462 ps->direct = DIRECT_NO;
ae026ec6 463 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forced non-direct)");
450fe1cb 464 } else if (request->flags.loopDetected) {
45e5102d 465 /** if we are in a forwarding-loop, direct is not an option. */
62e76326 466 ps->direct = DIRECT_YES;
ae026ec6 467 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forwarding loop detected)");
62e76326 468 } else if (peerCheckNetdbDirect(ps)) {
469 ps->direct = DIRECT_YES;
ae026ec6 470 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (checkNetdbDirect)");
62e76326 471 } else {
472 ps->direct = DIRECT_MAYBE;
ae026ec6 473 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (default)");
62e76326 474 }
475
bf8fe701 476 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
db1cd23c 477 }
62e76326 478
d67acb4e
AJ
479 if (!entry || entry->ping_status == PING_NONE)
480 peerSelectPinned(ps);
df503d6c 481 if (entry == NULL) {
62e76326 482 (void) 0;
df503d6c 483 } else if (entry->ping_status == PING_NONE) {
62e76326 484 peerGetSomeNeighbor(ps);
485
486 if (entry->ping_status == PING_WAITING)
487 return;
db1cd23c 488 } else if (entry->ping_status == PING_WAITING) {
62e76326 489 peerGetSomeNeighborReplies(ps);
490 entry->ping_status = PING_DONE;
db1cd23c 491 }
62e76326 492
168dfda9 493 switch (ps->direct) {
62e76326 494
168dfda9 495 case DIRECT_YES:
62e76326 496 peerGetSomeDirect(ps);
497 break;
498
168dfda9 499 case DIRECT_NO:
62e76326 500 peerGetSomeParent(ps);
501 peerGetAllParents(ps);
502 break;
503
168dfda9 504 default:
62e76326 505
506 if (Config.onoff.prefer_direct)
507 peerGetSomeDirect(ps);
508
45e5102d 509 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct) {
62e76326 510 peerGetSomeParent(ps);
8b9a89c9
AJ
511 peerGetAllParents(ps);
512 }
62e76326 513
514 if (!Config.onoff.prefer_direct)
515 peerGetSomeDirect(ps);
516
517 break;
168dfda9 518 }
62e76326 519
3dfe8371
AJ
520 // resolve the possible peers
521 peerSelectDnsPaths(ps);
db1cd23c 522}
523
a3c6762c 524bool peerAllowedToUse(const CachePeer * p, HttpRequest * request);
cfd66529 525
27774cee 526/**
d67acb4e
AJ
527 * peerSelectPinned
528 *
27774cee 529 * Selects a pinned connection.
d67acb4e 530 */
d67acb4e
AJ
531static void
532peerSelectPinned(ps_state * ps)
533{
534 HttpRequest *request = ps->request;
d67acb4e
AJ
535 if (!request->pinnedConnection())
536 return;
a3c6762c 537 CachePeer *pear = request->pinnedConnection()->pinnedPeer();
e3a4aecc
AJ
538 if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) {
539 if (pear && peerAllowedToUse(pear, request)) {
540 peerAddFwdServer(&ps->servers, pear, PINNED);
d67acb4e
AJ
541 if (ps->entry)
542 ps->entry->ping_status = PING_DONE; /* Skip ICP */
e3a4aecc 543 } else if (!pear && ps->direct != DIRECT_NO) {
d67acb4e
AJ
544 peerAddFwdServer(&ps->servers, NULL, PINNED);
545 if (ps->entry)
546 ps->entry->ping_status = PING_DONE; /* Skip ICP */
547 }
548 }
549}
550
27774cee 551/**
db1cd23c 552 * peerGetSomeNeighbor
26ac0430 553 *
db1cd23c 554 * Selects a neighbor (parent or sibling) based on one of the
555 * following methods:
556 * Cache Digests
557 * CARP
9b5c4a9a 558 * ICMP Netdb RTT estimates
db1cd23c 559 * ICP/HTCP queries
560 */
561static void
562peerGetSomeNeighbor(ps_state * ps)
563{
564 StoreEntry *entry = ps->entry;
190154cf 565 HttpRequest *request = ps->request;
a3c6762c 566 CachePeer *p;
db1cd23c 567 hier_code code = HIER_NONE;
568 assert(entry->ping_status == PING_NONE);
62e76326 569
db1cd23c 570 if (ps->direct == DIRECT_YES) {
62e76326 571 entry->ping_status = PING_DONE;
572 return;
124511e5 573 }
62e76326 574
6cfa8966 575#if USE_CACHE_DIGESTS
f66a9ef4 576 if ((p = neighborsDigestSelect(request))) {
62e76326 577 if (neighborType(p, request) == PEER_PARENT)
578 code = CD_PARENT_HIT;
579 else
580 code = CD_SIBLING_HIT;
db1cd23c 581 } else
c127134a 582#endif
62e76326 583 if ((p = netdbClosestParent(request))) {
584 code = CLOSEST_PARENT;
585 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
bf8fe701 586 debugs(44, 3, "peerSelect: Doing ICP pings");
62e76326 587 ps->ping.start = current_time;
588 ps->ping.n_sent = neighborsUdpPing(request,
589 entry,
590 peerHandlePingReply,
591 ps,
592 &ps->ping.n_replies_expected,
593 &ps->ping.timeout);
594
595 if (ps->ping.n_sent == 0)
fa84c01d 596 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
26ac0430
AJ
597 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
598 " ICP replies expected, RTT " << ps->ping.timeout <<
599 " msec");
62e76326 600
62e76326 601 if (ps->ping.n_replies_expected > 0) {
602 entry->ping_status = PING_WAITING;
603 eventAdd("peerPingTimeout",
604 peerPingTimeout,
605 ps,
606 0.001 * ps->ping.timeout,
607 0);
608 return;
609 }
610 }
611
db1cd23c 612 if (code != HIER_NONE) {
62e76326 613 assert(p);
4b981814 614 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 615 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 616 }
62e76326 617
db1cd23c 618 entry->ping_status = PING_DONE;
619}
620
621/*
622 * peerGetSomeNeighborReplies
26ac0430 623 *
db1cd23c 624 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
625 */
626static void
627peerGetSomeNeighborReplies(ps_state * ps)
628{
190154cf 629 HttpRequest *request = ps->request;
a3c6762c 630 CachePeer *p = NULL;
db1cd23c 631 hier_code code = HIER_NONE;
4c5a1592 632 assert(ps->entry->ping_status == PING_WAITING);
db1cd23c 633 assert(ps->direct != DIRECT_YES);
62e76326 634
db1cd23c 635 if (peerCheckNetdbDirect(ps)) {
62e76326 636 code = CLOSEST_DIRECT;
4b981814 637 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << request->GetHost());
62e76326 638 peerAddFwdServer(&ps->servers, NULL, code);
639 return;
db1cd23c 640 }
62e76326 641
db1cd23c 642 if ((p = ps->hit)) {
62e76326 643 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
26ac0430 644 } else {
4dd643d5 645 if (!ps->closest_parent_miss.isAnyAddr()) {
cc192b50 646 p = whichPeer(ps->closest_parent_miss);
647 code = CLOSEST_PARENT_MISS;
4dd643d5 648 } else if (!ps->first_parent_miss.isAnyAddr()) {
cc192b50 649 p = whichPeer(ps->first_parent_miss);
650 code = FIRST_PARENT_MISS;
651 }
652 }
db1cd23c 653 if (p && code != HIER_NONE) {
4b981814 654 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 655 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 656 }
657}
658
db1cd23c 659/*
660 * peerGetSomeDirect
26ac0430 661 *
db1cd23c 662 * Simply adds a 'direct' entry to the FwdServers list if this
663 * request can be forwarded directly to the origin server
664 */
665static void
666peerGetSomeDirect(ps_state * ps)
667{
668 if (ps->direct == DIRECT_NO)
62e76326 669 return;
670
db80e881 671 /* WAIS is not implemented natively */
4e3f4dc7 672 if (ps->request->url.getScheme() == AnyP::PROTO_WAIS)
26ac0430 673 return;
db80e881 674
675 peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
db1cd23c 676}
677
678static void
679peerGetSomeParent(ps_state * ps)
680{
a3c6762c 681 CachePeer *p;
190154cf 682 HttpRequest *request = ps->request;
db1cd23c 683 hier_code code = HIER_NONE;
7f06a3d8 684 debugs(44, 3, request->method << ' ' << request->GetHost());
62e76326 685
6b8e7481 686 if (ps->direct == DIRECT_YES)
62e76326 687 return;
688
b4ab8666
AJ
689 if ((p = peerSourceHashSelectParent(request))) {
690 code = SOURCEHASH_PARENT;
2f1431ea 691#if USE_AUTH
f7e1d9ce
HN
692 } else if ((p = peerUserHashSelectParent(request))) {
693 code = USERHASH_PARENT;
2f1431ea 694#endif
b3995439 695 } else if ((p = carpSelectParent(request))) {
62e76326 696 code = CARP;
db1cd23c 697 } else if ((p = getRoundRobinParent(request))) {
62e76326 698 code = ROUNDROBIN_PARENT;
d1b63fc8 699 } else if ((p = getWeightedRoundRobinParent(request))) {
62e76326 700 code = ROUNDROBIN_PARENT;
db1cd23c 701 } else if ((p = getFirstUpParent(request))) {
62e76326 702 code = FIRSTUP_PARENT;
b4ab8666
AJ
703 } else if ((p = getDefaultParent(request))) {
704 code = DEFAULT_PARENT;
db1cd23c 705 }
62e76326 706
db1cd23c 707 if (code != HIER_NONE) {
4b981814 708 debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
62e76326 709 peerAddFwdServer(&ps->servers, p, code);
062e2281 710 }
711}
712
168dfda9 713/* Adds alive parents. Used as a last resort for never_direct.
714 */
715static void
716peerGetAllParents(ps_state * ps)
717{
a3c6762c 718 CachePeer *p;
190154cf 719 HttpRequest *request = ps->request;
168dfda9 720 /* Add all alive parents */
62e76326 721
168dfda9 722 for (p = Config.peers; p; p = p->next) {
62e76326 723 /* XXX: neighbors.c lacks a public interface for enumerating
724 * parents to a request so we have to dig some here..
725 */
726
727 if (neighborType(p, request) != PEER_PARENT)
728 continue;
729
730 if (!peerHTTPOkay(p, request))
731 continue;
732
bf8fe701 733 debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
62e76326 734
735 peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
168dfda9 736 }
62e76326 737
168dfda9 738 /* XXX: should add dead parents here, but it is currently
739 * not possible to find out which parents are dead or which
740 * simply are not configured to handle the request.
741 */
742 /* Add default parent as a last resort */
743 if ((p = getDefaultParent(request))) {
62e76326 744 peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
168dfda9 745 }
746}
747
d9586c3c 748static void
93775f90 749peerPingTimeout(void *data)
062e2281 750{
e6ccf245 751 ps_state *psstate = (ps_state *)data;
b6c0e933 752 StoreEntry *entry = psstate->entry;
62e76326 753
fefb0227 754 if (entry)
4d75742e 755 debugs(44, 3, "peerPingTimeout: '" << psstate->url() << "'" );
62e76326 756
fa80a8ef 757 if (!cbdataReferenceValid(psstate->callback_data)) {
62e76326 758 /* request aborted */
759 entry->ping_status = PING_DONE;
760 cbdataReferenceDone(psstate->callback_data);
029c8349 761 delete psstate;
62e76326 762 return;
73a201f8 763 }
62e76326 764
5db6bf73 765 ++PeerStats.timeouts;
44e237d0 766 psstate->ping.timedout = 1;
b6c0e933 767 peerSelectFoo(psstate);
85034133 768}
769
770void
771peerSelectInit(void)
772{
75e88d56 773 memset(&PeerStats, '\0', sizeof(PeerStats));
cfd66529 774 memDataInit(MEM_FWD_SERVER, "FwdServer", sizeof(FwdServer), 0);
062e2281 775}
93775f90 776
b3264694 777static void
a3c6762c 778peerIcpParentMiss(CachePeer * p, icp_common_t * header, ps_state * ps)
b3264694 779{
780 int rtt;
62e76326 781
9b5c4a9a 782#if USE_ICMP
b3264694 783 if (Config.onoff.query_icmp) {
62e76326 784 if (header->flags & ICP_FLAG_SRC_RTT) {
785 rtt = header->pad & 0xFFFF;
9b5c4a9a 786 int hops = (header->pad >> 16) & 0xFFFF;
62e76326 787
788 if (rtt > 0 && rtt < 0xFFFF)
789 netdbUpdatePeer(ps->request, p, rtt, hops);
790
791 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
792 ps->closest_parent_miss = p->in_addr;
793 ps->ping.p_rtt = rtt;
794 }
795 }
b3264694 796 }
9b5c4a9a 797#endif /* USE_ICMP */
62e76326 798
18ec72b2 799 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 800 if (p->options.closest_only)
62e76326 801 return;
802
85223cd7 803 /* set FIRST_MISS if there is no CLOSEST parent */
4dd643d5 804 if (!ps->closest_parent_miss.isAnyAddr())
62e76326 805 return;
806
d1b63fc8 807 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 808
d1b63fc8 809 if (rtt < 1)
62e76326 810 rtt = 1;
811
4dd643d5 812 if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 813 ps->first_parent_miss = p->in_addr;
814 ps->ping.w_rtt = rtt;
b3264694 815 }
816}
93775f90 817
b6c0e933 818static void
a3c6762c 819peerHandleIcpReply(CachePeer * p, peer_t type, icp_common_t * header, void *data)
93775f90 820{
e6ccf245 821 ps_state *psstate = (ps_state *)data;
822 icp_opcode op = header->getOpCode();
4d75742e 823 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->url() );
69c95dd3 824#if USE_CACHE_DIGESTS && 0
26b164ac 825 /* do cd lookup to count false misses */
62e76326 826
3ab66981 827 if (p && request)
62e76326 828 peerNoteDigestLookup(request, p,
829 peerDigestLookup(p, request, psstate->entry));
830
26b164ac 831#endif
62e76326 832
5db6bf73 833 ++ psstate->ping.n_recv;
62e76326 834
27cd7235 835 if (op == ICP_MISS || op == ICP_DECHO) {
62e76326 836 if (type == PEER_PARENT)
837 peerIcpParentMiss(p, header, psstate);
a7c05555 838 } else if (op == ICP_HIT) {
62e76326 839 psstate->hit = p;
840 psstate->hit_type = type;
841 peerSelectFoo(psstate);
842 return;
db1cd23c 843 }
62e76326 844
44e237d0 845 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 846 return;
847
b6c0e933 848 peerSelectFoo(psstate);
93775f90 849}
86aebcda 850
851#if USE_HTCP
852static void
fad2588a 853peerHandleHtcpReply(CachePeer * p, peer_t type, HtcpReplyData * htcp, void *data)
86aebcda 854{
e6ccf245 855 ps_state *psstate = (ps_state *)data;
4d75742e 856 debugs(44, 3, "" << (htcp->hit ? "HIT" : "MISS") << " " << psstate->url());
5db6bf73 857 ++ psstate->ping.n_recv;
62e76326 858
44e237d0 859 if (htcp->hit) {
62e76326 860 psstate->hit = p;
861 psstate->hit_type = type;
862 peerSelectFoo(psstate);
863 return;
44e237d0 864 }
62e76326 865
44e237d0 866 if (type == PEER_PARENT)
62e76326 867 peerHtcpParentMiss(p, htcp, psstate);
868
44e237d0 869 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 870 return;
871
44e237d0 872 peerSelectFoo(psstate);
873}
874
875static void
fad2588a 876peerHtcpParentMiss(CachePeer * p, HtcpReplyData * htcp, ps_state * ps)
44e237d0 877{
878 int rtt;
62e76326 879
9b5c4a9a 880#if USE_ICMP
44e237d0 881 if (Config.onoff.query_icmp) {
62e76326 882 if (htcp->cto.rtt > 0) {
883 rtt = (int) htcp->cto.rtt * 1000;
9b5c4a9a 884 int hops = (int) htcp->cto.hops * 1000;
62e76326 885 netdbUpdatePeer(ps->request, p, rtt, hops);
886
887 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
888 ps->closest_parent_miss = p->in_addr;
889 ps->ping.p_rtt = rtt;
890 }
891 }
44e237d0 892 }
9b5c4a9a 893#endif /* USE_ICMP */
62e76326 894
44e237d0 895 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 896 if (p->options.closest_only)
62e76326 897 return;
898
44e237d0 899 /* set FIRST_MISS if there is no CLOSEST parent */
4dd643d5 900 if (!ps->closest_parent_miss.isAnyAddr())
62e76326 901 return;
902
d1b63fc8 903 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 904
d1b63fc8 905 if (rtt < 1)
62e76326 906 rtt = 1;
907
4dd643d5 908 if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 909 ps->first_parent_miss = p->in_addr;
910 ps->ping.w_rtt = rtt;
44e237d0 911 }
86aebcda 912}
62e76326 913
86aebcda 914#endif
915
916static void
a3c6762c 917peerHandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
86aebcda 918{
0c3d3f65 919 if (proto == AnyP::PROTO_ICP)
62e76326 920 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
921
86aebcda 922#if USE_HTCP
62e76326 923
0c3d3f65 924 else if (proto == AnyP::PROTO_HTCP)
fad2588a 925 peerHandleHtcpReply(p, type, (HtcpReplyData *)pingdata, data);
62e76326 926
86aebcda 927#endif
62e76326 928
86aebcda 929 else
e0236918 930 debugs(44, DBG_IMPORTANT, "peerHandlePingReply: unknown protocol " << proto);
86aebcda 931}
db1cd23c 932
933static void
a3c6762c 934peerAddFwdServer(FwdServer ** FSVR, CachePeer * p, hier_code code)
db1cd23c 935{
e6ccf245 936 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
26ac0430
AJ
937 debugs(44, 5, "peerAddFwdServer: adding " <<
938 (p ? p->host : "DIRECT") << " " <<
4b981814 939 hier_code_str[code] );
29b8d8d6 940 fs->_peer = cbdataReference(p);
db1cd23c 941 fs->code = code;
62e76326 942
b73f343e 943 while (*FSVR)
944 FSVR = &(*FSVR)->next;
62e76326 945
b73f343e 946 *FSVR = fs;
db1cd23c 947}
b24880fe 948
b24880fe 949ps_state::ps_state() : request (NULL),
f53969cc
SM
950 entry (NULL),
951 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
952 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
953 direct(DIRECT_UNKNOWN),
954 callback (NULL),
955 callback_data (NULL),
956 lastError(NULL),
09c7fb54 957 paths(NULL),
f53969cc
SM
958 servers (NULL),
959 first_parent_miss(),
960 closest_parent_miss(),
961 hit(NULL),
962 hit_type(PEER_NONE),
963 acl_checklist (NULL)
b24880fe 964{
cc192b50 965 ; // no local defaults.
b24880fe 966}
967
769c64cc
AJ
968const char *
969ps_state::url() const
970{
971 if (entry)
972 return entry->url();
973
974 if (request)
975 return urlCanonical(request);
976
977 return "[no URL]";
978}
979
b24880fe 980ping_data::ping_data() :
f53969cc
SM
981 n_sent(0),
982 n_recv(0),
983 n_replies_expected(0),
984 timeout(0),
985 timedout(0),
986 w_rtt(0),
987 p_rtt(0)
b24880fe 988{
989 start.tv_sec = 0;
990 start.tv_usec = 0;
991 stop.tv_sec = 0;
992 stop.tv_usec = 0;
993}
f53969cc 994