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