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