2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 44 Peer Selection Algorithm */
12 #include "acl/FilledChecklist.h"
13 #include "CachePeer.h"
15 #include "client_side.h"
16 #include "dns/LookupDetails.h"
17 #include "errorpage.h"
21 #include "hier_code.h"
23 #include "HttpRequest.h"
24 #include "icmp/net_db.h"
28 #include "neighbors.h"
29 #include "peer_sourcehash.h"
30 #include "peer_userhash.h"
31 #include "PeerSelectState.h"
32 #include "SquidConfig.h"
33 #include "SquidTime.h"
41 static const char *DirectStr
[] = {
48 static void peerSelectFoo(ps_state
*);
49 static void peerPingTimeout(void *data
);
50 static IRCB peerHandlePingReply
;
51 static void peerIcpParentMiss(CachePeer
*, icp_common_t
*, ps_state
*);
53 static void peerHtcpParentMiss(CachePeer
*, HtcpReplyData
*, ps_state
*);
54 static void peerHandleHtcpReply(CachePeer
*, peer_t
, HtcpReplyData
*, void *);
56 static int peerCheckNetdbDirect(ps_state
* psstate
);
57 static void peerGetSomeNeighbor(ps_state
*);
58 static void peerGetSomeNeighborReplies(ps_state
*);
59 static void peerGetSomeDirect(ps_state
*);
60 static void peerGetSomeParent(ps_state
*);
61 static void peerGetAllParents(ps_state
*);
62 static void peerAddFwdServer(FwdServer
**, CachePeer
*, hier_code
);
63 static void peerSelectPinned(ps_state
* ps
);
64 static void peerSelectDnsResults(const ipcache_addrs
*ia
, const Dns::LookupDetails
&details
, void *data
);
66 CBDATA_CLASS_INIT(ps_state
);
71 FwdServer
*next
= servers
->next
;
72 cbdataReferenceDone(servers
->_peer
);
73 memFree(servers
, MEM_FWD_SERVER
);
78 debugs(44, 3, entry
->url());
80 if (entry
->ping_status
== PING_WAITING
)
81 eventDelete(peerPingTimeout
, this);
83 entry
->ping_status
= PING_DONE
;
87 debugs(44, DBG_IMPORTANT
, "calling aclChecklistFree() from ps_state destructor");
91 HTTPMSGUNLOCK(request
);
94 assert(entry
->ping_status
!= PING_WAITING
);
95 entry
->unlock("peerSelect");
103 peerSelectIcpPing(HttpRequest
* request
, int direct
, StoreEntry
* entry
)
107 assert(entry
->ping_status
== PING_NONE
);
108 assert(direct
!= DIRECT_YES
);
109 debugs(44, 3, "peerSelectIcpPing: " << entry
->url());
111 if (!request
->flags
.hierarchical
&& direct
!= DIRECT_NO
)
114 if (EBIT_TEST(entry
->flags
, KEY_PRIVATE
) && !neighbors_do_private_keys
)
115 if (direct
!= DIRECT_NO
)
118 n
= neighborsCount(request
);
120 debugs(44, 3, "peerSelectIcpPing: counted " << n
<< " neighbors");
126 peerSelect(Comm::ConnectionList
* paths
,
127 HttpRequest
* request
,
128 AccessLogEntry::Pointer
const &al
,
136 debugs(44, 3, *entry
<< ' ' << entry
->url());
138 debugs(44, 3, request
->method
);
140 psstate
= new ps_state
;
142 psstate
->request
= request
;
143 HTTPMSGLOCK(psstate
->request
);
146 psstate
->entry
= entry
;
147 psstate
->paths
= paths
;
149 psstate
->callback
= callback
;
151 psstate
->callback_data
= cbdataReference(callback_data
);
153 #if USE_CACHE_DIGESTS
155 request
->hier
.peer_select_start
= current_time
;
160 psstate
->entry
->lock("peerSelect");
162 peerSelectFoo(psstate
);
166 peerCheckNeverDirectDone(allow_t answer
, void *data
)
168 ps_state
*psstate
= (ps_state
*) data
;
169 psstate
->acl_checklist
= NULL
;
170 debugs(44, 3, "peerCheckNeverDirectDone: " << answer
);
171 psstate
->never_direct
= answer
;
174 /** if never_direct says YES, do that. */
175 psstate
->direct
= DIRECT_NO
;
176 debugs(44, 3, HERE
<< "direct = " << DirectStr
[psstate
->direct
] << " (never_direct allow)");
178 case ACCESS_DENIED
: // not relevant.
179 case ACCESS_DUNNO
: // not relevant.
181 case ACCESS_AUTH_REQUIRED
:
182 debugs(44, DBG_IMPORTANT
, "WARNING: never_direct resulted in " << answer
<< ". Username ACLs are not reliable here.");
185 peerSelectFoo(psstate
);
189 peerCheckAlwaysDirectDone(allow_t answer
, void *data
)
191 ps_state
*psstate
= (ps_state
*)data
;
192 psstate
->acl_checklist
= NULL
;
193 debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer
);
194 psstate
->always_direct
= answer
;
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)");
201 case ACCESS_DENIED
: // not relevant.
202 case ACCESS_DUNNO
: // not relevant.
204 case ACCESS_AUTH_REQUIRED
:
205 debugs(44, DBG_IMPORTANT
, "WARNING: always_direct resulted in " << answer
<< ". Username ACLs are not reliable here.");
208 peerSelectFoo(psstate
);
212 peerSelectDnsPaths(ps_state
*psstate
)
214 FwdServer
*fs
= psstate
->servers
;
216 if (!cbdataReferenceValid(psstate
->callback_data
)) {
217 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
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
;
227 const bool isIntercepted
= !req
->flags
.redirected
&&
228 (req
->flags
.intercepted
|| req
->flags
.interceptTproxy
);
229 const bool useOriginalDst
= Config
.onoff
.client_dst_passthru
|| !req
->flags
.hostVerified
;
230 const bool choseDirect
= fs
&& fs
->code
== HIER_DIRECT
;
231 if (isIntercepted
&& useOriginalDst
&& choseDirect
) {
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
;
237 p
->peerType
= ORIGINAL_DST
; // fs->code is DIRECT. This fixes the display.
238 p
->setPeer(fs
->_peer
);
240 // check for a configured outgoing address for this destination...
241 getOutgoingAddress(psstate
->request
, p
);
242 psstate
->paths
->push_back(p
);
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
);
253 // convert the list of FwdServer destinations into destinations IP addresses
254 if (fs
&& psstate
->paths
->size() < (unsigned int)Config
.forward_max_tries
) {
255 // send the next one off for DNS lookup.
256 const char *host
= fs
->_peer
? fs
->_peer
->host
: psstate
->request
->GetHost();
257 debugs(44, 2, "Find IP destination for: " << psstate
->url() << "' via " << host
);
258 ipcache_nbgethostbyname(host
, peerSelectDnsResults
, psstate
);
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
) {
266 assert(fs
== psstate
->servers
);
268 psstate
->servers
= fs
->next
;
269 cbdataReferenceDone(fs
->_peer
);
270 memFree(fs
, MEM_FWD_SERVER
);
271 fs
= psstate
->servers
;
275 // done with DNS lookups. pass back to caller
276 PSC
*callback
= psstate
->callback
;
277 psstate
->callback
= NULL
;
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
) {
283 for (size_t i
= 0; i
< psstate
->paths
->size(); ++i
) {
284 if ((*psstate
->paths
)[i
]->peerType
== HIER_DIRECT
)
285 debugs(44, 2, " DIRECT = " << (*psstate
->paths
)[i
]);
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
]);
291 debugs(44, 2, " cache_peer = " << (*psstate
->paths
)[i
]);
294 debugs(44, 2, " timedout = " << psstate
->ping
.timedout
);
296 psstate
->ping
.stop
= current_time
;
297 psstate
->request
->hier
.ping
= psstate
->ping
;
300 if (cbdataReferenceValidDone(psstate
->callback_data
, &cbdata
)) {
301 callback(psstate
->paths
, psstate
->lastError
, cbdata
);
302 psstate
->lastError
= NULL
; // FwdState has taken control over the ErrorState object.
309 peerSelectDnsResults(const ipcache_addrs
*ia
, const Dns::LookupDetails
&details
, void *data
)
311 ps_state
*psstate
= (ps_state
*)data
;
313 if (!cbdataReferenceValid(psstate
->callback_data
)) {
314 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
319 psstate
->request
->recordLookup(details
);
321 FwdServer
*fs
= psstate
->servers
;
324 assert(ia
->cur
< ia
->count
);
326 // loop over each result address, adding to the possible destinations.
328 for (int n
= 0; n
< ia
->count
; ++n
, ++ip
) {
329 Comm::ConnectionPointer p
;
331 if (ip
>= ia
->count
) ip
= 0; // looped back to zero.
333 // Enforce forward_max_tries configuration.
334 if (psstate
->paths
->size() >= (unsigned int)Config
.forward_max_tries
)
337 // for TPROXY spoofing we must skip unusable addresses.
338 if (psstate
->request
->flags
.spoofClientIp
&& !(fs
->_peer
&& fs
->_peer
->options
.no_tproxy
) ) {
339 if (ia
->in_addrs
[ip
].isIPv4() != psstate
->request
->client_addr
.isIPv4()) {
340 // we CAN'T spoof the address on this link. find another.
345 p
= new Comm::Connection();
346 p
->remote
= ia
->in_addrs
[ip
];
348 // when IPv6 is disabled we cannot use it
349 if (!Ip::EnableIpv6
&& p
->remote
.isIPv6()) {
350 const char *host
= (fs
->_peer
? fs
->_peer
->host
: psstate
->request
->GetHost());
351 ipcacheMarkBadAddr(host
, p
->remote
);
356 p
->remote
.port(fs
->_peer
->http_port
);
358 p
->remote
.port(psstate
->request
->port
);
359 p
->peerType
= fs
->code
;
360 p
->setPeer(fs
->_peer
);
362 // check for a configured outgoing address for this destination...
363 getOutgoingAddress(psstate
->request
, p
);
364 psstate
->paths
->push_back(p
);
367 debugs(44, 3, HERE
<< "Unknown host: " << (fs
->_peer
? fs
->_peer
->host
: psstate
->request
->GetHost()));
368 // discard any previous error.
369 delete psstate
->lastError
;
370 psstate
->lastError
= NULL
;
371 if (fs
->code
== HIER_DIRECT
) {
372 psstate
->lastError
= new ErrorState(ERR_DNS_FAIL
, Http::scServiceUnavailable
, psstate
->request
);
373 psstate
->lastError
->dnsError
= details
.error
;
377 psstate
->servers
= fs
->next
;
378 cbdataReferenceDone(fs
->_peer
);
379 memFree(fs
, MEM_FWD_SERVER
);
381 // see if more paths can be found
382 peerSelectDnsPaths(psstate
);
386 peerCheckNetdbDirect(ps_state
* psstate
)
393 if (psstate
->direct
== DIRECT_NO
)
396 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
398 myrtt
= netdbHostRtt(psstate
->request
->GetHost());
400 debugs(44, 3, "peerCheckNetdbDirect: MY RTT = " << myrtt
<< " msec");
401 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_rtt = " << Config
.minDirectRtt
<< " msec");
403 if (myrtt
&& myrtt
<= Config
.minDirectRtt
)
406 myhops
= netdbHostHops(psstate
->request
->GetHost());
408 debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops
);
409 debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config
.minDirectHops
);
411 if (myhops
&& myhops
<= Config
.minDirectHops
)
414 p
= whichPeer(psstate
->closest_parent_miss
);
419 debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate
->ping
.p_rtt
<< " msec");
421 if (myrtt
&& myrtt
<= psstate
->ping
.p_rtt
)
424 #endif /* USE_ICMP */
430 peerSelectFoo(ps_state
* ps
)
432 if (!cbdataReferenceValid(ps
->callback_data
)) {
433 debugs(44, 3, "Aborting peer selection. Parent Job went away.");
438 StoreEntry
*entry
= ps
->entry
;
439 HttpRequest
*request
= ps
->request
;
440 debugs(44, 3, request
->method
<< ' ' << request
->GetHost());
442 /** If we don't know whether DIRECT is permitted ... */
443 if (ps
->direct
== DIRECT_UNKNOWN
) {
444 if (ps
->always_direct
== ACCESS_DUNNO
) {
445 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (always_direct to be checked)");
446 /** check always_direct; */
447 ACLFilledChecklist
*ch
= new ACLFilledChecklist(Config
.accessList
.AlwaysDirect
, request
, NULL
);
449 ps
->acl_checklist
= ch
;
450 ps
->acl_checklist
->nonBlockingCheck(peerCheckAlwaysDirectDone
, ps
);
452 } else if (ps
->never_direct
== ACCESS_DUNNO
) {
453 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (never_direct to be checked)");
454 /** check never_direct; */
455 ACLFilledChecklist
*ch
= new ACLFilledChecklist(Config
.accessList
.NeverDirect
, request
, NULL
);
457 ps
->acl_checklist
= ch
;
458 ps
->acl_checklist
->nonBlockingCheck(peerCheckNeverDirectDone
, ps
);
460 } else if (request
->flags
.noDirect
) {
461 /** if we are accelerating, direct is not an option. */
462 ps
->direct
= DIRECT_NO
;
463 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (forced non-direct)");
464 } else if (request
->flags
.loopDetected
) {
465 /** if we are in a forwarding-loop, direct is not an option. */
466 ps
->direct
= DIRECT_YES
;
467 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (forwarding loop detected)");
468 } else if (peerCheckNetdbDirect(ps
)) {
469 ps
->direct
= DIRECT_YES
;
470 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (checkNetdbDirect)");
472 ps
->direct
= DIRECT_MAYBE
;
473 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
] << " (default)");
476 debugs(44, 3, "peerSelectFoo: direct = " << DirectStr
[ps
->direct
]);
479 if (!entry
|| entry
->ping_status
== PING_NONE
)
480 peerSelectPinned(ps
);
483 } else if (entry
->ping_status
== PING_NONE
) {
484 peerGetSomeNeighbor(ps
);
486 if (entry
->ping_status
== PING_WAITING
)
488 } else if (entry
->ping_status
== PING_WAITING
) {
489 peerGetSomeNeighborReplies(ps
);
490 entry
->ping_status
= PING_DONE
;
493 switch (ps
->direct
) {
496 peerGetSomeDirect(ps
);
500 peerGetSomeParent(ps
);
501 peerGetAllParents(ps
);
506 if (Config
.onoff
.prefer_direct
)
507 peerGetSomeDirect(ps
);
509 if (request
->flags
.hierarchical
|| !Config
.onoff
.nonhierarchical_direct
) {
510 peerGetSomeParent(ps
);
511 peerGetAllParents(ps
);
514 if (!Config
.onoff
.prefer_direct
)
515 peerGetSomeDirect(ps
);
520 // resolve the possible peers
521 peerSelectDnsPaths(ps
);
524 bool peerAllowedToUse(const CachePeer
* p
, HttpRequest
* request
);
529 * Selects a pinned connection.
532 peerSelectPinned(ps_state
* ps
)
534 HttpRequest
*request
= ps
->request
;
535 if (!request
->pinnedConnection())
537 CachePeer
*pear
= request
->pinnedConnection()->pinnedPeer();
538 if (Comm::IsConnOpen(request
->pinnedConnection()->validatePinnedConnection(request
, pear
))) {
539 if (pear
&& peerAllowedToUse(pear
, request
)) {
540 peerAddFwdServer(&ps
->servers
, pear
, PINNED
);
542 ps
->entry
->ping_status
= PING_DONE
; /* Skip ICP */
543 } else if (!pear
&& ps
->direct
!= DIRECT_NO
) {
544 peerAddFwdServer(&ps
->servers
, NULL
, PINNED
);
546 ps
->entry
->ping_status
= PING_DONE
; /* Skip ICP */
552 * peerGetSomeNeighbor
554 * Selects a neighbor (parent or sibling) based on one of the
558 * ICMP Netdb RTT estimates
562 peerGetSomeNeighbor(ps_state
* ps
)
564 StoreEntry
*entry
= ps
->entry
;
565 HttpRequest
*request
= ps
->request
;
567 hier_code code
= HIER_NONE
;
568 assert(entry
->ping_status
== PING_NONE
);
570 if (ps
->direct
== DIRECT_YES
) {
571 entry
->ping_status
= PING_DONE
;
575 #if USE_CACHE_DIGESTS
576 if ((p
= neighborsDigestSelect(request
))) {
577 if (neighborType(p
, request
) == PEER_PARENT
)
578 code
= CD_PARENT_HIT
;
580 code
= CD_SIBLING_HIT
;
583 if ((p
= netdbClosestParent(request
))) {
584 code
= CLOSEST_PARENT
;
585 } else if (peerSelectIcpPing(request
, ps
->direct
, entry
)) {
586 debugs(44, 3, "peerSelect: Doing ICP pings");
587 ps
->ping
.start
= current_time
;
588 ps
->ping
.n_sent
= neighborsUdpPing(request
,
592 &ps
->ping
.n_replies_expected
,
595 if (ps
->ping
.n_sent
== 0)
596 debugs(44, DBG_CRITICAL
, "WARNING: neighborsUdpPing returned 0");
597 debugs(44, 3, "peerSelect: " << ps
->ping
.n_replies_expected
<<
598 " ICP replies expected, RTT " << ps
->ping
.timeout
<<
601 if (ps
->ping
.n_replies_expected
> 0) {
602 entry
->ping_status
= PING_WAITING
;
603 eventAdd("peerPingTimeout",
606 0.001 * ps
->ping
.timeout
,
612 if (code
!= HIER_NONE
) {
614 debugs(44, 3, "peerSelect: " << hier_code_str
[code
] << "/" << p
->host
);
615 peerAddFwdServer(&ps
->servers
, p
, code
);
618 entry
->ping_status
= PING_DONE
;
622 * peerGetSomeNeighborReplies
624 * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
627 peerGetSomeNeighborReplies(ps_state
* ps
)
629 HttpRequest
*request
= ps
->request
;
631 hier_code code
= HIER_NONE
;
632 assert(ps
->entry
->ping_status
== PING_WAITING
);
633 assert(ps
->direct
!= DIRECT_YES
);
635 if (peerCheckNetdbDirect(ps
)) {
636 code
= CLOSEST_DIRECT
;
637 debugs(44, 3, "peerSelect: " << hier_code_str
[code
] << "/" << request
->GetHost());
638 peerAddFwdServer(&ps
->servers
, NULL
, code
);
643 code
= ps
->hit_type
== PEER_PARENT
? PARENT_HIT
: SIBLING_HIT
;
645 if (!ps
->closest_parent_miss
.isAnyAddr()) {
646 p
= whichPeer(ps
->closest_parent_miss
);
647 code
= CLOSEST_PARENT_MISS
;
648 } else if (!ps
->first_parent_miss
.isAnyAddr()) {
649 p
= whichPeer(ps
->first_parent_miss
);
650 code
= FIRST_PARENT_MISS
;
653 if (p
&& code
!= HIER_NONE
) {
654 debugs(44, 3, "peerSelect: " << hier_code_str
[code
] << "/" << p
->host
);
655 peerAddFwdServer(&ps
->servers
, p
, code
);
662 * Simply adds a 'direct' entry to the FwdServers list if this
663 * request can be forwarded directly to the origin server
666 peerGetSomeDirect(ps_state
* ps
)
668 if (ps
->direct
== DIRECT_NO
)
671 /* WAIS is not implemented natively */
672 if (ps
->request
->url
.getScheme() == AnyP::PROTO_WAIS
)
675 peerAddFwdServer(&ps
->servers
, NULL
, HIER_DIRECT
);
679 peerGetSomeParent(ps_state
* ps
)
682 HttpRequest
*request
= ps
->request
;
683 hier_code code
= HIER_NONE
;
684 debugs(44, 3, request
->method
<< ' ' << request
->GetHost());
686 if (ps
->direct
== DIRECT_YES
)
689 if ((p
= peerSourceHashSelectParent(request
))) {
690 code
= SOURCEHASH_PARENT
;
692 } else if ((p
= peerUserHashSelectParent(request
))) {
693 code
= USERHASH_PARENT
;
695 } else if ((p
= carpSelectParent(request
))) {
697 } else if ((p
= getRoundRobinParent(request
))) {
698 code
= ROUNDROBIN_PARENT
;
699 } else if ((p
= getWeightedRoundRobinParent(request
))) {
700 code
= ROUNDROBIN_PARENT
;
701 } else if ((p
= getFirstUpParent(request
))) {
702 code
= FIRSTUP_PARENT
;
703 } else if ((p
= getDefaultParent(request
))) {
704 code
= DEFAULT_PARENT
;
707 if (code
!= HIER_NONE
) {
708 debugs(44, 3, "peerSelect: " << hier_code_str
[code
] << "/" << p
->host
);
709 peerAddFwdServer(&ps
->servers
, p
, code
);
713 /* Adds alive parents. Used as a last resort for never_direct.
716 peerGetAllParents(ps_state
* ps
)
719 HttpRequest
*request
= ps
->request
;
720 /* Add all alive parents */
722 for (p
= Config
.peers
; p
; p
= p
->next
) {
723 /* XXX: neighbors.c lacks a public interface for enumerating
724 * parents to a request so we have to dig some here..
727 if (neighborType(p
, request
) != PEER_PARENT
)
730 if (!peerHTTPOkay(p
, request
))
733 debugs(15, 3, "peerGetAllParents: adding alive parent " << p
->host
);
735 peerAddFwdServer(&ps
->servers
, p
, ANY_OLD_PARENT
);
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.
742 /* Add default parent as a last resort */
743 if ((p
= getDefaultParent(request
))) {
744 peerAddFwdServer(&ps
->servers
, p
, DEFAULT_PARENT
);
749 peerPingTimeout(void *data
)
751 ps_state
*psstate
= (ps_state
*)data
;
752 StoreEntry
*entry
= psstate
->entry
;
755 debugs(44, 3, "peerPingTimeout: '" << psstate
->url() << "'" );
757 if (!cbdataReferenceValid(psstate
->callback_data
)) {
758 /* request aborted */
759 entry
->ping_status
= PING_DONE
;
760 cbdataReferenceDone(psstate
->callback_data
);
765 ++PeerStats
.timeouts
;
766 psstate
->ping
.timedout
= 1;
767 peerSelectFoo(psstate
);
773 memset(&PeerStats
, '\0', sizeof(PeerStats
));
774 memDataInit(MEM_FWD_SERVER
, "FwdServer", sizeof(FwdServer
), 0);
778 peerIcpParentMiss(CachePeer
* p
, icp_common_t
* header
, ps_state
* ps
)
783 if (Config
.onoff
.query_icmp
) {
784 if (header
->flags
& ICP_FLAG_SRC_RTT
) {
785 rtt
= header
->pad
& 0xFFFF;
786 int hops
= (header
->pad
>> 16) & 0xFFFF;
788 if (rtt
> 0 && rtt
< 0xFFFF)
789 netdbUpdatePeer(ps
->request
, p
, rtt
, hops
);
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
;
797 #endif /* USE_ICMP */
799 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
800 if (p
->options
.closest_only
)
803 /* set FIRST_MISS if there is no CLOSEST parent */
804 if (!ps
->closest_parent_miss
.isAnyAddr())
807 rtt
= (tvSubMsec(ps
->ping
.start
, current_time
) - p
->basetime
) / p
->weight
;
812 if (ps
->first_parent_miss
.isAnyAddr() || rtt
< ps
->ping
.w_rtt
) {
813 ps
->first_parent_miss
= p
->in_addr
;
814 ps
->ping
.w_rtt
= rtt
;
819 peerHandleIcpReply(CachePeer
* p
, peer_t type
, icp_common_t
* header
, void *data
)
821 ps_state
*psstate
= (ps_state
*)data
;
822 icp_opcode op
= header
->getOpCode();
823 debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str
[op
] << " " << psstate
->url() );
824 #if USE_CACHE_DIGESTS && 0
825 /* do cd lookup to count false misses */
828 peerNoteDigestLookup(request
, p
,
829 peerDigestLookup(p
, request
, psstate
->entry
));
833 ++ psstate
->ping
.n_recv
;
835 if (op
== ICP_MISS
|| op
== ICP_DECHO
) {
836 if (type
== PEER_PARENT
)
837 peerIcpParentMiss(p
, header
, psstate
);
838 } else if (op
== ICP_HIT
) {
840 psstate
->hit_type
= type
;
841 peerSelectFoo(psstate
);
845 if (psstate
->ping
.n_recv
< psstate
->ping
.n_replies_expected
)
848 peerSelectFoo(psstate
);
853 peerHandleHtcpReply(CachePeer
* p
, peer_t type
, HtcpReplyData
* htcp
, void *data
)
855 ps_state
*psstate
= (ps_state
*)data
;
856 debugs(44, 3, "" << (htcp
->hit
? "HIT" : "MISS") << " " << psstate
->url());
857 ++ psstate
->ping
.n_recv
;
861 psstate
->hit_type
= type
;
862 peerSelectFoo(psstate
);
866 if (type
== PEER_PARENT
)
867 peerHtcpParentMiss(p
, htcp
, psstate
);
869 if (psstate
->ping
.n_recv
< psstate
->ping
.n_replies_expected
)
872 peerSelectFoo(psstate
);
876 peerHtcpParentMiss(CachePeer
* p
, HtcpReplyData
* htcp
, ps_state
* ps
)
881 if (Config
.onoff
.query_icmp
) {
882 if (htcp
->cto
.rtt
> 0) {
883 rtt
= (int) htcp
->cto
.rtt
* 1000;
884 int hops
= (int) htcp
->cto
.hops
* 1000;
885 netdbUpdatePeer(ps
->request
, p
, rtt
, hops
);
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
;
893 #endif /* USE_ICMP */
895 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
896 if (p
->options
.closest_only
)
899 /* set FIRST_MISS if there is no CLOSEST parent */
900 if (!ps
->closest_parent_miss
.isAnyAddr())
903 rtt
= (tvSubMsec(ps
->ping
.start
, current_time
) - p
->basetime
) / p
->weight
;
908 if (ps
->first_parent_miss
.isAnyAddr() || rtt
< ps
->ping
.w_rtt
) {
909 ps
->first_parent_miss
= p
->in_addr
;
910 ps
->ping
.w_rtt
= rtt
;
917 peerHandlePingReply(CachePeer
* p
, peer_t type
, AnyP::ProtocolType proto
, void *pingdata
, void *data
)
919 if (proto
== AnyP::PROTO_ICP
)
920 peerHandleIcpReply(p
, type
, (icp_common_t
*)pingdata
, data
);
924 else if (proto
== AnyP::PROTO_HTCP
)
925 peerHandleHtcpReply(p
, type
, (HtcpReplyData
*)pingdata
, data
);
930 debugs(44, DBG_IMPORTANT
, "peerHandlePingReply: unknown protocol " << proto
);
934 peerAddFwdServer(FwdServer
** FSVR
, CachePeer
* p
, hier_code code
)
936 FwdServer
*fs
= (FwdServer
*)memAllocate(MEM_FWD_SERVER
);
937 debugs(44, 5, "peerAddFwdServer: adding " <<
938 (p
? p
->host
: "DIRECT") << " " <<
939 hier_code_str
[code
] );
940 fs
->_peer
= cbdataReference(p
);
944 FSVR
= &(*FSVR
)->next
;
949 ps_state::ps_state() : request (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
),
955 callback_data (NULL
),
959 closest_parent_miss(),
964 ; // no local defaults.
968 ps_state::url() const
974 return urlCanonical(request
);
979 ping_data::ping_data() :
982 n_replies_expected(0),