]> git.ipfire.org Git - thirdparty/squid.git/blob - src/neighbors.cc
Simulate DIRECT tunnel to origin peers on CONNECT
[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, 1, "WARNING: Peer looks like this host");
593
594 debugs(15, 1, " 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 (ICP_INVALID == echo_hdr.opcode) {
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 const cache_key *key;
836 int best_rtt = 0;
837 int choice_count = 0;
838 int ichoice_count = 0;
839 peer *p;
840 int p_rtt;
841 int i;
842
843 if (!request->flags.hierarchical)
844 return NULL;
845
846 key = storeKeyPublicByRequest(request);
847
848 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
849 lookup_t lookup;
850
851 if (!p)
852 p = Config.peers;
853
854 if (i == 1)
855 first_ping = p;
856
857 lookup = peerDigestLookup(p, request);
858
859 if (lookup == LOOKUP_NONE)
860 continue;
861
862 choice_count++;
863
864 if (lookup == LOOKUP_MISS)
865 continue;
866
867 p_rtt = netdbHostRtt(p->host);
868
869 debugs(15, 5, "neighborsDigestSelect: peer " << p->host << " rtt: " << p_rtt);
870
871 /* is this peer better than others in terms of rtt ? */
872 if (!best_p || (p_rtt && p_rtt < best_rtt)) {
873 best_p = p;
874 best_rtt = p_rtt;
875
876 if (p_rtt) /* informative choice (aka educated guess) */
877 ichoice_count++;
878
879 debugs(15, 4, "neighborsDigestSelect: peer " << p->host << " leads with rtt " << best_rtt);
880 }
881 }
882
883 debugs(15, 4, "neighborsDigestSelect: choices: " << choice_count << " (" << ichoice_count << ")");
884 peerNoteDigestLookup(request, best_p,
885 best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE));
886 request->hier.n_choices = choice_count;
887 request->hier.n_ichoices = ichoice_count;
888 #endif
889
890 return best_p;
891 }
892
893 void
894 peerNoteDigestLookup(HttpRequest * request, peer * p, lookup_t lookup)
895 {
896 #if USE_CACHE_DIGESTS
897 if (p)
898 strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host));
899 else
900 *request->hier.cd_host = '\0';
901
902 request->hier.cd_lookup = lookup;
903 debugs(15, 4, "peerNoteDigestLookup: peer " << (p? p->host : "<none>") << ", lookup: " << lookup_t_str[lookup] );
904 #endif
905 }
906
907 static void
908 neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header)
909 {
910 peerAlive(p);
911 p->stats.pings_acked++;
912
913 if ((icp_opcode) header->opcode <= ICP_END)
914 p->icp.counts[header->opcode]++;
915
916 p->icp.version = (int) header->version;
917 }
918
919 static void
920 neighborUpdateRtt(peer * p, MemObject * mem)
921 {
922 int rtt, rtt_av_factor;
923
924 if (!mem)
925 return;
926
927 if (!mem->start_ping.tv_sec)
928 return;
929
930 rtt = tvSubMsec(mem->start_ping, current_time);
931
932 if (rtt < 1 || rtt > 10000)
933 return;
934
935 rtt_av_factor = RTT_AV_FACTOR;
936
937 if (p->options.weighted_roundrobin)
938 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
939
940 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, p->stats.pings_acked, rtt_av_factor);
941 }
942
943 #if USE_HTCP
944 static void
945 neighborAliveHtcp(peer * p, const MemObject * mem, const htcpReplyData * htcp)
946 {
947 peerAlive(p);
948 p->stats.pings_acked++;
949 p->htcp.counts[htcp->hit ? 1 : 0]++;
950 p->htcp.version = htcp->version;
951 }
952
953 #endif
954
955 static void
956 neighborCountIgnored(peer * p)
957 {
958 if (p == NULL)
959 return;
960
961 p->stats.ignored_replies++;
962
963 NLateReplies++;
964 }
965
966 static peer *non_peers = NULL;
967
968 static void
969 neighborIgnoreNonPeer(const Ip::Address &from, icp_opcode opcode)
970 {
971 peer *np;
972
973 for (np = non_peers; np; np = np->next) {
974 if (np->in_addr != from)
975 continue;
976
977 if (np->in_addr.GetPort() != from.GetPort())
978 continue;
979
980 break;
981 }
982
983 if (np == NULL) {
984 np = (peer *)xcalloc(1, sizeof(peer));
985 np->in_addr = from;
986 np->icp.port = from.GetPort();
987 np->type = PEER_NONE;
988 np->host = new char[MAX_IPSTRLEN];
989 from.NtoA(np->host,MAX_IPSTRLEN);
990 np->next = non_peers;
991 non_peers = np;
992 }
993
994 np->icp.counts[opcode]++;
995
996 if (isPowTen(++np->stats.ignored_replies))
997 debugs(15, 1, "WARNING: Ignored " << np->stats.ignored_replies << " replies from non-peer " << np->host);
998 }
999
1000 /* ignoreMulticastReply
1001 *
1002 * * We want to ignore replies from multicast peers if the
1003 * * cache_host_domain rules would normally prevent the peer
1004 * * from being used
1005 */
1006 static int
1007 ignoreMulticastReply(peer * p, MemObject * mem)
1008 {
1009 if (p == NULL)
1010 return 0;
1011
1012 if (!p->options.mcast_responder)
1013 return 0;
1014
1015 if (peerHTTPOkay(p, mem->request))
1016 return 0;
1017
1018 return 1;
1019 }
1020
1021 /**
1022 * I should attach these records to the entry. We take the first
1023 * hit we get our wait until everyone misses. The timeout handler
1024 * call needs to nip this shopping list or call one of the misses.
1025 *
1026 * If a hit process is already started, then sobeit
1027 */
1028 void
1029 neighborsUdpAck(const cache_key * key, icp_common_t * header, const Ip::Address &from)
1030 {
1031 peer *p = NULL;
1032 StoreEntry *entry;
1033 MemObject *mem = NULL;
1034 peer_t ntype = PEER_NONE;
1035 icp_opcode opcode = (icp_opcode) header->opcode;
1036
1037 debugs(15, 6, "neighborsUdpAck: opcode " << opcode << " '" << storeKeyText(key) << "'");
1038
1039 if (NULL != (entry = Store::Root().get(key)))
1040 mem = entry->mem_obj;
1041
1042 if ((p = whichPeer(from)))
1043 neighborAlive(p, mem, header);
1044
1045 if (opcode > ICP_END)
1046 return;
1047
1048 const char *opcode_d = icp_opcode_str[opcode];
1049
1050 if (p)
1051 neighborUpdateRtt(p, mem);
1052
1053 /* Does the entry exist? */
1054 if (NULL == entry) {
1055 debugs(12, 3, "neighborsUdpAck: Cache key '" << storeKeyText(key) << "' not found");
1056 neighborCountIgnored(p);
1057 return;
1058 }
1059
1060 /* check if someone is already fetching it */
1061 if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) {
1062 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
1063 neighborCountIgnored(p);
1064 return;
1065 }
1066
1067 if (mem == NULL) {
1068 debugs(15, 2, "Ignoring " << opcode_d << " for missing mem_obj: " << storeKeyText(key));
1069 neighborCountIgnored(p);
1070 return;
1071 }
1072
1073 if (entry->ping_status != PING_WAITING) {
1074 debugs(15, 2, "neighborsUdpAck: Late " << opcode_d << " for " << storeKeyText(key));
1075 neighborCountIgnored(p);
1076 return;
1077 }
1078
1079 if (entry->lock_count == 0) {
1080 debugs(12, 1, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
1081 neighborCountIgnored(p);
1082 return;
1083 }
1084
1085 debugs(15, 3, "neighborsUdpAck: " << opcode_d << " for '" << storeKeyText(key) << "' from " << (p ? p->host : "source") << " ");
1086
1087 if (p) {
1088 ntype = neighborType(p, mem->request);
1089 }
1090
1091 if (ignoreMulticastReply(p, mem)) {
1092 neighborCountIgnored(p);
1093 } else if (opcode == ICP_MISS) {
1094 if (p == NULL) {
1095 neighborIgnoreNonPeer(from, opcode);
1096 } else {
1097 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1098 }
1099 } else if (opcode == ICP_HIT) {
1100 if (p == NULL) {
1101 neighborIgnoreNonPeer(from, opcode);
1102 } else {
1103 header->opcode = ICP_HIT;
1104 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1105 }
1106 } else if (opcode == ICP_DECHO) {
1107 if (p == NULL) {
1108 neighborIgnoreNonPeer(from, opcode);
1109 } else if (ntype == PEER_SIBLING) {
1110 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
1111 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
1112 } else {
1113 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1114 }
1115 } else if (opcode == ICP_SECHO) {
1116 if (p) {
1117 debugs(15, 1, "Ignoring SECHO from neighbor " << p->host);
1118 neighborCountIgnored(p);
1119 } else {
1120 debugs(15, 1, "Unsolicited SECHO from " << from);
1121 }
1122 } else if (opcode == ICP_DENIED) {
1123 if (p == NULL) {
1124 neighborIgnoreNonPeer(from, opcode);
1125 } else if (p->stats.pings_acked > 100) {
1126 if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
1127 debugs(15, 0, "95%% of replies from '" << p->host << "' are UDP_DENIED");
1128 debugs(15, 0, "Disabling '" << p->host << "', please check your configuration.");
1129 neighborRemove(p);
1130 p = NULL;
1131 } else {
1132 neighborCountIgnored(p);
1133 }
1134 }
1135 } else if (opcode == ICP_MISS_NOFETCH) {
1136 mem->ping_reply_callback(p, ntype, AnyP::PROTO_ICP, header, mem->ircb_data);
1137 } else {
1138 debugs(15, 0, "neighborsUdpAck: Unexpected ICP reply: " << opcode_d);
1139 }
1140 }
1141
1142 peer *
1143 peerFindByName(const char *name)
1144 {
1145 peer *p = NULL;
1146
1147 for (p = Config.peers; p; p = p->next) {
1148 if (!strcasecmp(name, p->name))
1149 break;
1150 }
1151
1152 return p;
1153 }
1154
1155 peer *
1156 peerFindByNameAndPort(const char *name, unsigned short port)
1157 {
1158 peer *p = NULL;
1159
1160 for (p = Config.peers; p; p = p->next) {
1161 if (strcasecmp(name, p->name))
1162 continue;
1163
1164 if (port != p->http_port)
1165 continue;
1166
1167 break;
1168 }
1169
1170 return p;
1171 }
1172
1173 int
1174 neighborUp(const peer * p)
1175 {
1176 if (!p->tcp_up) {
1177 if (!peerProbeConnect((peer *) p)) {
1178 debugs(15, 8, "neighborUp: DOWN (probed): " << p->host << " (" << p->in_addr << ")");
1179 return 0;
1180 }
1181 }
1182
1183 /*
1184 * The peer can not be UP if we don't have any IP addresses
1185 * for it.
1186 */
1187 if (0 == p->n_addresses) {
1188 debugs(15, 8, "neighborUp: DOWN (no-ip): " << p->host << " (" << p->in_addr << ")");
1189 return 0;
1190 }
1191
1192 if (p->options.no_query) {
1193 debugs(15, 8, "neighborUp: UP (no-query): " << p->host << " (" << p->in_addr << ")");
1194 return 1;
1195 }
1196
1197 if (p->stats.probe_start != 0 &&
1198 squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer) {
1199 debugs(15, 8, "neighborUp: DOWN (dead): " << p->host << " (" << p->in_addr << ")");
1200 return 0;
1201 }
1202
1203 debugs(15, 8, "neighborUp: UP: " << p->host << " (" << p->in_addr << ")");
1204 return 1;
1205 }
1206
1207 void
1208 peerDestroy(void *data)
1209 {
1210 peer *p = (peer *)data;
1211
1212 if (p == NULL)
1213 return;
1214
1215 struct _domain_ping *nl = NULL;
1216
1217 for (struct _domain_ping *l = p->peer_domain; l; l = nl) {
1218 nl = l->next;
1219 safe_free(l->domain);
1220 xfree(l);
1221 }
1222
1223 safe_free(p->host);
1224 safe_free(p->name);
1225 safe_free(p->domain);
1226 #if USE_CACHE_DIGESTS
1227
1228 cbdataReferenceDone(p->digest);
1229 #endif
1230 }
1231
1232 void
1233 peerNoteDigestGone(peer * p)
1234 {
1235 #if USE_CACHE_DIGESTS
1236 cbdataReferenceDone(p->digest);
1237 #endif
1238 }
1239
1240 static void
1241 peerDNSConfigure(const ipcache_addrs *ia, const DnsLookupDetails &, void *data)
1242 {
1243 peer *p = (peer *)data;
1244
1245 int j;
1246
1247 if (p->n_addresses == 0) {
1248 debugs(15, 1, "Configuring " << neighborTypeStr(p) << " " << p->host << "/" << p->http_port << "/" << p->icp.port);
1249
1250 if (p->type == PEER_MULTICAST)
1251 debugs(15, 1, " Multicast TTL = " << p->mcast.ttl);
1252 }
1253
1254 p->n_addresses = 0;
1255
1256 if (ia == NULL) {
1257 debugs(0, 0, "WARNING: DNS lookup for '" << p->host << "' failed!");
1258 return;
1259 }
1260
1261 if ((int) ia->count < 1) {
1262 debugs(0, 0, "WARNING: No IP address found for '" << p->host << "'!");
1263 return;
1264 }
1265
1266 p->tcp_up = p->connect_fail_limit;
1267
1268 for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) {
1269 p->addresses[j] = ia->in_addrs[j];
1270 debugs(15, 2, "--> IP address #" << j << ": " << p->addresses[j]);
1271 p->n_addresses++;
1272 }
1273
1274 p->in_addr.SetEmpty();
1275 p->in_addr = p->addresses[0];
1276 p->in_addr.SetPort(p->icp.port);
1277
1278 if (p->type == PEER_MULTICAST)
1279 peerCountMcastPeersSchedule(p, 10);
1280
1281 #if USE_ICMP
1282 if (p->type != PEER_MULTICAST)
1283 if (!p->options.no_netdb_exchange)
1284 eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1);
1285 #endif
1286
1287 }
1288
1289 static void
1290 peerRefreshDNS(void *data)
1291 {
1292 peer *p = NULL;
1293
1294 if (eventFind(peerRefreshDNS, NULL))
1295 eventDelete(peerRefreshDNS, NULL);
1296
1297 if (!data && 0 == stat5minClientRequests()) {
1298 /* no recent client traffic, wait a bit */
1299 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1);
1300 return;
1301 }
1302
1303 for (p = Config.peers; p; p = p->next)
1304 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
1305
1306 /* Reconfigure the peers every hour */
1307 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1);
1308 }
1309
1310 static void
1311 peerConnectFailedSilent(peer * p)
1312 {
1313 p->stats.last_connect_failure = squid_curtime;
1314
1315 if (!p->tcp_up) {
1316 debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port <<
1317 " dead");
1318 return;
1319 }
1320
1321 p->tcp_up--;
1322
1323 if (!p->tcp_up) {
1324 debugs(15, 1, "Detected DEAD " << neighborTypeStr(p) << ": " << p->name);
1325 p->stats.logged_state = PEER_DEAD;
1326 }
1327 }
1328
1329 void
1330 peerConnectFailed(peer *p)
1331 {
1332 debugs(15, 1, "TCP connection to " << p->host << "/" << p->http_port << " failed");
1333 peerConnectFailedSilent(p);
1334 }
1335
1336 void
1337 peerConnectSucceded(peer * p)
1338 {
1339 if (!p->tcp_up) {
1340 debugs(15, 2, "TCP connection to " << p->host << "/" << p->http_port << " succeded");
1341 p->tcp_up = p->connect_fail_limit; // NP: so peerAlive(p) works properly.
1342 peerAlive(p);
1343 if (!p->n_addresses)
1344 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
1345 } else
1346 p->tcp_up = p->connect_fail_limit;
1347 }
1348
1349 /// called by Comm when test_fd is closed while connect is in progress
1350 static void
1351 peerProbeClosed(int fd, void *data)
1352 {
1353 peer *p = (peer*)data;
1354 p->test_fd = -1;
1355 // it is a failure because we failed to connect
1356 peerConnectFailedSilent(p);
1357 }
1358
1359 static void
1360 peerProbeConnectTimeout(int fd, void *data)
1361 {
1362 peer * p = (peer *)data;
1363 comm_remove_close_handler(fd, &peerProbeClosed, p);
1364 comm_close(fd);
1365 p->test_fd = -1;
1366 peerConnectFailedSilent(p);
1367 }
1368
1369 /*
1370 * peerProbeConnect will be called on dead peers by neighborUp
1371 */
1372 static int
1373 peerProbeConnect(peer * p)
1374 {
1375 int fd;
1376 time_t ctimeout = p->connect_timeout > 0 ? p->connect_timeout
1377 : Config.Timeout.peer_connect;
1378 int ret = squid_curtime - p->stats.last_connect_failure > ctimeout * 10;
1379
1380 if (p->test_fd != -1)
1381 return ret;/* probe already running */
1382
1383 if (squid_curtime - p->stats.last_connect_probe == 0)
1384 return ret;/* don't probe to often */
1385
1386 Ip::Address temp(getOutgoingAddr(NULL,p));
1387
1388 // if IPv6 is disabled try to force IPv4-only outgoing.
1389 if (!Ip::EnableIpv6 && !temp.SetIPv4()) {
1390 debugs(50, DBG_IMPORTANT, "WARNING: IPv6 is disabled. Failed to use " << temp << " to probe " << p->host);
1391 return ret;
1392 }
1393
1394 // if IPv6 is split-stack, prefer IPv4
1395 if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK) {
1396 // NP: This is not a great choice of default,
1397 // but with the current Internet being IPv4-majority has a higher success rate.
1398 // if setting to IPv4 fails we dont care, that just means to use IPv6 outgoing.
1399 temp.SetIPv4();
1400 }
1401
1402 fd = comm_open(SOCK_STREAM, IPPROTO_TCP, temp, COMM_NONBLOCKING, p->host);
1403
1404 if (fd < 0)
1405 return ret;
1406
1407 comm_add_close_handler(fd, &peerProbeClosed, p);
1408 commSetTimeout(fd, ctimeout, peerProbeConnectTimeout, p);
1409
1410 p->test_fd = fd;
1411
1412 p->stats.last_connect_probe = squid_curtime;
1413
1414 commConnectStart(p->test_fd,
1415 p->host,
1416 p->http_port,
1417 peerProbeConnectDone,
1418 p);
1419
1420 return ret;
1421 }
1422
1423 static void
1424 peerProbeConnectDone(int fd, const DnsLookupDetails &, comm_err_t status, int xerrno, void *data)
1425 {
1426 peer *p = (peer*)data;
1427
1428 if (status == COMM_OK) {
1429 peerConnectSucceded(p);
1430 } else {
1431 peerConnectFailedSilent(p);
1432 }
1433
1434 comm_remove_close_handler(fd, &peerProbeClosed, p);
1435 comm_close(fd);
1436 p->test_fd = -1;
1437 return;
1438 }
1439
1440 static void
1441 peerCountMcastPeersSchedule(peer * p, time_t when)
1442 {
1443 if (p->mcast.flags.count_event_pending)
1444 return;
1445
1446 eventAdd("peerCountMcastPeersStart",
1447 peerCountMcastPeersStart,
1448 p,
1449 (double) when, 1);
1450
1451 p->mcast.flags.count_event_pending = 1;
1452 }
1453
1454 static void
1455 peerCountMcastPeersStart(void *data)
1456 {
1457 peer *p = (peer *)data;
1458 ps_state *psstate;
1459 StoreEntry *fake;
1460 MemObject *mem;
1461 icp_common_t *query;
1462 int reqnum;
1463 LOCAL_ARRAY(char, url, MAX_URL);
1464 assert(p->type == PEER_MULTICAST);
1465 p->mcast.flags.count_event_pending = 0;
1466 snprintf(url, MAX_URL, "http://");
1467 p->in_addr.ToURL(url+7, MAX_URL -8 );
1468 strcat(url, "/");
1469 fake = storeCreateEntry(url, url, request_flags(), METHOD_GET);
1470 HttpRequest *req = HttpRequest::CreateFromUrl(url);
1471 psstate = new ps_state;
1472 psstate->request = HTTPMSGLOCK(req);
1473 psstate->entry = fake;
1474 psstate->callback = NULL;
1475 psstate->callback_data = cbdataReference(p);
1476 psstate->ping.start = current_time;
1477 mem = fake->mem_obj;
1478 mem->request = HTTPMSGLOCK(psstate->request);
1479 mem->start_ping = current_time;
1480 mem->ping_reply_callback = peerCountHandleIcpReply;
1481 mem->ircb_data = psstate;
1482 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
1483 p->mcast.id = mem->id;
1484 reqnum = icpSetCacheKey((const cache_key *)fake->key);
1485 query = _icp_common_t::createMessage(ICP_QUERY, 0, url, reqnum, 0);
1486 icpUdpSend(theOutIcpConnection,
1487 p->in_addr,
1488 query,
1489 LOG_ICP_QUERY,
1490 0);
1491 fake->ping_status = PING_WAITING;
1492 eventAdd("peerCountMcastPeersDone",
1493 peerCountMcastPeersDone,
1494 psstate,
1495 Config.Timeout.mcast_icp_query / 1000.0, 1);
1496 p->mcast.flags.counting = 1;
1497 peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
1498 }
1499
1500 static void
1501 peerCountMcastPeersDone(void *data)
1502 {
1503 ps_state *psstate = (ps_state *)data;
1504 StoreEntry *fake = psstate->entry;
1505
1506 if (cbdataReferenceValid(psstate->callback_data)) {
1507 peer *p = (peer *)psstate->callback_data;
1508 p->mcast.flags.counting = 0;
1509 p->mcast.avg_n_members = Math::doubleAverage(p->mcast.avg_n_members, (double) psstate->ping.n_recv, ++p->mcast.n_times_counted, 10);
1510 debugs(15, 1, "Group " << p->host << ": " << psstate->ping.n_recv <<
1511 " replies, "<< std::setw(4)<< std::setprecision(2) <<
1512 p->mcast.avg_n_members <<" average, RTT " << p->stats.rtt);
1513 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
1514 }
1515
1516 cbdataReferenceDone(psstate->callback_data);
1517
1518 EBIT_SET(fake->flags, ENTRY_ABORTED);
1519 HTTPMSGUNLOCK(fake->mem_obj->request);
1520 fake->releaseRequest();
1521 fake->unlock();
1522 HTTPMSGUNLOCK(psstate->request);
1523 cbdataFree(psstate);
1524 }
1525
1526 static void
1527 peerCountHandleIcpReply(peer * p, peer_t type, AnyP::ProtocolType proto, void *hdrnotused, void *data)
1528 {
1529 int rtt_av_factor;
1530
1531 ps_state *psstate = (ps_state *)data;
1532 StoreEntry *fake = psstate->entry;
1533 MemObject *mem = fake->mem_obj;
1534 int rtt = tvSubMsec(mem->start_ping, current_time);
1535 assert(proto == AnyP::PROTO_ICP);
1536 assert(fake);
1537 assert(mem);
1538 psstate->ping.n_recv++;
1539 rtt_av_factor = RTT_AV_FACTOR;
1540
1541 if (p->options.weighted_roundrobin)
1542 rtt_av_factor = RTT_BACKGROUND_AV_FACTOR;
1543
1544 p->stats.rtt = Math::intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, rtt_av_factor);
1545 }
1546
1547 static void
1548 neighborDumpPeers(StoreEntry * sentry)
1549 {
1550 dump_peers(sentry, Config.peers);
1551 }
1552
1553 static void
1554 neighborDumpNonPeers(StoreEntry * sentry)
1555 {
1556 dump_peers(sentry, non_peers);
1557 }
1558
1559 void
1560 dump_peer_options(StoreEntry * sentry, peer * p)
1561 {
1562 if (p->options.proxy_only)
1563 storeAppendPrintf(sentry, " proxy-only");
1564
1565 if (p->options.no_query)
1566 storeAppendPrintf(sentry, " no-query");
1567
1568 if (p->options.background_ping)
1569 storeAppendPrintf(sentry, " background-ping");
1570
1571 if (p->options.no_digest)
1572 storeAppendPrintf(sentry, " no-digest");
1573
1574 if (p->options.default_parent)
1575 storeAppendPrintf(sentry, " default");
1576
1577 if (p->options.roundrobin)
1578 storeAppendPrintf(sentry, " round-robin");
1579
1580 if (p->options.carp)
1581 storeAppendPrintf(sentry, " carp");
1582 #if USE_AUTH
1583 if (p->options.userhash)
1584 storeAppendPrintf(sentry, " userhash");
1585
1586 if (p->options.userhash)
1587 storeAppendPrintf(sentry, " sourcehash");
1588 #endif
1589 if (p->options.weighted_roundrobin)
1590 storeAppendPrintf(sentry, " weighted-round-robin");
1591
1592 if (p->options.mcast_responder)
1593 storeAppendPrintf(sentry, " multicast-responder");
1594
1595 #if PEER_MULTICAST_SIBLINGS
1596 if (p->options.mcast_siblings)
1597 storeAppendPrintf(sentry, " multicast-siblings");
1598 #endif
1599
1600 if (p->weight != 1)
1601 storeAppendPrintf(sentry, " weight=%d", p->weight);
1602
1603 if (p->options.closest_only)
1604 storeAppendPrintf(sentry, " closest-only");
1605
1606 #if USE_HTCP
1607 if (p->options.htcp) {
1608 storeAppendPrintf(sentry, " htcp");
1609 if (p->options.htcp_oldsquid || p->options.htcp_no_clr || p->options.htcp_no_purge_clr || p->options.htcp_only_clr) {
1610 int doneopts=0;
1611 if (p->options.htcp_oldsquid)
1612 storeAppendPrintf(sentry, "%soldsquid",(doneopts++>0?",":"="));
1613 if (p->options.htcp_no_clr)
1614 storeAppendPrintf(sentry, "%sno-clr",(doneopts++>0?",":"="));
1615 if (p->options.htcp_no_purge_clr)
1616 storeAppendPrintf(sentry, "%sno-purge-clr",(doneopts++>0?",":"="));
1617 if (p->options.htcp_only_clr)
1618 storeAppendPrintf(sentry, "%sonly-clr",(doneopts++>0?",":"="));
1619 }
1620 }
1621 #endif
1622
1623 if (p->options.no_netdb_exchange)
1624 storeAppendPrintf(sentry, " no-netdb-exchange");
1625
1626 #if USE_DELAY_POOLS
1627 if (p->options.no_delay)
1628 storeAppendPrintf(sentry, " no-delay");
1629 #endif
1630
1631 if (p->login)
1632 storeAppendPrintf(sentry, " login=%s", p->login);
1633
1634 if (p->mcast.ttl > 0)
1635 storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
1636
1637 if (p->connect_timeout > 0)
1638 storeAppendPrintf(sentry, " connect-timeout=%d", (int) p->connect_timeout);
1639
1640 if (p->connect_fail_limit != PEER_TCP_MAGIC_COUNT)
1641 storeAppendPrintf(sentry, " connect-fail-limit=%d", p->connect_fail_limit);
1642
1643 #if USE_CACHE_DIGESTS
1644
1645 if (p->digest_url)
1646 storeAppendPrintf(sentry, " digest-url=%s", p->digest_url);
1647
1648 #endif
1649
1650 if (p->options.allow_miss)
1651 storeAppendPrintf(sentry, " allow-miss");
1652
1653 if (p->options.no_tproxy)
1654 storeAppendPrintf(sentry, " no-tproxy");
1655
1656 if (p->max_conn > 0)
1657 storeAppendPrintf(sentry, " max-conn=%d", p->max_conn);
1658
1659 if (p->options.originserver)
1660 storeAppendPrintf(sentry, " originserver");
1661
1662 if (p->domain)
1663 storeAppendPrintf(sentry, " forceddomain=%s", p->domain);
1664
1665 if (p->connection_auth == 0)
1666 storeAppendPrintf(sentry, " connection-auth=off");
1667 else if (p->connection_auth == 1)
1668 storeAppendPrintf(sentry, " connection-auth=on");
1669 else if (p->connection_auth == 2)
1670 storeAppendPrintf(sentry, " connection-auth=auto");
1671
1672 storeAppendPrintf(sentry, "\n");
1673 }
1674
1675 static void
1676 dump_peers(StoreEntry * sentry, peer * peers)
1677 {
1678 peer *e = NULL;
1679 char ntoabuf[MAX_IPSTRLEN];
1680 struct _domain_ping *d = NULL;
1681 icp_opcode op;
1682 int i;
1683
1684 if (peers == NULL)
1685 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
1686
1687 for (e = peers; e; e = e->next) {
1688 assert(e->host != NULL);
1689 storeAppendPrintf(sentry, "\n%-11.11s: %s\n",
1690 neighborTypeStr(e),
1691 e->name);
1692 storeAppendPrintf(sentry, "Host : %s/%d/%d\n",
1693 e->host,
1694 e->http_port,
1695 e->icp.port);
1696 storeAppendPrintf(sentry, "Flags :");
1697 dump_peer_options(sentry, e);
1698
1699 for (i = 0; i < e->n_addresses; i++) {
1700 storeAppendPrintf(sentry, "Address[%d] : %s\n", i,
1701 e->addresses[i].NtoA(ntoabuf,MAX_IPSTRLEN) );
1702 }
1703
1704 storeAppendPrintf(sentry, "Status : %s\n",
1705 neighborUp(e) ? "Up" : "Down");
1706 storeAppendPrintf(sentry, "FETCHES : %d\n", e->stats.fetches);
1707 storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open);
1708 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
1709
1710 if (!e->options.no_query) {
1711 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
1712 (int) (squid_curtime - e->stats.last_query));
1713
1714 if (e->stats.last_reply > 0)
1715 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
1716 (int) (squid_curtime - e->stats.last_reply));
1717 else
1718 storeAppendPrintf(sentry, "LAST REPLY : none received\n");
1719
1720 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
1721
1722 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
1723 e->stats.pings_acked,
1724 Math::intPercent(e->stats.pings_acked, e->stats.pings_sent));
1725 }
1726
1727 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n", e->stats.ignored_replies, Math::intPercent(e->stats.ignored_replies, e->stats.pings_acked));
1728
1729 if (!e->options.no_query) {
1730 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1731 #if USE_HTCP
1732
1733 if (e->options.htcp) {
1734 storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
1735 e->htcp.counts[0],
1736 Math::intPercent(e->htcp.counts[0], e->stats.pings_acked));
1737 storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
1738 e->htcp.counts[1],
1739 Math::intPercent(e->htcp.counts[1], e->stats.pings_acked));
1740 } else {
1741 #endif
1742
1743 for (op = ICP_INVALID; op < ICP_END; ++op) {
1744 if (e->icp.counts[op] == 0)
1745 continue;
1746
1747 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1748 icp_opcode_str[op],
1749 e->icp.counts[op],
1750 Math::intPercent(e->icp.counts[op], e->stats.pings_acked));
1751 }
1752
1753 #if USE_HTCP
1754
1755 }
1756
1757 #endif
1758
1759 }
1760
1761 if (e->stats.last_connect_failure) {
1762 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
1763 Time::FormatHttpd(e->stats.last_connect_failure));
1764 }
1765
1766 if (e->peer_domain != NULL) {
1767 storeAppendPrintf(sentry, "DOMAIN LIST: ");
1768
1769 for (d = e->peer_domain; d; d = d->next) {
1770 storeAppendPrintf(sentry, "%s%s ",
1771 d->do_ping ? null_string : "!", d->domain);
1772 }
1773
1774 storeAppendPrintf(sentry, "\n");
1775 }
1776
1777 storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n", Math::intPercent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
1778 }
1779 }
1780
1781 #if USE_HTCP
1782 void
1783 neighborsHtcpReply(const cache_key * key, htcpReplyData * htcp, const Ip::Address &from)
1784 {
1785 StoreEntry *e = Store::Root().get(key);
1786 MemObject *mem = NULL;
1787 peer *p;
1788 peer_t ntype = PEER_NONE;
1789 debugs(15, 6, "neighborsHtcpReply: " <<
1790 (htcp->hit ? "HIT" : "MISS") << " " <<
1791 storeKeyText(key) );
1792
1793 if (NULL != e)
1794 mem = e->mem_obj;
1795
1796 if ((p = whichPeer(from)))
1797 neighborAliveHtcp(p, mem, htcp);
1798
1799 /* Does the entry exist? */
1800 if (NULL == e) {
1801 debugs(12, 3, "neighyborsHtcpReply: Cache key '" << storeKeyText(key) << "' not found");
1802 neighborCountIgnored(p);
1803 return;
1804 }
1805
1806 /* check if someone is already fetching it */
1807 if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) {
1808 debugs(15, 3, "neighborsUdpAck: '" << storeKeyText(key) << "' already being fetched.");
1809 neighborCountIgnored(p);
1810 return;
1811 }
1812
1813 if (mem == NULL) {
1814 debugs(15, 2, "Ignoring reply for missing mem_obj: " << storeKeyText(key));
1815 neighborCountIgnored(p);
1816 return;
1817 }
1818
1819 if (e->ping_status != PING_WAITING) {
1820 debugs(15, 2, "neighborsUdpAck: Entry " << storeKeyText(key) << " is not PING_WAITING");
1821 neighborCountIgnored(p);
1822 return;
1823 }
1824
1825 if (e->lock_count == 0) {
1826 debugs(12, 1, "neighborsUdpAck: '" << storeKeyText(key) << "' has no locks");
1827 neighborCountIgnored(p);
1828 return;
1829 }
1830
1831 if (p) {
1832 ntype = neighborType(p, mem->request);
1833 neighborUpdateRtt(p, mem);
1834 }
1835
1836 if (ignoreMulticastReply(p, mem)) {
1837 neighborCountIgnored(p);
1838 return;
1839 }
1840
1841 debugs(15, 3, "neighborsHtcpReply: e = " << e);
1842 mem->ping_reply_callback(p, ntype, AnyP::PROTO_HTCP, htcp, mem->ircb_data);
1843 }
1844
1845 /*
1846 * Send HTCP CLR messages to all peers configured to receive them.
1847 */
1848 void
1849 neighborsHtcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestMethod &method, htcp_clr_reason reason)
1850 {
1851 peer *p;
1852 char buf[128];
1853
1854 for (p = Config.peers; p; p = p->next) {
1855 if (!p->options.htcp) {
1856 continue;
1857 }
1858 if (p->options.htcp_no_clr) {
1859 continue;
1860 }
1861 if (p->options.htcp_no_purge_clr && reason == HTCP_CLR_PURGE) {
1862 continue;
1863 }
1864 debugs(15, 3, "neighborsHtcpClear: sending CLR to " << p->in_addr.ToURL(buf, 128));
1865 htcpClear(e, uri, req, method, p, reason);
1866 }
1867 }
1868
1869 #endif