peer_select.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 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
246 if (acl_checklist) {
247 debugs(44, DBG_IMPORTANT, "ERROR: Squid BUG: peer selector gone while waiting for a slow ACL");
248 delete acl_checklist;
249 }
250
252
253 if (entry) {
255 entry->unlock("peerSelect");
256 entry = nullptr;
257 }
258
259 delete lastError;
260}
261
262void
264{
265 assert(entry);
267 PingMonitor().monitor(this);
269}
270
271void
273{
274 PingMonitor().forget(this);
275}
276
277static int
279{
280 assert(ps);
281 HttpRequest *request = ps->request;
282
283 int n;
284 assert(entry);
285 assert(entry->ping_status == PING_NONE);
286 assert(direct != DIRECT_YES);
287 debugs(44, 3, entry->url());
288
289 if (!request->flags.hierarchical && direct != DIRECT_NO)
290 return 0;
291
293 if (direct != DIRECT_NO)
294 return 0;
295
296 n = neighborsCount(ps);
297
298 debugs(44, 3, "counted " << n << " neighbors");
299
300 return n;
301}
302
303static void
305 HttpRequest * request,
306 AccessLogEntry::Pointer const &al,
307 StoreEntry * entry)
308{
309 if (entry)
310 debugs(44, 3, *entry << ' ' << entry->url());
311 else
312 debugs(44, 3, request->method);
313
314 const auto selector = new PeerSelector(initiator);
315
316 selector->request = request;
317 HTTPMSGLOCK(selector->request);
318 selector->al = al;
319
320 selector->entry = entry;
321
322#if USE_CACHE_DIGESTS
323
325
326#endif
327
328 if (selector->entry)
329 selector->entry->lock("peerSelect");
330
331 selector->selectMore();
332}
333
334void
336{
337 subscribed = true;
338 peerSelect(this, request, ale, entry);
339 // and wait for noteDestination() and/or noteDestinationsEnd() calls
340}
341
342void
344{
345 acl_checklist = nullptr;
346 debugs(44, 3, answer);
347 never_direct = answer;
348 switch (answer) {
349 case ACCESS_ALLOWED:
352 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct allow)");
353 break;
354 case ACCESS_DENIED: // not relevant.
355 case ACCESS_DUNNO: // not relevant.
356 break;
358 debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
359 break;
360 }
361 selectMore();
362}
363
364void
366{
367 static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
368}
369
370void
372{
373 acl_checklist = nullptr;
374 debugs(44, 3, answer);
375 always_direct = answer;
376 switch (answer) {
377 case ACCESS_ALLOWED:
380 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct allow)");
381 break;
382 case ACCESS_DENIED: // not relevant.
383 case ACCESS_DUNNO: // not relevant.
384 break;
386 debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
387 break;
388 }
389 selectMore();
390}
391
392void
394{
395 static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
396}
397
400bool
402{
404 return false;
405
406 debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
407 delete this;
408 return true;
409}
410
412void
414{
415 if (selectionAborted())
416 return;
417
418 FwdServer *fs = servers;
419
420 // Bug 3243: CVE 2009-0801
421 // Bypass of browser same-origin access control in intercepted communication
422 // To resolve this we must use only the original client destination when going DIRECT
423 // on intercepted traffic which failed Host verification
424 const HttpRequest *req = request;
425 const bool isIntercepted = !req->flags.redirected &&
426 (req->flags.intercepted || req->flags.interceptTproxy);
427 const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
428 const bool choseDirect = fs && fs->code == HIER_DIRECT;
429 if (isIntercepted && useOriginalDst && choseDirect) {
430 // check the client is still around before using any of its details
431 if (req->clientConnectionManager.valid()) {
432 // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
435 fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
436 handlePath(p, *fs);
437 }
438
439 // clear the used fs and continue
440 servers = fs->next;
441 delete fs;
443 return;
444 }
445
446 if (fs && fs->code == PINNED) {
447 // Nil path signals a PINNED destination selection. Our initiator should
448 // borrow and use clientConnectionManager's pinned connection object
449 // (regardless of that connection destination).
450 handlePath(nullptr, *fs);
451 servers = fs->next;
452 delete fs;
454 return;
455 }
456
457 // convert the list of FwdServer destinations into destinations IP addresses
458 if (fs && wantsMoreDestinations()) {
459 // send the next one off for DNS lookup.
460 const char *host = fs->_peer.valid() ? fs->_peer->host : request->url.host();
461 debugs(44, 2, "Find IP destination for: " << url() << "' via " << host);
462 Dns::nbgethostbyname(host, this);
463 return;
464 }
465
466 // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
467 // due to the allocation method of fs, we must deallocate each manually.
468 // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
469 if (fs) {
470 assert(fs == servers);
471 while (fs) {
472 servers = fs->next;
473 delete fs;
474 fs = servers;
475 }
476 }
477
478 // done with DNS lookups. pass back to caller
479
480 debugs(44, 2, id << " found all " << foundPaths << " destinations for " << url());
481 debugs(44, 2, " always_direct = " << always_direct);
482 debugs(44, 2, " never_direct = " << never_direct);
483 debugs(44, 2, " timedout = " << ping.timedout);
484
486 request->hier.ping = ping; // final result
487
488 if (lastError && foundPaths) {
489 // nobody cares about errors if we found destinations despite them
490 debugs(44, 3, "forgetting the last error");
491 delete lastError;
492 lastError = nullptr;
493 }
494
495 if (const auto initiator = interestedInitiator())
496 initiator->noteDestinationsEnd(lastError);
497 lastError = nullptr; // initiator owns the ErrorState object now
498 delete this;
499}
500
501void
503{
504 /* ignore lookup delays that occurred after the initiator moved on */
505
506 if (selectionAborted())
507 return;
508
510 return;
511
512 request->recordLookup(details);
513}
514
515void
517{
518 if (selectionAborted())
519 return;
520
522 return;
523
524 const auto peer = servers->_peer.valid();
525
526 // for TPROXY spoofing, we must skip unusable addresses
527 if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) {
528 if (ip.isIPv4() != request->client_addr.isIPv4())
529 return; // cannot spoof the client address on this link
530 }
531
533 p->remote = ip;
534 // XXX: We return a (non-peer) destination with a zero port if the selection
535 // initiator supplied a request target without a port. If there are no valid
536 // use cases for this behavior, stop _selecting_ such destinations.
537 p->remote.port(peer ? peer->http_port : request->url.port().value_or(0));
538 handlePath(p, *servers);
539}
540
541void
543{
544 if (selectionAborted())
545 return;
546
547 FwdServer *fs = servers;
548 if (!ia) {
549 debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host()));
550 // discard any previous error.
551 delete lastError;
552 lastError = nullptr;
553 if (fs->code == HIER_DIRECT) {
555 lastError->dnsError = details.error;
556 }
557 }
558 // else noteIp() calls have already processed all IPs in *ia
559
560 servers = fs->next;
561 delete fs;
562
563 // continue resolving selected peers
565}
566
567int
569{
570#if USE_ICMP
571 CachePeer *p;
572 int myrtt;
573 int myhops;
574
575 if (direct == DIRECT_NO)
576 return 0;
577
578 /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
579
580 myrtt = netdbHostRtt(request->url.host());
581 debugs(44, 3, "MY RTT = " << myrtt << " msec");
582 debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
583
584 if (myrtt && myrtt <= Config.minDirectRtt)
585 return 1;
586
587 myhops = netdbHostHops(request->url.host());
588
589 debugs(44, 3, "MY hops = " << myhops);
590 debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops);
591
592 if (myhops && myhops <= Config.minDirectHops)
593 return 1;
594
596
597 if (p == nullptr)
598 return 0;
599
600 debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec");
601
602 if (myrtt && myrtt <= ping.p_rtt)
603 return 1;
604
605#endif /* USE_ICMP */
606
607 return 0;
608}
609
610void
612{
613 if (selectionAborted())
614 return;
615
616 debugs(44, 3, request->method << ' ' << request->url.host());
617
619 if (direct == DIRECT_UNKNOWN) {
621 debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)");
624 ch->al = al;
625 acl_checklist = ch;
626 acl_checklist->syncAle(request, nullptr);
628 return;
629 } else if (never_direct == ACCESS_DUNNO) {
630 debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)");
633 ch->al = al;
634 acl_checklist = ch;
635 acl_checklist->syncAle(request, nullptr);
637 return;
638 } else if (request->flags.noDirect) {
641 debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
642 } else if (request->flags.loopDetected) {
645 debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
646 } else if (checkNetdbDirect()) {
648 debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)");
649 } else {
651 debugs(44, 3, "direct = " << DirectStr[direct] << " (default)");
652 }
653
654 debugs(44, 3, "direct = " << DirectStr[direct]);
655 }
656
657 if (!entry || entry->ping_status == PING_NONE)
658 selectPinned();
659 if (entry == nullptr) {
660 (void) 0;
661 } else if (entry->ping_status == PING_NONE) {
663
665 return;
666 } else if (entry->ping_status == PING_WAITING) {
670 }
671
672 switch (direct) {
673
674 case DIRECT_YES:
676 break;
677
678 case DIRECT_NO:
681 break;
682
683 default:
684
687
691 }
692
695
696 break;
697 }
698
699 // end peer selection; start resolving selected peers
701}
702
704
706void
708{
709 // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
711 return;
712
713 const auto peer = request->pinnedConnection()->pinnedPeer();
714 const auto usePinned = peer ? peerAllowedToUse(peer, this) : (direct != DIRECT_NO);
715 // If the pinned connection is prohibited (for this request) then
716 // the initiator must decide whether it is OK to open a new one instead.
718
719 addSelection(peer, PINNED);
720 if (entry)
721 entry->ping_status = PING_DONE; // skip ICP
722}
723
732void
734{
735 CachePeer *p;
738
739 if (direct == DIRECT_YES) {
741 return;
742 }
743
744#if USE_CACHE_DIGESTS
745 if ((p = neighborsDigestSelect(this))) {
748 else
750 } else
751#endif
752 if ((p = netdbClosestParent(this))) {
754 } else if (peerSelectIcpPing(this, direct, entry)) {
755 debugs(44, 3, "Doing ICP pings");
758 entry,
760 this,
762 &ping.timeout);
763 // TODO: Refactor neighborsUdpPing() to guarantee positive timeouts.
764 if (ping.timeout < 0)
765 ping.timeout = 0;
766
767 if (ping.n_sent == 0)
768 debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
770 " ICP replies expected, RTT " << ping.timeout <<
771 " msec");
772
773 if (ping.n_replies_expected > 0) {
775 return;
776 }
777 }
778
779 if (code != HIER_NONE) {
780 assert(p);
781 addSelection(p, code);
782 }
783
785}
786
788void
790{
791 CachePeer *p = nullptr;
795
796 if (checkNetdbDirect()) {
798 addSelection(nullptr, code);
799 return;
800 }
801
802 if ((p = hit)) {
804 } else {
808 } else if (!first_parent_miss.isAnyAddr()) {
811 }
812 }
813 if (p && code != HIER_NONE) {
814 addSelection(p, code);
815 }
816}
817
819void
821{
822 if (direct == DIRECT_NO)
823 return;
824
825 /* WAIS is not implemented natively */
827 return;
828
829 addSelection(nullptr, HIER_DIRECT);
830}
831
832void
834{
835 CachePeer *p;
837 debugs(44, 3, request->method << ' ' << request->url.host());
838
839 if (direct == DIRECT_YES)
840 return;
841
842 if ((p = peerSourceHashSelectParent(this))) {
844#if USE_AUTH
845 } else if ((p = peerUserHashSelectParent(this))) {
847#endif
848 } else if ((p = carpSelectParent(this))) {
849 code = CARP;
850 } else if ((p = getRoundRobinParent(this))) {
852 } else if ((p = getWeightedRoundRobinParent(this))) {
854 } else if ((p = getFirstUpParent(this))) {
856 } else if ((p = getDefaultParent(this))) {
858 }
859
860 if (code != HIER_NONE) {
861 addSelection(p, code);
862 }
863}
864
866void
868{
869 /* Add all alive parents */
870
871 for (const auto &peer: CurrentCachePeers()) {
872 const auto p = peer.get();
873 /* XXX: neighbors.c lacks a public interface for enumerating
874 * parents to a request so we have to dig some here..
875 */
876
878 continue;
879
880 if (!peerHTTPOkay(p, this))
881 continue;
882
884 }
885
886 /* XXX: should add dead parents here, but it is currently
887 * not possible to find out which parents are dead or which
888 * simply are not configured to handle the request.
889 */
890 /* Add default parent as a last resort */
891 if (const auto p = getDefaultParent(this)) {
893 }
894}
895
896void
898{
899 debugs(44, 3, url());
900
901 // do nothing if ping reply came while handlePingTimeout() was queued
903 return;
904
906
907 if (selectionAborted())
908 return;
909
910 ++PeerStats.timeouts;
911 ping.timedout = 1;
912 selectMore();
913}
914
915void
917{
918 selector->handlePingTimeout();
919}
920
921void
923{
924 memset(&PeerStats, '\0', sizeof(PeerStats));
925}
926
927void
929{
930 int rtt;
931
932#if USE_ICMP
933 if (Config.onoff.query_icmp) {
934 if (header->flags & ICP_FLAG_SRC_RTT) {
935 rtt = header->pad & 0xFFFF;
936 int hops = (header->pad >> 16) & 0xFFFF;
937
938 if (rtt > 0 && rtt < 0xFFFF)
939 netdbUpdatePeer(request->url, p, rtt, hops);
940
941 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
943 ping.p_rtt = rtt;
944 }
945 }
946 }
947#else
948 (void)header;
949#endif /* USE_ICMP */
950
951 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
952 if (p->options.closest_only)
953 return;
954
955 /* set FIRST_MISS if there is no CLOSEST parent */
957 return;
958
959 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
960
961 if (rtt < 1)
962 rtt = 1;
963
964 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
966 ping.w_rtt = rtt;
967 }
968}
969
970void
972{
973 icp_opcode op = header->getOpCode();
974 debugs(44, 3, icp_opcode_str[op] << ' ' << url());
975#if USE_CACHE_DIGESTS && 0
976 /* do cd lookup to count false misses */
977
978 if (p && request)
980 peerDigestLookup(p, this));
981
982#endif
983
984 ++ping.n_recv;
985
986 if (op == ICP_MISS || op == ICP_DECHO) {
987 if (type == PEER_PARENT)
988 handleIcpParentMiss(p, header);
989 } else if (op == ICP_HIT) {
990 hit = p;
991 hit_type = type;
992 selectMore();
993 return;
994 }
995
997 return;
998
999 selectMore();
1000}
1001
1002#if USE_HTCP
1003void
1005{
1006 debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url());
1007 ++ping.n_recv;
1008
1009 if (htcp->hit) {
1010 hit = p;
1011 hit_type = type;
1012 selectMore();
1013 return;
1014 }
1015
1016 if (type == PEER_PARENT)
1017 handleHtcpParentMiss(p, htcp);
1018
1020 return;
1021
1022 selectMore();
1023}
1024
1025void
1027{
1028 int rtt;
1029
1030#if USE_ICMP
1031 if (Config.onoff.query_icmp) {
1032 if (htcp->cto.rtt > 0) {
1033 rtt = (int) htcp->cto.rtt * 1000;
1034 int hops = (int) htcp->cto.hops * 1000;
1035 netdbUpdatePeer(request->url, p, rtt, hops);
1036
1037 if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
1039 ping.p_rtt = rtt;
1040 }
1041 }
1042 }
1043#else
1044 (void)htcp;
1045#endif /* USE_ICMP */
1046
1047 /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
1048 if (p->options.closest_only)
1049 return;
1050
1051 /* set FIRST_MISS if there is no CLOSEST parent */
1053 return;
1054
1055 rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
1056
1057 if (rtt < 1)
1058 rtt = 1;
1059
1060 if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
1062 ping.w_rtt = rtt;
1063 }
1064}
1065
1066#endif
1067
1068void
1069PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
1070{
1071 if (proto == AnyP::PROTO_ICP)
1072 static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata));
1073
1074#if USE_HTCP
1075
1076 else if (proto == AnyP::PROTO_HTCP)
1077 static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata));
1078
1079#endif
1080
1081 else
1082 debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto);
1083}
1084
1085void
1087{
1088 // Find the end of the servers list. Bail on a duplicate destination.
1089 auto **serversTail = &servers;
1090 while (const auto server = *serversTail) {
1091 // There can be at most one PINNED destination.
1092 // Non-PINNED destinations are uniquely identified by their CachePeer
1093 // (even though a DIRECT destination might match a cache_peer address).
1094 // TODO: We may still add duplicates because the same peer could have
1095 // been removed from `servers` already (and given to the requestor).
1096 const bool duplicate = (server->code == PINNED) ?
1097 (code == PINNED) : (server->_peer == peer);
1098 if (duplicate) {
1099 debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) <<
1100 "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code));
1101 return;
1102 }
1103 serversTail = &server->next;
1104 }
1105
1106 debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code));
1107 *serversTail = new FwdServer(peer, code);
1108}
1109
1111 request(nullptr),
1112 entry (nullptr),
1113 always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
1114 never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
1115 direct(DIRECT_UNKNOWN),
1116 lastError(nullptr),
1117 servers (nullptr),
1118 first_parent_miss(),
1119 closest_parent_miss(),
1120 hit(nullptr),
1121 hit_type(PEER_NONE),
1122 acl_checklist (nullptr),
1123 initiator_(initiator)
1124{
1125 ; // no local defaults.
1126}
1127
1128const SBuf
1130{
1131 if (entry)
1132 return SBuf(entry->url());
1133
1134 if (request)
1135 return request->effectiveRequestUri();
1136
1137 static const SBuf noUrl("[no URL]");
1138 return noUrl;
1139}
1140
1144{
1145 const auto initiator = initiator_.valid();
1146
1147 if (!initiator) {
1148 debugs(44, 3, id << " initiator gone");
1149 return nullptr;
1150 }
1151
1152 if (!initiator->subscribed) {
1153 debugs(44, 3, id << " initiator lost interest");
1154 return nullptr;
1155 }
1156
1157 debugs(44, 7, id);
1158 return initiator;
1159}
1160
1161bool
1163 const auto maxCount = Config.forward_max_tries;
1164 return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount);
1165}
1166
1167void
1169{
1170 ++foundPaths;
1171
1172 if (path) {
1173 path->peerType = fs.code;
1174 path->setPeer(fs._peer.get());
1175
1176 // check for a configured outgoing address for this destination...
1178 debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url());
1179 } else
1180 debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url());
1181
1182 request->hier.ping = ping; // may be updated later
1183
1184 debugs(44, 2, " always_direct = " << always_direct);
1185 debugs(44, 2, " never_direct = " << never_direct);
1186 debugs(44, 2, " timedout = " << ping.timedout);
1187
1188 if (const auto initiator = interestedInitiator())
1189 initiator->noteDestination(path);
1190}
1191
1193
1195 n_sent(0),
1196 n_recv(0),
1197 n_replies_expected(0),
1198 timeout(0),
1199 timedout(0),
1200 w_rtt(0),
1201 p_rtt(0),
1202 monitorRegistration(PingMonitor().npos())
1203{
1204 start.tv_sec = 0;
1205 start.tv_usec = 0;
1206 stop.tv_sec = 0;
1207 stop.tv_usec = 0;
1208}
1209
1210timeval
1212{
1213 timeval timeInterval;
1214 timeInterval.tv_sec = timeout / 1000;
1215 timeInterval.tv_usec = (timeout % 1000) * 1000;
1216
1217 timeval result;
1218 tvAdd(result, start, timeInterval);
1219 return result;
1220}
1221
#define MEMPROXY_CLASS(CLASS)
const CachePeers & CurrentCachePeers()
Definition: CachePeers.cc:41
static CodeContext::Pointer & Instance()
guarantees the forever existence of the pointer, starting from the first use
Definition: CodeContext.cc:26
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:116
void getOutgoingAddress(HttpRequest *request, const Comm::ConnectionPointer &conn)
Definition: FwdState.cc:1476
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
Definition: SquidConfig.cc:12
#define assert(EX)
Definition: assert.h:17
static char server[MAXLINE]
CachePeer * carpSelectParent(PeerSelector *ps)
Definition: carp.cc:138
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:320
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const =0
assigns uninitialized adapted_request and url ALE components
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:237
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
AnyP::UriScheme const & getScheme() const
Definition: Uri.h:67
void port(const Port p)
reset authority port subcomponent
Definition: Uri.h:95
void host(const char *src)
Definition: Uri.cc:100
bool closest_only
Definition: CachePeer.h:116
int weight
Definition: CachePeer.h:151
struct CachePeer::@28 options
int basetime
Definition: CachePeer.h:152
char * host
Definition: CachePeer.h:65
Ip::Address in_addr
Definition: CachePeer.h:69
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:152
Ip::Address remote
Definition: Connection.h:149
Ip::Address local
Definition: Connection.h:146
void setPeer(CachePeer *p)
Definition: Connection.cc:133
CachePeer * pinnedPeer() const
Definition: client_side.h:203
struct ConnStateData::@37 pinning
bool peerAccessDenied
cache_peer_access denied pinned connection reuse
Definition: client_side.h:150
encapsulates DNS lookup results
Definition: LookupDetails.h:23
const std::optional< SBuf > error
error message (if any)
Definition: LookupDetails.h:39
std::optional< SBuf > dnsError
DNS lookup error message.
Definition: errorpage.h:180
FwdServer * next
Definition: peer_select.cc:63
CbcPointer< CachePeer > _peer
Definition: peer_select.cc:61
FwdServer(CachePeer *p, hier_code c)
Definition: peer_select.cc:55
hier_code code
Definition: peer_select.cc:62
struct timeval peer_select_start
struct HtcpReplyData::cto_t cto
int hit
Definition: htcp.h:30
void recordLookup(const Dns::LookupDetails &detail)
Definition: HttpRequest.cc:580
CbcPointer< ConnStateData > clientConnectionManager
Definition: HttpRequest.h:232
HttpRequestMethod method
Definition: HttpRequest.h:114
HierarchyLogEntry hier
Definition: HttpRequest.h:157
RequestFlags flags
Definition: HttpRequest.h:141
ConnStateData * pinnedConnection()
Definition: HttpRequest.cc:725
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
Ip::Address client_addr
Definition: HttpRequest.h:149
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:744
bool isIPv4() const
Definition: Address.cc:158
bool isAnyAddr() const
Definition: Address.cc:170
unsigned short port() const
Definition: Address.cc:778
a helper class to report a selected destination (for debugging)
Definition: peer_select.cc:79
PeerSelectionDumper(const PeerSelector *const aSelector, const CachePeer *const aPeer, const hier_code aCode)
Definition: peer_select.cc:81
const CachePeer *const peer
successful selection info
Definition: peer_select.cc:85
const PeerSelector *const selector
selection parameters
Definition: peer_select.cc:84
const hier_code code
selection algorithm
Definition: peer_select.cc:86
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)
Definition: peer_select.cc:335
void monitor(PeerSelector *)
registers the given selector to be notified about the IPC ping timeout
Definition: peer_select.cc:192
static void NoteWaitOver(void *monitor)
PeerSelectorPingMonitor::noteWaitOver() wrapper.
Definition: peer_select.cc:142
void abortWaiting()
undoes an earlier startWaiting() call
Definition: peer_select.cc:159
WaitingPeerSelectorPosition npos()
Definition: peer_select.cc:118
void startWaiting()
schedules a single event to represent all waiting selectors
Definition: peer_select.cc:150
void forget(PeerSelector *)
removes a PeerSelector from the waiting list
Definition: peer_select.cc:208
void noteWaitOver()
calls back all ready PeerSelectors and continues to wait for others
Definition: peer_select.cc:168
WaitingPeerSelectors selectors
Definition: peer_select.cc:127
Ip::Address closest_parent_miss
bool selectionAborted()
Definition: peer_select.cc:401
void selectAllParents()
Adds alive parents. Used as a last resort for never_direct.
Definition: peer_select.cc:867
PeerSelectionInitiator * interestedInitiator()
void handlePingTimeout()
Definition: peer_select.cc:897
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
Definition: peer_select.cc:916
void noteIps(const Dns::CachedIps *ips, const Dns::LookupDetails &details) override
Definition: peer_select.cc:542
Acl::Answer always_direct
void selectMore()
a single selection loop iteration: attempts to add more destinations
Definition: peer_select.cc:611
void startPingWaiting()
switches into the PING_WAITING state (and associated timeout monitoring)
Definition: peer_select.cc:263
void handleIcpParentMiss(CachePeer *, icp_common_t *)
Definition: peer_select.cc:928
void cancelPingTimeoutMonitoring()
terminates ICP ping timeout monitoring
Definition: peer_select.cc:272
int checkNetdbDirect()
Definition: peer_select.cc:568
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)
Definition: peer_select.cc:343
void checkAlwaysDirectDone(const Acl::Answer answer)
Definition: peer_select.cc:371
void handleHtcpReply(CachePeer *, const peer_t, HtcpReplyData *)
ping_data ping
void handleIcpReply(CachePeer *, const peer_t, icp_common_t *header)
Definition: peer_select.cc:971
static ACLCB CheckNeverDirectDone
void addSelection(CachePeer *, const hier_code)
static ACLCB CheckAlwaysDirectDone
void noteLookup(const Dns::LookupDetails &details) override
Definition: peer_select.cc:502
void selectSomeDirect()
Adds a "direct" entry if the request can be forwarded to the origin server.
Definition: peer_select.cc:820
StoreEntry * entry
void noteIp(const Ip::Address &ip) override
Called when/if nbgethostbyname() discovers a new good IP address.
Definition: peer_select.cc:516
Acl::Answer never_direct
PeerSelector(PeerSelectionInitiator *)
void resolveSelected()
A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
Definition: peer_select.cc:413
void selectSomeNeighbor()
Definition: peer_select.cc:733
void selectSomeParent()
Definition: peer_select.cc:833
CachePeer * hit
void selectPinned()
Selects a pinned connection if it exists, is valid, and is allowed.
Definition: peer_select.cc:707
~PeerSelector() override
Definition: peer_select.cc:231
void selectSomeNeighborReplies()
Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
Definition: peer_select.cc:789
size_t foundPaths
number of unique destinations identified so far
void handleHtcpParentMiss(CachePeer *, HtcpReplyData *)
static IRCB HandlePingReply
ACLChecklist * acl_checklist
bool interceptTproxy
Set for requests handled by a "tproxy" port.
Definition: RequestFlags.h:70
bool hostVerified
Definition: RequestFlags.h:68
bool intercepted
Definition: RequestFlags.h:66
bool loopDetected
Definition: RequestFlags.h:40
bool spoofClientIp
Definition: RequestFlags.h:74
bool hierarchical
Definition: RequestFlags.h:38
Definition: SBuf.h:94
Comm::ConnectionPointer clientConnection
Definition: Server.h:100
int nonhierarchical_direct
Definition: SquidConfig.h:299
struct SquidConfig::@107 accessList
int client_dst_passthru
Definition: SquidConfig.h:338
int forward_max_tries
Definition: SquidConfig.h:351
struct SquidConfig::@106 onoff
int minDirectRtt
Definition: SquidConfig.h:260
int prefer_direct
Definition: SquidConfig.h:298
int minDirectHops
Definition: SquidConfig.h:259
acl_access * NeverDirect
Definition: SquidConfig.h:363
acl_access * AlwaysDirect
Definition: SquidConfig.h:364
uint16_t flags
Definition: Store.h:232
int unlock(const char *context)
Definition: store.cc:455
const char * url() const
Definition: store.cc:1552
ping_status_t ping_status
Definition: Store.h:242
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:194
#define DBG_CRITICAL
Definition: Stream.h:37
#define DIRECT_NO
Definition: defines.h:49
#define DIRECT_MAYBE
Definition: defines.h:50
#define ICP_FLAG_SRC_RTT
Definition: defines.h:41
#define DIRECT_UNKNOWN
Definition: defines.h:48
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
#define DIRECT_YES
Definition: defines.h:51
@ PING_WAITING
Sent ICP queries to peers and still awaiting responses.
Definition: enums.h:43
@ PING_NONE
Has not considered whether to send ICP queries to peers yet.
Definition: enums.h:41
@ PING_DONE
Definition: enums.h:46
peer_t
Definition: enums.h:27
@ PEER_PARENT
Definition: enums.h:30
@ PEER_NONE
Definition: enums.h:28
@ KEY_PRIVATE
Definition: enums.h:102
@ 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:120
@ ACCESS_DENIED
Definition: Acl.h:115
@ ACCESS_ALLOWED
Definition: Acl.h:116
@ ACCESS_DUNNO
Definition: Acl.h:117
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[]
ProtocolType
Definition: ProtocolType.h:23
@ PROTO_HTCP
Definition: ProtocolType.h:33
@ PROTO_ICP
Definition: ProtocolType.h:31
@ PROTO_WAIS
Definition: ProtocolType.h:30
void nbgethostbyname(const char *name, const CbcPointer< IpReceiver > &receiver)
initiate an (often) asynchronous DNS lookup; the receiver gets the results
Definition: ipcache.cc:615
@ scServiceUnavailable
Definition: StatusCode.h:74
code related to Squid Instance and PID file management
Definition: Instance.h:17
SSL Connection
Definition: Session.h:45
void peerNoteDigestLookup(HttpRequest *request, CachePeer *p, lookup_t lookup)
Definition: neighbors.cc:813
CachePeer * getFirstUpParent(PeerSelector *ps)
Definition: neighbors.cc:286
CachePeer * whichPeer(const Ip::Address &from)
Definition: neighbors.cc:96
int neighborsCount(PeerSelector *ps)
Definition: neighbors.cc:272
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:257
CachePeer * getDefaultParent(PeerSelector *ps)
Definition: neighbors.cc:474
CachePeer * getRoundRobinParent(PeerSelector *ps)
Definition: neighbors.cc:313
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition: neighbors.cc:114
int neighborsUdpPing(HttpRequest *request, StoreEntry *entry, IRCB *callback, PeerSelector *ps, int *exprep, int *timeout)
Definition: neighbors.cc:550
CachePeer * neighborsDigestSelect(PeerSelector *ps)
Definition: neighbors.cc:751
lookup_t peerDigestLookup(CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:705
CachePeer * getWeightedRoundRobinParent(PeerSelector *ps)
Definition: neighbors.cc:355
int netdbHostHops(const char *host)
Definition: net_db.cc:944
int netdbHostRtt(const char *host)
Definition: net_db.cc:960
void netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
Definition: net_db.cc:1002
CachePeer * netdbClosestParent(PeerSelector *ps)
Definition: net_db.cc:1258
int timeouts
Definition: peer_select.cc:67
InstanceIdDefinitions(PeerSelector, "PeerSelector")
static int peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry *entry)
Definition: peer_select.cc:278
static struct @82 PeerStats
void peerSelectInit(void)
Definition: peer_select.cc:922
bool peerAllowedToUse(const CachePeer *, PeerSelector *)
Definition: neighbors.cc:137
static const char * DirectStr[]
Definition: peer_select.cc:70
static void peerSelect(PeerSelectionInitiator *initiator, HttpRequest *request, AccessLogEntry::Pointer const &al, StoreEntry *entry)
Definition: peer_select.cc:304
static std::ostream & operator<<(std::ostream &os, const PeerSelectionDumper &fsd)
prints PeerSelectionDumper (for debugging)
Definition: peer_select.cc:93
static PeerSelectorPingMonitor & PingMonitor()
monitors all PeerSelector ICP ping timeouts
Definition: peer_select.cc:132
CachePeer * peerSourceHashSelectParent(PeerSelector *ps)
CachePeer * peerUserHashSelectParent(PeerSelector *ps)
int code
Definition: smb-errors.c:145
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:17
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:51

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors