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