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