peer_select.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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/InstanceId.h"
14 #include "CachePeer.h"
15 #include "carp.h"
16 #include "client_side.h"
17 #include "dns/LookupDetails.h"
18 #include "errorpage.h"
19 #include "event.h"
20 #include "FwdState.h"
21 #include "globals.h"
22 #include "hier_code.h"
23 #include "htcp.h"
24 #include "http/Stream.h"
25 #include "HttpRequest.h"
26 #include "icmp/net_db.h"
27 #include "ICP.h"
28 #include "ip/tools.h"
29 #include "ipcache.h"
30 #include "neighbors.h"
31 #include "peer_sourcehash.h"
32 #include "peer_userhash.h"
33 #include "PeerSelectState.h"
34 #include "SquidConfig.h"
35 #include "SquidTime.h"
36 #include "Store.h"
37 #include "URL.h"
38 
48 class FwdServer
49 {
51 
52 public:
54  _peer(p),
55  code(c),
56  next(nullptr)
57  {}
58 
59  CbcPointer<CachePeer> _peer; /* NULL --> origin server */
62 };
63 
64 static struct {
65  int timeouts;
66 } PeerStats;
67 
68 static const char *DirectStr[] = {
69  "DIRECT_UNKNOWN",
70  "DIRECT_NO",
71  "DIRECT_MAYBE",
72  "DIRECT_YES"
73 };
74 
75 static void peerSelectFoo(ps_state *);
76 static void peerPingTimeout(void *data);
79 #if USE_HTCP
81 static void peerHandleHtcpReply(CachePeer *, peer_t, HtcpReplyData *, void *);
82 #endif
83 static int peerCheckNetdbDirect(ps_state * psstate);
84 static void peerGetSomeNeighbor(ps_state *);
86 static void peerGetSomeDirect(ps_state *);
87 static void peerGetSomeParent(ps_state *);
88 static void peerGetAllParents(ps_state *);
89 static void peerAddFwdServer(FwdServer **, CachePeer *, hier_code);
90 static void peerSelectPinned(ps_state * ps);
91 
93 
95 {
96  while (servers) {
97  FwdServer *next = servers->next;
98  delete servers;
99  servers = next;
100  }
101 
102  if (entry) {
103  debugs(44, 3, entry->url());
104 
107 
109  }
110 
111  if (acl_checklist) {
112  debugs(44, DBG_IMPORTANT, "calling aclChecklistFree() from ps_state destructor");
113  delete acl_checklist;
114  }
115 
117 
118  if (entry) {
120  entry->unlock("peerSelect");
121  entry = NULL;
122  }
123 
124  delete lastError;
125 }
126 
127 static int
129 {
130  int n;
131  assert(entry);
132  assert(entry->ping_status == PING_NONE);
133  assert(direct != DIRECT_YES);
134  debugs(44, 3, "peerSelectIcpPing: " << entry->url());
135 
136  if (!request->flags.hierarchical && direct != DIRECT_NO)
137  return 0;
138 
140  if (direct != DIRECT_NO)
141  return 0;
142 
143  n = neighborsCount(request);
144 
145  debugs(44, 3, "peerSelectIcpPing: counted " << n << " neighbors");
146 
147  return n;
148 }
149 
150 static void
153  AccessLogEntry::Pointer const &al,
154  StoreEntry * entry)
155 {
156  if (entry)
157  debugs(44, 3, *entry << ' ' << entry->url());
158  else
159  debugs(44, 3, request->method);
160 
161  const auto psstate = new ps_state(initiator);
162 
163  psstate->request = request;
164  HTTPMSGLOCK(psstate->request);
165  psstate->al = al;
166 
167  psstate->entry = entry;
168 
169 #if USE_CACHE_DIGESTS
170 
172 
173 #endif
174 
175  if (psstate->entry)
176  psstate->entry->lock("peerSelect");
177 
178  peerSelectFoo(psstate);
179 }
180 
181 void
183 {
184  subscribed = true;
185  peerSelect(this, request, ale, entry);
186  // and wait for noteDestination() and/or noteDestinationsEnd() calls
187 }
188 
189 static void
191 {
192  ps_state *psstate = (ps_state *) data;
193  psstate->acl_checklist = NULL;
194  debugs(44, 3, "peerCheckNeverDirectDone: " << answer);
195  psstate->never_direct = answer;
196  switch (answer) {
197  case ACCESS_ALLOWED:
199  psstate->direct = DIRECT_NO;
200  debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (never_direct allow)");
201  break;
202  case ACCESS_DENIED: // not relevant.
203  case ACCESS_DUNNO: // not relevant.
204  break;
206  debugs(44, DBG_IMPORTANT, "WARNING: never_direct resulted in " << answer << ". Username ACLs are not reliable here.");
207  break;
208  }
209  peerSelectFoo(psstate);
210 }
211 
212 static void
214 {
215  ps_state *psstate = (ps_state *)data;
216  psstate->acl_checklist = NULL;
217  debugs(44, 3, "peerCheckAlwaysDirectDone: " << answer);
218  psstate->always_direct = answer;
219  switch (answer) {
220  case ACCESS_ALLOWED:
222  psstate->direct = DIRECT_YES;
223  debugs(44, 3, HERE << "direct = " << DirectStr[psstate->direct] << " (always_direct allow)");
224  break;
225  case ACCESS_DENIED: // not relevant.
226  case ACCESS_DUNNO: // not relevant.
227  break;
229  debugs(44, DBG_IMPORTANT, "WARNING: always_direct resulted in " << answer << ". Username ACLs are not reliable here.");
230  break;
231  }
232  peerSelectFoo(psstate);
233 }
234 
237 static bool
239 {
240  if (psstate->interestedInitiator())
241  return false;
242 
243  debugs(44, 3, "Aborting peer selection: Initiator gone or lost interest.");
244  delete psstate;
245  return true;
246 }
247 
248 void
250 {
251  if (peerSelectionAborted(psstate))
252  return;
253 
254  FwdServer *fs = psstate->servers;
255 
256  // Bug 3243: CVE 2009-0801
257  // Bypass of browser same-origin access control in intercepted communication
258  // To resolve this we must use only the original client destination when going DIRECT
259  // on intercepted traffic which failed Host verification
260  const HttpRequest *req = psstate->request;
261  const bool isIntercepted = !req->flags.redirected &&
262  (req->flags.intercepted || req->flags.interceptTproxy);
263  const bool useOriginalDst = Config.onoff.client_dst_passthru || !req->flags.hostVerified;
264  const bool choseDirect = fs && fs->code == HIER_DIRECT;
265  if (isIntercepted && useOriginalDst && choseDirect) {
266  // check the client is still around before using any of its details
267  if (req->clientConnectionManager.valid()) {
268  // construct a "result" adding the ORIGINAL_DST to the set instead of DIRECT
271  fs->code = ORIGINAL_DST; // fs->code is DIRECT. This fixes the display.
272  psstate->handlePath(p, *fs);
273  }
274 
275  // clear the used fs and continue
276  psstate->servers = fs->next;
277  delete fs;
278  peerSelectDnsPaths(psstate);
279  return;
280  }
281 
282  // convert the list of FwdServer destinations into destinations IP addresses
283  if (fs && psstate->wantsMoreDestinations()) {
284  // send the next one off for DNS lookup.
285  const char *host = fs->_peer.valid() ? fs->_peer->host : psstate->request->url.host();
286  debugs(44, 2, "Find IP destination for: " << psstate->url() << "' via " << host);
287  Dns::nbgethostbyname(host, psstate);
288  return;
289  }
290 
291  // Bug 3605: clear any extra listed FwdServer destinations, when the options exceeds max_foward_tries.
292  // due to the allocation method of fs, we must deallocate each manually.
293  // TODO: use a std::list so we can get the size and abort adding whenever the selection loops reach Config.forward_max_tries
294  if (fs) {
295  assert(fs == psstate->servers);
296  while (fs) {
297  psstate->servers = fs->next;
298  delete fs;
299  fs = psstate->servers;
300  }
301  }
302 
303  // done with DNS lookups. pass back to caller
304 
305  debugs(44, 2, psstate->id << " found all " << psstate->foundPaths << " destinations for " << psstate->url());
306  debugs(44, 2, " always_direct = " << psstate->always_direct);
307  debugs(44, 2, " never_direct = " << psstate->never_direct);
308  debugs(44, 2, " timedout = " << psstate->ping.timedout);
309 
310  psstate->ping.stop = current_time;
311  psstate->request->hier.ping = psstate->ping; // final result
312 
313  if (psstate->lastError && psstate->foundPaths) {
314  // nobody cares about errors if we found destinations despite them
315  debugs(44, 3, "forgetting the last error");
316  delete psstate->lastError;
317  psstate->lastError = nullptr;
318  }
319 
320  if (const auto initiator = psstate->interestedInitiator())
321  initiator->noteDestinationsEnd(psstate->lastError);
322  psstate->lastError = nullptr; // initiator owns the ErrorState object now
323  delete psstate;
324 }
325 
326 void
328 {
329  /* ignore lookup delays that occurred after the initiator moved on */
330 
331  if (peerSelectionAborted(this))
332  return;
333 
334  if (!wantsMoreDestinations())
335  return;
336 
337  request->recordLookup(details);
338 }
339 
340 void
342 {
343  if (peerSelectionAborted(this))
344  return;
345 
346  if (!wantsMoreDestinations())
347  return;
348 
349  const auto peer = servers->_peer.valid();
350 
351  // for TPROXY spoofing, we must skip unusable addresses
352  if (request->flags.spoofClientIp && !(peer && peer->options.no_tproxy) ) {
353  if (ip.isIPv4() != request->client_addr.isIPv4())
354  return; // cannot spoof the client address on this link
355  }
356 
358  p->remote = ip;
359  p->remote.port(peer ? peer->http_port : request->url.port());
360  handlePath(p, *servers);
361 }
362 
363 void
365 {
366  if (peerSelectionAborted(this))
367  return;
368 
369  FwdServer *fs = servers;
370  if (!ia) {
371  debugs(44, 3, "Unknown host: " << (fs->_peer.valid() ? fs->_peer->host : request->url.host()));
372  // discard any previous error.
373  delete lastError;
374  lastError = NULL;
375  if (fs->code == HIER_DIRECT) {
377  lastError->dnsError = details.error;
378  }
379  }
380  // else noteIp() calls have already processed all IPs in *ia
381 
382  servers = fs->next;
383  delete fs;
384 
385  // see if more paths can be found
386  peerSelectDnsPaths(this);
387 }
388 
389 static int
391 {
392 #if USE_ICMP
393  CachePeer *p;
394  int myrtt;
395  int myhops;
396 
397  if (psstate->direct == DIRECT_NO)
398  return 0;
399 
400  /* base lookup on RTT and Hops if ICMP NetDB is enabled. */
401 
402  myrtt = netdbHostRtt(psstate->request->url.host());
403  debugs(44, 3, "MY RTT = " << myrtt << " msec");
404  debugs(44, 3, "minimum_direct_rtt = " << Config.minDirectRtt << " msec");
405 
406  if (myrtt && myrtt <= Config.minDirectRtt)
407  return 1;
408 
409  myhops = netdbHostHops(psstate->request->url.host());
410 
411  debugs(44, 3, "peerCheckNetdbDirect: MY hops = " << myhops);
412  debugs(44, 3, "peerCheckNetdbDirect: minimum_direct_hops = " << Config.minDirectHops);
413 
414  if (myhops && myhops <= Config.minDirectHops)
415  return 1;
416 
417  p = whichPeer(psstate->closest_parent_miss);
418 
419  if (p == NULL)
420  return 0;
421 
422  debugs(44, 3, "peerCheckNetdbDirect: closest_parent_miss RTT = " << psstate->ping.p_rtt << " msec");
423 
424  if (myrtt && myrtt <= psstate->ping.p_rtt)
425  return 1;
426 
427 #endif /* USE_ICMP */
428 
429  return 0;
430 }
431 
432 static void
434 {
435  if (peerSelectionAborted(ps))
436  return;
437 
438  StoreEntry *entry = ps->entry;
439  HttpRequest *request = ps->request;
440  debugs(44, 3, request->method << ' ' << request->url.host());
441 
443  if (ps->direct == DIRECT_UNKNOWN) {
444  if (ps->always_direct == ACCESS_DUNNO) {
445  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (always_direct to be checked)");
448  ch->al = ps->al;
449  ps->acl_checklist = ch;
451  return;
452  } else if (ps->never_direct == ACCESS_DUNNO) {
453  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (never_direct to be checked)");
456  ch->al = ps->al;
457  ps->acl_checklist = ch;
459  return;
460  } else if (request->flags.noDirect) {
462  ps->direct = DIRECT_NO;
463  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forced non-direct)");
464  } else if (request->flags.loopDetected) {
466  ps->direct = DIRECT_YES;
467  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (forwarding loop detected)");
468  } else if (peerCheckNetdbDirect(ps)) {
469  ps->direct = DIRECT_YES;
470  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (checkNetdbDirect)");
471  } else {
472  ps->direct = DIRECT_MAYBE;
473  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct] << " (default)");
474  }
475 
476  debugs(44, 3, "peerSelectFoo: direct = " << DirectStr[ps->direct]);
477  }
478 
479  if (!entry || entry->ping_status == PING_NONE)
480  peerSelectPinned(ps);
481  if (entry == NULL) {
482  (void) 0;
483  } else if (entry->ping_status == PING_NONE) {
485 
486  if (entry->ping_status == PING_WAITING)
487  return;
488  } else if (entry->ping_status == PING_WAITING) {
490  entry->ping_status = PING_DONE;
491  }
492 
493  switch (ps->direct) {
494 
495  case DIRECT_YES:
496  peerGetSomeDirect(ps);
497  break;
498 
499  case DIRECT_NO:
500  peerGetSomeParent(ps);
501  peerGetAllParents(ps);
502  break;
503 
504  default:
505 
507  peerGetSomeDirect(ps);
508 
510  peerGetSomeParent(ps);
511  peerGetAllParents(ps);
512  }
513 
515  peerGetSomeDirect(ps);
516 
517  break;
518  }
519 
520  // resolve the possible peers
521  peerSelectDnsPaths(ps);
522 }
523 
525 
531 static void
533 {
534  HttpRequest *request = ps->request;
535  if (!request->pinnedConnection())
536  return;
537  CachePeer *pear = request->pinnedConnection()->pinnedPeer();
538  if (Comm::IsConnOpen(request->pinnedConnection()->validatePinnedConnection(request, pear))) {
539  if (pear && peerAllowedToUse(pear, request)) {
540  peerAddFwdServer(&ps->servers, pear, PINNED);
541  if (ps->entry)
542  ps->entry->ping_status = PING_DONE; /* Skip ICP */
543  } else if (!pear && ps->direct != DIRECT_NO) {
545  if (ps->entry)
546  ps->entry->ping_status = PING_DONE; /* Skip ICP */
547  }
548  }
549 }
550 
561 static void
563 {
564  StoreEntry *entry = ps->entry;
565  HttpRequest *request = ps->request;
566  CachePeer *p;
568  assert(entry->ping_status == PING_NONE);
569 
570  if (ps->direct == DIRECT_YES) {
571  entry->ping_status = PING_DONE;
572  return;
573  }
574 
575 #if USE_CACHE_DIGESTS
576  if ((p = neighborsDigestSelect(request))) {
577  if (neighborType(p, request->url) == PEER_PARENT)
578  code = CD_PARENT_HIT;
579  else
580  code = CD_SIBLING_HIT;
581  } else
582 #endif
583  if ((p = netdbClosestParent(request))) {
584  code = CLOSEST_PARENT;
585  } else if (peerSelectIcpPing(request, ps->direct, entry)) {
586  debugs(44, 3, "peerSelect: Doing ICP pings");
587  ps->ping.start = current_time;
588  ps->ping.n_sent = neighborsUdpPing(request,
589  entry,
591  ps,
593  &ps->ping.timeout);
594 
595  if (ps->ping.n_sent == 0)
596  debugs(44, DBG_CRITICAL, "WARNING: neighborsUdpPing returned 0");
597  debugs(44, 3, "peerSelect: " << ps->ping.n_replies_expected <<
598  " ICP replies expected, RTT " << ps->ping.timeout <<
599  " msec");
600 
601  if (ps->ping.n_replies_expected > 0) {
602  entry->ping_status = PING_WAITING;
603  eventAdd("peerPingTimeout",
605  ps,
606  0.001 * ps->ping.timeout,
607  0);
608  return;
609  }
610  }
611 
612  if (code != HIER_NONE) {
613  assert(p);
614  debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
615  peerAddFwdServer(&ps->servers, p, code);
616  }
617 
618  entry->ping_status = PING_DONE;
619 }
620 
621 /*
622  * peerGetSomeNeighborReplies
623  *
624  * Selects a neighbor (parent or sibling) based on ICP/HTCP replies.
625  */
626 static void
628 {
629  HttpRequest *request = ps->request;
630  CachePeer *p = NULL;
633  assert(ps->direct != DIRECT_YES);
634 
635  if (peerCheckNetdbDirect(ps)) {
636  code = CLOSEST_DIRECT;
637  debugs(44, 3, hier_code_str[code] << "/" << request->url.host());
638  peerAddFwdServer(&ps->servers, NULL, code);
639  return;
640  }
641 
642  if ((p = ps->hit)) {
643  code = ps->hit_type == PEER_PARENT ? PARENT_HIT : SIBLING_HIT;
644  } else {
645  if (!ps->closest_parent_miss.isAnyAddr()) {
647  code = CLOSEST_PARENT_MISS;
648  } else if (!ps->first_parent_miss.isAnyAddr()) {
649  p = whichPeer(ps->first_parent_miss);
650  code = FIRST_PARENT_MISS;
651  }
652  }
653  if (p && code != HIER_NONE) {
654  debugs(44, 3, hier_code_str[code] << "/" << p->host);
655  peerAddFwdServer(&ps->servers, p, code);
656  }
657 }
658 
659 /*
660  * peerGetSomeDirect
661  *
662  * Simply adds a 'direct' entry to the FwdServers list if this
663  * request can be forwarded directly to the origin server
664  */
665 static void
667 {
668  if (ps->direct == DIRECT_NO)
669  return;
670 
671  /* WAIS is not implemented natively */
672  if (ps->request->url.getScheme() == AnyP::PROTO_WAIS)
673  return;
674 
676 }
677 
678 static void
680 {
681  CachePeer *p;
682  HttpRequest *request = ps->request;
684  debugs(44, 3, request->method << ' ' << request->url.host());
685 
686  if (ps->direct == DIRECT_YES)
687  return;
688 
689  if ((p = peerSourceHashSelectParent(request))) {
690  code = SOURCEHASH_PARENT;
691 #if USE_AUTH
692  } else if ((p = peerUserHashSelectParent(request))) {
693  code = USERHASH_PARENT;
694 #endif
695  } else if ((p = carpSelectParent(request))) {
696  code = CARP;
697  } else if ((p = getRoundRobinParent(request))) {
698  code = ROUNDROBIN_PARENT;
699  } else if ((p = getWeightedRoundRobinParent(request))) {
700  code = ROUNDROBIN_PARENT;
701  } else if ((p = getFirstUpParent(request))) {
702  code = FIRSTUP_PARENT;
703  } else if ((p = getDefaultParent(request))) {
704  code = DEFAULT_PARENT;
705  }
706 
707  if (code != HIER_NONE) {
708  debugs(44, 3, "peerSelect: " << hier_code_str[code] << "/" << p->host);
709  peerAddFwdServer(&ps->servers, p, code);
710  }
711 }
712 
713 /* Adds alive parents. Used as a last resort for never_direct.
714  */
715 static void
717 {
718  CachePeer *p;
719  HttpRequest *request = ps->request;
720  /* Add all alive parents */
721 
722  for (p = Config.peers; p; p = p->next) {
723  /* XXX: neighbors.c lacks a public interface for enumerating
724  * parents to a request so we have to dig some here..
725  */
726 
727  if (neighborType(p, request->url) != PEER_PARENT)
728  continue;
729 
730  if (!peerHTTPOkay(p, request))
731  continue;
732 
733  debugs(15, 3, "peerGetAllParents: adding alive parent " << p->host);
734 
736  }
737 
738  /* XXX: should add dead parents here, but it is currently
739  * not possible to find out which parents are dead or which
740  * simply are not configured to handle the request.
741  */
742  /* Add default parent as a last resort */
743  if ((p = getDefaultParent(request))) {
745  }
746 }
747 
748 static void
750 {
751  ps_state *psstate = (ps_state *)data;
752  debugs(44, 3, psstate->url());
753 
754  if (StoreEntry *entry = psstate->entry)
755  entry->ping_status = PING_DONE;
756 
757  if (peerSelectionAborted(psstate))
758  return;
759 
760  ++PeerStats.timeouts;
761  psstate->ping.timedout = 1;
762  peerSelectFoo(psstate);
763 }
764 
765 void
767 {
768  memset(&PeerStats, '\0', sizeof(PeerStats));
769 }
770 
771 static void
773 {
774  int rtt;
775 
776 #if USE_ICMP
777  if (Config.onoff.query_icmp) {
778  if (header->flags & ICP_FLAG_SRC_RTT) {
779  rtt = header->pad & 0xFFFF;
780  int hops = (header->pad >> 16) & 0xFFFF;
781 
782  if (rtt > 0 && rtt < 0xFFFF)
783  netdbUpdatePeer(ps->request->url, p, rtt, hops);
784 
785  if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
786  ps->closest_parent_miss = p->in_addr;
787  ps->ping.p_rtt = rtt;
788  }
789  }
790  }
791 #endif /* USE_ICMP */
792 
793  /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
794  if (p->options.closest_only)
795  return;
796 
797  /* set FIRST_MISS if there is no CLOSEST parent */
798  if (!ps->closest_parent_miss.isAnyAddr())
799  return;
800 
801  rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
802 
803  if (rtt < 1)
804  rtt = 1;
805 
806  if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
807  ps->first_parent_miss = p->in_addr;
808  ps->ping.w_rtt = rtt;
809  }
810 }
811 
812 static void
814 {
815  ps_state *psstate = (ps_state *)data;
816  icp_opcode op = header->getOpCode();
817  debugs(44, 3, "peerHandleIcpReply: " << icp_opcode_str[op] << " " << psstate->url() );
818 #if USE_CACHE_DIGESTS && 0
819  /* do cd lookup to count false misses */
820 
821  if (p && request)
823  peerDigestLookup(p, request, psstate->entry));
824 
825 #endif
826 
827  ++ psstate->ping.n_recv;
828 
829  if (op == ICP_MISS || op == ICP_DECHO) {
830  if (type == PEER_PARENT)
831  peerIcpParentMiss(p, header, psstate);
832  } else if (op == ICP_HIT) {
833  psstate->hit = p;
834  psstate->hit_type = type;
835  peerSelectFoo(psstate);
836  return;
837  }
838 
839  if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
840  return;
841 
842  peerSelectFoo(psstate);
843 }
844 
845 #if USE_HTCP
846 static void
848 {
849  ps_state *psstate = (ps_state *)data;
850  debugs(44, 3, "" << (htcp->hit ? "HIT" : "MISS") << " " << psstate->url());
851  ++ psstate->ping.n_recv;
852 
853  if (htcp->hit) {
854  psstate->hit = p;
855  psstate->hit_type = type;
856  peerSelectFoo(psstate);
857  return;
858  }
859 
860  if (type == PEER_PARENT)
861  peerHtcpParentMiss(p, htcp, psstate);
862 
863  if (psstate->ping.n_recv < psstate->ping.n_replies_expected)
864  return;
865 
866  peerSelectFoo(psstate);
867 }
868 
869 static void
871 {
872  int rtt;
873 
874 #if USE_ICMP
875  if (Config.onoff.query_icmp) {
876  if (htcp->cto.rtt > 0) {
877  rtt = (int) htcp->cto.rtt * 1000;
878  int hops = (int) htcp->cto.hops * 1000;
879  netdbUpdatePeer(ps->request->url, p, rtt, hops);
880 
881  if (rtt && (ps->ping.p_rtt == 0 || rtt < ps->ping.p_rtt)) {
882  ps->closest_parent_miss = p->in_addr;
883  ps->ping.p_rtt = rtt;
884  }
885  }
886  }
887 #endif /* USE_ICMP */
888 
889  /* if closest-only is set, then don't allow FIRST_PARENT_MISS */
890  if (p->options.closest_only)
891  return;
892 
893  /* set FIRST_MISS if there is no CLOSEST parent */
894  if (!ps->closest_parent_miss.isAnyAddr())
895  return;
896 
897  rtt = (tvSubMsec(ps->ping.start, current_time) - p->basetime) / p->weight;
898 
899  if (rtt < 1)
900  rtt = 1;
901 
902  if (ps->first_parent_miss.isAnyAddr() || rtt < ps->ping.w_rtt) {
903  ps->first_parent_miss = p->in_addr;
904  ps->ping.w_rtt = rtt;
905  }
906 }
907 
908 #endif
909 
910 static void
912 {
913  if (proto == AnyP::PROTO_ICP)
914  peerHandleIcpReply(p, type, (icp_common_t *)pingdata, data);
915 
916 #if USE_HTCP
917 
918  else if (proto == AnyP::PROTO_HTCP)
919  peerHandleHtcpReply(p, type, (HtcpReplyData *)pingdata, data);
920 
921 #endif
922 
923  else
924  debugs(44, DBG_IMPORTANT, "peerHandlePingReply: unknown protocol " << proto);
925 }
926 
927 static void
929 {
930  debugs(44, 5, "peerAddFwdServer: adding " <<
931  (p ? p->host : "DIRECT") << " " <<
932  hier_code_str[code] );
933  FwdServer *fs = new FwdServer(p, code);
934 
935  while (*FSVR)
936  FSVR = &(*FSVR)->next;
937 
938  *FSVR = fs;
939 }
940 
942  request(nullptr),
943  entry (NULL),
944  always_direct(Config.accessList.AlwaysDirect?ACCESS_DUNNO:ACCESS_DENIED),
945  never_direct(Config.accessList.NeverDirect?ACCESS_DUNNO:ACCESS_DENIED),
946  direct(DIRECT_UNKNOWN),
947  lastError(NULL),
948  servers (NULL),
949  first_parent_miss(),
950  closest_parent_miss(),
951  hit(NULL),
952  hit_type(PEER_NONE),
953  acl_checklist (NULL),
954  initiator_(initiator)
955 {
956  ; // no local defaults.
957 }
958 
959 const SBuf
961 {
962  if (entry)
963  return SBuf(entry->url());
964 
965  if (request)
966  return request->effectiveRequestUri();
967 
968  static const SBuf noUrl("[no URL]");
969  return noUrl;
970 }
971 
975 {
976  const auto initiator = initiator_.valid();
977 
978  if (!initiator) {
979  debugs(44, 3, id << " initiator gone");
980  return nullptr;
981  }
982 
983  if (!initiator->subscribed) {
984  debugs(44, 3, id << " initiator lost interest");
985  return nullptr;
986  }
987 
988  debugs(44, 7, id);
989  return initiator;
990 }
991 
992 bool
994  const auto maxCount = Config.forward_max_tries;
995  return maxCount >= 0 && foundPaths <
996  static_cast<std::make_unsigned<decltype(maxCount)>::type>(maxCount);
997 }
998 
999 void
1001 {
1002  ++foundPaths;
1003 
1004  path->peerType = fs.code;
1005  path->setPeer(fs._peer.get());
1006 
1007  // check for a configured outgoing address for this destination...
1008  getOutgoingAddress(request, path);
1009 
1010  request->hier.ping = ping; // may be updated later
1011 
1012  debugs(44, 2, id << " found " << path << ", destination #" << foundPaths << " for " << url());
1013  debugs(44, 2, " always_direct = " << always_direct);
1014  debugs(44, 2, " never_direct = " << never_direct);
1015  debugs(44, 2, " timedout = " << ping.timedout);
1016 
1017  if (const auto initiator = interestedInitiator())
1018  initiator->noteDestination(path);
1019 }
1020 
1021 InstanceIdDefinitions(ps_state, "PeerSelector");
1022 
1024  n_sent(0),
1025  n_recv(0),
1026  n_replies_expected(0),
1027  timeout(0),
1028  timedout(0),
1029  w_rtt(0),
1030  p_rtt(0)
1031 {
1032  start.tv_sec = 0;
1033  start.tv_usec = 0;
1034  stop.tv_sec = 0;
1035  stop.tv_usec = 0;
1036 }
1037 
void recordLookup(const Dns::LookupDetails &detail)
Definition: HttpRequest.cc:587
void nonBlockingCheck(ACLCB *callback, void *callback_data)
Definition: Checklist.cc:238
void getOutgoingAddress(HttpRequest *request, Comm::ConnectionPointer conn)
Definition: FwdState.cc:1293
HierarchyLogEntry hier
Definition: HttpRequest.h:145
#define DIRECT_YES
Definition: defines.h:83
bool interceptTproxy
Set for requests handled by a "tproxy" port.
Definition: RequestFlags.h:68
void peerSelectInit(void)
Definition: peer_select.cc:766
static struct @88 PeerStats
virtual void noteIp(const Ip::Address &ip) override
Called when/if nbgethostbyname() discovers a new good IP address.
Definition: peer_select.cc:341
ConnStateData * pinnedConnection()
Definition: HttpRequest.cc:654
hier_code code
Definition: peer_select.cc:60
int timeout
Definition: PingData.h:24
#define assert(EX)
Definition: assert.h:17
int n_recv
Definition: PingData.h:22
int timedout
Definition: PingData.h:25
int peerHTTPOkay(const CachePeer *p, HttpRequest *request)
Definition: neighbors.cc:248
static void peerCheckAlwaysDirectDone(allow_t answer, void *data)
Definition: peer_select.cc:213
bool hierarchical
Definition: RequestFlags.h:34
int neighbors_do_private_keys
allow_t never_direct
int unlock(const char *context)
Definition: store.cc:485
ACLChecklist * acl_checklist
CachePeer * next
Definition: CachePeer.h:154
static void peerGetSomeNeighbor(ps_state *)
Definition: peer_select.cc:562
ErrorState * lastError
bool loopDetected
Definition: RequestFlags.h:36
int type
Definition: errorpage.cc:79
static IRCB peerHandlePingReply
Definition: peer_select.cc:77
Definition: Acl.h:113
Definition: SBuf.h:87
virtual void noteIps(const Dns::CachedIps *ips, const Dns::LookupDetails &details) override
Definition: peer_select.cc:364
HttpRequestMethod method
Definition: HttpRequest.h:102
int prefer_direct
Definition: SquidConfig.h:296
void handlePath(Comm::ConnectionPointer &path, FwdServer &fs)
processes a newly discovered/finalized path
CachePeer * peers
Definition: SquidConfig.h:243
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
MEMPROXY_CLASS(FwdServer)
int client_dst_passthru
Definition: SquidConfig.h:338
#define DIRECT_NO
Definition: defines.h:81
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
Definition: hier_code.h:31
CachePeer * getRoundRobinParent(HttpRequest *request)
Definition: neighbors.cc:300
CachePeer * getWeightedRoundRobinParent(HttpRequest *request)
Definition: neighbors.cc:339
void port(unsigned short p)
Definition: URL.h:74
encapsulates DNS lookup results
Definition: LookupDetails.h:20
void HTTPMSGLOCK(Http::Message *a)
Definition: Message.h:154
int hit
Definition: htcp.h:25
int netdbHostRtt(const char *host)
Definition: net_db.cc:1051
static int peerSelectIcpPing(HttpRequest *request, int direct, StoreEntry *entry)
Definition: peer_select.cc:128
FwdServer * servers
temporary linked list of peers we will pass back.
Interface for those who need a list of peers to forward a request to.
#define DBG_CRITICAL
Definition: Debug.h:44
char * p
Definition: membanger.c:43
struct timeval stop
Definition: PingData.h:20
bool hostVerified
Definition: RequestFlags.h:66
static void peerSelectPinned(ps_state *ps)
Definition: peer_select.cc:532
struct timeval current_time
Definition: stub_time.cc:15
struct timeval peer_select_start
int timeouts
Definition: peer_select.cc:65
CachePeer * pinnedPeer() const
Definition: client_side.h:196
static void peerGetSomeNeighborReplies(ps_state *)
Definition: peer_select.cc:627
#define DIRECT_UNKNOWN
Definition: defines.h:80
bool isAnyAddr() const
Definition: Address.cc:163
static const char * DirectStr[]
Definition: peer_select.cc:68
static void peerGetSomeDirect(ps_state *)
Definition: peer_select.cc:666
CachePeer * peerUserHashSelectParent(HttpRequest *request)
void host(const char *src)
Definition: url.cc:47
int n_replies_expected
Definition: PingData.h:23
CachePeer * netdbClosestParent(HttpRequest *request)
Definition: net_db.cc:1307
#define DIRECT_MAYBE
Definition: defines.h:82
icp_opcode getOpCode() const
Definition: icp_v2.cc:101
struct HtcpReplyData::cto_t cto
CachePeer * getFirstUpParent(HttpRequest *request)
Definition: neighbors.cc:278
int tvSubMsec(struct timeval, struct timeval)
Definition: stub_time.cc:20
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:24
ping_status_t ping_status
Definition: Store.h:183
static void peerGetAllParents(ps_state *)
Definition: peer_select.cc:716
void const char HLPCB void * data
Definition: stub_helper.cc:16
bool subscribed
whether noteDestination() and noteDestinationsEnd() calls are allowed
static void peerPingTimeout(void *data)
Definition: peer_select.cc:749
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
int minDirectRtt
Definition: SquidConfig.h:256
static void peerCheckNeverDirectDone(allow_t answer, void *data)
Definition: peer_select.cc:190
#define DBG_IMPORTANT
Definition: Debug.h:45
void netdbUpdatePeer(const URL &url, CachePeer *e, int irtt, int ihops)
Definition: net_db.cc:1086
StoreEntry * entry
Ip::Address client_addr
Definition: HttpRequest.h:137
AnyP::UriScheme const & getScheme() const
Definition: URL.h:58
const InstanceId< ps_state > id
unique identification in worker log
Cbc * valid() const
was set and is valid
Definition: CbcPointer.h:41
static void peerAddFwdServer(FwdServer **, CachePeer *, hier_code)
Definition: peer_select.cc:928
int forward_max_tries
Definition: SquidConfig.h:349
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:673
Ip::Address in_addr
Definition: CachePeer.h:44
ps_state(PeerSelectionInitiator *initiator)
Definition: peer_select.cc:941
String error
error message for unsuccessful lookups; empty otherwise
Definition: LookupDetails.h:29
CbcPointer< ConnStateData > clientConnectionManager
Definition: HttpRequest.h:218
void nbgethostbyname(const char *name, const CbcPointer< IpReceiver > &receiver)
initiate an (often) asynchronous DNS lookup; the receiver gets the results
Definition: ipcache.cc:607
struct SquidConfig::@113 accessList
static void peerHandleHtcpReply(CachePeer *, peer_t, HtcpReplyData *, void *)
Definition: peer_select.cc:847
acl_access * NeverDirect
Definition: SquidConfig.h:359
size_t foundPaths
number of unique destinations identified so far
uint16_t flags
Definition: Store.h:173
icp_opcode
Definition: icp_opcode.h:13
CbcPointer< CachePeer > _peer
Definition: peer_select.cc:59
static bool peerSelectionAborted(ps_state *psstate)
Definition: peer_select.cc:238
const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request, const CachePeer *peer)
bool isIPv4() const
Definition: Address.cc:151
URL url
the request URI
Definition: HttpRequest.h:103
static void peerSelectFoo(ps_state *)
Definition: peer_select.cc:433
const char * url() const
Definition: store.cc:1672
virtual ~ps_state() override
Definition: peer_select.cc:94
void IRCB(CachePeer *, peer_t, AnyP::ProtocolType, void *, void *data)
Definition: typedefs.h:22
uint32_t flags
Definition: ICP.h:42
unsigned char code
Definition: html_quote.c:20
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:147
struct SquidConfig::@112 onoff
void eventDelete(EVH *func, void *arg)
Definition: event.cc:131
unsigned short port() const
Definition: Address.cc:786
CachePeer * whichPeer(const Ip::Address &from)
Definition: neighbors.cc:95
FwdServer(CachePeer *p, hier_code c)
Definition: peer_select.cc:53
Ip::Address local
Definition: Connection.h:135
static void peerGetSomeParent(ps_state *)
Definition: peer_select.cc:679
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
Initiator initiator_
recipient of the destinations we select; use interestedInitiator() to access
struct timeval start
Definition: PingData.h:18
CachePeer * neighborsDigestSelect(HttpRequest *request)
Definition: neighbors.cc:779
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:109
CachePeer * peerSourceHashSelectParent(HttpRequest *request)
int netdbHostHops(const char *host)
Definition: net_db.cc:1036
RequestFlags flags
Definition: HttpRequest.h:129
lookup_t peerDigestLookup(CachePeer *p, HttpRequest *request)
Definition: neighbors.cc:737
void setPeer(CachePeer *p)
Definition: Connection.cc:106
static void peerHtcpParentMiss(CachePeer *, HtcpReplyData *, ps_state *)
Definition: peer_select.cc:870
Ip::Address remote
Definition: Connection.h:138
hier_code
Definition: hier_code.h:12
String dnsError
DNS lookup error message.
Definition: errorpage.h:150
Ip::Address first_parent_miss
PeerSelectionInitiator * interestedInitiator()
Definition: peer_select.cc:974
bool intercepted
Definition: RequestFlags.h:64
peer_t neighborType(const CachePeer *p, const URL &url)
Definition: neighbors.cc:114
FwdServer * next
Definition: peer_select.cc:61
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
ping_data ping
static void peerSelect(PeerSelectionInitiator *initiator, HttpRequest *request, AccessLogEntry::Pointer const &al, StoreEntry *entry)
Definition: peer_select.cc:151
HttpRequest * request
int nonhierarchical_direct
Definition: SquidConfig.h:297
CachePeer * getDefaultParent(HttpRequest *request)
Definition: neighbors.cc:454
int weight
Definition: CachePeer.h:126
int w_rtt
Definition: PingData.h:26
peer_t
Definition: enums.h:27
hier_code peerType
Definition: Connection.h:141
ProtocolType
Definition: ProtocolType.h:22
Comm::ConnectionPointer clientConnection
Definition: Server.h:97
struct CachePeer::@31 options
bool closest_only
Definition: CachePeer.h:91
#define ICP_FLAG_SRC_RTT
Definition: defines.h:73
const char * hier_code_str[]
peer_t hit_type
const SBuf url() const
Definition: peer_select.cc:960
void startSelectingDestinations(HttpRequest *request, const AccessLogEntry::Pointer &ale, StoreEntry *entry)
Definition: peer_select.cc:182
int n_sent
Definition: PingData.h:21
AccessLogEntry::Pointer al
info for the future access.log entry
Ip::Address closest_parent_miss
uint32_t pad
Definition: ICP.h:43
CachePeer * hit
InstanceIdDefinitions(ps_state,"PeerSelector")
acl_access * AlwaysDirect
Definition: SquidConfig.h:360
void peerNoteDigestLookup(HttpRequest *request, CachePeer *p, lookup_t lookup)
Definition: neighbors.cc:842
allow_t always_direct
int p_rtt
Definition: PingData.h:27
static int peerCheckNetdbDirect(ps_state *psstate)
Definition: peer_select.cc:390
int neighborsCount(HttpRequest *request)
Definition: neighbors.cc:263
CachePeer * carpSelectParent(HttpRequest *request)
Definition: carp.cc:146
bool spoofClientIp
Definition: RequestFlags.h:72
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
static void peerIcpParentMiss(CachePeer *, icp_common_t *, ps_state *)
Definition: peer_select.cc:772
bool peerAllowedToUse(const CachePeer *p, HttpRequest *request)
Definition: neighbors.cc:137
int minDirectHops
Definition: SquidConfig.h:255
class SquidConfig Config
Definition: SquidConfig.cc:12
#define NULL
Definition: types.h:166
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition: CbcPointer.h:162
void HTTPMSGUNLOCK(Http::Message *a)
Definition: Message.h:144
static void peerHandleIcpReply(CachePeer *p, peer_t type, icp_common_t *header, void *data)
Definition: peer_select.cc:813
char * host
Definition: CachePeer.h:41
int neighborsUdpPing(HttpRequest *request, StoreEntry *entry, IRCB *callback, void *callback_data, int *exprep, int *timeout)
Definition: neighbors.cc:573
void peerSelectDnsPaths(ps_state *psstate)
Definition: peer_select.cc:249
int basetime
Definition: CachePeer.h:127
const char * icp_opcode_str[]
bool wantsMoreDestinations() const
Definition: peer_select.cc:993
virtual void noteLookup(const Dns::LookupDetails &details) override
Definition: peer_select.cc:327

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors