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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors