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