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"
13 #include "base/AsyncCbdataCalls.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 
50 class FwdServer
51 {
53 
54 public:
56  _peer(p),
57  code(c),
58  next(nullptr)
59  {}
60 
61  CbcPointer<CachePeer> _peer; /* NULL --> origin server */
64 };
65 
66 static struct {
67  int timeouts;
68 } PeerStats;
69 
70 static const char *DirectStr[] = {
71  "DIRECT_UNKNOWN",
72  "DIRECT_NO",
73  "DIRECT_MAYBE",
74  "DIRECT_YES"
75 };
76 
79 {
80 public:
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;
86  const hier_code code;
87 };
88 
90 
92 static std::ostream &
93 operator <<(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 {
110 public:
112  void monitor(PeerSelector *);
113 
115  void forget(PeerSelector *);
116 
119 
120 private:
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 
141 void
143 {
144  assert(raw);
145  static_cast<PeerSelectorPingMonitor*>(raw)->noteWaitOver();
146 }
147 
149 void
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 
158 void
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 
167 void
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 
191 void
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 
207 void
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 
257 void
259 {
260  assert(entry);
262  PingMonitor().monitor(this);
264 }
265 
266 void
268 {
269  PingMonitor().forget(this);
270 }
271 
272 static int
273 peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry * entry)
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 
298 static 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 
329 void
331 {
332  subscribed = true;
333  peerSelect(this, request, ale, entry);
334  // and wait for noteDestination() and/or noteDestinationsEnd() calls
335 }
336 
337 void
339 {
340  debugs(44, 3, answer);
341  never_direct = answer;
342  switch (answer) {
343  case ACCESS_ALLOWED:
345  direct = DIRECT_NO;
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 
358 void
360 {
361  static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
362 }
363 
364 void
366 {
367  debugs(44, 3, answer);
368  always_direct = answer;
369  switch (answer) {
370  case ACCESS_ALLOWED:
372  direct = DIRECT_YES;
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 
385 void
387 {
388  static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
389 }
390 
393 bool
395 {
396  if (interestedInitiator())
397  return false;
398 
399  debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
400  delete this;
401  return true;
402 }
403 
405 void
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;
435  resolveSelected();
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;
446  resolveSelected();
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 
494 void
496 {
497  /* ignore lookup delays that occurred after the initiator moved on */
498 
499  if (selectionAborted())
500  return;
501 
502  if (!wantsMoreDestinations())
503  return;
504 
505  request->recordLookup(details);
506 }
507 
508 void
510 {
511  if (selectionAborted())
512  return;
513 
514  if (!wantsMoreDestinations())
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 
534 void
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
557  resolveSelected();
558 }
559 
560 int
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 
603 void
605 {
606  if (selectionAborted())
607  return;
608 
609  debugs(44, 3, request->method << ' ' << request->url.host());
610 
612  if (direct == DIRECT_UNKNOWN) {
613  if (always_direct == ACCESS_DUNNO) {
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) {
631  direct = DIRECT_NO;
632  debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
633  } else if (request->flags.loopDetected) {
635  direct = DIRECT_YES;
636  debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
637  } else if (checkNetdbDirect()) {
638  direct = DIRECT_YES;
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
691  resolveSelected();
692 }
693 
694 bool peerAllowedToUse(const CachePeer *, PeerSelector*);
695 
697 void
699 {
700  // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
701  if (!request->pinnedConnection())
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 
723 void
725 {
726  CachePeer *p;
729 
730  if (direct == DIRECT_YES) {
732  return;
733  }
734 
735 #if USE_CACHE_DIGESTS
736  if ((p = neighborsDigestSelect(this))) {
737  if (neighborType(p, request->url) == PEER_PARENT)
739  else
741  } else
742 #endif
743  if ((p = netdbClosestParent(this))) {
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");
760  debugs(44, 3, ping.n_replies_expected <<
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 
779 void
781 {
782  CachePeer *p = nullptr;
786 
787  if (checkNetdbDirect()) {
789  addSelection(nullptr, code);
790  return;
791  }
792 
793  if ((p = hit)) {
795  } else {
799  } else if (!first_parent_miss.isAnyAddr()) {
802  }
803  }
804  if (p && code != HIER_NONE) {
805  addSelection(p, code);
806  }
807 }
808 
810 void
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 
823 void
825 {
826  CachePeer *p;
828  debugs(44, 3, request->method << ' ' << request->url.host());
829 
830  if (direct == DIRECT_YES)
831  return;
832 
833  if ((p = peerSourceHashSelectParent(this))) {
835 #if USE_AUTH
836  } else if ((p = peerUserHashSelectParent(this))) {
838 #endif
839  } else if ((p = carpSelectParent(this))) {
840  code = CARP;
841  } else if ((p = getRoundRobinParent(this))) {
843  } else if ((p = getWeightedRoundRobinParent(this))) {
845  } else if ((p = getFirstUpParent(this))) {
847  } else if ((p = getDefaultParent(this))) {
849  }
850 
851  if (code != HIER_NONE) {
852  addSelection(p, code);
853  }
854 }
855 
857 void
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 
868  if (neighborType(p, request->url) != PEER_PARENT)
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 
887 void
889 {
890  debugs(44, 3, url());
891 
892  // do nothing if ping reply came while handlePingTimeout() was queued
893  if (!entry || entry->ping_status != PING_WAITING)
894  return;
895 
897 
898  if (selectionAborted())
899  return;
900 
901  ++PeerStats.timeouts;
902  ping.timedout = 1;
903  selectMore();
904 }
905 
906 void
908 {
909  selector->handlePingTimeout();
910 }
911 
912 void
914 {
915  memset(&PeerStats, '\0', sizeof(PeerStats));
916 }
917 
918 void
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 
961 void
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
994 void
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 
1016 void
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 
1059 void
1060 PeerSelector::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 
1076 void
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 
1118 const 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 
1151 bool
1153  const auto maxCount = Config.forward_max_tries;
1154  return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount);
1155 }
1156 
1157 void
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...
1167  getOutgoingAddress(request, path);
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 
1182 InstanceIdDefinitions(PeerSelector, "PeerSelector");
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 
1200 timeval
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 
void selectSomeDirect()
Adds a "direct" entry if the request can be forwarded to the origin server.
Definition: peer_select.cc:811
~PeerSelector() override
Definition: peer_select.cc:231
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:159
double tvSubDsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:44
@ ANY_OLD_PARENT
Definition: hier_code.h:32
hier_code peerType
Definition: Connection.h:152
static CodeContext::Pointer & Instance()
guarantees the forever existence of the pointer, starting from the first use
Definition: CodeContext.cc:26
const PeerSelector *const selector
selection parameters
Definition: peer_select.cc:84
#define DBG_CRITICAL
Definition: Stream.h:37
@ CLOSEST_PARENT_MISS
Definition: hier_code.h:21
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
CachePeer * netdbClosestParent(PeerSelector *ps)
Definition: net_db.cc:1242
bool interceptTproxy
Set for requests handled by a "tproxy" port.
Definition: RequestFlags.h:70
void resolveSelected()
A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
Definition: peer_select.cc:406
int n_replies_expected
Definition: PingData.h:40
static void NoteWaitOver(void *monitor)
PeerSelectorPingMonitor::noteWaitOver() wrapper.
Definition: peer_select.cc:142
bool spoofClientIp
Definition: RequestFlags.h:74
HttpRequest * request
Interface for those who need a list of peers to forward a request to.
size_t foundPaths
number of unique destinations identified so far
Ip::Address in_addr
Definition: CachePeer.h:70
struct SquidConfig::@98 accessList
timeval deadline() const
@ FIRSTUP_PARENT
Definition: hier_code.h:19
int nonhierarchical_direct
Definition: SquidConfig.h:299
FwdServer * next
Definition: peer_select.cc:63
void eventDelete(EVH *func, void *arg)
Definition: event.cc:127
CachePeer * getDefaultParent(PeerSelector *ps)
Definition: neighbors.cc:468
int neighborsUdpPing(HttpRequest *request, StoreEntry *entry, IRCB *callback, PeerSelector *ps, int *exprep, int *timeout)
Definition: neighbors.cc:544
@ PARENT_HIT
Definition: hier_code.h:16
int n_recv
Definition: PingData.h:39
RequestFlags flags
Definition: HttpRequest.h:141
WaitingPeerSelectorPosition monitorRegistration
maintained by PeerSelectorPingMonitor
Definition: PingData.h:49
void handlePath(const Comm::ConnectionPointer &path, FwdServer &fs)
processes a newly discovered/finalized path
void netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
Definition: net_db.cc:986
void noteLookup(const Dns::LookupDetails &details) override
Definition: peer_select.cc:495
@ HIER_DIRECT
Definition: hier_code.h:14
ErrorState * lastError
const char * url() const
Definition: store.cc:1566
void handleHtcpParentMiss(CachePeer *, HtcpReplyData *)
bool peerAccessDenied
cache_peer_access denied pinned connection reuse
Definition: client_side.h:150
void noteIps(const Dns::CachedIps *ips, const Dns::LookupDetails &details) override
Definition: peer_select.cc:535
FwdServer * servers
a linked list of (unresolved) selected peers
bool isAnyAddr() const
Definition: Address.cc:190
void checkAlwaysDirectDone(const Acl::Answer answer)
Definition: peer_select.cc:365
WaitingPeerSelectors selectors
Definition: peer_select.cc:127
CachePeer * pinnedPeer() const
Definition: client_side.h:203
ping_data ping
@ KEY_PRIVATE
Definition: enums.h:97
bool loopDetected
Definition: RequestFlags.h:40
CachePeer * peerUserHashSelectParent(PeerSelector *ps)
void startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry)
Definition: peer_select.cc:330
Definition: SBuf.h:93
static void peerSelect(PeerSelectionInitiator *initiator, HttpRequest *request, AccessLogEntry::Pointer const &al, StoreEntry *entry)
Definition: peer_select.cc:299
@ PING_WAITING
Sent ICP queries to peers and still awaiting responses.
Definition: enums.h:38
struct timeval stop
Definition: PingData.h:37
int neighborsCount(PeerSelector *ps)
Definition: neighbors.cc:267
@ ICP_DECHO
Definition: icp_opcode.h:26
acl_access * AlwaysDirect
Definition: SquidConfig.h:364
bool hostVerified
Definition: RequestFlags.h:68
bool hierarchical
Definition: RequestFlags.h:38
WaitingPeerSelectorPosition npos()
Definition: peer_select.cc:118
StoreEntry * entry
void monitor(PeerSelector *)
registers the given selector to be notified about the IPC ping timeout
Definition: peer_select.cc:192
struct SquidConfig::@97 onoff
void handleHtcpReply(CachePeer *, const peer_t, HtcpReplyData *)
Definition: peer_select.cc:995
uint16_t flags
Definition: Store.h:231
static PeerSelectorPingMonitor & PingMonitor()
monitors all PeerSelector ICP ping timeouts
Definition: peer_select.cc:132
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
int netdbHostRtt(const char *host)
Definition: net_db.cc:944
@ CLOSEST_PARENT
Definition: hier_code.h:22
bool isIPv4() const
Definition: Address.cc:178
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:126
@ ORIGINAL_DST
Definition: hier_code.h:36
int hit
Definition: htcp.h:30
a helper class to report a selected destination (for debugging)
Definition: peer_select.cc:78
Ip::Address closest_parent_miss
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition: gadgets.cc:51
@ SOURCEHASH_PARENT
Definition: hier_code.h:34
static std::ostream & operator<<(std::ostream &os, const PeerSelectionDumper &fsd)
prints PeerSelectionDumper (for debugging)
Definition: peer_select.cc:93
void getOutgoingAddress(HttpRequest *request, const Comm::ConnectionPointer &conn)
Definition: FwdState.cc:1481
#define ICP_FLAG_SRC_RTT
Definition: defines.h:39
CachePeer * getFirstUpParent(PeerSelector *ps)
Definition: neighbors.cc:281
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition: neighbors.cc:116
int weight
Definition: CachePeer.h:150
CachePeer * getWeightedRoundRobinParent(PeerSelector *ps)
Definition: neighbors.cc:349
ProtocolType
Definition: ProtocolType.h:23
@ SIBLING_HIT
Definition: hier_code.h:15
static ACLCB CheckNeverDirectDone
@ ACCESS_AUTH_REQUIRED
Definition: Acl.h:46
void startWaiting()
schedules a single event to represent all waiting selectors
Definition: peer_select.cc:150
static MakingPointer Make(const acl_access *a, HttpRequest *r)
peer_t
Definition: enums.h:22
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition: gadgets.cc:18
WaitingPeerSelectors::iterator WaitingPeerSelectorPosition
Definition: PingData.h:22
static int peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry *entry)
Definition: peer_select.cc:273
void nbgethostbyname(const char *name, const CbcPointer< IpReceiver > &receiver)
initiate an (often) asynchronous DNS lookup; the receiver gets the results
Definition: ipcache.cc:616
const CachePeer *const peer
successful selection info
Definition: peer_select.cc:85
void handleIcpParentMiss(CachePeer *, icp_common_t *)
Definition: peer_select.cc:919
PeerSelectionInitiator * interestedInitiator()
@ PEER_PARENT
Definition: enums.h:25
struct timeval peer_select_start
@ HIER_NONE
Definition: hier_code.h:13
void recordLookup(const Dns::LookupDetails &detail)
Definition: HttpRequest.cc:580
#define DIRECT_YES
Definition: defines.h:49
std::multimap< timeval, PeerSelector *, std::less< timeval >, PoolingAllocator< WaitingPeerSelector > > WaitingPeerSelectors
waiting PeerSelector objects, ordered by their absolute deadlines
Definition: PingData.h:21
static IRCB HandlePingReply
unsigned short port() const
Definition: Address.cc:798
ConnStateData * pinnedConnection()
Definition: HttpRequest.cc:725
void checkNeverDirectDone(const Acl::Answer answer)
Definition: peer_select.cc:338
Ip::Address local
Definition: Connection.h:146
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
bool subscribed
whether noteDestination() and noteDestinationsEnd() calls are allowed
@ FIRST_PARENT_MISS
Definition: hier_code.h:20
void addSelection(CachePeer *, const hier_code)
ping_status_t ping_status
Definition: Store.h:241
int minDirectHops
Definition: SquidConfig.h:259
static void NonBlockingCheck(MakingPointer &&p, ACLCB *cb, void *data)
@ USERHASH_PARENT
Definition: hier_code.h:33
bool selectionAborted()
Definition: peer_select.cc:394
int unlock(const char *context)
Definition: store.cc:469
hier_code code
Definition: peer_select.cc:62
AccessLogEntry::Pointer al
info for the future access.log entry
int client_dst_passthru
Definition: SquidConfig.h:338
@ PEER_NONE
Definition: enums.h:23
Ip::Address remote
Definition: Connection.h:149
code related to Squid Instance and PID file management
Definition: Instance.h:17
int p_rtt
Definition: PingData.h:44
Acl::Answer never_direct
CachePeer * hit
bool closest_only
Definition: CachePeer.h:117
@ PINNED
Definition: hier_code.h:35
int neighbors_do_private_keys
bool peerAllowedToUse(const CachePeer *, PeerSelector *)
Definition: neighbors.cc:137
#define assert(EX)
Definition: assert.h:17
struct HtcpReplyData::cto_t cto
SSL Connection
Definition: Session.h:49
bool intercepted
Definition: RequestFlags.h:66
PeerSelectionDumper(const PeerSelector *const aSelector, const CachePeer *const aPeer, const hier_code aCode)
Definition: peer_select.cc:81
@ ROUNDROBIN_PARENT
Definition: hier_code.h:26
void handlePingTimeout()
Definition: peer_select.cc:888
HierarchyLogEntry hier
Definition: HttpRequest.h:157
CachePeer * whichPeer(const Ip::Address &from)
Definition: neighbors.cc:98
@ ICP_HIT
Definition: icp_opcode.h:17
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:161
struct timeval start
Definition: PingData.h:35
encapsulates DNS lookup results
Definition: LookupDetails.h:22
@ PING_NONE
Has not considered whether to send ICP queries to peers yet.
Definition: enums.h:36
@ scServiceUnavailable
Definition: StatusCode.h:76
void peerSelectInit(void)
Definition: peer_select.cc:913
std::optional< SBuf > dnsError
DNS lookup error message.
Definition: errorpage.h:180
const AnyP::UriScheme & getScheme() const
Definition: Uri.h:58
void selectSomeParent()
Definition: peer_select.cc:824
void port(const Port p)
reset authority port subcomponent
Definition: Uri.h:90
int prefer_direct
Definition: SquidConfig.h:298
void cancelPingTimeoutMonitoring()
terminates ICP ping timeout monitoring
Definition: peer_select.cc:267
CachePeer * neighborsDigestSelect(PeerSelector *ps)
Definition: neighbors.cc:749
icp_opcode getOpCode() const
Definition: icp_v2.cc:129
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
@ ERR_DNS_FAIL
Definition: forward.h:35
static void HandlePingTimeout(PeerSelector *)
called when the given selector should stop expecting ICP ping responses
Definition: peer_select.cc:907
Comm::ConnectionPointer clientConnection
Definition: Server.h:100
void startPingWaiting()
switches into the PING_WAITING state (and associated timeout monitoring)
Definition: peer_select.cc:258
int code
Definition: smb-errors.c:145
void peerNoteDigestLookup(HttpRequest *request, CachePeer *p, lookup_t lookup)
Definition: neighbors.cc:811
const SBuf url() const
void selectSomeNeighbor()
Definition: peer_select.cc:724
#define DIRECT_NO
Definition: defines.h:47
CachePeer * peerSourceHashSelectParent(PeerSelector *ps)
int w_rtt
Definition: PingData.h:43
@ CARP
Definition: hier_code.h:31
PeerSelector(PeerSelectionInitiator *)
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 &
Acl::Answer always_direct
@ ICP_MISS
Definition: icp_opcode.h:18
struct ConnStateData::@36 pinning
FwdServer(CachePeer *p, hier_code c)
Definition: peer_select.cc:55
HttpRequestMethod method
Definition: HttpRequest.h:114
const char * hier_code_str[]
@ PING_DONE
Definition: enums.h:41
Initiator initiator_
recipient of the destinations we select; use interestedInitiator() to access
static ACLCB CheckAlwaysDirectDone
char * host
Definition: CachePeer.h:66
int netdbHostHops(const char *host)
Definition: net_db.cc:928
uint32_t pad
Definition: ICP.h:46
void forget(PeerSelector *)
removes a PeerSelector from the waiting list
Definition: peer_select.cc:208
void noteIp(const Ip::Address &ip) override
Called when/if nbgethostbyname() discovers a new good IP address.
Definition: peer_select.cc:509
bool wantsMoreDestinations() const
InstanceIdDefinitions(PeerSelector, "PeerSelector")
#define MEMPROXY_CLASS(CLASS)
@ PROTO_WAIS
Definition: ProtocolType.h:30
static char server[MAXLINE]
void handleIcpReply(CachePeer *, const peer_t, icp_common_t *header)
Definition: peer_select.cc:962
static const char * DirectStr[]
Definition: peer_select.cc:70
int n_sent
Definition: PingData.h:38
void selectAllParents()
Adds alive parents. Used as a last resort for never_direct.
Definition: peer_select.cc:858
int timeout
Definition: PingData.h:41
void selectSomeNeighborReplies()
Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
Definition: peer_select.cc:780
@ ACCESS_ALLOWED
Definition: Acl.h:42
@ ACCESS_DENIED
Definition: Acl.h:41
#define DBG_IMPORTANT
Definition: Stream.h:38
hier_code
Definition: hier_code.h:12
struct CachePeer::@27 options
#define DIRECT_MAYBE
Definition: defines.h:48
@ ACCESS_DUNNO
Definition: Acl.h:43
static struct @73 PeerStats
lookup_t peerDigestLookup(CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:703
void noteWaitOver()
calls back all ready PeerSelectors and continues to wait for others
Definition: peer_select.cc:168
const std::optional< SBuf > error
error message (if any)
Definition: LookupDetails.h:39
int timeouts
Definition: peer_select.cc:67
void setPeer(CachePeer *p)
Definition: Connection.cc:130
int forward_max_tries
Definition: SquidConfig.h:351
@ PROTO_HTCP
Definition: ProtocolType.h:33
void selectPinned()
Selects a pinned connection if it exists, is valid, and is allowed.
Definition: peer_select.cc:698
#define DIRECT_UNKNOWN
Definition: defines.h:46
@ CD_SIBLING_HIT
Definition: hier_code.h:29
const hier_code code
selection algorithm
Definition: peer_select.cc:86
const CachePeers & CurrentCachePeers()
Definition: CachePeers.cc:43
uint32_t flags
Definition: ICP.h:45
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:252
acl_access * NeverDirect
Definition: SquidConfig.h:363
CachePeer * carpSelectParent(PeerSelector *ps)
Definition: carp.cc:150
icp_opcode
Definition: icp_opcode.h:13
Ip::Address first_parent_miss
@ CLOSEST_DIRECT
Definition: hier_code.h:23
int checkNetdbDirect()
Definition: peer_select.cc:561
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:744
Ip::Address client_addr
Definition: HttpRequest.h:149
void selectMore()
a single selection loop iteration: attempts to add more destinations
Definition: peer_select.cc:604
void host(const char *src)
Definition: Uri.cc:123
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
@ CD_PARENT_HIT
Definition: hier_code.h:28
CbcPointer< CachePeer > _peer
Definition: peer_select.cc:61
void abortWaiting()
undoes an earlier startWaiting() call
Definition: peer_select.cc:159
int minDirectRtt
Definition: SquidConfig.h:260
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
int basetime
Definition: CachePeer.h:151
@ PROTO_ICP
Definition: ProtocolType.h:31
CachePeer * getRoundRobinParent(PeerSelector *ps)
Definition: neighbors.cc:307
const char * icp_opcode_str[]
CbcPointer< ConnStateData > clientConnectionManager
Definition: HttpRequest.h:232
@ DEFAULT_PARENT
Definition: hier_code.h:17
class SquidConfig Config
Definition: SquidConfig.cc:12
int unsigned int
Definition: stub_fd.cc:19
int timedout
Definition: PingData.h:42

 

Introduction

Documentation

Support

Miscellaneous