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