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