]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
Mark successfull FTP transfers as complete to allow them to stay in the
[thirdparty/squid.git] / src / neighbors.cc
CommitLineData
22f3fd98 1
30a4f2a8 2/*
43af4d27 3 * $Id: neighbors.cc,v 1.300 2002/04/07 03:35:30 hno 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"
090089c4 37
429fdbec 38/* count mcast group peers every 15 minutes */
39#define MCAST_COUNT_RATE 900
40
f5b8bbc4 41static int peerAllowedToUse(const peer *, request_t *);
f5b8bbc4 42static int peerWouldBePinged(const peer *, request_t *);
43static void neighborRemove(peer *);
f5b8bbc4 44static void neighborAlive(peer *, const MemObject *, const icp_common_t *);
399cabec 45#if USE_HTCP
46static void neighborAliveHtcp(peer *, const MemObject *, const htcpReplyData *);
47#endif
56b62ecc 48static void neighborCountIgnored(peer *);
f5b8bbc4 49static void peerRefreshDNS(void *);
b69f7771 50static IPH peerDNSConfigure;
eb406bb7 51static void peerProbeConnect(peer *);
52static IPH peerProbeConnect2;
53static CNCB peerProbeConnectDone;
f5b8bbc4 54static void peerCountMcastPeersDone(void *data);
55static void peerCountMcastPeersStart(void *data);
56static void peerCountMcastPeersSchedule(peer * p, time_t when);
b3264694 57static IRCB peerCountHandleIcpReply;
f5b8bbc4 58static void neighborIgnoreNonPeer(const struct sockaddr_in *, icp_opcode);
ed7f5615 59static OBJH neighborDumpPeers;
60static OBJH neighborDumpNonPeers;
61static void dump_peers(StoreEntry * sentry, peer * peers);
090089c4 62
090089c4 63static icp_common_t echo_hdr;
30a4f2a8 64static u_short echo_port;
30a4f2a8 65
c7a3724d 66static int NLateReplies = 0;
40a1495e 67static peer *first_ping = NULL;
28070024 68
a2c963ae 69const char *
b69f7771 70neighborTypeStr(const peer * p)
69a71bd7 71{
e102ebda 72 if (p->type == PEER_NONE)
73 return "Non-Peer";
b69f7771 74 if (p->type == PEER_SIBLING)
69a71bd7 75 return "Sibling";
b69f7771 76 if (p->type == PEER_MULTICAST)
7b3bd12c 77 return "Multicast Group";
69a71bd7 78 return "Parent";
79}
80
090089c4 81
d9f9d78b 82peer *
0cdcddb9 83whichPeer(const struct sockaddr_in * from)
090089c4 84{
22e4fa85 85 int j;
4eda6afe 86 u_short port = ntohs(from->sin_port);
87 struct in_addr ip = from->sin_addr;
b69f7771 88 peer *p = NULL;
a3d5953d 89 debug(15, 3) ("whichPeer: from %s port %d\n", inet_ntoa(ip), port);
40a1495e 90 for (p = Config.peers; p; p = p->next) {
b69f7771 91 for (j = 0; j < p->n_addresses; j++) {
399cabec 92 if (ip.s_addr == p->addresses[j].s_addr && port == p->icp.port) {
b69f7771 93 return p;
090089c4 94 }
95 }
96 }
4eda6afe 97 return NULL;
090089c4 98}
99
701a8572 100peer_t
b69f7771 101neighborType(const peer * p, const request_t * request)
24a1003d 102{
b012353a 103 const struct _domain_type *d = NULL;
b69f7771 104 for (d = p->typelist; d; d = d->next) {
7e3ce7b9 105 if (0 == matchDomainName(request->host, d->domain))
deb79f06 106 if (d->type != PEER_NONE)
b012353a 107 return d->type;
24a1003d 108 }
b69f7771 109 return p->type;
24a1003d 110}
111
7b3bd12c 112/*
62fd6124 113 * peerAllowedToUse
7b3bd12c 114 *
dd4bd44e 115 * this function figures out if it is appropriate to fetch REQUEST
62fd6124 116 * from PEER.
7b3bd12c 117 */
b8d8561b 118static int
b69f7771 119peerAllowedToUse(const peer * p, request_t * request)
090089c4 120{
b012353a 121 const struct _domain_ping *d = NULL;
090089c4 122 int do_ping = 1;
f88bb09c 123 aclCheck_t checklist;
ce66013b 124 assert(request != NULL);
9689d97c 125 if (neighborType(p, request) == PEER_SIBLING) {
126 if (request->flags.nocache)
24a1003d 127 return 0;
9689d97c 128 if (request->flags.refresh)
d950cec8 129 return 0;
9689d97c 130 if (request->flags.loopdetect)
131 return 0;
132 if (request->flags.need_validation)
133 return 0;
134 }
b6a2f15e 135 if (p->peer_domain == NULL && p->access == NULL)
090089c4 136 return do_ping;
090089c4 137 do_ping = 0;
b6a2f15e 138 for (d = p->peer_domain; d; d = d->next) {
7e3ce7b9 139 if (0 == matchDomainName(request->host, d->domain)) {
130faaed 140 do_ping = d->do_ping;
141 break;
142 }
30a4f2a8 143 do_ping = !d->do_ping;
144 }
b6a2f15e 145 if (p->peer_domain && 0 == do_ping)
130faaed 146 return do_ping;
505e35db 147 if (p->access == NULL)
148 return do_ping;
d20b1cd0 149 memset(&checklist, '\0', sizeof(checklist));
8ae8bb05 150 checklist.src_addr = request->client_addr;
3c11d1f5 151 checklist.my_addr = request->my_addr;
7e3ce7b9 152 checklist.my_port = request->my_port;
f88bb09c 153 checklist.request = request;
4749aee5 154#if 0 && USE_IDENT
155 /*
156 * this is currently broken because 'request->user_ident' has been
157 * moved to conn->rfc931 and we don't have access to the parent
158 * ConnStateData here.
159 */
94e7025e 160 if (request->user_ident[0])
4749aee5 161 xstrncpy(checklist.rfc931, request->user_ident, USER_IDENT_SZ);
94e7025e 162#endif
505e35db 163 return aclCheckFast(p->access, &checklist);
090089c4 164}
165
62fd6124 166/* Return TRUE if it is okay to send an ICP request to this peer. */
167static int
b69f7771 168peerWouldBePinged(const peer * p, request_t * request)
62fd6124 169{
b69f7771 170 if (!peerAllowedToUse(p, request))
62fd6124 171 return 0;
cd196bc8 172 if (p->options.no_query)
62fd6124 173 return 0;
cd196bc8 174 if (p->options.mcast_responder)
62fd6124 175 return 0;
83b381d5 176 if (p->n_addresses == 0)
177 return 0;
0e614a06 178 if (p->icp.port == 0)
179 return 0;
62fd6124 180 /* the case below seems strange, but can happen if the
181 * URL host is on the other side of a firewall */
b69f7771 182 if (p->type == PEER_SIBLING)
92695e5e 183 if (!request->flags.hierarchical)
62fd6124 184 return 0;
83b381d5 185 /* Ping dead peers every timeout interval */
186 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
187 return 1;
188 if (!neighborUp(p))
dd4bd44e 189 return 0;
62fd6124 190 return 1;
191}
192
193/* Return TRUE if it is okay to send an HTTP request to this peer. */
8ff4505b 194int
b69f7771 195peerHTTPOkay(const peer * p, request_t * request)
62fd6124 196{
b69f7771 197 if (!peerAllowedToUse(p, request))
62fd6124 198 return 0;
b69f7771 199 if (!neighborUp(p))
62fd6124 200 return 0;
c7f9eb6d 201 if (p->max_conn)
202 if (p->stats.conn_open >= p->max_conn)
203 return 0;
62fd6124 204 return 1;
205}
206
3c6e634f 207int
208neighborsCount(request_t * request)
090089c4 209{
b69f7771 210 peer *p = NULL;
090089c4 211 int count = 0;
40a1495e 212 for (p = Config.peers; p; p = p->next)
b69f7771 213 if (peerWouldBePinged(p, request))
3c6e634f 214 count++;
a3d5953d 215 debug(15, 3) ("neighborsCount: %d\n", count);
3c6e634f 216 return count;
217}
090089c4 218
deb79f06 219peer *
3c6e634f 220getSingleParent(request_t * request)
221{
deb79f06 222 peer *p = NULL;
b69f7771 223 peer *q = NULL;
40a1495e 224 for (q = Config.peers; q; q = q->next) {
b69f7771 225 if (!peerHTTPOkay(q, request))
caebbe00 226 continue;
b69f7771 227 if (neighborType(q, request) != PEER_PARENT)
3c6e634f 228 return NULL; /* oops, found SIBLING */
229 if (p)
230 return NULL; /* oops, found second parent */
b69f7771 231 p = q;
090089c4 232 }
cd196bc8 233 if (p != NULL && !p->options.no_query)
a369131d 234 return NULL;
a3d5953d 235 debug(15, 3) ("getSingleParent: returning %s\n", p ? p->host : "NULL");
3c6e634f 236 return p;
090089c4 237}
238
deb79f06 239peer *
b8d8561b 240getFirstUpParent(request_t * request)
090089c4 241{
b69f7771 242 peer *p = NULL;
40a1495e 243 for (p = Config.peers; p; p = p->next) {
b69f7771 244 if (!neighborUp(p))
30a4f2a8 245 continue;
b69f7771 246 if (neighborType(p, request) != PEER_PARENT)
090089c4 247 continue;
b69f7771 248 if (!peerHTTPOkay(p, request))
e924600d 249 continue;
250 break;
090089c4 251 }
a3d5953d 252 debug(15, 3) ("getFirstUpParent: returning %s\n", p ? p->host : "NULL");
b69f7771 253 return p;
090089c4 254}
255
deb79f06 256peer *
48b38d01 257getRoundRobinParent(request_t * request)
258{
b69f7771 259 peer *p;
260 peer *q = NULL;
40a1495e 261 for (p = Config.peers; p; p = p->next) {
cd196bc8 262 if (!p->options.roundrobin)
0c8177e6 263 continue;
b69f7771 264 if (neighborType(p, request) != PEER_PARENT)
0c8177e6 265 continue;
b69f7771 266 if (!peerHTTPOkay(p, request))
48b38d01 267 continue;
b69f7771 268 if (q && q->rr_count < p->rr_count)
48b38d01 269 continue;
b69f7771 270 q = p;
48b38d01 271 }
b69f7771 272 if (q)
273 q->rr_count++;
a3d5953d 274 debug(15, 3) ("getRoundRobinParent: returning %s\n", q ? q->host : "NULL");
b69f7771 275 return q;
48b38d01 276}
277
82056f1e 278/* This gets called every 5 minutes to clear the round-robin counter. */
279void
280peerClearRR(void *data)
281{
282 peer *p = data;
283 p->rr_count -= p->rr_lastcount;
284 if (p->rr_count < 0)
285 p->rr_count = 0;
286 p->rr_lastcount = p->rr_count;
44a5f25e 287 eventAdd("peerClearRR", peerClearRR, p, 5 * 60.0, 0);
82056f1e 288}
289
deb79f06 290peer *
5269d0bd 291getDefaultParent(request_t * request)
292{
b69f7771 293 peer *p = NULL;
40a1495e 294 for (p = Config.peers; p; p = p->next) {
b69f7771 295 if (neighborType(p, request) != PEER_PARENT)
5269d0bd 296 continue;
cd196bc8 297 if (!p->options.default_parent)
5269d0bd 298 continue;
b69f7771 299 if (!peerHTTPOkay(p, request))
5269d0bd 300 continue;
a3d5953d 301 debug(15, 3) ("getDefaultParent: returning %s\n", p->host);
b69f7771 302 return p;
5269d0bd 303 }
669fefd4 304 debug(15, 3) ("getDefaultParent: returning NULL\n");
5269d0bd 305 return NULL;
306}
307
6a655df0 308peer *
309getAnyParent(request_t * request)
310{
311 peer *p = NULL;
312 for (p = Config.peers; p; p = p->next) {
313 if (neighborType(p, request) != PEER_PARENT)
314 continue;
315 if (!peerHTTPOkay(p, request))
316 continue;
317 debug(15, 3) ("getAnyParent: returning %s\n", p->host);
318 return p;
319 }
320 debug(15, 3) ("getAnyParent: returning NULL\n");
321 return NULL;
322}
323
deb79f06 324peer *
b69f7771 325getNextPeer(peer * p)
090089c4 326{
b69f7771 327 return p->next;
090089c4 328}
329
deb79f06 330peer *
331getFirstPeer(void)
090089c4 332{
40a1495e 333 return Config.peers;
090089c4 334}
335
b8d8561b 336static void
deb79f06 337neighborRemove(peer * target)
30a4f2a8 338{
b69f7771 339 peer *p = NULL;
340 peer **P = NULL;
40a1495e 341 p = Config.peers;
342 P = &Config.peers;
b69f7771 343 while (p) {
344 if (target == p)
30a4f2a8 345 break;
b69f7771 346 P = &p->next;
347 p = p->next;
348 }
349 if (p) {
350 *P = p->next;
b6a2f15e 351 cbdataFree(p);
40a1495e 352 Config.npeers--;
4d64d74a 353 }
40a1495e 354 first_ping = Config.peers;
4d64d74a 355}
356
b8d8561b 357void
358neighbors_open(int fd)
090089c4 359{
30a4f2a8 360 struct sockaddr_in name;
6637e3a5 361 socklen_t len = sizeof(struct sockaddr_in);
30a4f2a8 362 struct servent *sep = NULL;
b263bd3f 363 const char *me = getMyHostname();
364 peer *this;
365 peer *next;
30a4f2a8 366 memset(&name, '\0', sizeof(struct sockaddr_in));
367 if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
a3d5953d 368 debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
b263bd3f 369 for (this = Config.peers; this; this = next) {
370 sockaddr_in_list *s;
371 next = this->next;
372 if (0 != strcmp(this->host, me))
373 continue;
374 for (s = Config.Sockaddr.http; s; s = s->next) {
375 if (this->http_port != ntohs(s->s.sin_port))
376 continue;
377 debug(15, 1) ("WARNING: Peer looks like this host\n");
378 debug(15, 1) (" Ignoring %s %s/%d/%d\n",
379 neighborTypeStr(this), this->host, this->http_port,
380 this->icp.port);
381 neighborRemove(this);
382 }
383 }
384
5999b776 385 peerRefreshDNS((void *) 1);
30a4f2a8 386 if (0 == echo_hdr.opcode) {
27cd7235 387 echo_hdr.opcode = ICP_SECHO;
30a4f2a8 388 echo_hdr.version = ICP_VERSION_CURRENT;
389 echo_hdr.length = 0;
390 echo_hdr.reqnum = 0;
391 echo_hdr.flags = 0;
392 echo_hdr.pad = 0;
30a4f2a8 393 echo_hdr.shostid = name.sin_addr.s_addr;
394 sep = getservbyname("echo", "udp");
395 echo_port = sep ? ntohs((u_short) sep->s_port) : 7;
090089c4 396 }
c68e4e90 397 first_ping = Config.peers;
ed7f5615 398 cachemgrRegister("server_list",
399 "Peer Cache Statistics",
1da3b90b 400 neighborDumpPeers, 0, 1);
22f3fd98 401 cachemgrRegister("non_peers",
402 "List of Unknown sites sending ICP messages",
1da3b90b 403 neighborDumpNonPeers, 0, 1);
090089c4 404}
405
b8d8561b 406int
b6c0e933 407neighborsUdpPing(request_t * request,
641941c0 408 StoreEntry * entry,
582b6456 409 IRCB * callback,
641941c0 410 void *callback_data,
52040193 411 int *exprep,
465dc415 412 int *timeout)
090089c4 413{
9fb13bb6 414 const char *url = storeUrl(entry);
b6c0e933 415 MemObject *mem = entry->mem_obj;
b69f7771 416 peer *p = NULL;
090089c4 417 int i;
1061b406 418 int reqnum = 0;
6d2296d4 419 int flags;
9dee2904 420 icp_common_t *query;
429fdbec 421 int queries_sent = 0;
0a0bf5db 422 int peers_pinged = 0;
a8c926ff 423 int parent_timeout = 0, parent_exprep = 0;
424 int sibling_timeout = 0, sibling_exprep = 0;
090089c4 425
40a1495e 426 if (Config.peers == NULL)
090089c4 427 return 0;
ce66013b 428 if (theOutIcpConnection < 0)
429 fatal("neighborsUdpPing: There is no ICP socket!");
9fb13bb6 430 assert(entry->swap_status == SWAPOUT_NONE);
b6c0e933 431 mem->start_ping = current_time;
b4e7f82d 432 mem->ping_reply_callback = callback;
b6c0e933 433 mem->ircb_data = callback_data;
186477c1 434 reqnum = icpSetCacheKey(entry->hash.key);
40a1495e 435 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
b69f7771 436 if (p == NULL)
40a1495e 437 p = Config.peers;
a3d5953d 438 debug(15, 5) ("neighborsUdpPing: Peer %s\n", p->host);
b69f7771 439 if (!peerWouldBePinged(p, request))
deb79f06 440 continue; /* next peer */
0a0bf5db 441 peers_pinged++;
a3d5953d 442 debug(15, 4) ("neighborsUdpPing: pinging peer %s for '%s'\n",
b69f7771 443 p->host, url);
444 if (p->type == PEER_MULTICAST)
03a1ee42 445 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
186477c1 446 debug(15, 3) ("neighborsUdpPing: key = '%s'\n", storeKeyText(entry->hash.key));
a3d5953d 447 debug(15, 3) ("neighborsUdpPing: reqnum = %d\n", reqnum);
090089c4 448
dc9d133b 449#if USE_HTCP
cd196bc8 450 if (p->options.htcp) {
1994bb24 451 debug(15, 3) ("neighborsUdpPing: sending HTCP query\n");
176ae152 452 htcpQuery(entry, request, p);
dc9d133b 453 } else
454#endif
399cabec 455 if (p->icp.port == echo_port) {
a3d5953d 456 debug(15, 4) ("neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n");
1061b406 457 echo_hdr.reqnum = reqnum;
27cd7235 458 query = icpCreateMessage(ICP_DECHO, 0, url, reqnum, 0);
af00901c 459 icpUdpSend(theOutIcpConnection,
b69f7771 460 &p->in_addr,
9dee2904 461 query,
721b3dd8 462 LOG_ICP_QUERY,
aee4e794 463 0);
090089c4 464 } else {
6d2296d4 465 flags = 0;
17a0a4ee 466 if (Config.onoff.query_icmp)
399cabec 467 if (p->icp.version == ICP_VERSION_2)
429fdbec 468 flags |= ICP_FLAG_SRC_RTT;
27cd7235 469 query = icpCreateMessage(ICP_QUERY, flags, url, reqnum, 0);
af00901c 470 icpUdpSend(theOutIcpConnection,
b69f7771 471 &p->in_addr,
9dee2904 472 query,
721b3dd8 473 LOG_ICP_QUERY,
aee4e794 474 0);
090089c4 475 }
429fdbec 476 queries_sent++;
090089c4 477
b69f7771 478 p->stats.pings_sent++;
b69f7771 479 if (p->type == PEER_MULTICAST) {
dc835977 480 /*
481 * set a bogus last_reply time so neighborUp() never
482 * says a multicast peer is dead.
483 */
484 p->stats.last_reply = squid_curtime;
b69f7771 485 (*exprep) += p->mcast.n_replies_expected;
47b780e1 486 } else if (neighborUp(p)) {
487 /* its alive, expect a reply from it */
a8c926ff 488 if (neighborType(p, request) == PEER_PARENT) {
489 parent_exprep++;
490 parent_timeout += p->stats.rtt;
491 } else {
492 sibling_exprep++;
493 sibling_timeout += p->stats.rtt;
494 }
090089c4 495 } else {
1061b406 496 /* Neighbor is dead; ping it anyway, but don't expect a reply */
090089c4 497 /* log it once at the threshold */
dc835977 498 if (p->stats.logged_state == PEER_ALIVE) {
ab4b78e9 499 debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n",
b69f7771 500 neighborTypeStr(p),
399cabec 501 p->host, p->http_port, p->icp.port);
dc835977 502 p->stats.logged_state = PEER_DEAD;
090089c4 503 }
504 }
dc835977 505 p->stats.last_query = squid_curtime;
83b381d5 506 if (p->stats.probe_start == 0)
507 p->stats.probe_start = squid_curtime;
090089c4 508 }
40a1495e 509 if ((first_ping = first_ping->next) == NULL)
510 first_ping = Config.peers;
090089c4 511
0ab965da 512#if ALLOW_SOURCE_PING
090089c4 513 /* only do source_ping if we have neighbors */
40a1495e 514 if (Config.npeers) {
a369131d 515 const ipcache_addrs *ia = NULL;
516 struct sockaddr_in to_addr;
517 char *host = request->host;
17a0a4ee 518 if (!Config.onoff.source_ping) {
a3d5953d 519 debug(15, 6) ("neighborsUdpPing: Source Ping is disabled.\n");
0a0bf5db 520 } else if ((ia = ipcache_gethostbyname(host, 0))) {
a3d5953d 521 debug(15, 6) ("neighborsUdpPing: Source Ping: to %s for '%s'\n",
1061b406 522 host, url);
1061b406 523 echo_hdr.reqnum = reqnum;
16b204c4 524 if (icmp_sock != -1) {
e5f6c5c2 525 icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url);
16b204c4 526 } else {
527 to_addr.sin_family = AF_INET;
e5f6c5c2 528 to_addr.sin_addr = ia->in_addrs[ia->cur];
16b204c4 529 to_addr.sin_port = htons(echo_port);
27cd7235 530 query = icpCreateMessage(ICP_SECHO, 0, url, reqnum, 0);
16b204c4 531 icpUdpSend(theOutIcpConnection,
16b204c4 532 &to_addr,
9dee2904 533 query,
721b3dd8 534 LOG_ICP_QUERY,
aee4e794 535 0);
16b204c4 536 }
090089c4 537 } else {
a3d5953d 538 debug(15, 6) ("neighborsUdpPing: Source Ping: unknown host: %s\n",
1061b406 539 host);
090089c4 540 }
541 }
429fdbec 542#endif
a8c926ff 543 /*
544 * How many replies to expect?
545 */
546 *exprep = parent_exprep + sibling_exprep;
547
52040193 548 /*
465dc415 549 * If there is a configured timeout, use it
52040193 550 */
465dc415 551 if (Config.Timeout.icp_query)
bb337ad7 552 *timeout = Config.Timeout.icp_query;
28993292 553 else {
a8c926ff 554 if (*exprep > 0) {
555 if (parent_exprep)
556 *timeout = 2 * parent_timeout / parent_exprep;
557 else
558 *timeout = 2 * sibling_timeout / sibling_exprep;
559 } else
28993292 560 *timeout = 2000; /* 2 seconds */
561 if (Config.Timeout.icp_query_max)
562 if (*timeout > Config.Timeout.icp_query_max)
563 *timeout = Config.Timeout.icp_query_max;
43af4d27 564 if (*timeout < Config.Timeout.icp_query_min)
565 *timeout = Config.Timeout.icp_query_min;
28993292 566 }
0a0bf5db 567 return peers_pinged;
090089c4 568}
569
26b164ac 570/* lookup the digest of a given peer */
571lookup_t
f66a9ef4 572peerDigestLookup(peer * p, request_t * request)
26b164ac 573{
6cfa8966 574#if USE_CACHE_DIGESTS
f66a9ef4 575 const cache_key *key = request ? storeKeyPublicByRequest(request) : NULL;
26b164ac 576 assert(p);
0c511722 577 assert(request);
69c95dd3 578 debug(15, 5) ("peerDigestLookup: peer %s\n", p->host);
4b4cd312 579 /* does the peeer have a valid digest? */
e13ee7ad 580 if (!p->digest) {
581 debug(15, 5) ("peerDigestLookup: gone!\n");
26b164ac 582 return LOOKUP_NONE;
9689d97c 583 } else if (!peerHTTPOkay(p, request)) {
e13ee7ad 584 debug(15, 5) ("peerDigestLookup: !peerHTTPOkay\n");
26b164ac 585 return LOOKUP_NONE;
e13ee7ad 586 } else if (p->digest->flags.usable) {
587 debug(15, 5) ("peerDigestLookup: usable\n");
4b4cd312 588 /* fall through; put here to have common case on top */ ;
e13ee7ad 589 } else if (!p->digest->flags.needed) {
590 debug(15, 5) ("peerDigestLookup: note need\n");
591 peerDigestNeeded(p->digest);
26b164ac 592 return LOOKUP_NONE;
593 } else {
e13ee7ad 594 debug(15, 5) ("peerDigestLookup: !ready && %srequested\n",
595 p->digest->flags.requested ? "" : "!");
26b164ac 596 return LOOKUP_NONE;
597 }
69c95dd3 598 debug(15, 5) ("peerDigestLookup: OK to lookup peer %s\n", p->host);
e13ee7ad 599 assert(p->digest->cd);
26b164ac 600 /* does digest predict a hit? */
e13ee7ad 601 if (!cacheDigestTest(p->digest->cd, key))
26b164ac 602 return LOOKUP_MISS;
69c95dd3 603 debug(15, 5) ("peerDigestLookup: peer %s says HIT!\n", p->host);
26b164ac 604 return LOOKUP_HIT;
605#endif
606 return LOOKUP_NONE;
607}
608
609/* select best peer based on cache digests */
a54d3f8e 610peer *
f66a9ef4 611neighborsDigestSelect(request_t * request)
a54d3f8e 612{
613 peer *best_p = NULL;
6cfa8966 614#if USE_CACHE_DIGESTS
a54d3f8e 615 const cache_key *key;
616 int best_rtt = 0;
617 int choice_count = 0;
618 int ichoice_count = 0;
619 peer *p;
620 int p_rtt;
621 int i;
03058802 622 if (!request->flags.hierarchical)
623 return NULL;
f66a9ef4 624 key = storeKeyPublicByRequest(request);
a54d3f8e 625 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
26b164ac 626 lookup_t lookup;
a54d3f8e 627 if (!p)
628 p = Config.peers;
629 if (i == 1)
630 first_ping = p;
f66a9ef4 631 lookup = peerDigestLookup(p, request);
26b164ac 632 if (lookup == LOOKUP_NONE)
a54d3f8e 633 continue;
a54d3f8e 634 choice_count++;
26b164ac 635 if (lookup == LOOKUP_MISS)
a54d3f8e 636 continue;
26b164ac 637 p_rtt = netdbHostRtt(p->host);
638 debug(15, 5) ("neighborsDigestSelect: peer %s rtt: %d\n",
a54d3f8e 639 p->host, p_rtt);
640 /* is this peer better than others in terms of rtt ? */
641 if (!best_p || (p_rtt && p_rtt < best_rtt)) {
642 best_p = p;
643 best_rtt = p_rtt;
4b4cd312 644 if (p_rtt) /* informative choice (aka educated guess) */
a54d3f8e 645 ichoice_count++;
646 debug(15, 4) ("neighborsDigestSelect: peer %s leads with rtt %d\n",
647 p->host, best_rtt);
648 }
649 }
26b164ac 650 debug(15, 4) ("neighborsDigestSelect: choices: %d (%d)\n",
651 choice_count, ichoice_count);
652 peerNoteDigestLookup(request, best_p,
653 best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE));
a54d3f8e 654 request->hier.n_choices = choice_count;
655 request->hier.n_ichoices = ichoice_count;
656#endif
657 return best_p;
658}
659
26b164ac 660void
4b4cd312 661peerNoteDigestLookup(request_t * request, peer * p, lookup_t lookup)
26b164ac 662{
6cfa8966 663#if USE_CACHE_DIGESTS
26b164ac 664 if (p)
665 strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host));
666 else
667 *request->hier.cd_host = '\0';
668 request->hier.cd_lookup = lookup;
669 debug(15, 4) ("peerNoteDigestLookup: peer %s, lookup: %s\n",
670 p ? p->host : "<none>", lookup_t_str[lookup]);
671#endif
672}
673
d73844ca 674static void
b69f7771 675neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header)
4eda6afe 676{
39ac34a9 677 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
ab4b78e9 678 debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
b69f7771 679 neighborTypeStr(p),
399cabec 680 p->host, p->http_port, p->icp.port);
dc835977 681 p->stats.logged_state = PEER_ALIVE;
4eda6afe 682 }
dc835977 683 p->stats.last_reply = squid_curtime;
83b381d5 684 p->stats.probe_start = 0;
44f2f2e4 685 p->stats.pings_acked++;
27cd7235 686 if ((icp_opcode) header->opcode <= ICP_END)
399cabec 687 p->icp.counts[header->opcode]++;
44f2f2e4 688 p->icp.version = (int) header->version;
689}
690
691static void
692neighborUpdateRtt(peer * p, MemObject * mem)
693{
694 int rtt;
d2cd6935 695 if (!mem)
696 return;
44f2f2e4 697 if (!mem->start_ping.tv_sec)
698 return;
699 rtt = tvSubMsec(mem->start_ping, current_time);
1b6ef2d2 700 if (rtt < 1 || rtt > 10000)
701 return;
702 p->stats.rtt = intAverage(p->stats.rtt, rtt,
d46a87a8 703 p->stats.pings_acked, RTT_AV_FACTOR);
4eda6afe 704}
090089c4 705
399cabec 706#if USE_HTCP
707static void
708neighborAliveHtcp(peer * p, const MemObject * mem, const htcpReplyData * htcp)
709{
399cabec 710 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
711 debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
712 neighborTypeStr(p),
713 p->host, p->http_port, p->icp.port);
714 p->stats.logged_state = PEER_ALIVE;
715 }
716 p->stats.last_reply = squid_curtime;
83b381d5 717 p->stats.probe_start = 0;
44f2f2e4 718 p->stats.pings_acked++;
399cabec 719 p->htcp.counts[htcp->hit ? 1 : 0]++;
44f2f2e4 720 p->htcp.version = htcp->version;
399cabec 721}
722#endif
723
38792624 724static void
56b62ecc 725neighborCountIgnored(peer * p)
a7e59001 726{
b69f7771 727 if (p == NULL)
a7e59001 728 return;
b69f7771 729 p->stats.ignored_replies++;
c7a3724d 730 NLateReplies++;
a7e59001 731}
732
e102ebda 733static peer *non_peers = NULL;
734
735static void
736neighborIgnoreNonPeer(const struct sockaddr_in *from, icp_opcode opcode)
737{
738 peer *np;
e102ebda 739 for (np = non_peers; np; np = np->next) {
740 if (np->in_addr.sin_addr.s_addr != from->sin_addr.s_addr)
741 continue;
742 if (np->in_addr.sin_port != from->sin_port)
743 continue;
744 break;
745 }
746 if (np == NULL) {
747 np = xcalloc(1, sizeof(peer));
748 np->in_addr.sin_addr = from->sin_addr;
749 np->in_addr.sin_port = from->sin_port;
399cabec 750 np->icp.port = ntohl(from->sin_port);
e102ebda 751 np->type = PEER_NONE;
752 np->host = xstrdup(inet_ntoa(from->sin_addr));
753 np->next = non_peers;
754 non_peers = np;
755 }
399cabec 756 np->icp.counts[opcode]++;
e4cc2fdf 757 if (isPowTen(++np->stats.ignored_replies))
758 debug(15, 1) ("WARNING: Ignored %d replies from non-peer %s\n",
759 np->stats.ignored_replies, np->host);
e102ebda 760}
761
429fdbec 762/* ignoreMulticastReply
763 *
d6827718 764 * * We want to ignore replies from multicast peers if the
765 * * cache_host_domain rules would normally prevent the peer
766 * * from being used
429fdbec 767 */
768static int
b69f7771 769ignoreMulticastReply(peer * p, MemObject * mem)
429fdbec 770{
b69f7771 771 if (p == NULL)
429fdbec 772 return 0;
cd196bc8 773 if (!p->options.mcast_responder)
429fdbec 774 return 0;
b69f7771 775 if (peerHTTPOkay(p, mem->request))
429fdbec 776 return 0;
777 return 1;
778}
779
780/* I should attach these records to the entry. We take the first
781 * hit we get our wait until everyone misses. The timeout handler
782 * call needs to nip this shopping list or call one of the misses.
783 *
784 * If a hit process is already started, then sobeit
785 */
b8d8561b 786void
5ad33356 787neighborsUdpAck(const cache_key * key, icp_common_t * header, const struct sockaddr_in *from)
090089c4 788{
b69f7771 789 peer *p = NULL;
5ad33356 790 StoreEntry *entry;
791 MemObject *mem = NULL;
b6c0e933 792 peer_t ntype = PEER_NONE;
5e5126cc 793 char *opcode_d;
a7e59001 794 icp_opcode opcode = (icp_opcode) header->opcode;
090089c4 795
5ad33356 796 debug(15, 6) ("neighborsUdpAck: opcode %d '%s'\n",
797 (int) opcode, storeKeyText(key));
798 if (NULL != (entry = storeGet(key)))
799 mem = entry->mem_obj;
b69f7771 800 if ((p = whichPeer(from)))
801 neighborAlive(p, mem, header);
27cd7235 802 if (opcode > ICP_END)
d2af9477 803 return;
27cd7235 804 opcode_d = icp_opcode_str[opcode];
41587298 805 if (p)
806 neighborUpdateRtt(p, mem);
5ad33356 807 /* Does the entry exist? */
808 if (NULL == entry) {
809 debug(12, 3) ("neighborsUdpAck: Cache key '%s' not found\n",
810 storeKeyText(key));
56b62ecc 811 neighborCountIgnored(p);
5ad33356 812 return;
813 }
2d1c6a4f 814 /* check if someone is already fetching it */
d46a87a8 815 if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) {
5ad33356 816 debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n",
817 storeKeyText(key));
56b62ecc 818 neighborCountIgnored(p);
d2af9477 819 return;
820 }
2d1c6a4f 821 if (mem == NULL) {
5ad33356 822 debug(15, 2) ("Ignoring %s for missing mem_obj: %s\n",
823 opcode_d, storeKeyText(key));
56b62ecc 824 neighborCountIgnored(p);
8de2f7ad 825 return;
826 }
827 if (entry->ping_status != PING_WAITING) {
674ac814 828 debug(15, 2) ("neighborsUdpAck: Late %s for %s\n",
5ad33356 829 opcode_d, storeKeyText(key));
56b62ecc 830 neighborCountIgnored(p);
8de2f7ad 831 return;
832 }
4eda6afe 833 if (entry->lock_count == 0) {
5ad33356 834 debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n",
835 storeKeyText(key));
56b62ecc 836 neighborCountIgnored(p);
4eda6afe 837 return;
090089c4 838 }
a3d5953d 839 debug(15, 3) ("neighborsUdpAck: %s for '%s' from %s \n",
5ad33356 840 opcode_d, storeKeyText(key), p ? p->host : "source");
44f2f2e4 841 if (p) {
b69f7771 842 ntype = neighborType(p, mem->request);
44f2f2e4 843 }
b69f7771 844 if (ignoreMulticastReply(p, mem)) {
56b62ecc 845 neighborCountIgnored(p);
27cd7235 846 } else if (opcode == ICP_MISS) {
b69f7771 847 if (p == NULL) {
e102ebda 848 neighborIgnoreNonPeer(from, opcode);
d2af9477 849 } else {
b4e7f82d 850 mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data);
30a4f2a8 851 }
a7c05555 852 } else if (opcode == ICP_HIT) {
b69f7771 853 if (p == NULL) {
e102ebda 854 neighborIgnoreNonPeer(from, opcode);
d2af9477 855 } else {
27cd7235 856 header->opcode = ICP_HIT;
b4e7f82d 857 mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data);
090089c4 858 }
27cd7235 859 } else if (opcode == ICP_DECHO) {
b69f7771 860 if (p == NULL) {
e102ebda 861 neighborIgnoreNonPeer(from, opcode);
deb79f06 862 } else if (ntype == PEER_SIBLING) {
5e604a0e 863 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
864 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
090089c4 865 } else {
b4e7f82d 866 mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data);
090089c4 867 }
27cd7235 868 } else if (opcode == ICP_SECHO) {
b69f7771 869 if (p) {
a3d5953d 870 debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host);
56b62ecc 871 neighborCountIgnored(p);
0ab965da 872#if ALLOW_SOURCE_PING
873 } else if (Config.onoff.source_ping) {
b4e7f82d 874 mem->ping_reply_callback(NULL, ntype, PROTO_ICP, header, mem->ircb_data);
0ab965da 875#endif
876 } else {
877 debug(15, 1) ("Unsolicited SECHO from %s\n", inet_ntoa(from->sin_addr));
090089c4 878 }
27cd7235 879 } else if (opcode == ICP_DENIED) {
b69f7771 880 if (p == NULL) {
e102ebda 881 neighborIgnoreNonPeer(from, opcode);
b69f7771 882 } else if (p->stats.pings_acked > 100) {
399cabec 883 if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
a3d5953d 884 debug(15, 0) ("95%% of replies from '%s' are UDP_DENIED\n", p->host);
885 debug(15, 0) ("Disabling '%s', please check your configuration.\n", p->host);
b69f7771 886 neighborRemove(p);
887 p = NULL;
e006d372 888 } else {
56b62ecc 889 neighborCountIgnored(p);
30a4f2a8 890 }
891 }
27cd7235 892 } else if (opcode == ICP_MISS_NOFETCH) {
b4e7f82d 893 mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data);
090089c4 894 } else {
a3d5953d 895 debug(15, 0) ("neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d);
d2af9477 896 }
090089c4 897}
898
deb79f06 899peer *
40a1495e 900peerFindByName(const char *name)
98ffb7e4 901{
b69f7771 902 peer *p = NULL;
40a1495e 903 for (p = Config.peers; p; p = p->next) {
b69f7771 904 if (!strcasecmp(name, p->host))
98ffb7e4 905 break;
906 }
b69f7771 907 return p;
98ffb7e4 908}
b012353a 909
db1cd23c 910peer *
911peerFindByNameAndPort(const char *name, unsigned short port)
912{
913 peer *p = NULL;
914 for (p = Config.peers; p; p = p->next) {
915 if (strcasecmp(name, p->host))
916 continue;
917 if (port != p->http_port)
918 continue;
919 break;
920 }
921 return p;
922}
923
5269d0bd 924int
b69f7771 925neighborUp(const peer * p)
5269d0bd 926{
eb406bb7 927 if (!p->tcp_up) {
a4b8110e 928 peerProbeConnect((peer *) p);
03b82057 929 return 0;
eb406bb7 930 }
83b381d5 931 if (p->options.no_query)
03b82057 932 return 1;
83b381d5 933 if (p->stats.probe_start != 0 &&
a4b8110e 934 squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer)
03b82057 935 return 0;
64b8103b 936 /*
937 * The peer can not be UP if we don't have any IP addresses
186477c1 938 * for it.
64b8103b 939 */
940 if (0 == p->n_addresses)
941 return 0;
03b82057 942 return 1;
5269d0bd 943}
944
e6e3b09b 945void
28c60158 946peerDestroy(void *data)
e6e3b09b 947{
db1cd23c 948 peer *p = data;
ee4a1f5d 949 struct _domain_ping *l = NULL;
950 struct _domain_ping *nl = NULL;
b69f7771 951 if (p == NULL)
3c6e634f 952 return;
b6a2f15e 953 for (l = p->peer_domain; l; l = nl) {
ee4a1f5d 954 nl = l->next;
955 safe_free(l->domain);
956 safe_free(l);
957 }
b69f7771 958 safe_free(p->host);
e13ee7ad 959#if USE_CACHE_DIGESTS
b6a2f15e 960 if (p->digest) {
961 PeerDigest *pd = p->digest;
962 p->digest = NULL;
963 cbdataUnlock(pd);
964 }
e13ee7ad 965#endif
e6e3b09b 966}
c7a3724d 967
e13ee7ad 968void
e16d4250 969peerNoteDigestGone(peer * p)
e13ee7ad 970{
971#if USE_CACHE_DIGESTS
972 if (p->digest) {
fcbbccfe 973 PeerDigest *pd = p->digest;
e13ee7ad 974 p->digest = NULL;
fcbbccfe 975 cbdataUnlock(pd);
e13ee7ad 976 }
977#endif
978}
979
dd4bd44e 980static void
03a1ee42 981peerDNSConfigure(const ipcache_addrs * ia, void *data)
dd4bd44e 982{
b69f7771 983 peer *p = data;
dd4bd44e 984 struct sockaddr_in *ap;
985 int j;
b69f7771 986 if (p->n_addresses == 0) {
a3d5953d 987 debug(15, 1) ("Configuring %s %s/%d/%d\n", neighborTypeStr(p),
399cabec 988 p->host, p->http_port, p->icp.port);
b69f7771 989 if (p->type == PEER_MULTICAST)
a3d5953d 990 debug(15, 1) (" Multicast TTL = %d\n", p->mcast.ttl);
b69f7771 991 }
992 p->n_addresses = 0;
dd4bd44e 993 if (ia == NULL) {
a3d5953d 994 debug(0, 0) ("WARNING: DNS lookup for '%s' failed!\n", p->host);
dd4bd44e 995 return;
996 }
c04fe656 997 if ((int) ia->count < 1) {
a3d5953d 998 debug(0, 0) ("WARNING: No IP address found for '%s'!\n", p->host);
dd4bd44e 999 return;
1000 }
c04fe656 1001 for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) {
b69f7771 1002 p->addresses[j] = ia->in_addrs[j];
a3d5953d 1003 debug(15, 2) ("--> IP address #%d: %s\n", j, inet_ntoa(p->addresses[j]));
b69f7771 1004 p->n_addresses++;
dd4bd44e 1005 }
b69f7771 1006 ap = &p->in_addr;
dd4bd44e 1007 memset(ap, '\0', sizeof(struct sockaddr_in));
1008 ap->sin_family = AF_INET;
b69f7771 1009 ap->sin_addr = p->addresses[0];
399cabec 1010 ap->sin_port = htons(p->icp.port);
b69f7771 1011 if (p->type == PEER_MULTICAST)
1012 peerCountMcastPeersSchedule(p, 10);
1a827bc0 1013 if (p->type != PEER_MULTICAST)
cd196bc8 1014 if (!p->options.no_netdb_exchange)
223213df 1015 eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1);
dd4bd44e 1016}
1017
1018static void
51242273 1019peerRefreshDNS(void *data)
dd4bd44e 1020{
b69f7771 1021 peer *p = NULL;
46ca5fc6 1022 if (eventFind(peerRefreshDNS, NULL))
73cb7e3a 1023 eventDelete(peerRefreshDNS, NULL);
51242273 1024 if (!data && 0 == stat5minClientRequests()) {
1f3c4622 1025 /* no recent client traffic, wait a bit */
5999b776 1026 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1);
1f3c4622 1027 return;
dd4bd44e 1028 }
1f3c4622 1029 for (p = Config.peers; p; p = p->next)
1030 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
dd4bd44e 1031 /* Reconfigure the peers every hour */
52040193 1032 eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1);
dd4bd44e 1033}
e924600d 1034
eb406bb7 1035void
a4b8110e 1036peerConnectFailed(peer * p)
eb406bb7 1037{
1038 p->stats.last_connect_failure = squid_curtime;
1039 if (!p->tcp_up) {
1040 debug(15, 2) ("TCP connection to %s/%d dead\n", p->host, p->http_port);
1041 return;
1042 }
1043 debug(15, 1) ("TCP connection to %s/%d failed\n", p->host, p->http_port);
1044 p->tcp_up--;
1045 if (!p->tcp_up) {
1046 debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n",
1047 neighborTypeStr(p),
1048 p->host, p->http_port, p->icp.port);
1049 p->stats.logged_state = PEER_DEAD;
1050 }
1051}
1052
1053void
a4b8110e 1054peerConnectSucceded(peer * p)
eb406bb7 1055{
1056 if (!p->tcp_up) {
1057 debug(15, 2) ("TCP connection to %s/%d succeded\n", p->host, p->http_port);
1058 debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n",
1059 neighborTypeStr(p),
1060 p->host, p->http_port, p->icp.port);
1061 p->stats.logged_state = PEER_ALIVE;
1062 }
1063 p->tcp_up = PEER_TCP_MAGIC_COUNT;
1064}
1065
0e7b6b06 1066/*
eb406bb7 1067 * peerProbeConnect will be called on dead peers by neighborUp
0e7b6b06 1068 */
e924600d 1069static void
a4b8110e 1070peerProbeConnect(peer * p)
e924600d 1071{
e924600d 1072 int fd;
eb406bb7 1073 if (p->test_fd != -1)
a4b8110e 1074 return; /* probe already running */
eb406bb7 1075 if (squid_curtime - p->stats.last_connect_probe < Config.Timeout.connect)
a4b8110e 1076 return; /* don't probe to often */
d6827718 1077 fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL),
e924600d 1078 0, COMM_NONBLOCKING, p->host);
1079 if (fd < 0)
1080 return;
03a1ee42 1081 p->test_fd = fd;
eb406bb7 1082 p->stats.last_connect_probe = squid_curtime;
1083 ipcache_nbgethostbyname(p->host, peerProbeConnect2, p);
e924600d 1084}
1085
1086static void
eb406bb7 1087peerProbeConnect2(const ipcache_addrs * ianotused, void *data)
e924600d 1088{
1089 peer *p = data;
03a1ee42 1090 commConnectStart(p->test_fd,
e924600d 1091 p->host,
1092 p->http_port,
eb406bb7 1093 peerProbeConnectDone,
e924600d 1094 p);
1095}
1096
1097static void
eb406bb7 1098peerProbeConnectDone(int fd, int status, void *data)
e924600d 1099{
1100 peer *p = data;
98829f69 1101 if (status == COMM_OK) {
eb406bb7 1102 peerConnectSucceded(p);
e924600d 1103 } else {
eb406bb7 1104 peerConnectFailed(p);
e924600d 1105 }
1106 comm_close(fd);
eb406bb7 1107 p->test_fd = -1;
e924600d 1108 return;
1109}
1110
429fdbec 1111static void
1112peerCountMcastPeersSchedule(peer * p, time_t when)
1113{
b515fc11 1114 if (p->mcast.flags.count_event_pending)
429fdbec 1115 return;
1116 eventAdd("peerCountMcastPeersStart",
1117 peerCountMcastPeersStart,
1118 p,
52040193 1119 (double) when, 1);
b515fc11 1120 p->mcast.flags.count_event_pending = 1;
429fdbec 1121}
1122
1123static void
1124peerCountMcastPeersStart(void *data)
1125{
1126 peer *p = data;
28c60158 1127 ps_state *psstate;
429fdbec 1128 StoreEntry *fake;
1129 MemObject *mem;
1130 icp_common_t *query;
007b8be4 1131 int reqnum;
429fdbec 1132 LOCAL_ARRAY(char, url, MAX_URL);
ce66013b 1133 assert(p->type == PEER_MULTICAST);
b515fc11 1134 p->mcast.flags.count_event_pending = 0;
042461c3 1135 snprintf(url, MAX_URL, "http://%s/", inet_ntoa(p->in_addr.sin_addr));
92695e5e 1136 fake = storeCreateEntry(url, url, null_request_flags, METHOD_GET);
72711e31 1137 psstate = cbdataAlloc(ps_state);
429fdbec 1138 psstate->request = requestLink(urlParse(METHOD_GET, url));
1139 psstate->entry = fake;
1140 psstate->callback = NULL;
429fdbec 1141 psstate->callback_data = p;
b4e7f82d 1142 psstate->ping.start = current_time;
429fdbec 1143 mem = fake->mem_obj;
2db609de 1144 mem->request = requestLink(psstate->request);
429fdbec 1145 mem->start_ping = current_time;
b4e7f82d 1146 mem->ping_reply_callback = peerCountHandleIcpReply;
429fdbec 1147 mem->ircb_data = psstate;
03a1ee42 1148 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
007b8be4 1149 p->mcast.id = mem->id;
186477c1 1150 reqnum = icpSetCacheKey(fake->hash.key);
007b8be4 1151 query = icpCreateMessage(ICP_QUERY, 0, url, reqnum, 0);
429fdbec 1152 icpUdpSend(theOutIcpConnection,
1153 &p->in_addr,
1154 query,
721b3dd8 1155 LOG_ICP_QUERY,
aee4e794 1156 0);
429fdbec 1157 fake->ping_status = PING_WAITING;
1158 eventAdd("peerCountMcastPeersDone",
1159 peerCountMcastPeersDone,
2db609de 1160 psstate,
465dc415 1161 (double) Config.Timeout.mcast_icp_query, 1);
b515fc11 1162 p->mcast.flags.counting = 1;
429fdbec 1163 peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
1164}
1165
1166static void
1167peerCountMcastPeersDone(void *data)
1168{
1169 ps_state *psstate = data;
1170 peer *p = psstate->callback_data;
1171 StoreEntry *fake = psstate->entry;
b515fc11 1172 p->mcast.flags.counting = 0;
9e4ad609 1173 p->mcast.avg_n_members = doubleAverage(p->mcast.avg_n_members,
b4e7f82d 1174 (double) psstate->ping.n_recv,
9e4ad609 1175 ++p->mcast.n_times_counted,
1176 10);
1a827bc0 1177 debug(15, 1) ("Group %s: %d replies, %4.1f average, RTT %d\n",
429fdbec 1178 p->host,
b4e7f82d 1179 psstate->ping.n_recv,
1a827bc0 1180 p->mcast.avg_n_members,
1181 p->stats.rtt);
429fdbec 1182 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
b7fe0ab0 1183 EBIT_SET(fake->flags, ENTRY_ABORTED);
e7a22b88 1184 requestUnlink(fake->mem_obj->request);
f0d5043e 1185 fake->mem_obj->request = NULL;
429fdbec 1186 storeReleaseRequest(fake);
1187 storeUnlockObject(fake);
e7a22b88 1188 requestUnlink(psstate->request);
1a827bc0 1189 cbdataFree(psstate);
429fdbec 1190}
1191
1192static void
5942e8d4 1193peerCountHandleIcpReply(peer * p, peer_t type, protocol_t proto, void *hdrnotused, void *data)
429fdbec 1194{
1195 ps_state *psstate = data;
1a827bc0 1196 StoreEntry *fake = psstate->entry;
1197 MemObject *mem = fake->mem_obj;
1198 int rtt = tvSubMsec(mem->start_ping, current_time);
86aebcda 1199 assert(proto == PROTO_ICP);
1a827bc0 1200 assert(fake);
1201 assert(mem);
b4e7f82d 1202 psstate->ping.n_recv++;
1203 p->stats.rtt = intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, RTT_AV_FACTOR);
429fdbec 1204}
ed7f5615 1205
1206static void
1207neighborDumpPeers(StoreEntry * sentry)
1208{
1209 dump_peers(sentry, Config.peers);
1210}
1211
1212static void
1213neighborDumpNonPeers(StoreEntry * sentry)
1214{
1215 dump_peers(sentry, non_peers);
1216}
1217
a369131d 1218void
1219dump_peer_options(StoreEntry * sentry, peer * p)
1220{
cd196bc8 1221 if (p->options.proxy_only)
a369131d 1222 storeAppendPrintf(sentry, " proxy-only");
cd196bc8 1223 if (p->options.no_query)
a369131d 1224 storeAppendPrintf(sentry, " no-query");
cd196bc8 1225 if (p->options.no_digest)
8638fc66 1226 storeAppendPrintf(sentry, " no-digest");
cd196bc8 1227 if (p->options.default_parent)
a369131d 1228 storeAppendPrintf(sentry, " default");
cd196bc8 1229 if (p->options.roundrobin)
a369131d 1230 storeAppendPrintf(sentry, " round-robin");
cd196bc8 1231 if (p->options.mcast_responder)
a369131d 1232 storeAppendPrintf(sentry, " multicast-responder");
cd196bc8 1233 if (p->options.closest_only)
a369131d 1234 storeAppendPrintf(sentry, " closest-only");
dc9d133b 1235#if USE_HTCP
cd196bc8 1236 if (p->options.htcp)
e8d185d2 1237 storeAppendPrintf(sentry, " htcp");
dc9d133b 1238#endif
cd196bc8 1239 if (p->options.no_netdb_exchange)
1a5dae67 1240 storeAppendPrintf(sentry, " no-netdb-exchange");
95e36d02 1241#if DELAY_POOLS
cd196bc8 1242 if (p->options.no_delay)
95e36d02 1243 storeAppendPrintf(sentry, " no-delay");
1244#endif
c68e9c6b 1245 if (p->login)
1246 storeAppendPrintf(sentry, " login=%s", p->login);
a369131d 1247 if (p->mcast.ttl > 0)
1248 storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl);
1249 storeAppendPrintf(sentry, "\n");
1250}
1251
ed7f5615 1252static void
1253dump_peers(StoreEntry * sentry, peer * peers)
1254{
1255 peer *e = NULL;
1256 struct _domain_ping *d = NULL;
1257 icp_opcode op;
06e6d12a 1258 int i;
ed7f5615 1259 if (peers == NULL)
1260 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
1261 for (e = peers; e; e = e->next) {
1262 assert(e->host != NULL);
1263 storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n",
1264 neighborTypeStr(e),
1265 e->host,
1266 e->http_port,
399cabec 1267 e->icp.port);
a369131d 1268 storeAppendPrintf(sentry, "Flags :");
1269 dump_peer_options(sentry, e);
06e6d12a 1270 for (i = 0; i < e->n_addresses; i++) {
1271 storeAppendPrintf(sentry, "Address[%d] : %s\n", i,
1272 inet_ntoa(e->addresses[i]));
1273 }
ed7f5615 1274 storeAppendPrintf(sentry, "Status : %s\n",
1275 neighborUp(e) ? "Up" : "Down");
1276 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
c7f9eb6d 1277 storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open);
ed7f5615 1278 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
1279 (int) (squid_curtime - e->stats.last_query));
1280 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
1281 (int) (squid_curtime - e->stats.last_reply));
1282 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
1283 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
1284 e->stats.pings_acked,
1285 percent(e->stats.pings_acked, e->stats.pings_sent));
1286 storeAppendPrintf(sentry, "FETCHES : %8d %3d%%\n",
1287 e->stats.fetches,
1288 percent(e->stats.fetches, e->stats.pings_acked));
1289 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n",
1290 e->stats.ignored_replies,
1291 percent(e->stats.ignored_replies, e->stats.pings_acked));
1292 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1994bb24 1293#if USE_HTCP
cd196bc8 1294 if (e->options.htcp) {
1994bb24 1295 storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n",
1296 e->htcp.counts[0],
1297 percent(e->htcp.counts[0], e->stats.pings_acked));
1298 storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n",
1299 e->htcp.counts[1],
1300 percent(e->htcp.counts[1], e->stats.pings_acked));
1301 } else {
1302#endif
5942e8d4 1303 for (op = ICP_INVALID; op < ICP_END; op++) {
1304 if (e->icp.counts[op] == 0)
1305 continue;
1306 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1307 icp_opcode_str[op],
1308 e->icp.counts[op],
1309 percent(e->icp.counts[op], e->stats.pings_acked));
1310 }
1994bb24 1311#if USE_HTCP
5942e8d4 1312 }
1994bb24 1313#endif
eb406bb7 1314 if (e->stats.last_connect_failure) {
ed7f5615 1315 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
eb406bb7 1316 mkhttpdlogtime(&(e->stats.last_connect_failure)));
ed7f5615 1317 }
b6a2f15e 1318 if (e->peer_domain != NULL) {
ed7f5615 1319 storeAppendPrintf(sentry, "DOMAIN LIST: ");
b6a2f15e 1320 for (d = e->peer_domain; d; d = d->next) {
5942e8d4 1321 storeAppendPrintf(sentry, "%s%s ",
1322 d->do_ping ? null_string : "!", d->domain);
1994bb24 1323 }
1324 storeAppendPrintf(sentry, "\n");
ed7f5615 1325 }
99edd1c3 1326 storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n",
ed7f5615 1327 percent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
1328 }
1329}
86aebcda 1330
1331#if USE_HTCP
1332void
1333neighborsHtcpReply(const cache_key * key, htcpReplyData * htcp, const struct sockaddr_in *from)
1334{
b4e7f82d 1335 StoreEntry *e = storeGet(key);
399cabec 1336 MemObject *mem = NULL;
1337 peer *p;
1338 peer_t ntype = PEER_NONE;
399cabec 1339 debug(15, 6) ("neighborsHtcpReply: %s %s\n",
1340 htcp->hit ? "HIT" : "MISS", storeKeyText(key));
1341 if (NULL != (e = storeGet(key)))
1342 mem = e->mem_obj;
1343 if ((p = whichPeer(from)))
1344 neighborAliveHtcp(p, mem, htcp);
1345 /* Does the entry exist? */
1346 if (NULL == e) {
1347 debug(12, 3) ("neighyborsHtcpReply: Cache key '%s' not found\n",
1348 storeKeyText(key));
1349 neighborCountIgnored(p);
1350 return;
1351 }
1352 /* check if someone is already fetching it */
d46a87a8 1353 if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) {
399cabec 1354 debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n",
1355 storeKeyText(key));
1356 neighborCountIgnored(p);
1357 return;
1358 }
1359 if (mem == NULL) {
1360 debug(15, 2) ("Ignoring reply for missing mem_obj: %s\n",
1361 storeKeyText(key));
1362 neighborCountIgnored(p);
1363 return;
1364 }
1365 if (e->ping_status != PING_WAITING) {
1366 debug(15, 2) ("neighborsUdpAck: Entry %s is not PING_WAITING\n",
1367 storeKeyText(key));
1368 neighborCountIgnored(p);
1369 return;
1370 }
1371 if (e->lock_count == 0) {
1372 debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n",
1373 storeKeyText(key));
1374 neighborCountIgnored(p);
1375 return;
1376 }
44f2f2e4 1377 if (p) {
399cabec 1378 ntype = neighborType(p, mem->request);
44f2f2e4 1379 neighborUpdateRtt(p, mem);
1380 }
399cabec 1381 if (ignoreMulticastReply(p, mem)) {
1382 neighborCountIgnored(p);
1383 return;
1384 }
9bc73deb 1385 debug(15, 3) ("neighborsHtcpReply: e = %p\n", e);
b4e7f82d 1386 mem->ping_reply_callback(p, ntype, PROTO_HTCP, htcp, mem->ircb_data);
86aebcda 1387}
1388#endif