Squid Web Cache master
Loading...
Searching...
No Matches
peer_select.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 44 Peer Selection Algorithm */
10
11#include "squid.h"
12#include "acl/FilledChecklist.h"
14#include "base/InstanceId.h"
15#include "base/TypeTraits.h"
16#include "CachePeer.h"
17#include "CachePeers.h"
18#include "carp.h"
19#include "client_side.h"
20#include "dns/LookupDetails.h"
21#include "errorpage.h"
22#include "event.h"
23#include "FwdState.h"
24#include "globals.h"
25#include "hier_code.h"
26#include "htcp.h"
27#include "http/Stream.h"
28#include "HttpRequest.h"
29#include "icmp/net_db.h"
30#include "ICP.h"
31#include "ip/tools.h"
32#include "ipcache.h"
33#include "neighbors.h"
34#include "peer_sourcehash.h"
35#include "peer_userhash.h"
36#include "PeerSelectState.h"
37#include "SquidConfig.h"
38#include "Store.h"
39#include "time/gadgets.h"
40
51{
53
54public:
56 _peer(p),
57 code(c),
58 next(nullptr)
59 {}
60
61 CbcPointer<CachePeer> _peer; /* NULL --> origin server */
64};
65
66static struct {
69
70static const char *DirectStr[] = {
71 "DIRECT_UNKNOWN",
72 "DIRECT_NO",
73 "DIRECT_MAYBE",
74 "DIRECT_YES"
75};
76
79{
80public:
81 PeerSelectionDumper(const PeerSelector * const aSelector, const CachePeer * const aPeer, const hier_code aCode):
82 selector(aSelector), peer(aPeer), code(aCode) {}
83
84 const PeerSelector * const selector;
85 const CachePeer * const peer;
87};
88
90
92static std::ostream &
93operator <<(std::ostream &os, const PeerSelectionDumper &fsd)
94{
95 os << hier_code_str[fsd.code];
96
97 if (fsd.peer)
98 os << '/' << *fsd.peer;
99 else if (fsd.selector) // useful for DIRECT and gone PINNED destinations
100 os << '#' << fsd.selector->request->url.host();
101
102 return os;
103}
104
109{
110public:
112 void monitor(PeerSelector *);
113
115 void forget(PeerSelector *);
116
119
120private:
121 static void NoteWaitOver(void *monitor);
122
123 void startWaiting();
124 void abortWaiting();
125 void noteWaitOver();
126
128};
129
133{
134 static const auto Instance = new PeerSelectorPingMonitor();
135 return *Instance;
136}
137
138/* PeerSelectorPingMonitor */
139
141void
143{
144 assert(raw);
145 static_cast<PeerSelectorPingMonitor*>(raw)->noteWaitOver();
146}
147
149void
151{
152 assert(!selectors.empty());
153 const auto interval = tvSubDsec(current_time, selectors.begin()->first);
154 eventAdd("PeerSelectorPingMonitor::NoteWaitOver", &PeerSelectorPingMonitor::NoteWaitOver, this, interval, 0, false);
155}
156
158void
160{
161 // our event may be already in the AsyncCallQueue but that is OK:
162 // such queued calls cannot accumulate, and we ignore any stale ones
164}
165
167void
169{
170 while (!selectors.empty() && current_time >= selectors.begin()->first) {
171 const auto selector = selectors.begin()->second;
172 CallBack(selector->al, [selector,this] {
173 selector->ping.monitorRegistration = npos();
174 AsyncCall::Pointer callback = asyncCall(44, 4, "PeerSelector::HandlePingTimeout",
175 cbdataDialer(PeerSelector::HandlePingTimeout, selector));
176 ScheduleCallHere(callback);
177 });
178 selectors.erase(selectors.begin());
179 }
180
181 if (!selectors.empty()) {
182 // Since abortWaiting() is unreliable, we may have been awakened by a
183 // stale event A after event B has been scheduled. Now we are going to
184 // schedule event C. Prevent event accumulation by deleting B (if any).
185 abortWaiting();
186
187 startWaiting();
188 }
189}
190
191void
193{
194 assert(selector);
195
196 const auto deadline = selector->ping.deadline();
197 const auto position = selectors.emplace(deadline, selector);
198 selector->ping.monitorRegistration = position;
199
200 if (position == selectors.begin()) {
201 if (selectors.size() > 1)
202 abortWaiting(); // remove the previously scheduled earlier event
203 startWaiting();
204 } // else the already scheduled event is still the earliest one
205}
206
207void
209{
210 assert(selector);
211
212 if (selector->ping.monitorRegistration == npos())
213 return; // already forgotten
214
215 const auto wasFirst = selector->ping.monitorRegistration == selectors.begin();
216 selectors.erase(selector->ping.monitorRegistration);
217 selector->ping.monitorRegistration = npos();
218
219 if (wasFirst) {
220 // do not reschedule if there are still elements with the same deadline
221 if (!selectors.empty() && selectors.begin()->first == selector->ping.deadline())
222 return;
223 abortWaiting();
224 if (!selectors.empty())
225 startWaiting();
226 } // else do nothing since the old scheduled event is still the earliest one
227}
228
229/* PeerSelector */
230
232{
233 while (servers) {
234 FwdServer *next = servers->next;
235 delete servers;
236 servers = next;
237 }
238
240
241 if (entry) {
242 debugs(44, 3, entry->url());
244 }
245
247
248 if (entry) {
250 entry->unlock("peerSelect");
251 entry = nullptr;
252 }
253
254 delete lastError;
255}
256
257void
265
266void
271
272static int
274{
275 assert(ps);
276 HttpRequest *request = ps->request;
277
278 int n;
279 assert(entry);
280 assert(entry->ping_status == PING_NONE);
281 assert(direct != DIRECT_YES);
282 debugs(44, 3, entry->url());
283
284 if (!request->flags.hierarchical && direct != DIRECT_NO)
285 return 0;
286
288 if (direct != DIRECT_NO)
289 return 0;
290
291 n = neighborsCount(ps);
292
293 debugs(44, 3, "counted " << n << " neighbors");
294
295 return n;
296}
297
298static void
300 HttpRequest * request,
301 AccessLogEntry::Pointer const &al,
302 StoreEntry * entry)
303{
304 if (entry)
305 debugs(44, 3, *entry << ' ' << entry->url());
306 else
307 debugs(44, 3, request->method);
308
309 const auto selector = new PeerSelector(initiator);
310
311 selector->request = request;
312 HTTPMSGLOCK(selector->request);
313 selector->al = al;
314
315 selector->entry = entry;
316
317#if USE_CACHE_DIGESTS
318
320
321#endif
322
323 if (selector->entry)
324 selector->entry->lock("peerSelect");
325
326 selector->selectMore();
327}
328
329void
331{
332 subscribed = true;
333 peerSelect(this, request, ale, entry);
334 // and wait for noteDestination() and/or noteDestinationsEnd() calls
335}
336
337void
339{
340 debugs(44, 3, answer);
341 never_direct = answer;
342 switch (answer) {
343 case ACCESS_ALLOWED:
346 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)");
347 break;
348 case ACCESS_DENIED: // not relevant.
349 case ACCESS_DUNNO: // not relevant.
350 break;
352 debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
353 break;
354 }
355 selectMore();
356}
357
358void
360{
361 static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
362}
363
364void
366{
367 debugs(44, 3, answer);
368 always_direct = answer;
369 switch (answer) {
370 case ACCESS_ALLOWED:
373 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)");
374 break;
375 case ACCESS_DENIED: // not relevant.
376 case ACCESS_DUNNO: // not relevant.
377 break;
379 debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
380 break;
381 }
382 selectMore();
383}
384
385void
387{
388 static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
389}
390
393bool
395{
397 return false;
398
399 debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
400 delete this;
401 return true;
402}
403
405void
407{
408 if (selectionAborted())
409 return;
410
411 FwdServer *fs = servers;
412
413 // Bug 3243: CVE 2009-0801
414 // Bypass of browser same-origin access control in intercepted communication
415 // To resolve this we must use only the original client destination when going DIRECT
416 // on intercepted traffic which failed Host verification
417 const HttpRequest *req = request;
418 const bool isIntercepted = !req->flags.redirected &&
419 (req->flags.intercepted || req->flags.interceptTproxy);
420 const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
421 const bool choseDirect = fs && fs->code == HIER_DIRECT;
422 if (isIntercepted && useOriginalDst && choseDirect) {
423 // check the client is still around before using any of its details
424 if (req->clientConnectionManager.valid()) {
425 // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
428 fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
429 handlePath(p, *fs);
430 }
431
432 // clear the used fs and continue
433 servers = fs->next;
434 delete fs;
436 return;
437 }
438
439 if (fs && fs->code == PINNED) {
440 // Nil path signals a PINNED destination selection. Our initiator should
441 // borrow and use clientConnectionManager's pinned connection object
442 // (regardless of that connection destination).
443 handlePath(nullptr, *fs);
444 servers = fs->next;
445 delete fs;
447 return;
448 }
449
450 // convert the list of FwdServer destinations into destinations IP addresses
451 if (fs && wantsMoreDestinations()) {
452 // send the next one off for DNS lookup.
453 const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host();
454 debugs(44, 2, "Find IP destination for: " << url() << "' via " << host);
455 Dns::nbgethostbyname(host, this);
456 return;
457 }
458
459 // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
460 // due to the allocation method of fs, we must deallocate each manually.
461 // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
462 if (fs) {
463 assert(fs == servers);
464 while (fs) {
465 servers = fs->next;
466 delete fs;
467 fs = servers;
468 }
469 }
470
471 // done with DNS lookups. pass back to caller
472
473 debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url());
474 debugs(44, 2, " always_direct = " << always_direct);
475 debugs(44, 2, " never_direct = " << never_direct);
476 debugs(44, 2, " timedout = " << ping.timedout);
477
479 request->hier.ping = ping; // final result
480
481 if (lastError && foundPaths) {
482 // nobody cares about errors if we found destinations despite them
483 debugs(44, 3, "forgetting the last error");
484 delete lastError;
485 lastError = nullptr;
486 }
487
488 if (const auto initiator = interestedInitiator())
489 initiator->noteDestinationsEnd(lastError);
490 lastError = nullptr; // initiator owns the ErrorState object now
491 delete this;
492}
493
494void
496{
497 /* ignore lookup delays that occurred after the initiator moved on */
498
499 if (selectionAborted())
500 return;
501
503 return;
504
505 request->recordLookup(details);
506}
507
508void
510{
511 if (selectionAborted())
512 return;
513
515 return;
516
517 const auto peer = servers->_peer.valid();
518
519 // for TPROXY spoofing, we must skip unusable addresses
520 if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) {
521 if (ip.isIPv4() != request->client_addr.isIPv4())
522 return; // cannot spoof the client address on this link
523 }
524
526 p->remote = ip;
527 // XXX: We return a (non-peer) destination with a zero port if the selection
528 // initiator supplied a request target without a port. If there are no valid
529 // use cases for this behavior, stop _selecting_ such destinations.
530 p->remote.port(peer ? peer->http_port : request->url.port().value_or(0));
531 handlePath(p, *servers);
532}
533
534void
536{
537 if (selectionAborted())
538 return;
539
540 FwdServer *fs = servers;
541 if (!ia) {
542 debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host()));
543 // discard any previous error.
544 delete lastError;
545 lastError = nullptr;
546 if (fs->code == HIER_DIRECT) {
548 lastError->dnsError = details.error;
549 }
550 }
551 // else noteIp() calls have already processed all IPs in *ia
552
553 servers = fs->next;
554 delete fs;
555
556 // continue resolving selected peers
558}
559
560int
562{
563#if USE_ICMP
564 CachePeer *p;
565 int myrtt;
566 int myhops;
567
568 if (direct == DIRECT_NO)
569 return 0;
570
571 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
572
573 myrtt = netdbHostRtt(request->url.host());
574 debugs(44, 3, "MY RTT = " << myrtt << " msec");
575 debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
576
577 if (myrtt && myrtt <= Config.minDirectRtt)
578 return 1;
579
580 myhops = netdbHostHops(request->url.host());
581
582 debugs(44, 3, "MY hops = " << myhops);
583 debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops);
584
585 if (myhops && myhops <= Config.minDirectHops)
586 return 1;
587
589
590 if (p == nullptr)
591 return 0;
592
593 debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec");
594
595 if (myrtt && myrtt <= ping.p_rtt)
596 return 1;
597
598#endif /* USE_ICMP */
599
600 return 0;
601}
602
603void
605{
606 if (selectionAborted())
607 return;
608
609 debugs(44, 3, request->method << ' ' << request->url.host());
610
612 if (direct == DIRECT_UNKNOWN) {
614 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)");
617 ch->al = al;
618 ch->syncAle(request, nullptr);
620 return;
621 } else if (never_direct == ACCESS_DUNNO) {
622 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)");
625 ch->al = al;
626 ch->syncAle(request, nullptr);
628 return;
629 } else if (request->flags.noDirect) {
632 debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
633 } else if (request->flags.loopDetected) {
636 debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
637 } else if (checkNetdbDirect()) {
639 debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)");
640 } else {
642 debugs(44, 3, "direct = " << DirectStr[direct] << " (default)");
643 }
644
645 debugs(44, 3, "direct = " << DirectStr[direct]);
646 }
647
648 if (!entry || entry->ping_status == PING_NONE)
649 selectPinned();
650 if (entry == nullptr) {
651 (void) 0;
652 } else if (entry->ping_status == PING_NONE) {
654
656 return;
657 } else if (entry->ping_status == PING_WAITING) {
661 }
662
663 switch (direct) {
664
665 case DIRECT_YES:
667 break;
668
669 case DIRECT_NO:
672 break;
673
674 default:
675
678
682 }
683
686
687 break;
688 }
689
690 // end peer selection; start resolving selected peers
692}
693
695
697void
699{
700 // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
702 return;
703
704 const auto peer = request->pinnedConnection()->pinnedPeer();
705 const auto usePinned = peer ? peerAllowedToUse(peer, this) : (direct != DIRECT_NO);
706 // If the pinned connection is prohibited (for this request) then
707 // the initiator must decide whether it is OK to open a new one instead.
709
710 addSelection(peer, PINNED);
711 if (entry)
712 entry->ping_status = PING_DONE; // skip ICP
713}
714
723void
725{
726 CachePeer *p;
727 hier_code code = HIER_NONE;
729
730 if (direct == DIRECT_YES) {
732 return;
733 }
734
735#if USE_CACHE_DIGESTS
736 if ((p = neighborsDigestSelect(this))) {
738 code = CD_PARENT_HIT;
739 else
740 code = CD_SIBLING_HIT;
741 } else
742#endif
743 if ((p = netdbClosestParent(this))) {
744 code = CLOSEST_PARENT;
745 } else if (peerSelectIcpPing(this, direct, entry)) {
746 debugs(44, 3, "Doing ICP pings");
749 entry,
751 this,
753 &ping.timeout);
754 // TODO: Refactor neighborsUdpPing() to guarantee positive timeouts.
755 if (ping.timeout < 0)
756 ping.timeout = 0;
757
758 if (ping.n_sent == 0)
759 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
761 " ICP replies expected, RTT " << ping.timeout <<
762 " msec");
763
764 if (ping.n_replies_expected > 0) {
766 return;
767 }
768 }
769
770 if (code != HIER_NONE) {
771 assert(p);
772 addSelection(p, code);
773 }
774
776}
777
779void
781{
782 CachePeer *p = nullptr;
783 hier_code code = HIER_NONE;
786
787 if (checkNetdbDirect()) {
788 code = CLOSEST_DIRECT;
789 addSelection(nullptr, code);
790 return;
791 }
792
793 if ((p = hit)) {
795 } else {
798 code = CLOSEST_PARENT_MISS;
799 } else if (!first_parent_miss.isAnyAddr()) {
801 code = FIRST_PARENT_MISS;
802 }
803 }
804 if (p && code != HIER_NONE) {
805 addSelection(p, code);
806 }
807}
808
810void
812{
813 if (direct == DIRECT_NO)
814 return;
815
816 /* WAIS is not implemented natively */
818 return;
819
820 addSelection(nullptr, HIER_DIRECT);
821}
822
823void
825{
826 CachePeer *p;
827 hier_code code = HIER_NONE;
828 debugs(44, 3, request->method << ' ' << request->url.host());
829
830 if (direct == DIRECT_YES)
831 return;
832
833 if ((p = peerSourceHashSelectParent(this))) {
834 code = SOURCEHASH_PARENT;
835#if USE_AUTH
836 } else if ((p = peerUserHashSelectParent(this))) {
837 code = USERHASH_PARENT;
838#endif
839 } else if ((p = carpSelectParent(this))) {
840 code = CARP;
841 } else if ((p = getRoundRobinParent(this))) {
842 code = ROUNDROBIN_PARENT;
843 } else if ((p = getWeightedRoundRobinParent(this))) {
844 code = ROUNDROBIN_PARENT;
845 } else if ((p = getFirstUpParent(this))) {
846 code = FIRSTUP_PARENT;
847 } else if ((p = getDefaultParent(this))) {
848 code = DEFAULT_PARENT;
849 }
850
851 if (code != HIER_NONE) {
852 addSelection(p, code);
853 }
854}
855
857void
859{
860 /* Add all alive parents */
861
862 for (const auto &peer: CurrentCachePeers()) {
863 const auto p = peer.get();
864 /* XXX: neighbors.c lacks a public interface for enumerating
865 * parents to a request so we have to dig some here..
866 */
867
869 continue;
870
871 if (!peerHTTPOkay(p, this))
872 continue;
873
875 }
876
877 /* XXX: should add dead parents here, but it is currently
878 * not possible to find out which parents are dead or which
879 * simply are not configured to handle the request.
880 */
881 /* Add default parent as a last resort */
882 if (const auto p = getDefaultParent(this)) {
884 }
885}
886
887void
889{
890 debugs(44, 3, url());
891
892 // do nothing if ping reply came while handlePingTimeout() was queued
894 return;
895
897
898 if (selectionAborted())
899 return;
900
901 ++PeerStats.timeouts;
902 ping.timedout = 1;
903 selectMore();
904}
905
906void
911
912void
914{
915 memset(&PeerStats, '\0', sizeof(PeerStats));
916}
917
918void
920{
921 int rtt;
922
923#if USE_ICMP
924 if (Config.onoff.query_icmp) {
925 if (header->flags & ICP_FLAG_SRC_RTT) {
926 rtt = header->pad & 0xFFFF;
927 int hops = (header->pad >> 16) & 0xFFFF;
928
929 if (rtt > 0 && rtt < 0xFFFF)
930 netdbUpdatePeer(request->url, p, rtt, hops);
931
932 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
934 ping.p_rtt = rtt;
935 }
936 }
937 }
938#else
939 (void)header;
940#endif /* USE_ICMP */
941
942 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
943 if (p->options.closest_only)
944 return;
945
946 /* set FIRST_MISS if there is no CLOSEST parent */
948 return;
949
950 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
951
952 if (rtt < 1)
953 rtt = 1;
954
955 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
957 ping.w_rtt = rtt;
958 }
959}
960
961void
963{
964 icp_opcode op = header->getOpCode();
965 debugs(44, 3, icp_opcode_str[op] << ' ' << url());
966#if USE_CACHE_DIGESTS && 0
967 /* do cd lookup to count false misses */
968
969 if (p && request)
971 peerDigestLookup(p, this));
972
973#endif
974
975 ++ping.n_recv;
976
977 if (op == ICP_MISS || op == ICP_DECHO) {
978 if (type == PEER_PARENT)
979 handleIcpParentMiss(p, header);
980 } else if (op == ICP_HIT) {
981 hit = p;
982 hit_type = type;
983 selectMore();
984 return;
985 }
986
988 return;
989
990 selectMore();
991}
992
993#if USE_HTCP
994void
996{
997 debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url());
998 ++ping.n_recv;
999
1000 if (htcp->hit) {
1001 hit = p;
1002 hit_type = type;
1003 selectMore();
1004 return;
1005 }
1006
1007 if (type == PEER_PARENT)
1008 handleHtcpParentMiss(p, htcp);
1009
1011 return;
1012
1013 selectMore();
1014}
1015
1016void
1018{
1019 int rtt;
1020
1021#if USE_ICMP
1022 if (Config.onoff.query_icmp) {
1023 if (htcp->cto.rtt > 0) {
1024 rtt = (int) htcp->cto.rtt * 1000;
1025 int hops = (int) htcp->cto.hops * 1000;
1026 netdbUpdatePeer(request->url, p, rtt, hops);
1027
1028 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
1030 ping.p_rtt = rtt;
1031 }
1032 }
1033 }
1034#else
1035 (void)htcp;
1036#endif /* USE_ICMP */
1037
1038 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
1039 if (p->options.closest_only)
1040 return;
1041
1042 /* set FIRST_MISS if there is no CLOSEST parent */
1044 return;
1045
1046 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
1047
1048 if (rtt < 1)
1049 rtt = 1;
1050
1051 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
1053 ping.w_rtt = rtt;
1054 }
1055}
1056
1057#endif
1058
1059void
1060PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
1061{
1062 if (proto == AnyP::PROTO_ICP)
1063 static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata));
1064
1065#if USE_HTCP
1066
1067 else if (proto == AnyP::PROTO_HTCP)
1068 static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata));
1069
1070#endif
1071
1072 else
1073 debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto);
1074}
1075
1076void
1078{
1079 // Find the end of the servers list. Bail on a duplicate destination.
1080 auto **serversTail = &servers;
1081 while (const auto server = *serversTail) {
1082 // There can be at most one PINNED destination.
1083 // Non-PINNED destinations are uniquely identified by their CachePeer
1084 // (even though a DIRECT destination might match a cache_peer address).
1085 // TODO: We may still add duplicates because the same peer could have
1086 // been removed from `servers` already (and given to the requestor).
1087 const bool duplicate = (server->code == PINNED) ?
1088 (code == PINNED) : (server->_peer == peer);
1089 if (duplicate) {
1090 debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) <<
1091 "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code));
1092 return;
1093 }
1094 serversTail = &server->next;
1095 }
1096
1097 debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code));
1098 *serversTail = new FwdServer(peer, code);
1099}
1100
1102 request(nullptr),
1103 entry (nullptr),
1104 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
1105 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
1106 direct(DIRECT_UNKNOWN),
1107 lastError(nullptr),
1108 servers (nullptr),
1109 first_parent_miss(),
1110 closest_parent_miss(),
1111 hit(nullptr),
1112 hit_type(PEER_NONE),
1113 initiator_(initiator)
1114{
1115 ; // no local defaults.
1116}
1117
1118const SBuf
1120{
1121 if (entry)
1122 return SBuf(entry->url());
1123
1124 if (request)
1125 return request->effectiveRequestUri();
1126
1127 static const SBuf noUrl("[no URL]");
1128 return noUrl;
1129}
1130
1134{
1135 const auto initiator = initiator_.valid();
1136
1137 if (!initiator) {
1138 debugs(44, 3, id << " initiator gone");
1139 return nullptr;
1140 }
1141
1142 if (!initiator->subscribed) {
1143 debugs(44, 3, id << " initiator lost interest");
1144 return nullptr;
1145 }
1146
1147 debugs(44, 7, id);
1148 return initiator;
1149}
1150
1151bool
1153 const auto maxCount = Config.forward_max_tries;
1154 return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount);
1155}
1156
1157void
1159{
1160 ++foundPaths;
1161
1162 if (path) {
1163 path->peerType = fs.code;
1164 path->setPeer(fs._peer.get());
1165
1166 // check for a configured outgoing address for this destination...
1168 debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url());
1169 } else
1170 debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url());
1171
1172 request->hier.ping = ping; // may be updated later
1173
1174 debugs(44, 2, " always_direct = " << always_direct);
1175 debugs(44, 2, " never_direct = " << never_direct);
1176 debugs(44, 2, " timedout = " << ping.timedout);
1177
1178 if (const auto initiator = interestedInitiator())
1179 initiator->noteDestination(path);
1180}
1181
1183
1185 n_sent(0),
1186 n_recv(0),
1187 n_replies_expected(0),
1188 timeout(0),
1189 timedout(0),
1190 w_rtt(0),
1191 p_rtt(0),
1192 monitorRegistration(PingMonitor().npos())
1193{
1194 start.tv_sec = 0;
1195 start.tv_usec = 0;
1196 stop.tv_sec = 0;
1197 stop.tv_usec = 0;
1198}
1199
1200timeval
1202{
1203 timeval timeInterval;
1204 timeInterval.tv_sec = timeout / 1000;
1205 timeInterval.tv_usec = (timeout % 1000) * 1000;
1206
1207 timeval result;
1208 tvAdd(result, start, timeInterval);
1209 return result;
1210}
1211
#define MEMPROXY_CLASS(CLASS)
const CachePeers & CurrentCachePeers()
Definition CachePeers.cc:43
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
void getOutgoingAddress(HttpRequest *request, const Comm::ConnectionPointer &conn)
Definition FwdState.cc:1481
#define InstanceIdDefinitions(...)
convenience macro to instantiate Class-specific stuff in .cc files
Definition InstanceId.h:90
WaitingPeerSelectors::iterator WaitingPeerSelectorPosition
Definition PingData.h:22
std::multimap< timeval, PeerSelector *, std::less< timeval >, PoolingAllocator< WaitingPeerSelector > > WaitingPeerSelectors
waiting PeerSelector objects, ordered by their absolute deadlines
Definition PingData.h:21
class SquidConfig Config
#define assert(EX)
Definition assert.h:17
static char server[MAXLINE]
CachePeer * carpSelectParent(PeerSelector *ps)
Definition carp.cc:150
#define CBDATA_CLASS_INIT(type)
Definition cbdata.h:325
static MakingPointer Make(const acl_access *a, HttpRequest *r)
static void NonBlockingCheck(MakingPointer &&p, ACLCB *cb, void *data)
AnyP::UriScheme const & getScheme() const
Definition Uri.h:58
void port(const Port p)
reset authority port subcomponent
Definition Uri.h:90
void host(const char *src)
Definition Uri.cc:154
bool closest_only
Definition CachePeer.h:117
int basetime
Definition CachePeer.h:151
struct CachePeer::@20 options
char * host
Definition CachePeer.h:66
Ip::Address in_addr
Definition CachePeer.h:70
Cbc * valid() const
was set and is valid
Definition CbcPointer.h:41
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition CbcPointer.h:159
hier_code peerType
Definition Connection.h:155
Ip::Address remote
Definition Connection.h:152
Ip::Address local
Definition Connection.h:149
void setPeer(CachePeer *p)
struct ConnStateData::@29 pinning
CachePeer * pinnedPeer() const
bool peerAccessDenied
cache_peer_access denied pinned connection reuse
encapsulates DNS lookup results
const std::optional< SBuf > error
error message (if any)
std::optional< SBuf > dnsError
DNS lookup error message.
Definition errorpage.h:180
FwdServer * next
CbcPointer< CachePeer > _peer
FwdServer(CachePeer *p, hier_code c)
hier_code code
struct timeval peer_select_start
struct HtcpReplyData::cto_t cto
int hit
Definition htcp.h:30
void recordLookup(const Dns::LookupDetails &detail)
CbcPointer< ConnStateData > clientConnectionManager
HttpRequestMethod method
HierarchyLogEntry hier
RequestFlags flags
ConnStateData * pinnedConnection()
AnyP::Uri url
the request URI
Ip::Address client_addr
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
bool isIPv4() const
Definition Address.cc:178
bool isAnyAddr() const
Definition Address.cc:190
unsigned short port() const
Definition Address.cc:790
a helper class to report a selected destination (for debugging)
PeerSelectionDumper(const PeerSelector *const aSelector, const CachePeer *const aPeer, const hier_code aCode)
const CachePeer *const peer
successful selection info
const PeerSelector *const selector
selection parameters
const hier_code code
selection algorithm
Interface for those who need a list of peers to forward a request to.
bool subscribed
whether noteDestination() and noteDestinationsEnd() calls are allowed
void startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry)
void monitor(PeerSelector *)
registers the given selector to be notified about the IPC ping timeout
static void NoteWaitOver(void *monitor)
PeerSelectorPingMonitor::noteWaitOver() wrapper.
void abortWaiting()
undoes an earlier startWaiting() call
WaitingPeerSelectorPosition npos()
void startWaiting()
schedules a single event to represent all waiting selectors
void forget(PeerSelector *)
removes a PeerSelector from the waiting list
void noteWaitOver()
calls back all ready PeerSelectors and continues to wait for others
WaitingPeerSelectors selectors
Ip::Address closest_parent_miss
bool selectionAborted()
void selectAllParents()
Adds alive parents. Used as a last resort for never_direct.
PeerSelectionInitiator * interestedInitiator()
void handlePingTimeout()
FwdServer * servers
a linked list of (unresolved) selected peers
HttpRequest * request
Initiator initiator_
recipient of the destinations we select; use interestedInitiator() to access
ErrorState * lastError
static void HandlePingTimeout(PeerSelector *)
called when the given selector should stop expecting ICP ping responses
void noteIps(const Dns::CachedIps *ips, const Dns::LookupDetails &details) override
Acl::Answer always_direct
void selectMore()
a single selection loop iteration: attempts to add more destinations
void startPingWaiting()
switches into the PING_WAITING state (and associated timeout monitoring)
void handleIcpParentMiss(CachePeer *, icp_common_t *)
void cancelPingTimeoutMonitoring()
terminates ICP ping timeout monitoring
int checkNetdbDirect()
Ip::Address first_parent_miss
void handlePath(const Comm::ConnectionPointer &path, FwdServer &fs)
processes a newly discovered/finalized path
const SBuf url() const
AccessLogEntry::Pointer al
info for the future access.log entry
bool wantsMoreDestinations() const
void checkNeverDirectDone(const Acl::Answer answer)
void checkAlwaysDirectDone(const Acl::Answer answer)
void handleHtcpReply(CachePeer *, const peer_t, HtcpReplyData *)
void handleIcpReply(CachePeer *, const peer_t, icp_common_t *header)
static ACLCB CheckNeverDirectDone
void addSelection(CachePeer *, const hier_code)
static ACLCB CheckAlwaysDirectDone
void noteLookup(const Dns::LookupDetails &details) override
void selectSomeDirect()
Adds a "direct" entry if the request can be forwarded to the origin server.
StoreEntry * entry
void noteIp(const Ip::Address &ip) override
Called when/if nbgethostbyname() discovers a new good IP address.
Acl::Answer never_direct
PeerSelector(PeerSelectionInitiator *)
void resolveSelected()
A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
void selectSomeNeighbor()
void selectSomeParent()
CachePeer * hit
void selectPinned()
Selects a pinned connection if it exists, is valid, and is allowed.
~PeerSelector() override
void selectSomeNeighborReplies()
Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
size_t foundPaths
number of unique destinations identified so far
void handleHtcpParentMiss(CachePeer *, HtcpReplyData *)
static IRCB HandlePingReply
bool interceptTproxy
Set for requests handled by a "tproxy" port.
Definition SBuf.h:94
Comm::ConnectionPointer clientConnection
Definition Server.h:100
int nonhierarchical_direct
int client_dst_passthru
int forward_max_tries
struct SquidConfig::@90 onoff
struct SquidConfig::@91 accessList
acl_access * NeverDirect
acl_access * AlwaysDirect
uint16_t flags
Definition Store.h:231
int unlock(const char *context)
Definition store.cc:469
const char * url() const
Definition store.cc:1566
ping_status_t ping_status
Definition Store.h:241
uint32_t flags
Definition ICP.h:45
icp_opcode getOpCode() const
Definition icp_v2.cc:129
uint32_t pad
Definition ICP.h:46
int timedout
Definition PingData.h:42
struct timeval start
Definition PingData.h:35
int p_rtt
Definition PingData.h:44
int n_recv
Definition PingData.h:39
int timeout
Definition PingData.h:41
WaitingPeerSelectorPosition monitorRegistration
maintained by PeerSelectorPingMonitor
Definition PingData.h:49
struct timeval stop
Definition PingData.h:37
timeval deadline() const
int n_replies_expected
Definition PingData.h:40
int w_rtt
Definition PingData.h:43
int n_sent
Definition PingData.h:38
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define DIRECT_NO
Definition defines.h:47
#define DIRECT_MAYBE
Definition defines.h:48
#define ICP_FLAG_SRC_RTT
Definition defines.h:39
#define DIRECT_UNKNOWN
Definition defines.h:46
#define EBIT_TEST(flag, bit)
Definition defines.h:67
#define DIRECT_YES
Definition defines.h:49
@ KEY_PRIVATE
Definition enums.h:97
@ PING_WAITING
Sent ICP queries to peers and still awaiting responses.
Definition enums.h:38
@ PING_NONE
Has not considered whether to send ICP queries to peers yet.
Definition enums.h:36
@ PING_DONE
Definition enums.h:41
peer_t
Definition enums.h:22
@ PEER_PARENT
Definition enums.h:25
@ PEER_NONE
Definition enums.h:23
@ ERR_DNS_FAIL
Definition forward.h:35
void eventDelete(EVH *func, void *arg)
Definition event.cc:127
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition event.cc:107
int neighbors_do_private_keys
@ ACCESS_AUTH_REQUIRED
Definition Acl.h:46
@ ACCESS_DENIED
Definition Acl.h:41
@ ACCESS_ALLOWED
Definition Acl.h:42
@ ACCESS_DUNNO
Definition Acl.h:43
icp_opcode
Definition icp_opcode.h:13
@ ICP_DECHO
Definition icp_opcode.h:26
@ ICP_MISS
Definition icp_opcode.h:18
@ ICP_HIT
Definition icp_opcode.h:17
const char * hier_code_str[]
hier_code
Definition hier_code.h:12
@ CARP
Definition hier_code.h:31
@ SOURCEHASH_PARENT
Definition hier_code.h:34
@ CD_SIBLING_HIT
Definition hier_code.h:29
@ CD_PARENT_HIT
Definition hier_code.h:28
@ HIER_NONE
Definition hier_code.h:13
@ PARENT_HIT
Definition hier_code.h:16
@ CLOSEST_PARENT
Definition hier_code.h:22
@ SIBLING_HIT
Definition hier_code.h:15
@ DEFAULT_PARENT
Definition hier_code.h:17
@ CLOSEST_DIRECT
Definition hier_code.h:23
@ USERHASH_PARENT
Definition hier_code.h:33
@ PINNED
Definition hier_code.h:35
@ FIRST_PARENT_MISS
Definition hier_code.h:20
@ ROUNDROBIN_PARENT
Definition hier_code.h:26
@ FIRSTUP_PARENT
Definition hier_code.h:19
@ CLOSEST_PARENT_MISS
Definition hier_code.h:21
@ ANY_OLD_PARENT
Definition hier_code.h:32
@ ORIGINAL_DST
Definition hier_code.h:36
@ HIER_DIRECT
Definition hier_code.h:14
void HTTPMSGUNLOCK(M *&a)
Definition Message.h:150
void HTTPMSGLOCK(Http::Message *a)
Definition Message.h:161
const char * icp_opcode_str[]
@ PROTO_HTCP
@ PROTO_ICP
@ PROTO_WAIS
void nbgethostbyname(const char *name, const CbcPointer< IpReceiver > &receiver)
initiate an (often) asynchronous DNS lookup; the receiver gets the results
Definition ipcache.cc:616
@ scServiceUnavailable
Definition StatusCode.h:76
code related to Squid Instance and PID file management
Definition Instance.h:19
void peerNoteDigestLookup(HttpRequest *request, CachePeer *p, lookup_t lookup)
Definition neighbors.cc:811
CachePeer * getFirstUpParent(PeerSelector *ps)
Definition neighbors.cc:282
CachePeer * whichPeer(const Ip::Address &from)
Definition neighbors.cc:99
int neighborsCount(PeerSelector *ps)
Definition neighbors.cc:268
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition neighbors.cc:253
CachePeer * getDefaultParent(PeerSelector *ps)
Definition neighbors.cc:469
CachePeer * getRoundRobinParent(PeerSelector *ps)
Definition neighbors.cc:308
bool peerAllowedToUse(const CachePeer *, PeerSelector *)
Definition neighbors.cc:138
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition neighbors.cc:117
int neighborsUdpPing(HttpRequest *request, StoreEntry *entry, IRCB *callback, PeerSelector *ps, int *exprep, int *timeout)
Definition neighbors.cc:544
CachePeer * neighborsDigestSelect(PeerSelector *ps)
Definition neighbors.cc:749
lookup_t peerDigestLookup(CachePeer *p, PeerSelector *ps)
Definition neighbors.cc:703
CachePeer * getWeightedRoundRobinParent(PeerSelector *ps)
Definition neighbors.cc:350
int netdbHostHops(const char *host)
Definition net_db.cc:928
int netdbHostRtt(const char *host)
Definition net_db.cc:944
void netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
Definition net_db.cc:986
CachePeer * netdbClosestParent(PeerSelector *ps)
Definition net_db.cc:1242
int timeouts
static int peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry *entry)
static struct @71 PeerStats
void peerSelectInit(void)
bool peerAllowedToUse(const CachePeer *, PeerSelector *)
Definition neighbors.cc:138
static const char * DirectStr[]
static void peerSelect(PeerSelectionInitiator *initiator, HttpRequest *request, AccessLogEntry::Pointer const &al, StoreEntry *entry)
static std::ostream & operator<<(std::ostream &os, const PeerSelectionDumper &fsd)
prints PeerSelectionDumper (for debugging)
static PeerSelectorPingMonitor & PingMonitor()
monitors all PeerSelector ICP ping timeouts
CachePeer * peerSourceHashSelectParent(PeerSelector *ps)
CachePeer * peerUserHashSelectParent(PeerSelector *ps)
int unsigned int
Definition stub_fd.cc:19
time_t struct timeval struct timeval struct timeval struct timeval const struct timeval const &STUB void tvAdd(struct timeval &, struct timeval const &, struct timeval const &) STUB void tvAssignAdd(struct timeval &
double tvSubDsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:44
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition gadgets.cc:18
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:51