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