]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
- Improved statHistInit() assertions for value_in/out functions. We used to
[thirdparty/squid.git] / src / neighbors.cc
CommitLineData
22f3fd98 1
30a4f2a8 2/*
ed7f5615 3 * $Id: neighbors.cc,v 1.176 1998/02/19 23:28:40 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 15 Neighbor Routines
6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
4d64d74a 30 */
019dd986 31
32/*
30a4f2a8 33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
019dd986 105 */
090089c4 106
44a47c6e 107#include "squid.h"
090089c4 108
429fdbec 109/* count mcast group peers every 15 minutes */
110#define MCAST_COUNT_RATE 900
111
f5b8bbc4 112static int peerAllowedToUse(const peer *, request_t *);
113static int peerHTTPOkay(const peer *, request_t *);
114static int peerWouldBePinged(const peer *, request_t *);
115static void neighborRemove(peer *);
ea3a2a69 116static peer *whichPeer(const struct sockaddr_in *from);
f5b8bbc4 117static void neighborAlive(peer *, const MemObject *, const icp_common_t *);
118static void neighborCountIgnored(peer *, icp_opcode op_unused);
119static void peerRefreshDNS(void *);
b69f7771 120static IPH peerDNSConfigure;
f5b8bbc4 121static void peerCheckConnect(void *);
b69f7771 122static IPH peerCheckConnect2;
603a02fd 123static CNCB peerCheckConnectDone;
f5b8bbc4 124static void peerCountMcastPeersDone(void *data);
125static void peerCountMcastPeersStart(void *data);
126static void peerCountMcastPeersSchedule(peer * p, time_t when);
b3264694 127static IRCB peerCountHandleIcpReply;
f5b8bbc4 128static void neighborIgnoreNonPeer(const struct sockaddr_in *, icp_opcode);
ed7f5615 129static OBJH neighborDumpPeers;
130static OBJH neighborDumpNonPeers;
131static void dump_peers(StoreEntry * sentry, peer * peers);
090089c4 132
090089c4 133static icp_common_t echo_hdr;
30a4f2a8 134static u_short echo_port;
30a4f2a8 135
c7a3724d 136static int NLateReplies = 0;
40a1495e 137static peer *first_ping = NULL;
28070024 138
c7a3724d 139char *
b69f7771 140neighborTypeStr(const peer * p)
69a71bd7 141{
e102ebda 142 if (p->type == PEER_NONE)
143 return "Non-Peer";
b69f7771 144 if (p->type == PEER_SIBLING)
69a71bd7 145 return "Sibling";
b69f7771 146 if (p->type == PEER_MULTICAST)
7b3bd12c 147 return "Multicast Group";
69a71bd7 148 return "Parent";
149}
150
090089c4 151
deb79f06 152static peer *
153whichPeer(const struct sockaddr_in *from)
090089c4 154{
22e4fa85 155 int j;
4eda6afe 156 u_short port = ntohs(from->sin_port);
157 struct in_addr ip = from->sin_addr;
b69f7771 158 peer *p = NULL;
a3d5953d 159 debug(15, 3) ("whichPeer: from %s port %d\n", inet_ntoa(ip), port);
40a1495e 160 for (p = Config.peers; p; p = p->next) {
b69f7771 161 for (j = 0; j < p->n_addresses; j++) {
162 if (ip.s_addr == p->addresses[j].s_addr && port == p->icp_port) {
163 return p;
090089c4 164 }
165 }
166 }
4eda6afe 167 return NULL;
090089c4 168}
169
b6c0e933 170static peer_t
b69f7771 171neighborType(const peer * p, const request_t * request)
24a1003d 172{
b012353a 173 const struct _domain_type *d = NULL;
b69f7771 174 for (d = p->typelist; d; d = d->next) {
24a1003d 175 if (matchDomainName(d->domain, request->host))
deb79f06 176 if (d->type != PEER_NONE)
b012353a 177 return d->type;
24a1003d 178 }
b69f7771 179 return p->type;
24a1003d 180}
181
7b3bd12c 182/*
62fd6124 183 * peerAllowedToUse
7b3bd12c 184 *
dd4bd44e 185 * this function figures out if it is appropriate to fetch REQUEST
62fd6124 186 * from PEER.
7b3bd12c 187 */
b8d8561b 188static int
b69f7771 189peerAllowedToUse(const peer * p, request_t * request)
090089c4 190{
b012353a 191 const struct _domain_ping *d = NULL;
090089c4 192 int do_ping = 1;
0ee4272b 193 const struct _acl_list *a = NULL;
f88bb09c 194 aclCheck_t checklist;
ce66013b 195 assert(request != NULL);
79a15e0a 196 if (EBIT_TEST(request->flags, REQ_NOCACHE))
b69f7771 197 if (neighborType(p, request) == PEER_SIBLING)
24a1003d 198 return 0;
79a15e0a 199 if (EBIT_TEST(request->flags, REQ_REFRESH))
b69f7771 200 if (neighborType(p, request) == PEER_SIBLING)
d950cec8 201 return 0;
b69f7771 202 if (p->pinglist == NULL && p->acls == NULL)
090089c4 203 return do_ping;
090089c4 204 do_ping = 0;
b69f7771 205 for (d = p->pinglist; d; d = d->next) {
30a4f2a8 206 if (matchDomainName(d->domain, request->host))
207 return d->do_ping;
208 do_ping = !d->do_ping;
209 }
8ae8bb05 210 checklist.src_addr = request->client_addr;
f88bb09c 211 checklist.request = request;
b69f7771 212 for (a = p->acls; a; a = a->next) {
f88bb09c 213 if (aclMatchAcl(a->acl, &checklist))
30a4f2a8 214 return a->op;
215 do_ping = !a->op;
090089c4 216 }
217 return do_ping;
218}
219
62fd6124 220/* Return TRUE if it is okay to send an ICP request to this peer. */
221static int
b69f7771 222peerWouldBePinged(const peer * p, request_t * request)
62fd6124 223{
b69f7771 224 if (!peerAllowedToUse(p, request))
62fd6124 225 return 0;
b69f7771 226 if (p->options & NEIGHBOR_NO_QUERY)
62fd6124 227 return 0;
b69f7771 228 if (p->options & NEIGHBOR_MCAST_RESPONDER)
62fd6124 229 return 0;
230 /* the case below seems strange, but can happen if the
231 * URL host is on the other side of a firewall */
b69f7771 232 if (p->type == PEER_SIBLING)
79a15e0a 233 if (!EBIT_TEST(request->flags, REQ_HIERARCHICAL))
62fd6124 234 return 0;
b69f7771 235 if (p->icp_port == echo_port)
236 if (!neighborUp(p))
62fd6124 237 return 0;
b69f7771 238 if (p->n_addresses == 0)
dd4bd44e 239 return 0;
62fd6124 240 return 1;
241}
242
243/* Return TRUE if it is okay to send an HTTP request to this peer. */
244static int
b69f7771 245peerHTTPOkay(const peer * p, request_t * request)
62fd6124 246{
b69f7771 247 if (!peerAllowedToUse(p, request))
62fd6124 248 return 0;
b69f7771 249 if (!neighborUp(p))
62fd6124 250 return 0;
251 return 1;
252}
253
3c6e634f 254int
255neighborsCount(request_t * request)
090089c4 256{
b69f7771 257 peer *p = NULL;
090089c4 258 int count = 0;
40a1495e 259 for (p = Config.peers; p; p = p->next)
b69f7771 260 if (peerWouldBePinged(p, request))
3c6e634f 261 count++;
a3d5953d 262 debug(15, 3) ("neighborsCount: %d\n", count);
3c6e634f 263 return count;
264}
090089c4 265
deb79f06 266peer *
3c6e634f 267getSingleParent(request_t * request)
268{
deb79f06 269 peer *p = NULL;
b69f7771 270 peer *q = NULL;
40a1495e 271 for (q = Config.peers; q; q = q->next) {
b69f7771 272 if (!peerHTTPOkay(q, request))
caebbe00 273 continue;
b69f7771 274 if (neighborType(q, request) != PEER_PARENT)
3c6e634f 275 return NULL; /* oops, found SIBLING */
276 if (p)
277 return NULL; /* oops, found second parent */
b69f7771 278 p = q;
090089c4 279 }
a3d5953d 280 debug(15, 3) ("getSingleParent: returning %s\n", p ? p->host : "NULL");
3c6e634f 281 return p;
090089c4 282}
283
deb79f06 284peer *
b8d8561b 285getFirstUpParent(request_t * request)
090089c4 286{
b69f7771 287 peer *p = NULL;
40a1495e 288 for (p = Config.peers; p; p = p->next) {
b69f7771 289 if (!neighborUp(p))
30a4f2a8 290 continue;
b69f7771 291 if (neighborType(p, request) != PEER_PARENT)
090089c4 292 continue;
b69f7771 293 if (!peerHTTPOkay(p, request))
e924600d 294 continue;
295 break;
090089c4 296 }
a3d5953d 297 debug(15, 3) ("getFirstUpParent: returning %s\n", p ? p->host : "NULL");
b69f7771 298 return p;
090089c4 299}
300
deb79f06 301peer *
48b38d01 302getRoundRobinParent(request_t * request)
303{
b69f7771 304 peer *p;
305 peer *q = NULL;
40a1495e 306 for (p = Config.peers; p; p = p->next) {
79a15e0a 307 if (!EBIT_TEST(p->options, NEIGHBOR_ROUNDROBIN))
0c8177e6 308 continue;
b69f7771 309 if (neighborType(p, request) != PEER_PARENT)
0c8177e6 310 continue;
b69f7771 311 if (!peerHTTPOkay(p, request))
48b38d01 312 continue;
b69f7771 313 if (q && q->rr_count < p->rr_count)
48b38d01 314 continue;
b69f7771 315 q = p;
48b38d01 316 }
b69f7771 317 if (q)
318 q->rr_count++;
a3d5953d 319 debug(15, 3) ("getRoundRobinParent: returning %s\n", q ? q->host : "NULL");
b69f7771 320 return q;
48b38d01 321}
322
deb79f06 323peer *
5269d0bd 324getDefaultParent(request_t * request)
325{
b69f7771 326 peer *p = NULL;
40a1495e 327 for (p = Config.peers; p; p = p->next) {
b69f7771 328 if (neighborType(p, request) != PEER_PARENT)
5269d0bd 329 continue;
79a15e0a 330 if (!EBIT_TEST(p->options, NEIGHBOR_DEFAULT_PARENT))
5269d0bd 331 continue;
b69f7771 332 if (!peerHTTPOkay(p, request))
5269d0bd 333 continue;
a3d5953d 334 debug(15, 3) ("getDefaultParent: returning %s\n", p->host);
b69f7771 335 return p;
5269d0bd 336 }
337 return NULL;
338}
339
deb79f06 340peer *
b69f7771 341getNextPeer(peer * p)
090089c4 342{
b69f7771 343 return p->next;
090089c4 344}
345
deb79f06 346peer *
347getFirstPeer(void)
090089c4 348{
40a1495e 349 return Config.peers;
090089c4 350}
351
b8d8561b 352static void
deb79f06 353neighborRemove(peer * target)
30a4f2a8 354{
b69f7771 355 peer *p = NULL;
356 peer **P = NULL;
40a1495e 357 p = Config.peers;
358 P = &Config.peers;
b69f7771 359 while (p) {
360 if (target == p)
30a4f2a8 361 break;
b69f7771 362 P = &p->next;
363 p = p->next;
364 }
365 if (p) {
366 *P = p->next;
b69f7771 367 peerDestroy(p);
40a1495e 368 Config.npeers--;
4d64d74a 369 }
40a1495e 370 first_ping = Config.peers;
4d64d74a 371}
372
b8d8561b 373void
374neighbors_open(int fd)
090089c4 375{
30a4f2a8 376 struct sockaddr_in name;
30a4f2a8 377 int len = sizeof(struct sockaddr_in);
30a4f2a8 378 struct servent *sep = NULL;
30a4f2a8 379 memset(&name, '\0', sizeof(struct sockaddr_in));
380 if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
a3d5953d 381 debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
dd4bd44e 382 peerRefreshDNS(NULL);
30a4f2a8 383 if (0 == echo_hdr.opcode) {
27cd7235 384 echo_hdr.opcode = ICP_SECHO;
30a4f2a8 385 echo_hdr.version = ICP_VERSION_CURRENT;
386 echo_hdr.length = 0;
387 echo_hdr.reqnum = 0;
388 echo_hdr.flags = 0;
389 echo_hdr.pad = 0;
30a4f2a8 390 echo_hdr.shostid = name.sin_addr.s_addr;
391 sep = getservbyname("echo", "udp");
392 echo_port = sep ? ntohs((u_short) sep->s_port) : 7;
090089c4 393 }
c68e4e90 394 first_ping = Config.peers;
ed7f5615 395 cachemgrRegister("server_list",
396 "Peer Cache Statistics",
397 neighborDumpPeers, 0);
22f3fd98 398 cachemgrRegister("non_peers",
399 "List of Unknown sites sending ICP messages",
400 neighborDumpNonPeers, 0);
090089c4 401}
402
b8d8561b 403int
b6c0e933 404neighborsUdpPing(request_t * request,
641941c0 405 StoreEntry * entry,
582b6456 406 IRCB * callback,
641941c0 407 void *callback_data,
408 int *exprep)
090089c4 409{
2cc3f720 410 char *host = request->host;
9fb13bb6 411 const char *url = storeUrl(entry);
b6c0e933 412 MemObject *mem = entry->mem_obj;
0ee4272b 413 const ipcache_addrs *ia = NULL;
090089c4 414 struct sockaddr_in to_addr;
b69f7771 415 peer *p = NULL;
090089c4 416 int i;
1061b406 417 int reqnum = 0;
6d2296d4 418 int flags;
9dee2904 419 icp_common_t *query;
429fdbec 420 int queries_sent = 0;
0a0bf5db 421 int peers_pinged = 0;
090089c4 422
40a1495e 423 if (Config.peers == NULL)
090089c4 424 return 0;
ce66013b 425 if (theOutIcpConnection < 0)
426 fatal("neighborsUdpPing: There is no ICP socket!");
9fb13bb6 427 assert(entry->swap_status == SWAPOUT_NONE);
b6c0e933 428 mem->start_ping = current_time;
429 mem->icp_reply_callback = callback;
430 mem->ircb_data = callback_data;
40a1495e 431 for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) {
b69f7771 432 if (p == NULL)
40a1495e 433 p = Config.peers;
a3d5953d 434 debug(15, 5) ("neighborsUdpPing: Peer %s\n", p->host);
b69f7771 435 if (!peerWouldBePinged(p, request))
deb79f06 436 continue; /* next peer */
0a0bf5db 437 peers_pinged++;
a3d5953d 438 debug(15, 4) ("neighborsUdpPing: pinging peer %s for '%s'\n",
b69f7771 439 p->host, url);
440 if (p->type == PEER_MULTICAST)
03a1ee42 441 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
9fb13bb6 442 reqnum = mem->reqnum;
a3d5953d 443 debug(15, 3) ("neighborsUdpPing: key = '%s'\n", entry->key);
444 debug(15, 3) ("neighborsUdpPing: reqnum = %d\n", reqnum);
090089c4 445
b69f7771 446 if (p->icp_port == echo_port) {
a3d5953d 447 debug(15, 4) ("neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n");
1061b406 448 echo_hdr.reqnum = reqnum;
27cd7235 449 query = icpCreateMessage(ICP_DECHO, 0, url, reqnum, 0);
af00901c 450 icpUdpSend(theOutIcpConnection,
b69f7771 451 &p->in_addr,
9dee2904 452 query,
a528eb87 453 LOG_TAG_NONE,
454 PROTO_NONE);
090089c4 455 } else {
6d2296d4 456 flags = 0;
17a0a4ee 457 if (Config.onoff.query_icmp)
b69f7771 458 if (p->icp_version == ICP_VERSION_2)
429fdbec 459 flags |= ICP_FLAG_SRC_RTT;
27cd7235 460 query = icpCreateMessage(ICP_QUERY, flags, url, reqnum, 0);
af00901c 461 icpUdpSend(theOutIcpConnection,
b69f7771 462 &p->in_addr,
9dee2904 463 query,
a528eb87 464 LOG_TAG_NONE,
465 PROTO_NONE);
090089c4 466 }
429fdbec 467 queries_sent++;
090089c4 468
b69f7771 469 p->stats.pings_sent++;
b69f7771 470 if (p->type == PEER_MULTICAST) {
dc835977 471 /*
472 * set a bogus last_reply time so neighborUp() never
473 * says a multicast peer is dead.
474 */
475 p->stats.last_reply = squid_curtime;
b69f7771 476 (*exprep) += p->mcast.n_replies_expected;
477 } else if (neighborUp(p)) {
1061b406 478 /* its alive, expect a reply from it */
b6c0e933 479 (*exprep)++;
090089c4 480 } else {
1061b406 481 /* Neighbor is dead; ping it anyway, but don't expect a reply */
090089c4 482 /* log it once at the threshold */
dc835977 483 if (p->stats.logged_state == PEER_ALIVE) {
a3d5953d 484 debug(15, 0) ("Detected DEAD %s: %s/%d/%d\n",
b69f7771 485 neighborTypeStr(p),
486 p->host, p->http_port, p->icp_port);
dc835977 487 p->stats.logged_state = PEER_DEAD;
090089c4 488 }
489 }
dc835977 490 p->stats.last_query = squid_curtime;
090089c4 491 }
40a1495e 492 if ((first_ping = first_ping->next) == NULL)
493 first_ping = Config.peers;
090089c4 494
495 /* only do source_ping if we have neighbors */
40a1495e 496 if (Config.npeers) {
17a0a4ee 497 if (!Config.onoff.source_ping) {
a3d5953d 498 debug(15, 6) ("neighborsUdpPing: Source Ping is disabled.\n");
0a0bf5db 499 } else if ((ia = ipcache_gethostbyname(host, 0))) {
a3d5953d 500 debug(15, 6) ("neighborsUdpPing: Source Ping: to %s for '%s'\n",
1061b406 501 host, url);
1061b406 502 echo_hdr.reqnum = reqnum;
16b204c4 503 if (icmp_sock != -1) {
e5f6c5c2 504 icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url);
16b204c4 505 } else {
506 to_addr.sin_family = AF_INET;
e5f6c5c2 507 to_addr.sin_addr = ia->in_addrs[ia->cur];
16b204c4 508 to_addr.sin_port = htons(echo_port);
27cd7235 509 query = icpCreateMessage(ICP_SECHO, 0, url, reqnum, 0);
16b204c4 510 icpUdpSend(theOutIcpConnection,
16b204c4 511 &to_addr,
9dee2904 512 query,
16b204c4 513 LOG_TAG_NONE,
514 PROTO_NONE);
515 }
090089c4 516 } else {
a3d5953d 517 debug(15, 6) ("neighborsUdpPing: Source Ping: unknown host: %s\n",
1061b406 518 host);
090089c4 519 }
520 }
429fdbec 521#if LOG_ICP_NUMBERS
522 request->hierarchy.n_sent = peers_pinged;
523 request->hierarchy.n_expect = *exprep;
524#endif
0a0bf5db 525 return peers_pinged;
090089c4 526}
527
d73844ca 528static void
b69f7771 529neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header)
4eda6afe 530{
531 int rtt;
532 int n;
39ac34a9 533 if (p->stats.logged_state == PEER_DEAD && p->tcp_up) {
a3d5953d 534 debug(15, 0) ("Detected REVIVED %s: %s/%d/%d\n",
b69f7771 535 neighborTypeStr(p),
536 p->host, p->http_port, p->icp_port);
dc835977 537 p->stats.logged_state = PEER_ALIVE;
4eda6afe 538 }
dc835977 539 p->stats.last_reply = squid_curtime;
b69f7771 540 n = ++p->stats.pings_acked;
27cd7235 541 if ((icp_opcode) header->opcode <= ICP_END)
b69f7771 542 p->stats.counts[header->opcode]++;
4eda6afe 543 if (mem) {
544 rtt = tvSubMsec(mem->start_ping, current_time);
9e4ad609 545 p->stats.rtt = intAverage(p->stats.rtt, rtt, n, RTT_AV_FACTOR);
b69f7771 546 p->icp_version = (int) header->version;
4eda6afe 547 }
548}
090089c4 549
38792624 550static void
79d39a72 551neighborCountIgnored(peer * p, icp_opcode opnotused)
a7e59001 552{
b69f7771 553 if (p == NULL)
a7e59001 554 return;
b69f7771 555 p->stats.ignored_replies++;
c7a3724d 556 NLateReplies++;
a7e59001 557}
558
e102ebda 559static peer *non_peers = NULL;
560
561static void
562neighborIgnoreNonPeer(const struct sockaddr_in *from, icp_opcode opcode)
563{
564 peer *np;
565 double x;
566 for (np = non_peers; np; np = np->next) {
567 if (np->in_addr.sin_addr.s_addr != from->sin_addr.s_addr)
568 continue;
569 if (np->in_addr.sin_port != from->sin_port)
570 continue;
571 break;
572 }
573 if (np == NULL) {
574 np = xcalloc(1, sizeof(peer));
575 np->in_addr.sin_addr = from->sin_addr;
576 np->in_addr.sin_port = from->sin_port;
577 np->icp_port = ntohl(from->sin_port);
578 np->type = PEER_NONE;
579 np->host = xstrdup(inet_ntoa(from->sin_addr));
580 np->next = non_peers;
581 non_peers = np;
582 }
583 np->stats.ignored_replies++;
584 np->stats.counts[opcode]++;
585 x = log(np->stats.ignored_replies) / log(10.0);
586 if (0.0 != x - (double) (int) x)
587 return;
588 debug(15, 1) ("WARNING: Ignored %d replies from non-peer %s\n",
589 np->stats.ignored_replies, np->host);
590}
591
429fdbec 592/* ignoreMulticastReply
593 *
594 * We want to ignore replies from multicast peers if the
595 * cache_host_domain rules would normally prevent the peer
596 * from being used
597 */
598static int
b69f7771 599ignoreMulticastReply(peer * p, MemObject * mem)
429fdbec 600{
b69f7771 601 if (p == NULL)
429fdbec 602 return 0;
79a15e0a 603 if (!EBIT_TEST(p->options, NEIGHBOR_MCAST_RESPONDER))
429fdbec 604 return 0;
b69f7771 605 if (peerHTTPOkay(p, mem->request))
429fdbec 606 return 0;
607 return 1;
608}
609
610/* I should attach these records to the entry. We take the first
611 * hit we get our wait until everyone misses. The timeout handler
612 * call needs to nip this shopping list or call one of the misses.
613 *
614 * If a hit process is already started, then sobeit
615 */
b8d8561b 616void
79d39a72 617neighborsUdpAck(const char *url, icp_common_t * header, const struct sockaddr_in *from, StoreEntry * entry)
090089c4 618{
b69f7771 619 peer *p = NULL;
30a4f2a8 620 MemObject *mem = entry->mem_obj;
b6c0e933 621 peer_t ntype = PEER_NONE;
5e5126cc 622 char *opcode_d;
a7e59001 623 icp_opcode opcode = (icp_opcode) header->opcode;
090089c4 624
a3d5953d 625 debug(15, 6) ("neighborsUdpAck: opcode %d '%s'\n", (int) opcode, url);
b69f7771 626 if ((p = whichPeer(from)))
627 neighborAlive(p, mem, header);
27cd7235 628 if (opcode > ICP_END)
d2af9477 629 return;
27cd7235 630 opcode_d = icp_opcode_str[opcode];
2d1c6a4f 631 /* check if someone is already fetching it */
79a15e0a 632 if (EBIT_TEST(entry->flag, ENTRY_DISPATCHED)) {
a3d5953d 633 debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n", url);
b69f7771 634 neighborCountIgnored(p, opcode);
d2af9477 635 return;
636 }
2d1c6a4f 637 if (mem == NULL) {
a3d5953d 638 debug(15, 2) ("Ignoring %s for missing mem_obj: %s\n", opcode_d, url);
b69f7771 639 neighborCountIgnored(p, opcode);
8de2f7ad 640 return;
641 }
642 if (entry->ping_status != PING_WAITING) {
a3d5953d 643 debug(15, 2) ("neighborsUdpAck: Unexpected %s for %s\n", opcode_d, url);
b69f7771 644 neighborCountIgnored(p, opcode);
8de2f7ad 645 return;
646 }
4eda6afe 647 if (entry->lock_count == 0) {
a3d5953d 648 debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n", url);
b69f7771 649 neighborCountIgnored(p, opcode);
4eda6afe 650 return;
090089c4 651 }
a3d5953d 652 debug(15, 3) ("neighborsUdpAck: %s for '%s' from %s \n",
b69f7771 653 opcode_d, url, p ? p->host : "source");
654 if (p)
655 ntype = neighborType(p, mem->request);
656 if (ignoreMulticastReply(p, mem)) {
657 neighborCountIgnored(p, opcode);
27cd7235 658 } else if (opcode == ICP_SECHO) {
429fdbec 659 /* Received source-ping reply */
b69f7771 660 if (p) {
a3d5953d 661 debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host);
b69f7771 662 neighborCountIgnored(p, opcode);
429fdbec 663 } else {
664 /* if we reach here, source-ping reply is the first 'parent',
665 * so fetch directly from the source */
a3d5953d 666 debug(15, 6) ("Source is the first to respond.\n");
b3264694 667 mem->icp_reply_callback(NULL, ntype, header, mem->ircb_data);
429fdbec 668 }
27cd7235 669 } else if (opcode == ICP_MISS) {
b69f7771 670 if (p == NULL) {
e102ebda 671 neighborIgnoreNonPeer(from, opcode);
d2af9477 672 } else {
b3264694 673 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
30a4f2a8 674 }
a7c05555 675 } else if (opcode == ICP_HIT) {
b69f7771 676 if (p == NULL) {
e102ebda 677 neighborIgnoreNonPeer(from, opcode);
d2af9477 678 } else {
27cd7235 679 header->opcode = ICP_HIT;
b3264694 680 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 681 }
27cd7235 682 } else if (opcode == ICP_DECHO) {
b69f7771 683 if (p == NULL) {
e102ebda 684 neighborIgnoreNonPeer(from, opcode);
deb79f06 685 } else if (ntype == PEER_SIBLING) {
5e604a0e 686 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
687 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
090089c4 688 } else {
b3264694 689 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 690 }
27cd7235 691 } else if (opcode == ICP_SECHO) {
b69f7771 692 if (p) {
a3d5953d 693 debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host);
b69f7771 694 neighborCountIgnored(p, opcode);
17a0a4ee 695 } else if (!Config.onoff.source_ping) {
a3d5953d 696 debug(15, 1) ("Unsolicited SECHO from %s\n", inet_ntoa(from->sin_addr));
1327c792 697 } else {
b3264694 698 mem->icp_reply_callback(NULL, ntype, header, mem->ircb_data);
090089c4 699 }
27cd7235 700 } else if (opcode == ICP_DENIED) {
b69f7771 701 if (p == NULL) {
e102ebda 702 neighborIgnoreNonPeer(from, opcode);
b69f7771 703 } else if (p->stats.pings_acked > 100) {
27cd7235 704 if (100 * p->stats.counts[ICP_DENIED] / p->stats.pings_acked > 95) {
a3d5953d 705 debug(15, 0) ("95%% of replies from '%s' are UDP_DENIED\n", p->host);
706 debug(15, 0) ("Disabling '%s', please check your configuration.\n", p->host);
b69f7771 707 neighborRemove(p);
708 p = NULL;
e006d372 709 } else {
b69f7771 710 neighborCountIgnored(p, opcode);
30a4f2a8 711 }
712 }
27cd7235 713 } else if (opcode == ICP_MISS_NOFETCH) {
b3264694 714 mem->icp_reply_callback(p, ntype, header, mem->ircb_data);
090089c4 715 } else {
a3d5953d 716 debug(15, 0) ("neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d);
d2af9477 717 }
090089c4 718}
719
deb79f06 720peer *
40a1495e 721peerFindByName(const char *name)
98ffb7e4 722{
b69f7771 723 peer *p = NULL;
40a1495e 724 for (p = Config.peers; p; p = p->next) {
b69f7771 725 if (!strcasecmp(name, p->host))
98ffb7e4 726 break;
727 }
b69f7771 728 return p;
98ffb7e4 729}
b012353a 730
5269d0bd 731int
b69f7771 732neighborUp(const peer * p)
5269d0bd 733{
b69f7771 734 if (!p->tcp_up)
e924600d 735 return 0;
dc835977 736 if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer)
737 return 1;
738 if (p->stats.last_query - p->stats.last_reply >= Config.Timeout.deadPeer)
5269d0bd 739 return 0;
740 return 1;
741}
742
e6e3b09b 743void
b69f7771 744peerDestroy(peer * p)
e6e3b09b 745{
ee4a1f5d 746 struct _domain_ping *l = NULL;
747 struct _domain_ping *nl = NULL;
b69f7771 748 if (p == NULL)
3c6e634f 749 return;
88738790 750 if (p->ck_conn_event_pend)
b69f7771 751 eventDelete(peerCheckConnect, p);
752 if (p->type == PEER_MULTICAST) {
753 if (p->mcast.flags & PEER_COUNT_EVENT_PENDING)
754 eventDelete(peerCountMcastPeersStart, p);
755 if (p->mcast.flags & PEER_COUNTING)
756 eventDelete(peerCountMcastPeersDone, p);
757 }
758 for (l = p->pinglist; l; l = nl) {
ee4a1f5d 759 nl = l->next;
760 safe_free(l->domain);
761 safe_free(l);
762 }
8407afee 763 if (p->ip_lookup_pending)
270b86af 764 ipcacheUnregister(p->host, p);
b69f7771 765 safe_free(p->host);
8407afee 766 cbdataFree(p);
e6e3b09b 767}
c7a3724d 768
dd4bd44e 769static void
03a1ee42 770peerDNSConfigure(const ipcache_addrs * ia, void *data)
dd4bd44e 771{
b69f7771 772 peer *p = data;
dd4bd44e 773 struct sockaddr_in *ap;
774 int j;
8407afee 775 p->ip_lookup_pending = 0;
b69f7771 776 if (p->n_addresses == 0) {
a3d5953d 777 debug(15, 1) ("Configuring %s %s/%d/%d\n", neighborTypeStr(p),
b69f7771 778 p->host, p->http_port, p->icp_port);
779 if (p->type == PEER_MULTICAST)
a3d5953d 780 debug(15, 1) (" Multicast TTL = %d\n", p->mcast.ttl);
b69f7771 781 }
782 p->n_addresses = 0;
dd4bd44e 783 if (ia == NULL) {
a3d5953d 784 debug(0, 0) ("WARNING: DNS lookup for '%s' failed!\n", p->host);
dd4bd44e 785 return;
786 }
c04fe656 787 if ((int) ia->count < 1) {
a3d5953d 788 debug(0, 0) ("WARNING: No IP address found for '%s'!\n", p->host);
dd4bd44e 789 return;
790 }
c04fe656 791 for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) {
b69f7771 792 p->addresses[j] = ia->in_addrs[j];
a3d5953d 793 debug(15, 2) ("--> IP address #%d: %s\n", j, inet_ntoa(p->addresses[j]));
b69f7771 794 p->n_addresses++;
dd4bd44e 795 }
b69f7771 796 ap = &p->in_addr;
dd4bd44e 797 memset(ap, '\0', sizeof(struct sockaddr_in));
798 ap->sin_family = AF_INET;
b69f7771 799 ap->sin_addr = p->addresses[0];
800 ap->sin_port = htons(p->icp_port);
801 if (p->type == PEER_MULTICAST)
802 peerCountMcastPeersSchedule(p, 10);
dd4bd44e 803}
804
805static void
79d39a72 806peerRefreshDNS(void *datanotused)
dd4bd44e 807{
b69f7771 808 peer *p = NULL;
40a1495e 809 peer *next = Config.peers;
79d39a72 810 while ((p = next) != NULL) {
b69f7771 811 next = p->next;
270b86af 812 p->ip_lookup_pending = 1;
429fdbec 813 /* some random, bogus FD for ipcache */
03a1ee42 814 p->test_fd = Squid_MaxFD + current_time.tv_usec;
8407afee 815 ipcache_nbgethostbyname(p->host, peerDNSConfigure, p);
dd4bd44e 816 }
817 /* Reconfigure the peers every hour */
818 eventAdd("peerRefreshDNS", peerRefreshDNS, NULL, 3600);
819}
e924600d 820
821static void
822peerCheckConnect(void *data)
823{
824 peer *p = data;
825 int fd;
88738790 826 if (p->ck_conn_event_pend != 1)
827 debug_trap("bad ck_conn_event_pend counter");
828 p->ck_conn_event_pend--;
e924600d 829 fd = comm_open(SOCK_STREAM, 0, Config.Addrs.tcp_outgoing,
830 0, COMM_NONBLOCKING, p->host);
831 if (fd < 0)
832 return;
8407afee 833 p->ip_lookup_pending = 1;
03a1ee42 834 p->test_fd = fd;
8407afee 835 ipcache_nbgethostbyname(p->host, peerCheckConnect2, p);
e924600d 836}
837
838static void
79d39a72 839peerCheckConnect2(const ipcache_addrs * ianotused, void *data)
e924600d 840{
841 peer *p = data;
8407afee 842 p->ip_lookup_pending = 0;
03a1ee42 843 commConnectStart(p->test_fd,
e924600d 844 p->host,
845 p->http_port,
846 peerCheckConnectDone,
847 p);
848}
849
850static void
851peerCheckConnectDone(int fd, int status, void *data)
852{
853 peer *p = data;
854 p->tcp_up = status == COMM_OK ? 1 : 0;
855 if (p->tcp_up) {
a3d5953d 856 debug(15, 0) ("TCP connection to %s/%d succeeded\n",
e924600d 857 p->host, p->http_port);
858 } else {
88738790 859 p->ck_conn_event_pend++;
e924600d 860 eventAdd("peerCheckConnect", peerCheckConnect, p, 80);
861 }
862 comm_close(fd);
863 return;
864}
865
866void
867peerCheckConnectStart(peer * p)
868{
869 if (!p->tcp_up)
870 return;
a3d5953d 871 debug(15, 0) ("TCP connection to %s/%d failed\n", p->host, p->http_port);
e924600d 872 p->tcp_up = 0;
873 p->last_fail_time = squid_curtime;
88738790 874 p->ck_conn_event_pend++;
e924600d 875 eventAdd("peerCheckConnect", peerCheckConnect, p, 80);
876}
429fdbec 877
878static void
879peerCountMcastPeersSchedule(peer * p, time_t when)
880{
881 if (p->mcast.flags & PEER_COUNT_EVENT_PENDING)
882 return;
883 eventAdd("peerCountMcastPeersStart",
884 peerCountMcastPeersStart,
885 p,
886 when);
887 p->mcast.flags |= PEER_COUNT_EVENT_PENDING;
888}
889
890static void
891peerCountMcastPeersStart(void *data)
892{
893 peer *p = data;
894 ps_state *psstate = xcalloc(1, sizeof(ps_state));
895 StoreEntry *fake;
896 MemObject *mem;
897 icp_common_t *query;
898 LOCAL_ARRAY(char, url, MAX_URL);
ce66013b 899 assert(p->type == PEER_MULTICAST);
429fdbec 900 p->mcast.flags &= ~PEER_COUNT_EVENT_PENDING;
042461c3 901 snprintf(url, MAX_URL, "http://%s/", inet_ntoa(p->in_addr.sin_addr));
88738790 902 fake = storeCreateEntry(url, url, 0, METHOD_GET);
429fdbec 903 psstate->request = requestLink(urlParse(METHOD_GET, url));
904 psstate->entry = fake;
905 psstate->callback = NULL;
906 psstate->fail_callback = NULL;
907 psstate->callback_data = p;
908 psstate->icp.start = current_time;
909 mem = fake->mem_obj;
2db609de 910 mem->request = requestLink(psstate->request);
429fdbec 911 mem->start_ping = current_time;
912 mem->icp_reply_callback = peerCountHandleIcpReply;
913 mem->ircb_data = psstate;
03a1ee42 914 mcastSetTtl(theOutIcpConnection, p->mcast.ttl);
9fb13bb6 915 p->mcast.reqnum = mem->reqnum;
27cd7235 916 query = icpCreateMessage(ICP_QUERY, 0, url, p->mcast.reqnum, 0);
429fdbec 917 icpUdpSend(theOutIcpConnection,
918 &p->in_addr,
919 query,
920 LOG_TAG_NONE,
921 PROTO_NONE);
922 fake->ping_status = PING_WAITING;
923 eventAdd("peerCountMcastPeersDone",
924 peerCountMcastPeersDone,
2db609de 925 psstate,
429fdbec 926 Config.neighborTimeout);
927 p->mcast.flags |= PEER_COUNTING;
928 peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE);
929}
930
931static void
932peerCountMcastPeersDone(void *data)
933{
934 ps_state *psstate = data;
935 peer *p = psstate->callback_data;
936 StoreEntry *fake = psstate->entry;
429fdbec 937 p->mcast.flags &= ~PEER_COUNTING;
9e4ad609 938 p->mcast.avg_n_members = doubleAverage(p->mcast.avg_n_members,
939 (double) psstate->icp.n_recv,
940 ++p->mcast.n_times_counted,
941 10);
a3d5953d 942 debug(15, 1) ("Group %s: %d replies, %4.1f average\n",
429fdbec 943 p->host,
944 psstate->icp.n_recv,
945 p->mcast.avg_n_members);
946 p->mcast.n_replies_expected = (int) p->mcast.avg_n_members;
947 fake->store_status = STORE_ABORTED;
e7a22b88 948 requestUnlink(fake->mem_obj->request);
f0d5043e 949 fake->mem_obj->request = NULL;
429fdbec 950 storeReleaseRequest(fake);
951 storeUnlockObject(fake);
e7a22b88 952 requestUnlink(psstate->request);
2db609de 953 xfree(psstate);
429fdbec 954}
955
956static void
79d39a72 957peerCountHandleIcpReply(peer * pnotused, peer_t type, icp_common_t * hdrnotused, void *data)
429fdbec 958{
959 ps_state *psstate = data;
960 psstate->icp.n_recv++;
429fdbec 961}
ed7f5615 962
963static void
964neighborDumpPeers(StoreEntry * sentry)
965{
966 dump_peers(sentry, Config.peers);
967}
968
969static void
970neighborDumpNonPeers(StoreEntry * sentry)
971{
972 dump_peers(sentry, non_peers);
973}
974
975static void
976dump_peers(StoreEntry * sentry, peer * peers)
977{
978 peer *e = NULL;
979 struct _domain_ping *d = NULL;
980 icp_opcode op;
981 if (peers == NULL)
982 storeAppendPrintf(sentry, "There are no neighbors installed.\n");
983 for (e = peers; e; e = e->next) {
984 assert(e->host != NULL);
985 storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n",
986 neighborTypeStr(e),
987 e->host,
988 e->http_port,
989 e->icp_port);
990 storeAppendPrintf(sentry, "Status : %s\n",
991 neighborUp(e) ? "Up" : "Down");
992 storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt);
993 storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n",
994 (int) (squid_curtime - e->stats.last_query));
995 storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n",
996 (int) (squid_curtime - e->stats.last_reply));
997 storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent);
998 storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n",
999 e->stats.pings_acked,
1000 percent(e->stats.pings_acked, e->stats.pings_sent));
1001 storeAppendPrintf(sentry, "FETCHES : %8d %3d%%\n",
1002 e->stats.fetches,
1003 percent(e->stats.fetches, e->stats.pings_acked));
1004 storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n",
1005 e->stats.ignored_replies,
1006 percent(e->stats.ignored_replies, e->stats.pings_acked));
1007 storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n");
1008 for (op = ICP_INVALID; op < ICP_END; op++) {
1009 if (e->stats.counts[op] == 0)
1010 continue;
1011 storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n",
1012 icp_opcode_str[op],
1013 e->stats.counts[op],
1014 percent(e->stats.counts[op], e->stats.pings_acked));
1015 }
1016 if (e->last_fail_time) {
1017 storeAppendPrintf(sentry, "Last failed connect() at: %s\n",
1018 mkhttpdlogtime(&(e->last_fail_time)));
1019 }
1020 if (e->pinglist != NULL)
1021 storeAppendPrintf(sentry, "DOMAIN LIST: ");
1022 for (d = e->pinglist; d; d = d->next) {
1023 if (d->do_ping)
1024 storeAppendPrintf(sentry, "%s ", d->domain);
1025 else
1026 storeAppendPrintf(sentry, "!%s ", d->domain);
1027 }
1028 storeAppendPrintf(sentry, "Keep-Alive Ratio: %d%%\n",
1029 percent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent));
1030 }
1031}