peer_select.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2021 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 "carp.h"
18 #include "client_side.h"
19 #include "dns/LookupDetails.h"
20 #include "errorpage.h"
21 #include "event.h"
22 #include "FwdState.h"
23 #include "globals.h"
24 #include "hier_code.h"
25 #include "htcp.h"
26 #include "http/Stream.h"
27 #include "HttpRequest.h"
28 #include "icmp/net_db.h"
29 #include "ICP.h"
30 #include "ip/tools.h"
31 #include "ipcache.h"
32 #include "neighbors.h"
33 #include "peer_sourcehash.h"
34 #include "peer_userhash.h"
35 #include "PeerSelectState.h"
36 #include "SquidConfig.h"
37 #include "SquidTime.h"
38 #include "Store.h"
39 #include "util.h" // for tvSubDsec() which should be in SquidTime.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;
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->host;
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 
246  if (acl_checklist) {
247  debugs(44, DBG_IMPORTANT, "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 = NULL;
257  }
258 
259  delete lastError;
260 }
261 
262 void
264 {
265  assert(entry);
267  PingMonitor().monitor(this);
269 }
270 
271 void
273 {
274  PingMonitor().forget(this);
275 }
276 
277 static int
278 peerSelectIcpPing(PeerSelector *ps, int direct, StoreEntry * entry)
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 
303 static void
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 
324  request->hier.peer_select_start = current_time;
325 
326 #endif
327 
328  if (selector->entry)
329  selector->entry->lock("peerSelect");
330 
331  selector->selectMore();
332 }
333 
334 void
336 {
337  subscribed = true;
338  peerSelect(this, request, ale, entry);
339  // and wait for noteDestination() and/or noteDestinationsEnd() calls
340 }
341 
342 void
344 {
345  acl_checklist = nullptr;
346  debugs(44, 3, answer);
347  never_direct = answer;
348  switch (answer) {
349  case ACCESS_ALLOWED:
351  direct = DIRECT_NO;
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 
364 void
366 {
367  static_cast<PeerSelector*>(data)->checkNeverDirectDone(answer);
368 }
369 
370 void
372 {
373  acl_checklist = nullptr;
374  debugs(44, 3, answer);
375  always_direct = answer;
376  switch (answer) {
377  case ACCESS_ALLOWED:
379  direct = DIRECT_YES;
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 
392 void
394 {
395  static_cast<PeerSelector*>(data)->checkAlwaysDirectDone(answer);
396 }
397 
400 bool
402 {
403  if (interestedInitiator())
404  return false;
405 
406  debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
407  delete this;
408  return true;
409 }
410 
412 void
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;
442  resolveSelected();
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;
453  resolveSelected();
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 
501 void
503 {
504  /* ignore lookup delays that occurred after the initiator moved on */
505 
506  if (selectionAborted())
507  return;
508 
509  if (!wantsMoreDestinations())
510  return;
511 
512  request->recordLookup(details);
513 }
514 
515 void
517 {
518  if (selectionAborted())
519  return;
520 
521  if (!wantsMoreDestinations())
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  p->remote.port(peer ? peer->http_port : request->url.port());
535  handlePath(p, *servers);
536 }
537 
538 void
540 {
541  if (selectionAborted())
542  return;
543 
544  FwdServer *fs = servers;
545  if (!ia) {
546  debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host()));
547  // discard any previous error.
548  delete lastError;
549  lastError = NULL;
550  if (fs->code == HIER_DIRECT) {
552  lastError->dnsError = details.error;
553  }
554  }
555  // else noteIp() calls have already processed all IPs in *ia
556 
557  servers = fs->next;
558  delete fs;
559 
560  // continue resolving selected peers
561  resolveSelected();
562 }
563 
564 int
566 {
567 #if USE_ICMP
568  CachePeer *p;
569  int myrtt;
570  int myhops;
571 
572  if (direct == DIRECT_NO)
573  return 0;
574 
575  /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
576 
577  myrtt = netdbHostRtt(request->url.host());
578  debugs(44, 3, "MY RTT = " << myrtt << " msec");
579  debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
580 
581  if (myrtt && myrtt <= Config.minDirectRtt)
582  return 1;
583 
584  myhops = netdbHostHops(request->url.host());
585 
586  debugs(44, 3, "MY hops = " << myhops);
587  debugs(44, 3, "minimum_direct_hops = " << Config.minDirectHops);
588 
589  if (myhops && myhops <= Config.minDirectHops)
590  return 1;
591 
593 
594  if (p == NULL)
595  return 0;
596 
597  debugs(44, 3, "closest_parent_miss RTT = " << ping.p_rtt << " msec");
598 
599  if (myrtt && myrtt <= ping.p_rtt)
600  return 1;
601 
602 #endif /* USE_ICMP */
603 
604  return 0;
605 }
606 
607 void
609 {
610  if (selectionAborted())
611  return;
612 
613  debugs(44, 3, request->method << ' ' << request->url.host());
614 
616  if (direct == DIRECT_UNKNOWN) {
617  if (always_direct == ACCESS_DUNNO) {
618  debugs(44, 3, "direct = " << DirectStr[direct] << " (always_direct to be checked)");
621  ch->al = al;
622  acl_checklist = ch;
623  acl_checklist->syncAle(request, nullptr);
625  return;
626  } else if (never_direct == ACCESS_DUNNO) {
627  debugs(44, 3, "direct = " << DirectStr[direct] << " (never_direct to be checked)");
630  ch->al = al;
631  acl_checklist = ch;
632  acl_checklist->syncAle(request, nullptr);
634  return;
635  } else if (request->flags.noDirect) {
637  direct = DIRECT_NO;
638  debugs(44, 3, "direct = " << DirectStr[direct] << " (forced non-direct)");
639  } else if (request->flags.loopDetected) {
641  direct = DIRECT_YES;
642  debugs(44, 3, "direct = " << DirectStr[direct] << " (forwarding loop detected)");
643  } else if (checkNetdbDirect()) {
644  direct = DIRECT_YES;
645  debugs(44, 3, "direct = " << DirectStr[direct] << " (checkNetdbDirect)");
646  } else {
648  debugs(44, 3, "direct = " << DirectStr[direct] << " (default)");
649  }
650 
651  debugs(44, 3, "direct = " << DirectStr[direct]);
652  }
653 
654  if (!entry || entry->ping_status == PING_NONE)
655  selectPinned();
656  if (entry == NULL) {
657  (void) 0;
658  } else if (entry->ping_status == PING_NONE) {
660 
662  return;
663  } else if (entry->ping_status == PING_WAITING) {
667  }
668 
669  switch (direct) {
670 
671  case DIRECT_YES:
673  break;
674 
675  case DIRECT_NO:
678  break;
679 
680  default:
681 
684 
688  }
689 
692 
693  break;
694  }
695 
696  // end peer selection; start resolving selected peers
697  resolveSelected();
698 }
699 
700 bool peerAllowedToUse(const CachePeer *, PeerSelector*);
701 
703 void
705 {
706  // TODO: Avoid all repeated calls. Relying on PING_DONE is not enough.
707  if (!request->pinnedConnection())
708  return;
709 
710  const auto peer = request->pinnedConnection()->pinnedPeer();
711  const auto usePinned = peer ? peerAllowedToUse(peer, this) : (direct != DIRECT_NO);
712  // If the pinned connection is prohibited (for this request) then
713  // the initiator must decide whether it is OK to open a new one instead.
715 
716  addSelection(peer, PINNED);
717  if (entry)
718  entry->ping_status = PING_DONE; // skip ICP
719 }
720 
729 void
731 {
732  CachePeer *p;
735 
736  if (direct == DIRECT_YES) {
738  return;
739  }
740 
741 #if USE_CACHE_DIGESTS
742  if ((p = neighborsDigestSelect(this))) {
743  if (neighborType(p, request->url) == PEER_PARENT)
745  else
747  } else
748 #endif
749  if ((p = netdbClosestParent(this))) {
751  } else if (peerSelectIcpPing(this, direct, entry)) {
752  debugs(44, 3, "Doing ICP pings");
755  entry,
757  this,
759  &ping.timeout);
760  // TODO: Refactor neighborsUdpPing() to guarantee positive timeouts.
761  if (ping.timeout < 0)
762  ping.timeout = 0;
763 
764  if (ping.n_sent == 0)
765  debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
766  debugs(44, 3, ping.n_replies_expected <<
767  " ICP replies expected, RTT " << ping.timeout <<
768  " msec");
769 
770  if (ping.n_replies_expected > 0) {
772  return;
773  }
774  }
775 
776  if (code != HIER_NONE) {
777  assert(p);
778  addSelection(p, code);
779  }
780 
782 }
783 
785 void
787 {
788  CachePeer *p = NULL;
792 
793  if (checkNetdbDirect()) {
795  addSelection(nullptr, code);
796  return;
797  }
798 
799  if ((p = hit)) {
801  } else {
805  } else if (!first_parent_miss.isAnyAddr()) {
808  }
809  }
810  if (p && code != HIER_NONE) {
811  addSelection(p, code);
812  }
813 }
814 
816 void
818 {
819  if (direct == DIRECT_NO)
820  return;
821 
822  /* WAIS is not implemented natively */
824  return;
825 
826  addSelection(nullptr, HIER_DIRECT);
827 }
828 
829 void
831 {
832  CachePeer *p;
834  debugs(44, 3, request->method << ' ' << request->url.host());
835 
836  if (direct == DIRECT_YES)
837  return;
838 
839  if ((p = peerSourceHashSelectParent(this))) {
841 #if USE_AUTH
842  } else if ((p = peerUserHashSelectParent(this))) {
844 #endif
845  } else if ((p = carpSelectParent(this))) {
846  code = CARP;
847  } else if ((p = getRoundRobinParent(this))) {
849  } else if ((p = getWeightedRoundRobinParent(this))) {
851  } else if ((p = getFirstUpParent(this))) {
853  } else if ((p = getDefaultParent(this))) {
855  }
856 
857  if (code != HIER_NONE) {
858  addSelection(p, code);
859  }
860 }
861 
863 void
865 {
866  CachePeer *p;
867  /* Add all alive parents */
868 
869  for (p = Config.peers; p; p = p->next) {
870  /* XXX: neighbors.c lacks a public interface for enumerating
871  * parents to a request so we have to dig some here..
872  */
873 
874  if (neighborType(p, request->url) != PEER_PARENT)
875  continue;
876 
877  if (!peerHTTPOkay(p, this))
878  continue;
879 
881  }
882 
883  /* XXX: should add dead parents here, but it is currently
884  * not possible to find out which parents are dead or which
885  * simply are not configured to handle the request.
886  */
887  /* Add default parent as a last resort */
888  if ((p = getDefaultParent(this))) {
890  }
891 }
892 
893 void
895 {
896  debugs(44, 3, url());
897 
898  // do nothing if ping reply came while handlePingTimeout() was queued
899  if (!entry || entry->ping_status != PING_WAITING)
900  return;
901 
903 
904  if (selectionAborted())
905  return;
906 
907  ++PeerStats.timeouts;
908  ping.timedout = 1;
909  selectMore();
910 }
911 
912 void
914 {
915  selector->handlePingTimeout();
916 }
917 
918 void
920 {
921  memset(&PeerStats, '\0', sizeof(PeerStats));
922 }
923 
924 void
926 {
927  int rtt;
928 
929 #if USE_ICMP
930  if (Config.onoff.query_icmp) {
931  if (header->flags & ICP_FLAG_SRC_RTT) {
932  rtt = header->pad & 0xFFFF;
933  int hops = (header->pad >> 16) & 0xFFFF;
934 
935  if (rtt > 0 && rtt < 0xFFFF)
936  netdbUpdatePeer(request->url, p, rtt, hops);
937 
938  if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
940  ping.p_rtt = rtt;
941  }
942  }
943  }
944 #else
945  (void)header;
946 #endif /* USE_ICMP */
947 
948  /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
949  if (p->options.closest_only)
950  return;
951 
952  /* set FIRST_MISS if there is no CLOSEST parent */
954  return;
955 
956  rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
957 
958  if (rtt < 1)
959  rtt = 1;
960 
961  if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
963  ping.w_rtt = rtt;
964  }
965 }
966 
967 void
969 {
970  icp_opcode op = header->getOpCode();
971  debugs(44, 3, icp_opcode_str[op] << ' ' << url());
972 #if USE_CACHE_DIGESTS && 0
973  /* do cd lookup to count false misses */
974 
975  if (p && request)
977  peerDigestLookup(p, this));
978 
979 #endif
980 
981  ++ping.n_recv;
982 
983  if (op == ICP_MISS || op == ICP_DECHO) {
984  if (type == PEER_PARENT)
985  handleIcpParentMiss(p, header);
986  } else if (op == ICP_HIT) {
987  hit = p;
988  hit_type = type;
989  selectMore();
990  return;
991  }
992 
994  return;
995 
996  selectMore();
997 }
998 
999 #if USE_HTCP
1000 void
1002 {
1003  debugs(44, 3, (htcp->hit ? "HIT" : "MISS") << ' ' << url());
1004  ++ping.n_recv;
1005 
1006  if (htcp->hit) {
1007  hit = p;
1008  hit_type = type;
1009  selectMore();
1010  return;
1011  }
1012 
1013  if (type == PEER_PARENT)
1014  handleHtcpParentMiss(p, htcp);
1015 
1017  return;
1018 
1019  selectMore();
1020 }
1021 
1022 void
1024 {
1025  int rtt;
1026 
1027 #if USE_ICMP
1028  if (Config.onoff.query_icmp) {
1029  if (htcp->cto.rtt > 0) {
1030  rtt = (int) htcp->cto.rtt * 1000;
1031  int hops = (int) htcp->cto.hops * 1000;
1032  netdbUpdatePeer(request->url, p, rtt, hops);
1033 
1034  if (rtt && (ping.p_rtt == 0 || rtt < ping.p_rtt)) {
1036  ping.p_rtt = rtt;
1037  }
1038  }
1039  }
1040 #else
1041  (void)htcp;
1042 #endif /* USE_ICMP */
1043 
1044  /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
1045  if (p->options.closest_only)
1046  return;
1047 
1048  /* set FIRST_MISS if there is no CLOSEST parent */
1050  return;
1051 
1052  rtt = (tvSubMsec(ping.start, current_time) - p->basetime) / p->weight;
1053 
1054  if (rtt < 1)
1055  rtt = 1;
1056 
1057  if (first_parent_miss.isAnyAddr() || rtt < ping.w_rtt) {
1059  ping.w_rtt = rtt;
1060  }
1061 }
1062 
1063 #endif
1064 
1065 void
1066 PeerSelector::HandlePingReply(CachePeer * p, peer_t type, AnyP::ProtocolType proto, void *pingdata, void *data)
1067 {
1068  if (proto == AnyP::PROTO_ICP)
1069  static_cast<PeerSelector*>(data)->handleIcpReply(p, type, static_cast<icp_common_t*>(pingdata));
1070 
1071 #if USE_HTCP
1072 
1073  else if (proto == AnyP::PROTO_HTCP)
1074  static_cast<PeerSelector*>(data)->handleHtcpReply(p, type, static_cast<HtcpReplyData*>(pingdata));
1075 
1076 #endif
1077 
1078  else
1079  debugs(44, DBG_IMPORTANT, "ERROR: ignoring an ICP reply with unknown protocol " << proto);
1080 }
1081 
1082 void
1084 {
1085  // Find the end of the servers list. Bail on a duplicate destination.
1086  auto **serversTail = &servers;
1087  while (const auto server = *serversTail) {
1088  // There can be at most one PINNED destination.
1089  // Non-PINNED destinations are uniquely identified by their CachePeer
1090  // (even though a DIRECT destination might match a cache_peer address).
1091  // TODO: We may still add duplicates because the same peer could have
1092  // been removed from `servers` already (and given to the requestor).
1093  const bool duplicate = (server->code == PINNED) ?
1094  (code == PINNED) : (server->_peer == peer);
1095  if (duplicate) {
1096  debugs(44, 3, "skipping " << PeerSelectionDumper(this, peer, code) <<
1097  "; have " << PeerSelectionDumper(this, server->_peer.get(), server->code));
1098  return;
1099  }
1100  serversTail = &server->next;
1101  }
1102 
1103  debugs(44, 3, "adding " << PeerSelectionDumper(this, peer, code));
1104  *serversTail = new FwdServer(peer, code);
1105 }
1106 
1108  request(nullptr),
1109  entry (NULL),
1110  always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
1111  never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
1112  direct(DIRECT_UNKNOWN),
1113  lastError(NULL),
1114  servers (NULL),
1115  first_parent_miss(),
1116  closest_parent_miss(),
1117  hit(NULL),
1118  hit_type(PEER_NONE),
1119  acl_checklist (NULL),
1120  initiator_(initiator)
1121 {
1122  ; // no local defaults.
1123 }
1124 
1125 const SBuf
1127 {
1128  if (entry)
1129  return SBuf(entry->url());
1130 
1131  if (request)
1132  return request->effectiveRequestUri();
1133 
1134  static const SBuf noUrl("[no URL]");
1135  return noUrl;
1136 }
1137 
1141 {
1142  const auto initiator = initiator_.valid();
1143 
1144  if (!initiator) {
1145  debugs(44, 3, id << " initiator gone");
1146  return nullptr;
1147  }
1148 
1149  if (!initiator->subscribed) {
1150  debugs(44, 3, id << " initiator lost interest");
1151  return nullptr;
1152  }
1153 
1154  debugs(44, 7, id);
1155  return initiator;
1156 }
1157 
1158 bool
1160  const auto maxCount = Config.forward_max_tries;
1161  return maxCount >= 0 && foundPaths < static_cast<size_t>(maxCount);
1162 }
1163 
1164 void
1166 {
1167  ++foundPaths;
1168 
1169  if (path) {
1170  path->peerType = fs.code;
1171  path->setPeer(fs._peer.get());
1172 
1173  // check for a configured outgoing address for this destination...
1174  getOutgoingAddress(request, path);
1175  debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url());
1176  } else
1177  debugs(44, 2, id << " found pinned, destination #" << foundPaths << " for " << url());
1178 
1179  request->hier.ping = ping; // may be updated later
1180 
1181  debugs(44, 2, " always_direct = " << always_direct);
1182  debugs(44, 2, " never_direct = " << never_direct);
1183  debugs(44, 2, " timedout = " << ping.timedout);
1184 
1185  if (const auto initiator = interestedInitiator())
1186  initiator->noteDestination(path);
1187 }
1188 
1190 
1192  n_sent(0),
1193  n_recv(0),
1194  n_replies_expected(0),
1195  timeout(0),
1196  timedout(0),
1197  w_rtt(0),
1198  p_rtt(0),
1199  monitorRegistration(PingMonitor().npos())
1200 {
1201  start.tv_sec = 0;
1202  start.tv_usec = 0;
1203  stop.tv_sec = 0;
1204  stop.tv_usec = 0;
1205 }
1206 
1207 timeval
1209 {
1210  timeval timeInterval;
1211  timeInterval.tv_sec = timeout / 1000;
1212  timeInterval.tv_usec = (timeout % 1000) * 1000;
1213 
1214  timeval result;
1215  tvAdd(result, start, timeInterval);
1216  return result;
1217 }
1218 
void selectSomeDirect()
Adds a "direct" entry if the request can be forwarded to the origin server.
Definition: peer_select.cc:817
virtual ~PeerSelector() override
Definition: peer_select.cc:231
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:150
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
@ 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:1333
bool interceptTproxy
Set for requests handled by a "tproxy" port.
Definition: RequestFlags.h:66
Cbc * valid() const
was set and is valid
Definition: CbcPointer.h:41
void resolveSelected()
A single DNS resolution loop iteration: Converts selected FwdServer to IPs.
Definition: peer_select.cc:413
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: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:306
FwdServer * next
Definition: peer_select.cc:63
void eventDelete(EVH *func, void *arg)
Definition: event.cc:131
CachePeer * getDefaultParent(PeerSelector *ps)
Definition: neighbors.cc:475
int neighborsUdpPing(HttpRequest *request, StoreEntry *entry, IRCB *callback, PeerSelector *ps, int *exprep, int *timeout)
Definition: neighbors.cc:596
@ PARENT_HIT
Definition: hier_code.h:16
struct CachePeer::@30 options
int n_recv
Definition: PingData.h:39
SQUIDCEXTERN double tvSubDsec(struct timeval, struct timeval)
Definition: util.c:46
RequestFlags flags
Definition: HttpRequest.h:141
struct ConnStateData::@39 pinning
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:1098
virtual void noteLookup(const Dns::LookupDetails &details) override
Definition: peer_select.cc:502
@ HIER_DIRECT
Definition: hier_code.h:14
ErrorState * lastError
const char * url() const
Definition: store.cc:1592
void handleHtcpParentMiss(CachePeer *, HtcpReplyData *)
bool peerAccessDenied
cache_peer_access denied pinned connection reuse
Definition: client_side.h:151
virtual void noteIps(const Dns::CachedIps *ips, const Dns::LookupDetails &details) override
Definition: peer_select.cc:539
FwdServer * servers
a linked list of (unresolved) selected peers
@ KEY_PRIVATE
Definition: enums.h:102
bool isAnyAddr() const
Definition: Address.cc:170
void checkAlwaysDirectDone(const Acl::Answer answer)
Definition: peer_select.cc:371
WaitingPeerSelectors selectors
Definition: peer_select.cc:127
CachePeer * pinnedPeer() const
Definition: client_side.h:204
ping_data ping
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:335
String error
error message for unsuccessful lookups; empty otherwise
Definition: LookupDetails.h:29
Definition: SBuf.h:87
static void peerSelect(PeerSelectionInitiator *initiator, HttpRequest *request, AccessLogEntry::Pointer const &al, StoreEntry *entry)
Definition: peer_select.cc:304
@ PING_WAITING
Sent ICP queries to peers and still awaiting responses.
Definition: enums.h:43
struct timeval stop
Definition: PingData.h:37
int neighborsCount(PeerSelector *ps)
Definition: neighbors.cc:274
@ ICP_DECHO
Definition: icp_opcode.h:26
acl_access * AlwaysDirect
Definition: SquidConfig.h:371
bool hostVerified
Definition: RequestFlags.h:64
bool hierarchical
Definition: RequestFlags.h:34
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
void handleHtcpReply(CachePeer *, const peer_t, HtcpReplyData *)
int type
Definition: errorpage.cc:153
uint16_t flags
Definition: Store.h:233
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:132
void HTTPMSGUNLOCK(M *&a)
Definition: Message.h:150
int netdbHostRtt(const char *host)
Definition: net_db.cc:1056
@ CLOSEST_PARENT
Definition: hier_code.h:22
#define DBG_CRITICAL
Definition: Debug.h:40
bool isIPv4() const
Definition: Address.cc:158
void CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
Definition: CodeContext.h:112
@ ORIGINAL_DST
Definition: hier_code.h:36
int hit
Definition: htcp.h:29
#define DBG_IMPORTANT
Definition: Debug.h:41
a helper class to report a selected destination (for debugging)
Definition: peer_select.cc:79
Ip::Address closest_parent_miss
AnyP::UriScheme const & getScheme() const
Definition: Uri.h:67
@ 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
ACLChecklist * acl_checklist
void getOutgoingAddress(HttpRequest *request, const Comm::ConnectionPointer &conn)
Definition: FwdState.cc:1509
#define ICP_FLAG_SRC_RTT
Definition: defines.h:41
CachePeer * getFirstUpParent(PeerSelector *ps)
Definition: neighbors.cc:289
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition: neighbors.cc:116
int weight
Definition: CachePeer.h:125
CachePeer * getWeightedRoundRobinParent(PeerSelector *ps)
Definition: neighbors.cc:356
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:150
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:278
void nbgethostbyname(const char *name, const CbcPointer< IpReceiver > &receiver)
initiate an (often) asynchronous DNS lookup; the receiver gets the results
Definition: ipcache.cc:607
#define NULL
Definition: types.h:166
const CachePeer *const peer
successful selection info
Definition: peer_select.cc:85
void handleIcpParentMiss(CachePeer *, icp_common_t *)
Definition: peer_select.cc:925
PeerSelectionInitiator * interestedInitiator()
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
@ PEER_PARENT
Definition: enums.h:30
@ HIER_NONE
Definition: hier_code.h:13
void recordLookup(const Dns::LookupDetails &detail)
Definition: HttpRequest.cc:588
#define DIRECT_YES
Definition: defines.h:51
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:733
void checkNeverDirectDone(const Acl::Answer answer)
Definition: peer_select.cc:343
Ip::Address local
Definition: Connection.h:144
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
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:243
int minDirectHops
Definition: SquidConfig.h:264
@ USERHASH_PARENT
Definition: hier_code.h:33
bool selectionAborted()
Definition: peer_select.cc:401
int unlock(const char *context)
Definition: store.cc:484
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:345
@ PEER_NONE
Definition: enums.h:28
int tvSubMsec(struct timeval, struct timeval)
Definition: time.cc:37
Ip::Address remote
Definition: Connection.h:147
code related to Squid Instance and PID file management
Definition: Instance.h:17
int p_rtt
Definition: PingData.h:44
Acl::Answer never_direct
struct SquidConfig::@112 accessList
CachePeer * hit
bool closest_only
Definition: CachePeer.h:90
@ PINNED
Definition: hier_code.h:35
int neighbors_do_private_keys
bool peerAllowedToUse(const CachePeer *, PeerSelector *)
Definition: neighbors.cc:139
#define assert(EX)
Definition: assert.h:19
struct HtcpReplyData::cto_t cto
SSL Connection
Definition: Session.h:45
bool intercepted
Definition: RequestFlags.h:62
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:894
HierarchyLogEntry hier
Definition: HttpRequest.h:157
CachePeer * whichPeer(const Ip::Address &from)
Definition: neighbors.cc:97
@ 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:21
@ 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:919
void selectSomeParent()
Definition: peer_select.cc:830
int prefer_direct
Definition: SquidConfig.h:305
void cancelPingTimeoutMonitoring()
terminates ICP ping timeout monitoring
Definition: peer_select.cc:272
CachePeer * neighborsDigestSelect(PeerSelector *ps)
Definition: neighbors.cc:806
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:179
@ ERR_DNS_FAIL
Definition: forward.h:35
static struct @86 PeerStats
static void HandlePingTimeout(PeerSelector *)
called when the given selector should stop expecting ICP ping responses
Definition: peer_select.cc:913
virtual void syncAle(HttpRequest *adaptedRequest, const char *logUri) const =0
assigns uninitialized adapted_request and url ALE components
Comm::ConnectionPointer clientConnection
Definition: Server.h:98
void startPingWaiting()
switches into the PING_WAITING state (and associated timeout monitoring)
Definition: peer_select.cc:263
void peerNoteDigestLookup(HttpRequest *request, CachePeer *p, lookup_t lookup)
Definition: neighbors.cc:873
const SBuf url() const
void selectSomeNeighbor()
Definition: peer_select.cc:730
#define DIRECT_NO
Definition: defines.h:49
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:55
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:1040
uint32_t pad
Definition: ICP.h:46
struct SquidConfig::@111 onoff
void forget(PeerSelector *)
removes a PeerSelector from the waiting list
Definition: peer_select.cc:208
virtual void noteIp(const Ip::Address &ip) override
Called when/if nbgethostbyname() discovers a new good IP address.
Definition: peer_select.cc:516
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:968
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:864
int timeout
Definition: PingData.h:41
void selectSomeNeighborReplies()
Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
Definition: peer_select.cc:786
@ 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:50
@ ACCESS_DUNNO
Definition: Acl.h:105
CachePeer * peers
Definition: SquidConfig.h:252
lookup_t peerDigestLookup(CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:760
void noteWaitOver()
calls back all ready PeerSelectors and continues to wait for others
Definition: peer_select.cc:168
int timeouts
Definition: peer_select.cc:67
void setPeer(CachePeer *p)
Definition: Connection.cc:133
int forward_max_tries
Definition: SquidConfig.h:358
@ PROTO_HTCP
Definition: ProtocolType.h:35
void selectPinned()
Selects a pinned connection if it exists, is valid, and is allowed.
Definition: peer_select.cc:704
#define DIRECT_UNKNOWN
Definition: defines.h:48
@ CD_SIBLING_HIT
Definition: hier_code.h:29
const hier_code code
selection algorithm
Definition: peer_select.cc:86
uint32_t flags
Definition: ICP.h:45
CachePeer * next
Definition: CachePeer.h:153
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition: neighbors.cc:259
acl_access * NeverDirect
Definition: SquidConfig.h:370
CachePeer * carpSelectParent(PeerSelector *ps)
Definition: carp.cc:147
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:565
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:752
Ip::Address client_addr
Definition: HttpRequest.h:149
void selectMore()
a single selection loop iteration: attempts to add more destinations
Definition: peer_select.cc:608
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:61
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:238
void abortWaiting()
undoes an earlier startWaiting() call
Definition: peer_select.cc:159
int minDirectRtt
Definition: SquidConfig.h:265
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:314
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

Web Site Translations

Mirrors