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