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