]> git.ipfire.org Git - thirdparty/squid.git/blob - src/neighbors.cc
Supply ALE to request_header_access/reply_header_access (#477)
[thirdparty/squid.git] / src / neighbors.cc
1 /*
2 * Copyright (C) 1996-2019 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 15 Neighbor Routines */
10
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "anyp/PortCfg.h"
14 #include "base/EnumIterator.h"
15 #include "CacheDigest.h"
16 #include "CachePeer.h"
17 #include "comm/Connection.h"
18 #include "comm/ConnOpener.h"
19 #include "event.h"
20 #include "FwdState.h"
21 #include "globals.h"
22 #include "htcp.h"
23 #include "HttpRequest.h"
24 #include "icmp/net_db.h"
25 #include "ICP.h"
26 #include "int.h"
27 #include "ip/Address.h"
28 #include "ip/tools.h"
29 #include "ipcache.h"
30 #include "MemObject.h"
31 #include "mgr/Registration.h"
32 #include "multicast.h"
33 #include "neighbors.h"
34 #include "NeighborTypeDomainList.h"
35 #include "pconn.h"
36 #include "PeerDigest.h"
37 #include "PeerPoolMgr.h"
38 #include "PeerSelectState.h"
39 #include "RequestFlags.h"
40 #include "SquidConfig.h"
41 #include "SquidMath.h"
42 #include "SquidTime.h"
43 #include "stat.h"
44 #include "Store.h"
45 #include "store_key_md5.h"
46 #include "tools.h"
47
48 /* count mcast group peers every 15 minutes */
49 #define MCAST_COUNT_RATE 900
50
51 bool peerAllowedToUse(const CachePeer *, PeerSelector *);
52 static int peerWouldBePinged(const CachePeer *, PeerSelector *);
53 static void neighborRemove(CachePeer *);
54 static void neighborAlive(CachePeer *, const MemObject *, const icp_common_t *);
55 #if USE_HTCP
56 static void neighborAliveHtcp(CachePeer *, const MemObject *, const HtcpReplyData *);
57 #endif
58 static void neighborCountIgnored(CachePeer *);
59 static void peerRefreshDNS(void *);
60 static IPH peerDNSConfigure;
61 static void peerProbeConnect(CachePeer *, const bool reprobeIfBusy = false);
62 static CNCB peerProbeConnectDone;
63 static void peerCountMcastPeersDone(void *data);
64 static void peerCountMcastPeersStart(void *data);
65 static void peerCountMcastPeersSchedule(CachePeer * p, time_t when);
66 static IRCB peerCountHandleIcpReply;
67
68 static void neighborIgnoreNonPeer(const Ip::Address &, icp_opcode);
69 static OBJH neighborDumpPeers;
70 static OBJH neighborDumpNonPeers;
71 static void dump_peers(StoreEntry * sentry, CachePeer * peers);
72
73 static unsigned short echo_port;
74
75 static int NLateReplies = 0;
76 static CachePeer *first_ping = NULL;
77
78 const char *
79 neighborTypeStr(const CachePeer * p)
80 {
81 if (p->type == PEER_NONE)
82 return "Non-Peer";
83
84 if (p->type == PEER_SIBLING)
85 return "Sibling";
86
87 if (p->type == PEER_MULTICAST)
88 return "Multicast Group";
89
90 return "Parent";
91 }
92
93 CachePeer *
94 whichPeer(const Ip::Address &from)
95 {
96 int j;
97
98 CachePeer *p = NULL;
99 debugs(15, 3, "whichPeer: from " << from);
100
101 for (p = Config.peers; p; p = p->next) {
102 for (j = 0; j < p->n_addresses; ++j) {
103 if (from == p->addresses[j] && from.port() == p->icp.port) {
104 return p;
105 }
106 }
107 }
108
109 return NULL;
110 }
111
112 peer_t
113 neighborType(const CachePeer * p, const AnyP::Uri &url)
114 {
115
116 const NeighborTypeDomainList *d = NULL;
117
118 for (d = p->typelist; d; d = d->next) {
119 if (0 == matchDomainName(url.host(), d->domain))
120 if (d->type != PEER_NONE)
121 return d->type;
122 }
123 #if PEER_MULTICAST_SIBLINGS
124 if (p->type == PEER_MULTICAST)
125 if (p->options.mcast_siblings)
126 return PEER_SIBLING;
127 #endif
128
129 return p->type;
130 }
131
132 /**
133 * \return Whether it is appropriate to fetch REQUEST from PEER.
134 */
135 bool
136 peerAllowedToUse(const CachePeer * p, PeerSelector * ps)
137 {
138 assert(ps);
139 HttpRequest *request = ps->request;
140 assert(request != NULL);
141
142 if (neighborType(p, request->url) == PEER_SIBLING) {
143 #if PEER_MULTICAST_SIBLINGS
144 if (p->type == PEER_MULTICAST && p->options.mcast_siblings &&
145 (request->flags.noCache || request->flags.refresh || request->flags.loopDetected || request->flags.needValidation))
146 debugs(15, 2, "peerAllowedToUse(" << p->name << ", " << request->url.authority() << ") : multicast-siblings optimization match");
147 #endif
148 if (request->flags.noCache)
149 return false;
150
151 if (request->flags.refresh)
152 return false;
153
154 if (request->flags.loopDetected)
155 return false;
156
157 if (request->flags.needValidation)
158 return false;
159 }
160
161 // CONNECT requests are proxy requests. Not to be forwarded to origin servers.
162 // Unless the destination port matches, in which case we MAY perform a 'DIRECT' to this CachePeer.
163 if (p->options.originserver && request->method == Http::METHOD_CONNECT && request->url.port() != p->http_port)
164 return false;
165
166 if (p->access == NULL)
167 return true;
168
169 ACLFilledChecklist checklist(p->access, request, NULL);
170 checklist.al = ps->al;
171 if (ps->al && ps->al->reply) {
172 checklist.reply = ps->al->reply.getRaw();
173 HTTPMSGLOCK(checklist.reply);
174 }
175 checklist.syncAle(request, nullptr);
176 return checklist.fastCheck().allowed();
177 }
178
179 /* Return TRUE if it is okay to send an ICP request to this CachePeer. */
180 static int
181 peerWouldBePinged(const CachePeer * p, PeerSelector * ps)
182 {
183 assert(ps);
184 HttpRequest *request = ps->request;
185
186 if (p->icp.port == 0)
187 return 0;
188
189 if (p->options.no_query)
190 return 0;
191
192 if (p->options.mcast_responder)
193 return 0;
194
195 if (p->n_addresses == 0)
196 return 0;
197
198 if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate))
199 return 0;
200
201 /* the case below seems strange, but can happen if the
202 * URL host is on the other side of a firewall */
203 if (p->type == PEER_SIBLING)
204 if (!request->flags.hierarchical)
205 return 0;
206
207 if (!peerAllowedToUse(p, ps))
208 return 0;
209
210 /* Ping dead peers every timeout interval */
211 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
212 return 1;
213
214 if (!neighborUp(p))
215 return 0;
216
217 return 1;
218 }
219
220 bool
221 peerCanOpenMore(const CachePeer *p)
222 {
223 const int effectiveLimit = p->max_conn <= 0 ? Squid_MaxFD : p->max_conn;
224 const int remaining = effectiveLimit - p->stats.conn_open;
225 debugs(15, 7, remaining << '=' << effectiveLimit << '-' << p->stats.conn_open);
226 return remaining > 0;
227 }
228
229 bool
230 peerHasConnAvailable(const CachePeer *p)
231 {
232 // Standby connections can be used without opening new connections.
233 const int standbys = p->standby.pool ? p->standby.pool->count() : 0;
234
235 // XXX: Some idle pconns can be used without opening new connections.
236 // Complication: Idle pconns cannot be reused for some requests.
237 const int usableIdles = 0;
238
239 const int available = standbys + usableIdles;
240 debugs(15, 7, available << '=' << standbys << '+' << usableIdles);
241 return available > 0;
242 }
243
244 void
245 peerConnClosed(CachePeer *p)
246 {
247 --p->stats.conn_open;
248 if (p->standby.waitingForClose && peerCanOpenMore(p)) {
249 p->standby.waitingForClose = false;
250 PeerPoolMgr::Checkpoint(p->standby.mgr, "conn closed");
251 }
252 }
253
254 /* Return TRUE if it is okay to send an HTTP request to this CachePeer. */
255 int
256 peerHTTPOkay(const CachePeer * p, PeerSelector * ps)
257 {
258 if (!peerCanOpenMore(p) && !peerHasConnAvailable(p))
259 return 0;
260
261 if (!peerAllowedToUse(p, ps))
262 return 0;
263
264 if (!neighborUp(p))
265 return 0;
266
267 return 1;
268 }
269
270 int
271 neighborsCount(PeerSelector *ps)
272 {
273 CachePeer *p = NULL;
274 int count = 0;
275
276 for (p = Config.peers; p; p = p->next)
277 if (peerWouldBePinged(p, ps))
278 ++count;
279
280 debugs(15, 3, "neighborsCount: " << count);
281
282 return count;
283 }
284
285 CachePeer *
286 getFirstUpParent(PeerSelector *ps)
287 {
288 assert(ps);
289 HttpRequest *request = ps->request;
290
291 CachePeer *p = NULL;
292
293 for (p = Config.peers; p; p = p->next) {
294 if (!neighborUp(p))
295 continue;
296
297 if (neighborType(p, request->url) != PEER_PARENT)
298 continue;
299
300 if (!peerHTTPOkay(p, ps))
301 continue;
302
303 break;
304 }
305
306 debugs(15, 3, "getFirstUpParent: returning " << (p ? p->host : "NULL"));
307 return p;
308 }
309
310 CachePeer *
311 getRoundRobinParent(PeerSelector *ps)
312 {
313 assert(ps);
314 HttpRequest *request = ps->request;
315
316 CachePeer *p;
317 CachePeer *q = NULL;
318
319 for (p = Config.peers; p; p = p->next) {
320 if (!p->options.roundrobin)
321 continue;
322
323 if (neighborType(p, request->url) != PEER_PARENT)
324 continue;
325
326 if (!peerHTTPOkay(p, ps))
327 continue;
328
329 if (p->weight == 0)
330 continue;
331
332 if (q) {
333 if (p->weight == q->weight) {
334 if (q->rr_count < p->rr_count)
335 continue;
336 } else if ( ((double) q->rr_count / q->weight) < ((double) p->rr_count / p->weight)) {
337 continue;
338 }
339 }
340
341 q = p;
342 }
343
344 if (q)
345 ++ q->rr_count;
346
347 debugs(15, 3, HERE << "returning " << (q ? q->host : "NULL"));
348
349 return q;
350 }
351
352 CachePeer *
353 getWeightedRoundRobinParent(PeerSelector *ps)
354 {
355 assert(ps);
356 HttpRequest *request = ps->request;
357
358 CachePeer *p;
359 CachePeer *q = NULL;
360 int weighted_rtt;
361
362 for (p = Config.peers; p; p = p->next) {
363 if (!p->options.weighted_roundrobin)
364 continue;
365
366 if (neighborType(p, request->url) != PEER_PARENT)
367 continue;
368
369 if (!peerHTTPOkay(p, ps))
370 continue;
371
372 if (q && q->rr_count < p->rr_count)
373 continue;
374
375 q = p;
376 }
377
378 if (q && q->rr_count > 1000000)
379 for (p = Config.peers; p; p = p->next) {
380 if (!p->options.weighted_roundrobin)
381 continue;
382
383 if (neighborType(p, request->url) != PEER_PARENT)
384 continue;
385
386 p->rr_count = 0;
387 }
388
389 if (q) {
390 weighted_rtt = (q->stats.rtt - q->basetime) / q->weight;
391
392 if (weighted_rtt < 1)
393 weighted_rtt = 1;
394
395 q->rr_count += weighted_rtt;
396
397 debugs(15, 3, "getWeightedRoundRobinParent: weighted_rtt " << weighted_rtt);
398 }
399
400 debugs(15, 3, "getWeightedRoundRobinParent: returning " << (q ? q->host : "NULL"));
401 return q;
402 }
403
404 /**
405 * This gets called every 5 minutes to clear the round-robin counter.
406 * The exact timing is an arbitrary default, set on estimate timing of a
407 * large number of requests in a high-performance environment during the
408 * period. The larger the number of requests between cycled resets the
409 * more balanced the operations.
410 *
411 \param data unused.
412 \todo Make the reset timing a selectable parameter in squid.conf
413 */
414 static void
415 peerClearRRLoop(void *data)
416 {
417 peerClearRR();
418 eventAdd("peerClearRR", peerClearRRLoop, data, 5 * 60.0, 0);
419 }
420
421 /**
422 * This gets called on startup and restart to kick off the CachePeer round-robin
423 * maintenance event. It ensures that no matter how many times its called
424 * no more than one event is scheduled.
425 */
426 void
427 peerClearRRStart(void)
428 {
429 static bool event_added = false;
430 if (!event_added) {
431 peerClearRRLoop(NULL);
432 event_added=true;
433 }
434 }
435
436 /**
437 * Called whenever the round-robin counters need to be reset to a sane state.
438 * So far those times are:
439 * - On startup and reconfigure - to set the counters to sane initial settings.
440 * - When a CachePeer has revived from dead, to prevent the revived CachePeer being
441 * flooded with requests which it has 'missed' during the down period.
442 */
443 void
444 peerClearRR()
445 {
446 CachePeer *p = NULL;
447 for (p = Config.peers; p; p = p->next) {
448 p->rr_count = 1;
449 }
450 }
451
452 /**
453 * Perform all actions when a CachePeer is detected revived.
454 */
455 void
456 peerAlive(CachePeer *p)
457 {
458 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
459 debugs(15, DBG_IMPORTANT, "Detected REVIVED " << neighborTypeStr(p) << ": " << p->name);
460 p->stats.logged_state = PEER_ALIVE;
461 peerClearRR();
462 if (p->standby.mgr.valid())
463 PeerPoolMgr::Checkpoint(p->standby.mgr, "revived peer");
464 }
465
466 p->stats.last_reply = squid_curtime;
467 p->stats.probe_start = 0;
468 }
469
470 CachePeer *
471 getDefaultParent(PeerSelector *ps)
472 {
473 assert(ps);
474 HttpRequest *request = ps->request;
475
476 CachePeer *p = NULL;
477
478 for (p = Config.peers; p; p = p->next) {
479 if (neighborType(p, request->url) != PEER_PARENT)
480 continue;
481
482 if (!p->options.default_parent)
483 continue;
484
485 if (!peerHTTPOkay(p, ps))
486 continue;
487
488 debugs(15, 3, "getDefaultParent: returning " << p->host);
489
490 return p;
491 }
492
493 debugs(15, 3, "getDefaultParent: returning NULL");
494 return NULL;
495 }
496
497 CachePeer *
498 getNextPeer(CachePeer * p)
499 {
500 return p->next;
501 }
502
503 CachePeer *
504 getFirstPeer(void)
505 {
506 return Config.peers;
507 }
508
509 static void
510 neighborRemove(CachePeer * target)
511 {
512 CachePeer *p = NULL;
513 CachePeer **P = NULL;
514 p = Config.peers;
515 P = &Config.peers;
516
517 while (p) {
518 if (target == p)
519 break;
520
521 P = &p->next;
522
523 p = p->next;
524 }
525
526 if (p) {
527 *P = p->next;
528 p->next = NULL;
529 delete p;
530 --Config.npeers;
531 }
532
533 first_ping = Config.peers;
534 }
535
536 static void
537 neighborsRegisterWithCacheManager()
538 {
539 Mgr::RegisterAction("server_list",
540 "Peer Cache Statistics",
541 neighborDumpPeers, 0, 1);
542
543 if (Comm::IsConnOpen(icpIncomingConn)) {
544 Mgr::RegisterAction("non_peers",
545 "List of Unknown sites sending ICP messages",
546 neighborDumpNonPeers, 0, 1);
547 }
548 }
549
550 void
551 neighbors_init(void)
552 {
553 struct servent *sep = NULL;
554 const char *me = getMyHostname();
555 CachePeer *thisPeer = NULL;
556 CachePeer *next = NULL;
557
558 neighborsRegisterWithCacheManager();
559
560 if (Comm::IsConnOpen(icpIncomingConn)) {
561
562 for (thisPeer = Config.peers; thisPeer; thisPeer = next) {
563 next = thisPeer->next;
564
565 if (0 != strcmp(thisPeer->host, me))
566 continue;
567
568 for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
569 if (thisPeer->http_port != s->s.port())
570 continue;
571
572 debugs(15, DBG_IMPORTANT, "WARNING: Peer looks like this host");
573
574 debugs(15, DBG_IMPORTANT, " Ignoring " <<
575 neighborTypeStr(thisPeer) << " " << thisPeer->host <<
576 "/" << thisPeer->http_port << "/" <<
577 thisPeer->icp.port);
578
579 neighborRemove(thisPeer);
580 }
581 }
582 }
583
584 peerRefreshDNS((void *) 1);
585
586 sep = getservbyname("echo", "udp");
587 echo_port = sep ? ntohs((unsigned short) sep->s_port) : 7;
588
589 first_ping = Config.peers;
590 }
591
592 int
593 neighborsUdpPing(HttpRequest * request,
594 StoreEntry * entry,
595 IRCB * callback,
596 PeerSelector *ps,
597 int *exprep,
598 int *timeout)
599 {
600 const char *url = entry->url();
601 MemObject *mem = entry->mem_obj;
602 CachePeer *p = NULL;
603 int i;
604 int reqnum = 0;
605 int flags;
606 int queries_sent = 0;
607 int peers_pinged = 0;
608 int parent_timeout = 0, parent_exprep = 0;
609 int sibling_timeout = 0, sibling_exprep = 0;
610 int mcast_timeout = 0, mcast_exprep = 0;
611
612 if (Config.peers == NULL)
613 return 0;
614
615 assert(!entry->hasDisk());
616
617 mem->start_ping = current_time;
618
619 mem->ping_reply_callback = callback;
620
621 mem->ircb_data = ps;
622
623 reqnum = icpSetCacheKey((const cache_key *)entry->key);
624
625 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
626 if (p == NULL)
627 p = Config.peers;
628
629 debugs(15, 5, "neighborsUdpPing: Peer " << p->host);
630
631 if (!peerWouldBePinged(p, ps))
632 continue; /* next CachePeer */
633
634 ++peers_pinged;
635
636 debugs(15, 4, "neighborsUdpPing: pinging peer " << p->host << " for '" << url << "'");
637
638 debugs(15, 3, "neighborsUdpPing: key = '" << entry->getMD5Text() << "'");
639
640 debugs(15, 3, "neighborsUdpPing: reqnum = " << reqnum);
641
642 #if USE_HTCP
643 if (p->options.htcp && !p->options.htcp_only_clr) {
644 if (Config.Port.htcp <= 0) {
645 debugs(15, DBG_CRITICAL, "HTCP is disabled! Cannot send HTCP request to peer.");
646 continue;
647 }
648
649 debugs(15, 3, "neighborsUdpPing: sending HTCP query");
650 if (htcpQuery(entry, request, p) <= 0)
651 continue; // unable to send.
652 } else
653 #endif
654 {
655 if (Config.Port.icp <= 0 || !Comm::IsConnOpen(icpOutgoingConn)) {
656 debugs(15, DBG_CRITICAL, "ICP is disabled! Cannot send ICP request to peer.");
657 continue;
658 } else {
659
660 if (p->type == PEER_MULTICAST)
661 mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
662
663 if (p->icp.port == echo_port) {
664 debugs(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping");
665 // TODO: Get ALE from callback_data if possible.
666 icpCreateAndSend(ICP_DECHO, 0, url, reqnum, 0,
667 icpOutgoingConn->fd, p->in_addr, nullptr);
668 } else {
669 flags = 0;
670
671 if (Config.onoff.query_icmp)
672 if (p->icp.version == ICP_VERSION_2)
673 flags |= ICP_FLAG_SRC_RTT;
674
675 // TODO: Get ALE from callback_data if possible.
676 icpCreateAndSend(ICP_QUERY, flags, url, reqnum, 0,
677 icpOutgoingConn->fd, p->in_addr, nullptr);
678 }
679 }
680 }
681
682 ++queries_sent;
683
684 ++ p->stats.pings_sent;
685
686 if (p->type == PEER_MULTICAST) {
687 mcast_exprep += p->mcast.n_replies_expected;
688 mcast_timeout += (p->stats.rtt * p->mcast.n_replies_expected);
689 } else if (neighborUp(p)) {
690 /* its alive, expect a reply from it */
691
692 if (neighborType(p, request->url) == PEER_PARENT) {
693 ++parent_exprep;
694 parent_timeout += p->stats.rtt;
695 } else {
696 ++sibling_exprep;
697 sibling_timeout += p->stats.rtt;
698 }
699 } else {
700 /* Neighbor is dead; ping it anyway, but don't expect a reply */
701 /* log it once at the threshold */
702
703 if (p->stats.logged_state == PEER_ALIVE) {
704 debugs(15, DBG_IMPORTANT, "Detected DEAD " << neighborTypeStr(p) << ": " << p->name);
705 p->stats.logged_state = PEER_DEAD;
706 }
707 }
708
709 p->stats.last_query = squid_curtime;
710
711 /*
712 * keep probe_start == 0 for a multicast CachePeer,
713 * so neighborUp() never says this CachePeer is dead.
714 */
715
716 if ((p->type != PEER_MULTICAST) && (p->stats.probe_start == 0))
717 p->stats.probe_start = squid_curtime;
718 }
719
720 if ((first_ping = first_ping->next) == NULL)
721 first_ping = Config.peers;
722
723 /*
724 * How many replies to expect?
725 */
726 *exprep = parent_exprep + sibling_exprep + mcast_exprep;
727
728 /*
729 * If there is a configured timeout, use it
730 */
731 if (Config.Timeout.icp_query)
732 *timeout = Config.Timeout.icp_query;
733 else {
734 if (*exprep > 0) {
735 if (parent_exprep)
736 *timeout = 2 * parent_timeout / parent_exprep;
737 else if (mcast_exprep)
738 *timeout = 2 * mcast_timeout / mcast_exprep;
739 else
740 *timeout = 2 * sibling_timeout / sibling_exprep;
741 } else
742 *timeout = 2000; /* 2 seconds */
743
744 if (Config.Timeout.icp_query_max)
745 if (*timeout > Config.Timeout.icp_query_max)
746 *timeout = Config.Timeout.icp_query_max;
747
748 if (*timeout < Config.Timeout.icp_query_min)
749 *timeout = Config.Timeout.icp_query_min;
750 }
751
752 return peers_pinged;
753 }
754
755 /* lookup the digest of a given CachePeer */
756 lookup_t
757 peerDigestLookup(CachePeer * p, PeerSelector * ps)
758 {
759 #if USE_CACHE_DIGESTS
760 assert(ps);
761 HttpRequest *request = ps->request;
762 const cache_key *key = request ? storeKeyPublicByRequest(request) : NULL;
763 assert(p);
764 assert(request);
765 debugs(15, 5, "peerDigestLookup: peer " << p->host);
766 /* does the peeer have a valid digest? */
767
768 if (!p->digest) {
769 debugs(15, 5, "peerDigestLookup: gone!");
770 return LOOKUP_NONE;
771 } else if (!peerHTTPOkay(p, ps)) {
772 debugs(15, 5, "peerDigestLookup: !peerHTTPOkay");
773 return LOOKUP_NONE;
774 } else if (!p->digest->flags.needed) {
775 debugs(15, 5, "peerDigestLookup: note need");
776 peerDigestNeeded(p->digest);
777 return LOOKUP_NONE;
778 } else if (!p->digest->flags.usable) {
779 debugs(15, 5, "peerDigestLookup: !ready && " << (p->digest->flags.requested ? "" : "!") << "requested");
780 return LOOKUP_NONE;
781 }
782
783 debugs(15, 5, "peerDigestLookup: OK to lookup peer " << p->host);
784 assert(p->digest->cd);
785 /* does digest predict a hit? */
786
787 if (!p->digest->cd->contains(key))
788 return LOOKUP_MISS;
789
790 debugs(15, 5, "peerDigestLookup: peer " << p->host << " says HIT!");
791
792 return LOOKUP_HIT;
793
794 #endif
795
796 return LOOKUP_NONE;
797 }
798
799 /* select best CachePeer based on cache digests */
800 CachePeer *
801 neighborsDigestSelect(PeerSelector *ps)
802 {
803 CachePeer *best_p = NULL;
804 #if USE_CACHE_DIGESTS
805 assert(ps);
806 HttpRequest *request = ps->request;
807
808 int best_rtt = 0;
809 int choice_count = 0;
810 int ichoice_count = 0;
811 CachePeer *p;
812 int p_rtt;
813 int i;
814
815 if (!request->flags.hierarchical)
816 return NULL;
817
818 storeKeyPublicByRequest(request);
819
820 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
821 lookup_t lookup;
822
823 if (!p)
824 p = Config.peers;
825
826 if (i == 1)
827 first_ping = p;
828
829 lookup = peerDigestLookup(p, ps);
830
831 if (lookup == LOOKUP_NONE)
832 continue;
833
834 ++choice_count;
835
836 if (lookup == LOOKUP_MISS)
837 continue;
838
839 p_rtt = netdbHostRtt(p->host);
840
841 debugs(15, 5, "neighborsDigestSelect: peer " << p->host << " rtt: " << p_rtt);
842
843 /* is this CachePeer better than others in terms of rtt ? */
844 if (!best_p || (p_rtt && p_rtt < best_rtt)) {
845 best_p = p;
846 best_rtt = p_rtt;
847
848 if (p_rtt) /* informative choice (aka educated guess) */
849 ++ichoice_count;
850
851 debugs(15, 4, "neighborsDigestSelect: peer " << p->host << " leads with rtt " << best_rtt);
852 }
853 }
854
855 debugs(15, 4, "neighborsDigestSelect: choices: " << choice_count << " (" << ichoice_count << ")");
856 peerNoteDigestLookup(request, best_p,
857 best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE));
858 request->hier.n_choices = choice_count;
859 request->hier.n_ichoices = ichoice_count;
860 #endif
861
862 return best_p;
863 }
864
865 void
866 peerNoteDigestLookup(HttpRequest * request, CachePeer * p, lookup_t lookup)
867 {
868 #if USE_CACHE_DIGESTS
869 if (p)
870 strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host)-1);
871 else
872 *request->hier.cd_host = '\0';
873
874 request->hier.cd_lookup = lookup;
875 debugs(15, 4, "peerNoteDigestLookup: peer " << (p? p->host : "<none>") << ", lookup: " << lookup_t_str[lookup] );
876 #endif
877 }
878
879 static void
880 neighborAlive(CachePeer * p, const MemObject *, const icp_common_t * header)
881 {
882 peerAlive(p);
883 ++ p->stats.pings_acked;
884
885 if ((icp_opcode) header->opcode <= ICP_END)
886 ++ p->icp.counts[header->opcode];
887
888 p->icp.version = (int) header->version;
889 }
890
891 static void
892 neighborUpdateRtt(CachePeer * p, MemObject * mem)
893 {
894 int rtt, rtt_av_factor;
895
896 if (!mem)
897 return;
898
899 if (!mem->start_ping.tv_sec)
900 return;
901
902 rtt = tvSubMsec(mem->start_ping, current_time);
903
904 if (rtt < 1 || rtt > 10000)
905 return;
906
907 rtt_av_factor = RTT_AV_FACTOR;
908
909 if (p->options.weighted_roundrobin)
910 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
911
912 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, p->stats.pings_acked, rtt_av_factor);
913 }
914
915 #if USE_HTCP
916 static void
917 neighborAliveHtcp(CachePeer * p, const MemObject *, const HtcpReplyData * htcp)
918 {
919 peerAlive(p);
920 ++ p->stats.pings_acked;
921 ++ p->htcp.counts[htcp->hit ? 1 : 0];
922 p->htcp.version = htcp->version;
923 }
924
925 #endif
926
927 static void
928 neighborCountIgnored(CachePeer * p)
929 {
930 if (p == NULL)
931 return;
932
933 ++ p->stats.ignored_replies;
934
935 ++NLateReplies;
936 }
937
938 static CachePeer *non_peers = NULL;
939
940 static void
941 neighborIgnoreNonPeer(const Ip::Address &from, icp_opcode opcode)
942 {
943 CachePeer *np;
944
945 for (np = non_peers; np; np = np->next) {
946 if (np->in_addr != from)
947 continue;
948
949 if (np->in_addr.port() != from.port())
950 continue;
951
952 break;
953 }
954
955 if (np == NULL) {
956 np = new CachePeer;
957 np->in_addr = from;
958 np->icp.port = from.port();
959 np->type = PEER_NONE;
960 np->host = new char[MAX_IPSTRLEN];
961 from.toStr(np->host,MAX_IPSTRLEN);
962 np->next = non_peers;
963 non_peers = np;
964 }
965
966 ++ np->icp.counts[opcode];
967
968 if (isPowTen(++np->stats.ignored_replies))
969 debugs(15, DBG_IMPORTANT, "WARNING: Ignored " << np->stats.ignored_replies << " replies from non-peer " << np->host);
970 }
971
972 /* ignoreMulticastReply
973 *
974 * * We want to ignore replies from multicast peers if the
975 * * cache_host_domain rules would normally prevent the CachePeer
976 * * from being used
977 */
978 static int
979 ignoreMulticastReply(CachePeer * p, PeerSelector * ps)
980 {
981 if (p == NULL)
982 return 0;
983
984 if (!p->options.mcast_responder)
985 return 0;
986
987 if (peerHTTPOkay(p, ps))
988 return 0;
989
990 return 1;
991 }
992
993 /**
994 * I should attach these records to the entry. We take the first
995 * hit we get our wait until everyone misses. The timeout handler
996 * call needs to nip this shopping list or call one of the misses.
997 *
998 * If a hit process is already started, then sobeit
999 */
1000 void
1001 neighborsUdpAck(const cache_key * key, icp_common_t * header, const Ip::Address &from)
1002 {
1003 CachePeer *p = NULL;
1004 StoreEntry *entry;
1005 MemObject *mem = NULL;
1006 peer_t ntype = PEER_NONE;
1007 icp_opcode opcode = (icp_opcode) header->opcode;
1008
1009 debugs(15, 6, "neighborsUdpAck: opcode " << opcode << " '" << storeKeyText(key) << "'");
1010
1011 if ((entry = Store::Root().findCallbackXXX(key)))
1012 mem = entry->mem_obj;
1013
1014 if ((p = whichPeer(from)))
1015 neighborAlive(p, mem, header);
1016
1017 if (opcode > ICP_END)
1018 return;
1019
1020 const char *opcode_d = icp_opcode_str[opcode];
1021
1022 if (p)
1023 neighborUpdateRtt(p, mem);
1024
1025 /* Does the entry exist? */
1026 if (NULL == entry) {
1027 debugs(12, 3, "neighborsUdpAck: Cache key '" << storeKeyText(key) << "' not found");
1028 neighborCountIgnored(p);
1029 return;
1030 }
1031
1032 /* check if someone is already fetching it */
1033 if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) {
1034 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
1035 neighborCountIgnored(p);
1036 return;
1037 }
1038
1039 if (mem == NULL) {
1040 debugs(15, 2, "Ignoring " << opcode_d << " for missing mem_obj: " << storeKeyText(key));
1041 neighborCountIgnored(p);
1042 return;
1043 }
1044
1045 if (entry->ping_status != PING_WAITING) {
1046 debugs(15, 2, "neighborsUdpAck: Late " << opcode_d << " for " << storeKeyText(key));
1047 neighborCountIgnored(p);
1048 return;
1049 }
1050
1051 if (!entry->locked()) {
1052 // TODO: many entries are unlocked; why is this reported at level 1?
1053 debugs(12, DBG_IMPORTANT, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
1054 neighborCountIgnored(p);
1055 return;
1056 }
1057
1058 if (!mem->ircb_data) {
1059 debugs(12, DBG_IMPORTANT, "BUG: missing ICP callback data for " << *entry);
1060 neighborCountIgnored(p);
1061 return;
1062 }
1063
1064 debugs(15, 3, "neighborsUdpAck: " << opcode_d << " for '" << storeKeyText(key) << "' from " << (p ? p->host : "source") << " ");
1065
1066 if (p) {
1067 ntype = neighborType(p, mem->request->url);
1068 }
1069
1070 if (ignoreMulticastReply(p, mem->ircb_data)) {
1071 neighborCountIgnored(p);
1072 } else if (opcode == ICP_MISS) {
1073 if (p == NULL) {
1074 neighborIgnoreNonPeer(from, opcode);
1075 } else {
1076 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1077 }
1078 } else if (opcode == ICP_HIT) {
1079 if (p == NULL) {
1080 neighborIgnoreNonPeer(from, opcode);
1081 } else {
1082 header->opcode = ICP_HIT;
1083 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1084 }
1085 } else if (opcode == ICP_DECHO) {
1086 if (p == NULL) {
1087 neighborIgnoreNonPeer(from, opcode);
1088 } else if (ntype == PEER_SIBLING) {
1089 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
1090 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
1091 } else {
1092 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1093 }
1094 } else if (opcode == ICP_SECHO) {
1095 if (p) {
1096 debugs(15, DBG_IMPORTANT, "Ignoring SECHO from neighbor " << p->host);
1097 neighborCountIgnored(p);
1098 } else {
1099 debugs(15, DBG_IMPORTANT, "Unsolicited SECHO from " << from);
1100 }
1101 } else if (opcode == ICP_DENIED) {
1102 if (p == NULL) {
1103 neighborIgnoreNonPeer(from, opcode);
1104 } else if (p->stats.pings_acked > 100) {
1105 if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
1106 debugs(15, DBG_CRITICAL, "95%% of replies from '" << p->host << "' are UDP_DENIED");
1107 debugs(15, DBG_CRITICAL, "Disabling '" << p->host << "', please check your configuration.");
1108 neighborRemove(p);
1109 p = NULL;
1110 } else {
1111 neighborCountIgnored(p);
1112 }
1113 }
1114 } else if (opcode == ICP_MISS_NOFETCH) {
1115 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1116 } else {
1117 debugs(15, DBG_CRITICAL, "neighborsUdpAck: Unexpected ICP reply: " << opcode_d);
1118 }
1119 }
1120
1121 CachePeer *
1122 peerFindByName(const char *name)
1123 {
1124 CachePeer *p = NULL;
1125
1126 for (p = Config.peers; p; p = p->next) {
1127 if (!strcasecmp(name, p->name))
1128 break;
1129 }
1130
1131 return p;
1132 }
1133
1134 CachePeer *
1135 peerFindByNameAndPort(const char *name, unsigned short port)
1136 {
1137 CachePeer *p = NULL;
1138
1139 for (p = Config.peers; p; p = p->next) {
1140 if (strcasecmp(name, p->name))
1141 continue;
1142
1143 if (port != p->http_port)
1144 continue;
1145
1146 break;
1147 }
1148
1149 return p;
1150 }
1151
1152 int
1153 neighborUp(const CachePeer * p)
1154 {
1155 if (!p->tcp_up) {
1156 peerProbeConnect(const_cast<CachePeer*>(p));
1157 return 0;
1158 }
1159
1160 /*
1161 * The CachePeer can not be UP if we don't have any IP addresses
1162 * for it.
1163 */
1164 if (0 == p->n_addresses) {
1165 debugs(15, 8, "neighborUp: DOWN (no-ip): " << p->host << " (" << p->in_addr << ")");
1166 return 0;
1167 }
1168
1169 if (p->options.no_query) {
1170 debugs(15, 8, "neighborUp: UP (no-query): " << p->host << " (" << p->in_addr << ")");
1171 return 1;
1172 }
1173
1174 if (p->stats.probe_start != 0 &&
1175 squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer) {
1176 debugs(15, 8, "neighborUp: DOWN (dead): " << p->host << " (" << p->in_addr << ")");
1177 return 0;
1178 }
1179
1180 debugs(15, 8, "neighborUp: UP: " << p->host << " (" << p->in_addr << ")");
1181 return 1;
1182 }
1183
1184 /// \returns the effective connect timeout for this peer
1185 time_t
1186 peerConnectTimeout(const CachePeer *peer)
1187 {
1188 return peer->connect_timeout_raw > 0 ?
1189 peer->connect_timeout_raw : Config.Timeout.peer_connect;
1190 }
1191
1192 time_t
1193 positiveTimeout(const time_t timeout)
1194 {
1195 return max(static_cast<time_t>(1), timeout);
1196 }
1197
1198 static void
1199 peerDNSConfigure(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
1200 {
1201 // TODO: connections to no-longer valid IP addresses should be
1202 // closed when we can detect such IP addresses.
1203
1204 CachePeer *p = (CachePeer *)data;
1205
1206 if (p->n_addresses == 0) {
1207 debugs(15, DBG_IMPORTANT, "Configuring " << neighborTypeStr(p) << " " << p->host << "/" << p->http_port << "/" << p->icp.port);
1208
1209 if (p->type == PEER_MULTICAST)
1210 debugs(15, DBG_IMPORTANT, " Multicast TTL = " << p->mcast.ttl);
1211 }
1212
1213 p->n_addresses = 0;
1214
1215 if (ia == NULL) {
1216 debugs(0, DBG_CRITICAL, "WARNING: DNS lookup for '" << p->host << "' failed!");
1217 return;
1218 }
1219
1220 if (ia->empty()) {
1221 debugs(0, DBG_CRITICAL, "WARNING: No IP address found for '" << p->host << "'!");
1222 return;
1223 }
1224
1225 for (const auto &ip: ia->goodAndBad()) { // TODO: Consider using just good().
1226 if (p->n_addresses < PEER_MAX_ADDRESSES) {
1227 const auto idx = p->n_addresses++;
1228 p->addresses[idx] = ip;
1229 debugs(15, 2, "--> IP address #" << idx << ": " << p->addresses[idx]);
1230 } else {
1231 debugs(15, 3, "ignoring remaining " << (ia->size() - p->n_addresses) << " ips");
1232 break;
1233 }
1234 }
1235
1236 p->in_addr.setEmpty();
1237 p->in_addr = p->addresses[0];
1238 p->in_addr.port(p->icp.port);
1239
1240 peerProbeConnect(p, true); // detect any died or revived peers ASAP
1241
1242 if (p->type == PEER_MULTICAST)
1243 peerCountMcastPeersSchedule(p, 10);
1244
1245 #if USE_ICMP
1246 if (p->type != PEER_MULTICAST && IamWorkerProcess())
1247 if (!p->options.no_netdb_exchange)
1248 eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1);
1249 #endif
1250
1251 if (p->standby.mgr.valid())
1252 PeerPoolMgr::Checkpoint(p->standby.mgr, "resolved peer");
1253 }
1254
1255 static void
1256 peerRefreshDNS(void *data)
1257 {
1258 CachePeer *p = NULL;
1259
1260 if (eventFind(peerRefreshDNS, NULL))
1261 eventDelete(peerRefreshDNS, NULL);
1262
1263 if (!data && 0 == stat5minClientRequests()) {
1264 /* no recent client traffic, wait a bit */
1265 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1);
1266 return;
1267 }
1268
1269 for (p = Config.peers; p; p = p->next)
1270 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
1271
1272 /* Reconfigure the peers every hour */
1273 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1);
1274 }
1275
1276 static void
1277 peerConnectFailedSilent(CachePeer * p)
1278 {
1279 p->stats.last_connect_failure = squid_curtime;
1280
1281 if (!p->tcp_up) {
1282 debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port <<
1283 " dead");
1284 return;
1285 }
1286
1287 -- p->tcp_up;
1288
1289 if (!p->tcp_up) {
1290 debugs(15, DBG_IMPORTANT, "Detected DEAD " << neighborTypeStr(p) << ": " << p->name);
1291 p->stats.logged_state = PEER_DEAD;
1292 }
1293 }
1294
1295 void
1296 peerConnectFailed(CachePeer *p)
1297 {
1298 debugs(15, DBG_IMPORTANT, "TCP connection to " << p->host << "/" << p->http_port << " failed");
1299 peerConnectFailedSilent(p);
1300 }
1301
1302 void
1303 peerConnectSucceded(CachePeer * p)
1304 {
1305 if (!p->tcp_up) {
1306 debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port << " succeeded");
1307 p->tcp_up = p->connect_fail_limit; // NP: so peerAlive(p) works properly.
1308 peerAlive(p);
1309 if (!p->n_addresses)
1310 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
1311 } else
1312 p->tcp_up = p->connect_fail_limit;
1313 }
1314
1315 /// whether new TCP probes are currently banned
1316 static bool
1317 peerProbeIsBusy(const CachePeer *p)
1318 {
1319 if (p->testing_now > 0) {
1320 debugs(15, 8, "yes, probing " << p);
1321 return true;
1322 }
1323 if (squid_curtime - p->stats.last_connect_probe == 0) {
1324 debugs(15, 8, "yes, just probed " << p);
1325 return true;
1326 }
1327 return false;
1328 }
1329 /*
1330 * peerProbeConnect will be called on dead peers by neighborUp
1331 */
1332 static void
1333 peerProbeConnect(CachePeer *p, const bool reprobeIfBusy)
1334 {
1335 if (peerProbeIsBusy(p)) {
1336 p->reprobe = reprobeIfBusy;
1337 return;
1338 }
1339 p->reprobe = false;
1340
1341 const time_t ctimeout = peerConnectTimeout(p);
1342 /* for each IP address of this CachePeer. find one that we can connect to and probe it. */
1343 for (int i = 0; i < p->n_addresses; ++i) {
1344 Comm::ConnectionPointer conn = new Comm::Connection;
1345 conn->remote = p->addresses[i];
1346 conn->remote.port(p->http_port);
1347 conn->setPeer(p);
1348 getOutgoingAddress(NULL, conn);
1349
1350 ++ p->testing_now;
1351
1352 AsyncCall::Pointer call = commCbCall(15,3, "peerProbeConnectDone", CommConnectCbPtrFun(peerProbeConnectDone, p));
1353 Comm::ConnOpener *cs = new Comm::ConnOpener(conn, call, ctimeout);
1354 cs->setHost(p->host);
1355 AsyncJob::Start(cs);
1356 }
1357
1358 p->stats.last_connect_probe = squid_curtime;
1359 }
1360
1361 static void
1362 peerProbeConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data)
1363 {
1364 CachePeer *p = (CachePeer*)data;
1365
1366 if (status == Comm::OK) {
1367 peerConnectSucceded(p);
1368 } else {
1369 peerConnectFailedSilent(p);
1370 }
1371
1372 -- p->testing_now;
1373 conn->close();
1374 // TODO: log this traffic.
1375
1376 if (p->reprobe)
1377 peerProbeConnect(p);
1378 }
1379
1380 static void
1381 peerCountMcastPeersSchedule(CachePeer * p, time_t when)
1382 {
1383 if (p->mcast.flags.count_event_pending)
1384 return;
1385
1386 eventAdd("peerCountMcastPeersStart",
1387 peerCountMcastPeersStart,
1388 p,
1389 (double) when, 1);
1390
1391 p->mcast.flags.count_event_pending = true;
1392 }
1393
1394 static void
1395 peerCountMcastPeersStart(void *data)
1396 {
1397 // XXX: Do not create lots of complex fake objects (while abusing their
1398 // APIs) to pass around a few basic data points like start_ping and ping!
1399 CachePeer *p = (CachePeer *)data;
1400 MemObject *mem;
1401 int reqnum;
1402 // TODO: use class AnyP::Uri instead of constructing and re-parsing a string
1403 LOCAL_ARRAY(char, url, MAX_URL);
1404 assert(p->type == PEER_MULTICAST);
1405 p->mcast.flags.count_event_pending = false;
1406 snprintf(url, MAX_URL, "http://");
1407 p->in_addr.toUrl(url+7, MAX_URL -8 );
1408 strcat(url, "/");
1409 const MasterXaction::Pointer mx = new MasterXaction(XactionInitiator::initPeerMcast);
1410 auto *req = HttpRequest::FromUrlXXX(url, mx);
1411 assert(req != nullptr);
1412 StoreEntry *fake = storeCreateEntry(url, url, RequestFlags(), Http::METHOD_GET);
1413 const auto psstate = new PeerSelector(nullptr);
1414 psstate->request = req;
1415 HTTPMSGLOCK(psstate->request);
1416 psstate->entry = fake;
1417 psstate->peerCountMcastPeerXXX = cbdataReference(p);
1418 psstate->ping.start = current_time;
1419 mem = fake->mem_obj;
1420 mem->request = psstate->request;
1421 mem->start_ping = current_time;
1422 mem->ping_reply_callback = peerCountHandleIcpReply;
1423 mem->ircb_data = psstate;
1424 mcastSetTtl(icpOutgoingConn->fd, p->mcast.ttl);
1425 p->mcast.id = mem->id;
1426 reqnum = icpSetCacheKey((const cache_key *)fake->key);
1427 icpCreateAndSend(ICP_QUERY, 0, url, reqnum, 0,
1428 icpOutgoingConn->fd, p->in_addr, psstate->al);
1429 fake->ping_status = PING_WAITING;
1430 eventAdd("peerCountMcastPeersDone",
1431 peerCountMcastPeersDone,
1432 psstate,
1433 Config.Timeout.mcast_icp_query / 1000.0, 1);
1434 p->mcast.flags.counting = true;
1435 peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
1436 }
1437
1438 static void
1439 peerCountMcastPeersDone(void *data)
1440 {
1441 const auto psstate = static_cast<PeerSelector*>(data);
1442 StoreEntry *fake = psstate->entry;
1443
1444 if (cbdataReferenceValid(psstate->peerCountMcastPeerXXX)) {
1445 CachePeer *p = (CachePeer *)psstate->peerCountMcastPeerXXX;
1446 p->mcast.flags.counting = false;
1447 p->mcast.avg_n_members = Math::doubleAverage(p->mcast.avg_n_members, (double) psstate->ping.n_recv, ++p->mcast.n_times_counted, 10);
1448 debugs(15, DBG_IMPORTANT, "Group " << p->host << ": " << psstate->ping.n_recv <<
1449 " replies, "<< std::setw(4)<< std::setprecision(2) <<
1450 p->mcast.avg_n_members <<" average, RTT " << p->stats.rtt);
1451 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
1452 }
1453
1454 cbdataReferenceDone(psstate->peerCountMcastPeerXXX);
1455
1456 fake->abort(); // sets ENTRY_ABORTED and initiates releated cleanup
1457 fake->mem_obj->request = nullptr;
1458 fake->unlock("peerCountMcastPeersDone");
1459 delete psstate;
1460 }
1461
1462 static void
1463 peerCountHandleIcpReply(CachePeer * p, peer_t, AnyP::ProtocolType proto, void *, void *data)
1464 {
1465 const auto psstate = static_cast<PeerSelector*>(data);
1466 StoreEntry *fake = psstate->entry;
1467 assert(fake);
1468 MemObject *mem = fake->mem_obj;
1469 assert(mem);
1470 int rtt = tvSubMsec(mem->start_ping, current_time);
1471 assert(proto == AnyP::PROTO_ICP);
1472 ++ psstate->ping.n_recv;
1473 int rtt_av_factor = RTT_AV_FACTOR;
1474
1475 if (p->options.weighted_roundrobin)
1476 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
1477
1478 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, rtt_av_factor);
1479 }
1480
1481 static void
1482 neighborDumpPeers(StoreEntry * sentry)
1483 {
1484 dump_peers(sentry, Config.peers);
1485 }
1486
1487 static void
1488 neighborDumpNonPeers(StoreEntry * sentry)
1489 {
1490 dump_peers(sentry, non_peers);
1491 }
1492
1493 void
1494 dump_peer_options(StoreEntry * sentry, CachePeer * p)
1495 {
1496 if (p->options.proxy_only)
1497 storeAppendPrintf(sentry, " proxy-only");
1498
1499 if (p->options.no_query)
1500 storeAppendPrintf(sentry, " no-query");
1501
1502 if (p->options.background_ping)
1503 storeAppendPrintf(sentry, " background-ping");
1504
1505 if (p->options.no_digest)
1506 storeAppendPrintf(sentry, " no-digest");
1507
1508 if (p->options.default_parent)
1509 storeAppendPrintf(sentry, " default");
1510
1511 if (p->options.roundrobin)
1512 storeAppendPrintf(sentry, " round-robin");
1513
1514 if (p->options.carp)
1515 storeAppendPrintf(sentry, " carp");
1516
1517 #if USE_AUTH
1518 if (p->options.userhash)
1519 storeAppendPrintf(sentry, " userhash");
1520 #endif
1521
1522 if (p->options.sourcehash)
1523 storeAppendPrintf(sentry, " sourcehash");
1524
1525 if (p->options.weighted_roundrobin)
1526 storeAppendPrintf(sentry, " weighted-round-robin");
1527
1528 if (p->options.mcast_responder)
1529 storeAppendPrintf(sentry, " multicast-responder");
1530
1531 #if PEER_MULTICAST_SIBLINGS
1532 if (p->options.mcast_siblings)
1533 storeAppendPrintf(sentry, " multicast-siblings");
1534 #endif
1535
1536 if (p->weight != 1)
1537 storeAppendPrintf(sentry, " weight=%d", p->weight);
1538
1539 if (p->options.closest_only)
1540 storeAppendPrintf(sentry, " closest-only");
1541
1542 #if USE_HTCP
1543 if (p->options.htcp) {
1544 storeAppendPrintf(sentry, " htcp");
1545 if (p->options.htcp_oldsquid || p->options.htcp_no_clr || p->options.htcp_no_purge_clr || p->options.htcp_only_clr) {
1546 bool doneopts = false;
1547 if (p->options.htcp_oldsquid) {
1548 storeAppendPrintf(sentry, "oldsquid");
1549 doneopts = true;
1550 }
1551 if (p->options.htcp_no_clr) {
1552 storeAppendPrintf(sentry, "%sno-clr",(doneopts?",":"="));
1553 doneopts = true;
1554 }
1555 if (p->options.htcp_no_purge_clr) {
1556 storeAppendPrintf(sentry, "%sno-purge-clr",(doneopts?",":"="));
1557 doneopts = true;
1558 }
1559 if (p->options.htcp_only_clr) {
1560 storeAppendPrintf(sentry, "%sonly-clr",(doneopts?",":"="));
1561 //doneopts = true; // uncomment if more opts are added
1562 }
1563 }
1564 }
1565 #endif
1566
1567 if (p->options.no_netdb_exchange)
1568 storeAppendPrintf(sentry, " no-netdb-exchange");
1569
1570 #if USE_DELAY_POOLS
1571 if (p->options.no_delay)
1572 storeAppendPrintf(sentry, " no-delay");
1573 #endif
1574
1575 if (p->login)
1576 storeAppendPrintf(sentry, " login=%s", p->login);
1577
1578 if (p->mcast.ttl > 0)
1579 storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
1580
1581 if (p->connect_timeout_raw > 0)
1582 storeAppendPrintf(sentry, " connect-timeout=%d", (int)p->connect_timeout_raw);
1583
1584 if (p->connect_fail_limit != PEER_TCP_MAGIC_COUNT)
1585 storeAppendPrintf(sentry, " connect-fail-limit=%d", p->connect_fail_limit);
1586
1587 #if USE_CACHE_DIGESTS
1588
1589 if (p->digest_url)
1590 storeAppendPrintf(sentry, " digest-url=%s", p->digest_url);
1591
1592 #endif
1593
1594 if (p->options.allow_miss)
1595 storeAppendPrintf(sentry, " allow-miss");
1596
1597 if (p->options.no_tproxy)
1598 storeAppendPrintf(sentry, " no-tproxy");
1599
1600 if (p->max_conn > 0)
1601 storeAppendPrintf(sentry, " max-conn=%d", p->max_conn);
1602 if (p->standby.limit > 0)
1603 storeAppendPrintf(sentry, " standby=%d", p->standby.limit);
1604
1605 if (p->options.originserver)
1606 storeAppendPrintf(sentry, " originserver");
1607
1608 if (p->domain)
1609 storeAppendPrintf(sentry, " forceddomain=%s", p->domain);
1610
1611 if (p->connection_auth == 0)
1612 storeAppendPrintf(sentry, " connection-auth=off");
1613 else if (p->connection_auth == 1)
1614 storeAppendPrintf(sentry, " connection-auth=on");
1615 else if (p->connection_auth == 2)
1616 storeAppendPrintf(sentry, " connection-auth=auto");
1617
1618 p->secure.dumpCfg(sentry,"tls-");
1619 storeAppendPrintf(sentry, "\n");
1620 }
1621
1622 static void
1623 dump_peers(StoreEntry * sentry, CachePeer * peers)
1624 {
1625 char ntoabuf[MAX_IPSTRLEN];
1626 int i;
1627
1628 if (peers == NULL)
1629 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
1630
1631 for (CachePeer *e = peers; e; e = e->next) {
1632 assert(e->host != NULL);
1633 storeAppendPrintf(sentry, "\n%-11.11s: %s\n",
1634 neighborTypeStr(e),
1635 e->name);
1636 storeAppendPrintf(sentry, "Host : %s/%d/%d\n",
1637 e->host,
1638 e->http_port,
1639 e->icp.port);
1640 storeAppendPrintf(sentry, "Flags :");
1641 dump_peer_options(sentry, e);
1642
1643 for (i = 0; i < e->n_addresses; ++i) {
1644 storeAppendPrintf(sentry, "Address[%d] : %s\n", i,
1645 e->addresses[i].toStr(ntoabuf,MAX_IPSTRLEN) );
1646 }
1647
1648 storeAppendPrintf(sentry, "Status : %s\n",
1649 neighborUp(e) ? "Up" : "Down");
1650 storeAppendPrintf(sentry, "FETCHES : %d\n", e->stats.fetches);
1651 storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open);
1652 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
1653
1654 if (!e->options.no_query) {
1655 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
1656 (int) (squid_curtime - e->stats.last_query));
1657
1658 if (e->stats.last_reply > 0)
1659 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
1660 (int) (squid_curtime - e->stats.last_reply));
1661 else
1662 storeAppendPrintf(sentry, "LAST REPLY : none received\n");
1663
1664 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
1665
1666 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
1667 e->stats.pings_acked,
1668 Math::intPercent(e->stats.pings_acked, e->stats.pings_sent));
1669 }
1670
1671 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n", e->stats.ignored_replies, Math::intPercent(e->stats.ignored_replies, e->stats.pings_acked));
1672
1673 if (!e->options.no_query) {
1674 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1675 #if USE_HTCP
1676
1677 if (e->options.htcp) {
1678 storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
1679 e->htcp.counts[0],
1680 Math::intPercent(e->htcp.counts[0], e->stats.pings_acked));
1681 storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
1682 e->htcp.counts[1],
1683 Math::intPercent(e->htcp.counts[1], e->stats.pings_acked));
1684 } else {
1685 #endif
1686
1687 for (auto op : WholeEnum<icp_opcode>()) {
1688 if (e->icp.counts[op] == 0)
1689 continue;
1690
1691 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1692 icp_opcode_str[op],
1693 e->icp.counts[op],
1694 Math::intPercent(e->icp.counts[op], e->stats.pings_acked));
1695 }
1696
1697 #if USE_HTCP
1698
1699 }
1700
1701 #endif
1702
1703 }
1704
1705 if (e->stats.last_connect_failure) {
1706 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
1707 Time::FormatHttpd(e->stats.last_connect_failure));
1708 }
1709
1710 storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n", Math::intPercent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
1711 }
1712 }
1713
1714 #if USE_HTCP
1715 void
1716 neighborsHtcpReply(const cache_key * key, HtcpReplyData * htcp, const Ip::Address &from)
1717 {
1718 StoreEntry *e = Store::Root().findCallbackXXX(key);
1719 MemObject *mem = NULL;
1720 CachePeer *p;
1721 peer_t ntype = PEER_NONE;
1722 debugs(15, 6, "neighborsHtcpReply: " <<
1723 (htcp->hit ? "HIT" : "MISS") << " " <<
1724 storeKeyText(key) );
1725
1726 if (NULL != e)
1727 mem = e->mem_obj;
1728
1729 if ((p = whichPeer(from)))
1730 neighborAliveHtcp(p, mem, htcp);
1731
1732 /* Does the entry exist? */
1733 if (NULL == e) {
1734 debugs(12, 3, "neighyborsHtcpReply: Cache key '" << storeKeyText(key) << "' not found");
1735 neighborCountIgnored(p);
1736 return;
1737 }
1738
1739 /* check if someone is already fetching it */
1740 if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) {
1741 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
1742 neighborCountIgnored(p);
1743 return;
1744 }
1745
1746 if (mem == NULL) {
1747 debugs(15, 2, "Ignoring reply for missing mem_obj: " << storeKeyText(key));
1748 neighborCountIgnored(p);
1749 return;
1750 }
1751
1752 if (e->ping_status != PING_WAITING) {
1753 debugs(15, 2, "neighborsUdpAck: Entry " << storeKeyText(key) << " is not PING_WAITING");
1754 neighborCountIgnored(p);
1755 return;
1756 }
1757
1758 if (!e->locked()) {
1759 // TODO: many entries are unlocked; why is this reported at level 1?
1760 debugs(12, DBG_IMPORTANT, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
1761 neighborCountIgnored(p);
1762 return;
1763 }
1764
1765 if (!mem->ircb_data) {
1766 debugs(12, DBG_IMPORTANT, "BUG: missing HTCP callback data for " << *e);
1767 neighborCountIgnored(p);
1768 return;
1769 }
1770
1771 if (p) {
1772 ntype = neighborType(p, mem->request->url);
1773 neighborUpdateRtt(p, mem);
1774 }
1775
1776 if (ignoreMulticastReply(p, mem->ircb_data)) {
1777 neighborCountIgnored(p);
1778 return;
1779 }
1780
1781 debugs(15, 3, "neighborsHtcpReply: e = " << e);
1782 mem->ping_reply_callback(p, ntype, AnyP::PROTO_HTCP, htcp, mem->ircb_data);
1783 }
1784
1785 /*
1786 * Send HTCP CLR messages to all peers configured to receive them.
1787 */
1788 void
1789 neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
1790 {
1791 CachePeer *p;
1792 char buf[128];
1793
1794 for (p = Config.peers; p; p = p->next) {
1795 if (!p->options.htcp) {
1796 continue;
1797 }
1798 if (p->options.htcp_no_clr) {
1799 continue;
1800 }
1801 if (p->options.htcp_no_purge_clr && reason == HTCP_CLR_PURGE) {
1802 continue;
1803 }
1804 debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.toUrl(buf, 128));
1805 htcpClear(e, uri, req, method, p, reason);
1806 }
1807 }
1808
1809 #endif
1810