]> git.ipfire.org Git - thirdparty/squid.git/blame - src/peer_select.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / peer_select.cc
CommitLineData
062e2281 1/*
262a0e14 2 * $Id$
062e2281 3 *
4 * DEBUG: section 44 Peer Selection Algorithm
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
062e2281 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
062e2281 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
062e2281 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
062e2281 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
062e2281 33 */
34
35#include "squid.h"
a553a5a3 36#include "event.h"
b24880fe 37#include "PeerSelectState.h"
e6ccf245 38#include "Store.h"
39#include "ICP.h"
528b2c61 40#include "HttpRequest.h"
8000a965 41#include "ACLChecklist.h"
924f73bc 42#include "htcp.h"
b6b6f466 43#include "forward.h"
cc192b50 44#include "SquidTime.h"
9b5c4a9a 45#include "icmp/net_db.h"
062e2281 46
26ac0430
AJ
47const char *hier_strings[] = {
48 "NONE",
49 "DIRECT",
50 "SIBLING_HIT",
51 "PARENT_HIT",
52 "DEFAULT_PARENT",
53 "SINGLE_PARENT",
54 "FIRST_UP_PARENT",
55 "FIRST_PARENT_MISS",
56 "CLOSEST_PARENT_MISS",
57 "CLOSEST_PARENT",
58 "CLOSEST_DIRECT",
59 "NO_DIRECT_FAIL",
60 "SOURCE_FASTEST",
61 "ROUNDROBIN_PARENT",
6cfa8966 62#if USE_CACHE_DIGESTS
26ac0430
AJ
63 "CD_PARENT_HIT",
64 "CD_SIBLING_HIT",
afd88fbe 65#endif
26ac0430
AJ
66 "CARP",
67 "ANY_PARENT",
68 "USERHASH",
69 "SOURCEHASH",
70 "INVALID CODE"
71};
72
73static struct {
75e88d56 74 int timeouts;
2fadd50d 75} PeerStats;
062e2281 76
26ac0430
AJ
77static const char *DirectStr[] = {
78 "DIRECT_UNKNOWN",
79 "DIRECT_NO",
80 "DIRECT_MAYBE",
81 "DIRECT_YES"
82};
75e88d56 83
f5b8bbc4 84static void peerSelectFoo(ps_state *);
85static void peerPingTimeout(void *data);
db1cd23c 86static void peerSelectCallback(ps_state * psstate);
86aebcda 87static IRCB peerHandlePingReply;
f5b8bbc4 88static void peerSelectStateFree(ps_state * psstate);
89static void peerIcpParentMiss(peer *, icp_common_t *, ps_state *);
44e237d0 90#if USE_HTCP
91static void peerHtcpParentMiss(peer *, htcpReplyData *, ps_state *);
92static void peerHandleHtcpReply(peer *, peer_t, htcpReplyData *, void *);
93#endif
f5b8bbc4 94static int peerCheckNetdbDirect(ps_state * psstate);
db1cd23c 95static void peerGetSomeNeighbor(ps_state *);
96static void peerGetSomeNeighborReplies(ps_state *);
97static void peerGetSomeDirect(ps_state *);
98static void peerGetSomeParent(ps_state *);
168dfda9 99static void peerGetAllParents(ps_state *);
db1cd23c 100static void peerAddFwdServer(FwdServer **, peer *, hier_code);
d67acb4e 101static void peerSelectPinned(ps_state * ps);
348b2031 102
aa839030 103CBDATA_CLASS_INIT(ps_state);
104
348b2031 105static void
106peerSelectStateFree(ps_state * psstate)
107{
108 if (psstate->acl_checklist) {
bf8fe701 109 debugs(44, 1, "calling aclChecklistFree() from peerSelectStateFree");
62e76326 110 delete (psstate->acl_checklist);
348b2031 111 }
62e76326 112
6dd9f4bd 113 HTTPMSGUNLOCK(psstate->request);
62e76326 114
73a201f8 115 if (psstate->entry) {
62e76326 116 assert(psstate->entry->ping_status != PING_WAITING);
97b5e68f 117 psstate->entry->unlock();
62e76326 118 psstate->entry = NULL;
73a201f8 119 }
62e76326 120
8407afee 121 cbdataFree(psstate);
348b2031 122}
062e2281 123
2d72d4fd 124static int
190154cf 125peerSelectIcpPing(HttpRequest * request, int direct, StoreEntry * entry)
062e2281 126{
7b665aeb 127 int n;
db1cd23c 128 assert(entry);
129 assert(entry->ping_status == PING_NONE);
9bd6a36d 130 assert(direct != DIRECT_YES);
bf8fe701 131 debugs(44, 3, "peerSelectIcpPing: " << entry->url() );
62e76326 132
92695e5e 133 if (!request->flags.hierarchical && direct != DIRECT_NO)
62e76326 134 return 0;
135
d46a87a8 136 if (EBIT_TEST(entry->flags, KEY_PRIVATE) && !neighbors_do_private_keys)
62e76326 137 if (direct != DIRECT_NO)
138 return 0;
139
7b665aeb 140 n = neighborsCount(request);
62e76326 141
bf8fe701 142 debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
62e76326 143
7b665aeb 144 return n;
062e2281 145}
146
147
062e2281 148void
190154cf 149peerSelect(HttpRequest * request,
62e76326 150 StoreEntry * entry,
151 PSC * callback,
152 void *callback_data)
75e88d56 153{
28c60158 154 ps_state *psstate;
62e76326 155
86b389fc 156 if (entry)
bf8fe701 157 debugs(44, 3, "peerSelect: " << entry->url() );
86b389fc 158 else
60745f24 159 debugs(44, 3, "peerSelect: " << RequestMethodStr(request->method));
62e76326 160
b24880fe 161 psstate = new ps_state;
62e76326 162
6dd9f4bd 163 psstate->request = HTTPMSGLOCK(request);
62e76326 164
b6c0e933 165 psstate->entry = entry;
62e76326 166
b6c0e933 167 psstate->callback = callback;
62e76326 168
fa80a8ef 169 psstate->callback_data = cbdataReference(callback_data);
62e76326 170
db1cd23c 171 psstate->direct = DIRECT_UNKNOWN;
62e76326 172
6cfa8966 173#if USE_CACHE_DIGESTS
62e76326 174
39edba21 175 request->hier.peer_select_start = current_time;
62e76326 176
39edba21 177#endif
62e76326 178
2395cb21 179 if (psstate->entry)
cc192b50 180 psstate->entry->lock();
62e76326 181
b6c0e933 182 peerSelectFoo(psstate);
75e88d56 183}
184
185static void
186peerCheckNeverDirectDone(int answer, void *data)
187{
e6ccf245 188 ps_state *psstate = (ps_state *) data;
348b2031 189 psstate->acl_checklist = NULL;
bf8fe701 190 debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
b6c0e933 191 psstate->never_direct = answer ? 1 : -1;
192 peerSelectFoo(psstate);
75e88d56 193}
194
195static void
196peerCheckAlwaysDirectDone(int answer, void *data)
197{
e6ccf245 198 ps_state *psstate = (ps_state *)data;
348b2031 199 psstate->acl_checklist = NULL;
bf8fe701 200 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
7b665aeb 201 psstate->always_direct = answer ? 1 : -1;
b6c0e933 202 peerSelectFoo(psstate);
75e88d56 203}
204
93775f90 205static void
db1cd23c 206peerSelectCallback(ps_state * psstate)
93775f90 207{
b6c0e933 208 StoreEntry *entry = psstate->entry;
db1cd23c 209 FwdServer *fs = psstate->servers;
fa80a8ef 210 PSC *callback;
211 void *cbdata;
62e76326 212
b6c0e933 213 if (entry) {
bf8fe701 214 debugs(44, 3, "peerSelectCallback: " << entry->url() );
62e76326 215
216 if (entry->ping_status == PING_WAITING)
217 eventDelete(peerPingTimeout, psstate);
218
219 entry->ping_status = PING_DONE;
b6c0e933 220 }
62e76326 221
db1cd23c 222 if (fs == NULL) {
bf8fe701 223 debugs(44, 1, "Failed to select source for '" << entry->url() << "'" );
224 debugs(44, 1, " always_direct = " << psstate->always_direct );
225 debugs(44, 1, " never_direct = " << psstate->never_direct );
226 debugs(44, 1, " timedout = " << psstate->ping.timedout );
db1cd23c 227 }
62e76326 228
44e237d0 229 psstate->ping.stop = current_time;
0bdf2621 230 psstate->request->hier.ping = psstate->ping;
fa80a8ef 231 callback = psstate->callback;
232 psstate->callback = NULL;
62e76326 233
fa80a8ef 234 if (cbdataReferenceValidDone(psstate->callback_data, &cbdata)) {
62e76326 235 psstate->servers = NULL;
236 callback(fs, cbdata);
db1cd23c 237 }
62e76326 238
348b2031 239 peerSelectStateFree(psstate);
93775f90 240}
241
b3264694 242static int
243peerCheckNetdbDirect(ps_state * psstate)
244{
9b5c4a9a 245#if USE_ICMP
5f84d830 246 peer *p;
b3264694 247 int myrtt;
248 int myhops;
62e76326 249
0886a797 250 if (psstate->direct == DIRECT_NO)
62e76326 251 return 0;
252
9b5c4a9a
AJ
253 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
254
cc192b50 255 myrtt = netdbHostRtt(psstate->request->GetHost());
62e76326 256
bf8fe701 257 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt << " msec");
258 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config.minDirectRtt << " msec");
62e76326 259
5f84d830 260 if (myrtt && myrtt <= Config.minDirectRtt)
62e76326 261 return 1;
262
cc192b50 263 myhops = netdbHostHops(psstate->request->GetHost());
62e76326 264
bf8fe701 265 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
266 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
62e76326 267
b3264694 268 if (myhops && myhops <= Config.minDirectHops)
62e76326 269 return 1;
270
cc192b50 271 p = whichPeer(psstate->closest_parent_miss);
62e76326 272
5f84d830 273 if (p == NULL)
62e76326 274 return 0;
275
bf8fe701 276 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
62e76326 277
5f84d830 278 if (myrtt && myrtt <= psstate->ping.p_rtt)
62e76326 279 return 1;
280
9b5c4a9a
AJ
281#endif /* USE_ICMP */
282
b3264694 283 return 0;
284}
285
93775f90 286static void
511f47bb 287peerSelectFoo(ps_state * ps)
062e2281 288{
511f47bb 289 StoreEntry *entry = ps->entry;
190154cf 290 HttpRequest *request = ps->request;
60745f24 291 debugs(44, 3, "peerSelectFoo: '" << RequestMethodStr(request->method) << " " << request->GetHost() << "'");
62e76326 292
511f47bb 293 if (ps->direct == DIRECT_UNKNOWN) {
62e76326 294 if (ps->always_direct == 0 && Config.accessList.AlwaysDirect) {
295 ps->acl_checklist = aclChecklistCreate(
296 Config.accessList.AlwaysDirect,
297 request,
298 NULL); /* ident */
299 ps->acl_checklist->nonBlockingCheck(peerCheckAlwaysDirectDone,
300 ps);
301 return;
302 } else if (ps->always_direct > 0) {
303 ps->direct = DIRECT_YES;
304 } else if (ps->never_direct == 0 && Config.accessList.NeverDirect) {
305 ps->acl_checklist = aclChecklistCreate(
306 Config.accessList.NeverDirect,
307 request,
308 NULL); /* ident */
309 ps->acl_checklist->nonBlockingCheck(peerCheckNeverDirectDone,
310 ps);
311 return;
312 } else if (ps->never_direct > 0) {
313 ps->direct = DIRECT_NO;
314 } else if (request->flags.accelerated) {
315 ps->direct = DIRECT_NO;
316 } else if (request->flags.loopdetect) {
317 ps->direct = DIRECT_YES;
318 } else if (peerCheckNetdbDirect(ps)) {
319 ps->direct = DIRECT_YES;
320 } else {
321 ps->direct = DIRECT_MAYBE;
322 }
323
bf8fe701 324 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
db1cd23c 325 }
62e76326 326
d67acb4e
AJ
327 if (!entry || entry->ping_status == PING_NONE)
328 peerSelectPinned(ps);
df503d6c 329 if (entry == NULL) {
62e76326 330 (void) 0;
df503d6c 331 } else if (entry->ping_status == PING_NONE) {
62e76326 332 peerGetSomeNeighbor(ps);
333
334 if (entry->ping_status == PING_WAITING)
335 return;
db1cd23c 336 } else if (entry->ping_status == PING_WAITING) {
62e76326 337 peerGetSomeNeighborReplies(ps);
338 entry->ping_status = PING_DONE;
db1cd23c 339 }
62e76326 340
168dfda9 341 switch (ps->direct) {
62e76326 342
168dfda9 343 case DIRECT_YES:
62e76326 344 peerGetSomeDirect(ps);
345 break;
346
168dfda9 347 case DIRECT_NO:
62e76326 348 peerGetSomeParent(ps);
349 peerGetAllParents(ps);
350 break;
351
168dfda9 352 default:
62e76326 353
354 if (Config.onoff.prefer_direct)
355 peerGetSomeDirect(ps);
356
357 if (request->flags.hierarchical || !Config.onoff.nonhierarchical_direct)
358 peerGetSomeParent(ps);
359
360 if (!Config.onoff.prefer_direct)
361 peerGetSomeDirect(ps);
362
363 break;
168dfda9 364 }
62e76326 365
511f47bb 366 peerSelectCallback(ps);
db1cd23c 367}
368
d67acb4e
AJ
369/*
370 * peerSelectPinned
371 *
372 * Selects a pinned connection
373 */
374int peerAllowedToUse(const peer * p, HttpRequest * request);
375static void
376peerSelectPinned(ps_state * ps)
377{
378 HttpRequest *request = ps->request;
379 peer *peer;
380 if (!request->pinnedConnection())
381 return;
382 if (request->pinnedConnection()->validatePinnedConnection(request) != -1) {
26ac0430 383 peer = request->pinnedConnection()->pinnedPeer();
d67acb4e
AJ
384 if (peer && peerAllowedToUse(peer, request)) {
385 peerAddFwdServer(&ps->servers, peer, PINNED);
386 if (ps->entry)
387 ps->entry->ping_status = PING_DONE; /* Skip ICP */
388 } else if (!peer && ps->direct != DIRECT_NO) {
389 peerAddFwdServer(&ps->servers, NULL, PINNED);
390 if (ps->entry)
391 ps->entry->ping_status = PING_DONE; /* Skip ICP */
392 }
393 }
394}
395
db1cd23c 396/*
397 * peerGetSomeNeighbor
26ac0430 398 *
db1cd23c 399 * Selects a neighbor (parent or sibling) based on one of the
400 * following methods:
401 * Cache Digests
402 * CARP
9b5c4a9a 403 * ICMP Netdb RTT estimates
db1cd23c 404 * ICP/HTCP queries
405 */
406static void
407peerGetSomeNeighbor(ps_state * ps)
408{
409 StoreEntry *entry = ps->entry;
190154cf 410 HttpRequest *request = ps->request;
db1cd23c 411 peer *p;
412 hier_code code = HIER_NONE;
413 assert(entry->ping_status == PING_NONE);
62e76326 414
db1cd23c 415 if (ps->direct == DIRECT_YES) {
62e76326 416 entry->ping_status = PING_DONE;
417 return;
124511e5 418 }
62e76326 419
6cfa8966 420#if USE_CACHE_DIGESTS
f66a9ef4 421 if ((p = neighborsDigestSelect(request))) {
62e76326 422 if (neighborType(p, request) == PEER_PARENT)
423 code = CD_PARENT_HIT;
424 else
425 code = CD_SIBLING_HIT;
db1cd23c 426 } else
c127134a 427#endif
62e76326 428 if ((p = netdbClosestParent(request))) {
429 code = CLOSEST_PARENT;
430 } else if (peerSelectIcpPing(request, ps->direct, entry)) {
bf8fe701 431 debugs(44, 3, "peerSelect: Doing ICP pings");
62e76326 432 ps->ping.start = current_time;
433 ps->ping.n_sent = neighborsUdpPing(request,
434 entry,
435 peerHandlePingReply,
436 ps,
437 &ps->ping.n_replies_expected,
438 &ps->ping.timeout);
439
440 if (ps->ping.n_sent == 0)
bf8fe701 441 debugs(44, 0, "WARNING: neighborsUdpPing returned 0");
26ac0430
AJ
442 debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
443 " ICP replies expected, RTT " << ps->ping.timeout <<
444 " msec");
62e76326 445
62e76326 446
447 if (ps->ping.n_replies_expected > 0) {
448 entry->ping_status = PING_WAITING;
449 eventAdd("peerPingTimeout",
450 peerPingTimeout,
451 ps,
452 0.001 * ps->ping.timeout,
453 0);
454 return;
455 }
456 }
457
db1cd23c 458 if (code != HIER_NONE) {
62e76326 459 assert(p);
bf8fe701 460 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
62e76326 461 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 462 }
62e76326 463
db1cd23c 464 entry->ping_status = PING_DONE;
465}
466
467/*
468 * peerGetSomeNeighborReplies
26ac0430 469 *
db1cd23c 470 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
471 */
472static void
473peerGetSomeNeighborReplies(ps_state * ps)
474{
190154cf 475 HttpRequest *request = ps->request;
db1cd23c 476 peer *p = NULL;
477 hier_code code = HIER_NONE;
4c5a1592 478 assert(ps->entry->ping_status == PING_WAITING);
db1cd23c 479 assert(ps->direct != DIRECT_YES);
62e76326 480
db1cd23c 481 if (peerCheckNetdbDirect(ps)) {
62e76326 482 code = CLOSEST_DIRECT;
cc192b50 483 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << request->GetHost());
62e76326 484 peerAddFwdServer(&ps->servers, NULL, code);
485 return;
db1cd23c 486 }
62e76326 487
db1cd23c 488 if ((p = ps->hit)) {
62e76326 489 code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
26ac0430 490 } else {
cc192b50 491 if (!ps->closest_parent_miss.IsAnyAddr()) {
492 p = whichPeer(ps->closest_parent_miss);
493 code = CLOSEST_PARENT_MISS;
494 } else if (!ps->first_parent_miss.IsAnyAddr()) {
495 p = whichPeer(ps->first_parent_miss);
496 code = FIRST_PARENT_MISS;
497 }
498 }
db1cd23c 499 if (p && code != HIER_NONE) {
bf8fe701 500 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
62e76326 501 peerAddFwdServer(&ps->servers, p, code);
db1cd23c 502 }
503}
504
505
506/*
507 * peerGetSomeDirect
26ac0430 508 *
db1cd23c 509 * Simply adds a 'direct' entry to the FwdServers list if this
510 * request can be forwarded directly to the origin server
511 */
512static void
513peerGetSomeDirect(ps_state * ps)
514{
515 if (ps->direct == DIRECT_NO)
62e76326 516 return;
517
db80e881 518 /* WAIS is not implemented natively */
db1cd23c 519 if (ps->request->protocol == PROTO_WAIS)
26ac0430 520 return;
db80e881 521
522 peerAddFwdServer(&ps->servers, NULL, HIER_DIRECT);
db1cd23c 523}
524
525static void
526peerGetSomeParent(ps_state * ps)
527{
528 peer *p;
190154cf 529 HttpRequest *request = ps->request;
db1cd23c 530 hier_code code = HIER_NONE;
60745f24 531 debugs(44, 3, "peerGetSomeParent: " << RequestMethodStr(request->method) << " " << request->GetHost());
62e76326 532
6b8e7481 533 if (ps->direct == DIRECT_YES)
62e76326 534 return;
535
db1cd23c 536 if ((p = getDefaultParent(request))) {
62e76326 537 code = DEFAULT_PARENT;
f7e1d9ce
HN
538 } else if ((p = peerUserHashSelectParent(request))) {
539 code = USERHASH_PARENT;
540 } else if ((p = peerSourceHashSelectParent(request))) {
541 code = SOURCEHASH_PARENT;
b3995439 542 } else if ((p = carpSelectParent(request))) {
62e76326 543 code = CARP;
db1cd23c 544 } else if ((p = getRoundRobinParent(request))) {
62e76326 545 code = ROUNDROBIN_PARENT;
d1b63fc8 546 } else if ((p = getWeightedRoundRobinParent(request))) {
62e76326 547 code = ROUNDROBIN_PARENT;
db1cd23c 548 } else if ((p = getFirstUpParent(request))) {
62e76326 549 code = FIRSTUP_PARENT;
db1cd23c 550 } else if ((p = getAnyParent(request))) {
62e76326 551 code = ANY_OLD_PARENT;
db1cd23c 552 }
62e76326 553
db1cd23c 554 if (code != HIER_NONE) {
bf8fe701 555 debugs(44, 3, "peerSelect: " << hier_strings[code] << "/" << p->host);
62e76326 556 peerAddFwdServer(&ps->servers, p, code);
062e2281 557 }
558}
559
168dfda9 560/* Adds alive parents. Used as a last resort for never_direct.
561 */
562static void
563peerGetAllParents(ps_state * ps)
564{
565 peer *p;
190154cf 566 HttpRequest *request = ps->request;
168dfda9 567 /* Add all alive parents */
62e76326 568
168dfda9 569 for (p = Config.peers; p; p = p->next) {
62e76326 570 /* XXX: neighbors.c lacks a public interface for enumerating
571 * parents to a request so we have to dig some here..
572 */
573
574 if (neighborType(p, request) != PEER_PARENT)
575 continue;
576
577 if (!peerHTTPOkay(p, request))
578 continue;
579
bf8fe701 580 debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
62e76326 581
582 peerAddFwdServer(&ps->servers, p, ANY_OLD_PARENT);
168dfda9 583 }
62e76326 584
168dfda9 585 /* XXX: should add dead parents here, but it is currently
586 * not possible to find out which parents are dead or which
587 * simply are not configured to handle the request.
588 */
589 /* Add default parent as a last resort */
590 if ((p = getDefaultParent(request))) {
62e76326 591 peerAddFwdServer(&ps->servers, p, DEFAULT_PARENT);
168dfda9 592 }
593}
594
d9586c3c 595static void
93775f90 596peerPingTimeout(void *data)
062e2281 597{
e6ccf245 598 ps_state *psstate = (ps_state *)data;
b6c0e933 599 StoreEntry *entry = psstate->entry;
62e76326 600
fefb0227 601 if (entry)
bf8fe701 602 debugs(44, 3, "peerPingTimeout: '" << entry->url() << "'" );
62e76326 603
fa80a8ef 604 if (!cbdataReferenceValid(psstate->callback_data)) {
62e76326 605 /* request aborted */
606 entry->ping_status = PING_DONE;
607 cbdataReferenceDone(psstate->callback_data);
608 peerSelectStateFree(psstate);
609 return;
73a201f8 610 }
62e76326 611
75e88d56 612 PeerStats.timeouts++;
44e237d0 613 psstate->ping.timedout = 1;
b6c0e933 614 peerSelectFoo(psstate);
85034133 615}
616
617void
618peerSelectInit(void)
619{
75e88d56 620 memset(&PeerStats, '\0', sizeof(PeerStats));
9bdbfe33 621 assert(sizeof(hier_strings) == (HIER_MAX + 1) * sizeof(char *));
062e2281 622}
93775f90 623
b3264694 624static void
625peerIcpParentMiss(peer * p, icp_common_t * header, ps_state * ps)
626{
627 int rtt;
62e76326 628
9b5c4a9a 629#if USE_ICMP
b3264694 630 if (Config.onoff.query_icmp) {
62e76326 631 if (header->flags & ICP_FLAG_SRC_RTT) {
632 rtt = header->pad & 0xFFFF;
9b5c4a9a 633 int hops = (header->pad >> 16) & 0xFFFF;
62e76326 634
635 if (rtt > 0 && rtt < 0xFFFF)
636 netdbUpdatePeer(ps->request, p, rtt, hops);
637
638 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
639 ps->closest_parent_miss = p->in_addr;
640 ps->ping.p_rtt = rtt;
641 }
642 }
b3264694 643 }
9b5c4a9a 644#endif /* USE_ICMP */
62e76326 645
18ec72b2 646 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 647 if (p->options.closest_only)
62e76326 648 return;
649
85223cd7 650 /* set FIRST_MISS if there is no CLOSEST parent */
cc192b50 651 if (!ps->closest_parent_miss.IsAnyAddr())
62e76326 652 return;
653
d1b63fc8 654 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 655
d1b63fc8 656 if (rtt < 1)
62e76326 657 rtt = 1;
658
cc192b50 659 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 660 ps->first_parent_miss = p->in_addr;
661 ps->ping.w_rtt = rtt;
b3264694 662 }
663}
93775f90 664
b6c0e933 665static void
b3264694 666peerHandleIcpReply(peer * p, peer_t type, icp_common_t * header, void *data)
93775f90 667{
e6ccf245 668 ps_state *psstate = (ps_state *)data;
669 icp_opcode op = header->getOpCode();
bf8fe701 670 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->entry->url() );
69c95dd3 671#if USE_CACHE_DIGESTS && 0
26b164ac 672 /* do cd lookup to count false misses */
62e76326 673
3ab66981 674 if (p && request)
62e76326 675 peerNoteDigestLookup(request, p,
676 peerDigestLookup(p, request, psstate->entry));
677
26b164ac 678#endif
62e76326 679
44e237d0 680 psstate->ping.n_recv++;
62e76326 681
27cd7235 682 if (op == ICP_MISS || op == ICP_DECHO) {
62e76326 683 if (type == PEER_PARENT)
684 peerIcpParentMiss(p, header, psstate);
a7c05555 685 } else if (op == ICP_HIT) {
62e76326 686 psstate->hit = p;
687 psstate->hit_type = type;
688 peerSelectFoo(psstate);
689 return;
db1cd23c 690 }
62e76326 691
44e237d0 692 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 693 return;
694
b6c0e933 695 peerSelectFoo(psstate);
93775f90 696}
86aebcda 697
698#if USE_HTCP
699static void
44e237d0 700peerHandleHtcpReply(peer * p, peer_t type, htcpReplyData * htcp, void *data)
86aebcda 701{
e6ccf245 702 ps_state *psstate = (ps_state *)data;
26ac0430
AJ
703 debugs(44, 3, "peerHandleHtcpReply: " <<
704 (htcp->hit ? "HIT" : "MISS") << " " <<
705 psstate->entry->url() );
44e237d0 706 psstate->ping.n_recv++;
62e76326 707
44e237d0 708 if (htcp->hit) {
62e76326 709 psstate->hit = p;
710 psstate->hit_type = type;
711 peerSelectFoo(psstate);
712 return;
44e237d0 713 }
62e76326 714
44e237d0 715 if (type == PEER_PARENT)
62e76326 716 peerHtcpParentMiss(p, htcp, psstate);
717
44e237d0 718 if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
62e76326 719 return;
720
44e237d0 721 peerSelectFoo(psstate);
722}
723
724static void
725peerHtcpParentMiss(peer * p, htcpReplyData * htcp, ps_state * ps)
726{
727 int rtt;
62e76326 728
9b5c4a9a 729#if USE_ICMP
44e237d0 730 if (Config.onoff.query_icmp) {
62e76326 731 if (htcp->cto.rtt > 0) {
732 rtt = (int) htcp->cto.rtt * 1000;
9b5c4a9a 733 int hops = (int) htcp->cto.hops * 1000;
62e76326 734 netdbUpdatePeer(ps->request, p, rtt, hops);
735
736 if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
737 ps->closest_parent_miss = p->in_addr;
738 ps->ping.p_rtt = rtt;
739 }
740 }
44e237d0 741 }
9b5c4a9a 742#endif /* USE_ICMP */
62e76326 743
44e237d0 744 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
cd196bc8 745 if (p->options.closest_only)
62e76326 746 return;
747
44e237d0 748 /* set FIRST_MISS if there is no CLOSEST parent */
cc192b50 749 if (!ps->closest_parent_miss.IsAnyAddr())
62e76326 750 return;
751
d1b63fc8 752 rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
62e76326 753
d1b63fc8 754 if (rtt < 1)
62e76326 755 rtt = 1;
756
cc192b50 757 if (ps->first_parent_miss.IsAnyAddr() || rtt < ps->ping.w_rtt) {
62e76326 758 ps->first_parent_miss = p->in_addr;
759 ps->ping.w_rtt = rtt;
44e237d0 760 }
86aebcda 761}
62e76326 762
86aebcda 763#endif
764
765static void
9ef28b60 766peerHandlePingReply(peer * p, peer_t type, protocol_t proto, void *pingdata, void *data)
86aebcda 767{
768 if (proto == PROTO_ICP)
62e76326 769 peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
770
86aebcda 771#if USE_HTCP
62e76326 772
86aebcda 773 else if (proto == PROTO_HTCP)
62e76326 774 peerHandleHtcpReply(p, type, (htcpReplyData *)pingdata, data);
775
86aebcda 776#endif
62e76326 777
86aebcda 778 else
4a7a3d56 779 debugs(44, 1, "peerHandlePingReply: unknown protocol_t " << proto);
86aebcda 780}
db1cd23c 781
782static void
b73f343e 783peerAddFwdServer(FwdServer ** FSVR, peer * p, hier_code code)
db1cd23c 784{
e6ccf245 785 FwdServer *fs = (FwdServer *)memAllocate(MEM_FWD_SERVER);
26ac0430
AJ
786 debugs(44, 5, "peerAddFwdServer: adding " <<
787 (p ? p->host : "DIRECT") << " " <<
788 hier_strings[code] );
29b8d8d6 789 fs->_peer = cbdataReference(p);
db1cd23c 790 fs->code = code;
62e76326 791
b73f343e 792 while (*FSVR)
793 FSVR = &(*FSVR)->next;
62e76326 794
b73f343e 795 *FSVR = fs;
db1cd23c 796}
b24880fe 797
798void *
799ps_state::operator new(size_t)
800{
aa839030 801 CBDATA_INIT_TYPE(ps_state);
b24880fe 802 return cbdataAlloc(ps_state);
803}
804
805ps_state::ps_state() : request (NULL),
806 entry (NULL),
807 always_direct (0),
808 never_direct (0),
809 direct (0),
810 callback (NULL),
811 callback_data (NULL),
812 servers (NULL),
cc192b50 813 first_parent_miss(),
814 closest_parent_miss(),
b24880fe 815 hit(NULL),
816 hit_type(PEER_NONE),
b24880fe 817 acl_checklist (NULL)
818{
cc192b50 819 ; // no local defaults.
b24880fe 820}
821
822ping_data::ping_data() :
823 n_sent(0),
824 n_recv(0),
825 n_replies_expected(0),
826 timeout(0),
827 timedout(0),
828 w_rtt(0),
829 p_rtt(0)
830{
831 start.tv_sec = 0;
832 start.tv_usec = 0;
833 stop.tv_sec = 0;
834 stop.tv_usec = 0;
835}