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