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