]> git.ipfire.org Git - thirdparty/squid.git/blame - src/neighbors.cc
update
[thirdparty/squid.git] / src / neighbors.cc
CommitLineData
30a4f2a8 1/*
e6e3b09b 2 * $Id: neighbors.cc,v 1.88 1996/11/25 18:47:17 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
0ee4272b 108static int edgeWouldBePinged _PARAMS((const edge *, request_t *));
67508012 109static void neighborRemove _PARAMS((edge *));
0ee4272b 110static edge *whichEdge _PARAMS((const struct sockaddr_in * from));
111static void neighborAlive _PARAMS((edge *, const MemObject *, const icp_common_t *));
a7e59001 112static void neighborCountIgnored _PARAMS((edge * e, icp_opcode op_unused));
b012353a 113static neighbor_t parseNeighborType _PARAMS((const char *s));
090089c4 114
090089c4 115static icp_common_t echo_hdr;
30a4f2a8 116static u_short echo_port;
30a4f2a8 117
28070024 118static struct {
119 int n;
120 int n_parent;
121 int n_sibling;
122 edge *edges_head;
123 edge *edges_tail;
124 edge *first_ping;
125} friends = {
126
127 0, 0, 0, NULL, NULL, NULL
128};
129
0ee4272b 130const char *hier_strings[] =
090089c4 131{
132 "NONE",
133 "DIRECT",
a99f5332 134 "SIBLING_HIT",
090089c4 135 "PARENT_HIT",
136 "SINGLE_PARENT",
30a4f2a8 137 "FIRST_UP_PARENT",
090089c4 138 "NO_PARENT_DIRECT",
139 "FIRST_PARENT_MISS",
140 "LOCAL_IP_DIRECT",
30a4f2a8 141 "FIREWALL_IP_DIRECT",
090089c4 142 "NO_DIRECT_FAIL",
143 "SOURCE_FASTEST",
a99f5332 144 "SIBLING_UDP_HIT_OBJ",
145 "PARENT_UDP_HIT_OBJ",
e6e3b09b 146 "PASSTHROUGH_PARENT",
090089c4 147 "INVALID CODE"
148};
149
150
b8d8561b 151static edge *
0ee4272b 152whichEdge(const struct sockaddr_in *from)
090089c4 153{
22e4fa85 154 int j;
4eda6afe 155 u_short port = ntohs(from->sin_port);
156 struct in_addr ip = from->sin_addr;
090089c4 157 edge *e = NULL;
019dd986 158 debug(15, 3, "whichEdge: from %s port %d\n", inet_ntoa(ip), port);
28070024 159 for (e = friends.edges_head; e; e = e->next) {
090089c4 160 for (j = 0; j < e->n_addresses; j++) {
30a4f2a8 161 if (ip.s_addr == e->addresses[j].s_addr && port == e->icp_port) {
090089c4 162 return e;
163 }
164 }
165 }
4eda6afe 166 return NULL;
090089c4 167}
168
b8d8561b 169void
fe4e214f 170hierarchyNote(request_t * request, hier_code code, int timeout, const char *cache_host)
090089c4 171{
eaa77841 172 if (request) {
173 request->hierarchy.code = code;
174 request->hierarchy.timeout = timeout;
175 request->hierarchy.host = xstrdup(cache_host);
090089c4 176 }
090089c4 177}
178
24a1003d 179static neighbor_t
fe4e214f 180neighborType(const edge * e, const request_t * request)
24a1003d 181{
b012353a 182 const struct _domain_type *d = NULL;
183 for (d = e->typelist; d; d = d->next) {
24a1003d 184 if (matchDomainName(d->domain, request->host))
b012353a 185 if (d->type != EDGE_NONE)
186 return d->type;
24a1003d 187 }
188 return e->type;
189}
190
b8d8561b 191static int
fe4e214f 192edgeWouldBePinged(const edge * e, request_t * request)
090089c4 193{
b012353a 194 const struct _domain_ping *d = NULL;
090089c4 195 int do_ping = 1;
0ee4272b 196 const struct _acl_list *a = NULL;
f88bb09c 197 aclCheck_t checklist;
090089c4 198
24a1003d 199 if (BIT_TEST(request->flags, REQ_NOCACHE))
24382924 200 if (neighborType(e, request) == EDGE_SIBLING)
24a1003d 201 return 0;
b012353a 202 if (e->pinglist == NULL && e->acls == NULL)
090089c4 203 return do_ping;
090089c4 204 do_ping = 0;
b012353a 205 for (d = e->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 }
f88bb09c 210 checklist.src_addr = any_addr; /* XXX bogus! */
211 checklist.request = request;
30a4f2a8 212 for (a = e->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
b8d8561b 220edge *
221getSingleParent(request_t * request, int *n)
090089c4 222{
223 edge *p = NULL;
224 edge *e = NULL;
090089c4 225 int count = 0;
226
28070024 227 if (n == NULL && friends.n_parent < 1)
090089c4 228 return NULL;
28070024 229 for (e = friends.edges_head; e; e = e->next) {
caebbe00 230 if (!edgeWouldBePinged(e, request))
231 continue;
232 count++;
24a1003d 233 if (neighborType(e, request) != EDGE_PARENT) {
caebbe00 234 /* we matched a neighbor, not a parent. There
235 * can be no single parent */
236 if (n == NULL)
237 return NULL;
238 continue;
090089c4 239 }
caebbe00 240 if (p) {
241 /* already have a parent, this makes the second,
242 * so there can be no single parent */
243 if (n == NULL)
244 return NULL;
245 continue;
246 }
247 p = e;
090089c4 248 }
249 /* Ok, all done checking the edges. If only one parent matched, then
250 * p will already point to it */
251 if (n)
252 *n = count;
253 if (count == 1)
254 return p;
255 return NULL;
256}
257
b8d8561b 258edge *
259getFirstUpParent(request_t * request)
090089c4 260{
261 edge *e = NULL;
28070024 262 if (friends.n_parent < 1)
090089c4 263 return NULL;
28070024 264 for (e = friends.edges_head; e; e = e->next) {
30a4f2a8 265 if (!e->neighbor_up)
266 continue;
24a1003d 267 if (neighborType(e, request) != EDGE_PARENT)
090089c4 268 continue;
30a4f2a8 269 if (edgeWouldBePinged(e, request))
090089c4 270 return e;
271 }
272 return NULL;
273}
274
b8d8561b 275edge *
276getNextEdge(edge * e)
090089c4 277{
278 return e->next;
279}
280
b8d8561b 281edge *
282getFirstEdge(void)
090089c4 283{
28070024 284 return friends.edges_head;
090089c4 285}
286
b8d8561b 287static void
288neighborRemove(edge * target)
30a4f2a8 289{
290 edge *e = NULL;
291 edge **E = NULL;
28070024 292 e = friends.edges_head;
293 E = &friends.edges_head;
30a4f2a8 294 while (e) {
295 if (target == e)
296 break;
297 E = &e->next;
298 e = e->next;
299 }
300 if (e) {
301 *E = e->next;
5560c2ae 302 safe_free(e->host);
30a4f2a8 303 safe_free(e);
28070024 304 friends.n--;
30a4f2a8 305 }
306}
307
b8d8561b 308void
0673c0ba 309neighborsDestroy(void)
4d64d74a 310{
311 edge *e = NULL;
312 edge *next = NULL;
313
0ffd22bc 314 debug(15, 3, "neighborsDestroy: called\n");
4d64d74a 315
28070024 316 for (e = friends.edges_head; e; e = next) {
4d64d74a 317 next = e->next;
e6e3b09b 318 edgeDestroy(e);
28070024 319 friends.n--;
4d64d74a 320 }
28070024 321 memset(&friends, '\0', sizeof(friends));
4d64d74a 322}
323
b8d8561b 324void
325neighbors_open(int fd)
090089c4 326{
22e4fa85 327 int j;
30a4f2a8 328 struct sockaddr_in name;
090089c4 329 struct sockaddr_in *ap;
30a4f2a8 330 int len = sizeof(struct sockaddr_in);
0ee4272b 331 const ipcache_addrs *ia = NULL;
090089c4 332 edge *e = NULL;
30a4f2a8 333 edge *next = NULL;
334 edge **E = NULL;
30a4f2a8 335 struct servent *sep = NULL;
090089c4 336
30a4f2a8 337 memset(&name, '\0', sizeof(struct sockaddr_in));
338 if (getsockname(fd, (struct sockaddr *) &name, &len) < 0)
339 debug(15, 1, "getsockname(%d,%p,%p) failed.\n", fd, &name, &len);
090089c4 340
090089c4 341 /* Prepare neighbor connections, one at a time */
28070024 342 E = &friends.edges_head;
343 next = friends.edges_head;
30a4f2a8 344 while ((e = next)) {
c021888f 345 getCurrentTime();
30a4f2a8 346 next = e->next;
c09dd385 347 debug(15, 1, "Configuring neighbor %s/%d/%d\n",
9dee2904 348 e->host, e->http_port, e->icp_port);
e5f6c5c2 349 if ((ia = ipcache_gethostbyname(e->host, IP_BLOCKING_LOOKUP)) == NULL) {
30a4f2a8 350 debug(0, 0, "WARNING!!: DNS lookup for '%s' failed!\n", e->host);
351 debug(0, 0, "THIS NEIGHBOR WILL BE IGNORED.\n");
352 *E = next; /* skip */
353 safe_free(e);
354 continue;
090089c4 355 }
356 e->n_addresses = 0;
e5f6c5c2 357 for (j = 0; j < (int) ia->count && j < EDGE_MAX_ADDRESSES; j++) {
358 e->addresses[j] = ia->in_addrs[j];
090089c4 359 e->n_addresses++;
360 }
361 if (e->n_addresses < 1) {
30a4f2a8 362 debug(0, 0, "WARNING!!: No IP address found for '%s'!\n", e->host);
363 debug(0, 0, "THIS NEIGHBOR WILL BE IGNORED.\n");
364 *E = next; /* skip */
365 safe_free(e);
366 continue;
090089c4 367 }
368 for (j = 0; j < e->n_addresses; j++) {
30a4f2a8 369 debug(15, 2, "--> IP address #%d: %s\n",
370 j, inet_ntoa(e->addresses[j]));
090089c4 371 }
30a4f2a8 372 e->stats.rtt = 0;
090089c4 373
090089c4 374 ap = &e->in_addr;
375 memset(ap, '\0', sizeof(struct sockaddr_in));
376 ap->sin_family = AF_INET;
377 ap->sin_addr = e->addresses[0];
30a4f2a8 378 ap->sin_port = htons(e->icp_port);
090089c4 379
24a1003d 380 e->neighbor_up = 1;
30a4f2a8 381 E = &e->next;
382 }
090089c4 383
30a4f2a8 384 if (0 == echo_hdr.opcode) {
385 echo_hdr.opcode = ICP_OP_SECHO;
386 echo_hdr.version = ICP_VERSION_CURRENT;
387 echo_hdr.length = 0;
388 echo_hdr.reqnum = 0;
389 echo_hdr.flags = 0;
390 echo_hdr.pad = 0;
391 /* memset(echo_hdr.auth, '\0', sizeof(u_num32) * ICP_AUTH_SIZE); */
392 echo_hdr.shostid = name.sin_addr.s_addr;
393 sep = getservbyname("echo", "udp");
394 echo_port = sep ? ntohs((u_short) sep->s_port) : 7;
090089c4 395 }
396}
397
b8d8561b 398int
399neighborsUdpPing(protodispatch_data * proto)
090089c4 400{
7111c86a 401 char *host = proto->request->host;
090089c4 402 char *url = proto->url;
403 StoreEntry *entry = proto->entry;
0ee4272b 404 const ipcache_addrs *ia = NULL;
090089c4 405 struct sockaddr_in to_addr;
406 edge *e = NULL;
407 int i;
30a4f2a8 408 MemObject *mem = entry->mem_obj;
1061b406 409 int reqnum = 0;
6d2296d4 410 int flags;
9dee2904 411 icp_common_t *query;
090089c4 412
30a4f2a8 413 mem->e_pings_n_pings = 0;
414 mem->e_pings_n_acks = 0;
415 mem->e_pings_first_miss = NULL;
416 mem->w_rtt = 0;
417 mem->start_ping = current_time;
090089c4 418
28070024 419 if (friends.edges_head == NULL)
090089c4 420 return 0;
af00901c 421 if (theOutIcpConnection < 0) {
422 debug(15, 0, "neighborsUdpPing: There is no ICP socket!\n");
423 debug(15, 0, "Cannot query neighbors for '%s'.\n", url);
424 debug(15, 0, "Check 'icp_port' in your config file\n");
425 fatal_dump(NULL);
426 }
28070024 427 for (i = 0, e = friends.first_ping; i++ < friends.n; e = e->next) {
a11fc0d2 428 if (entry->swap_status != NO_SWAP)
d556a268 429 fatal_dump("neighborsUdpPing: bad swap_status");
090089c4 430 if (e == (edge *) NULL)
28070024 431 e = friends.edges_head;
019dd986 432 debug(15, 5, "neighborsUdpPing: Edge %s\n", e->host);
090089c4 433
30a4f2a8 434 /* skip any cache where we failed to connect() w/in the last 60s */
435 if (squid_curtime - e->last_fail_time < 60)
090089c4 436 continue;
437
30a4f2a8 438 if (!edgeWouldBePinged(e, proto->request))
090089c4 439 continue; /* next edge */
30a4f2a8 440 if (e->options & NEIGHBOR_NO_QUERY)
441 continue;
ba600184 442 /* the case below seems strange, but can happen if the
443 * URL host is on the other side of a firewall */
444 if (e->type == EDGE_SIBLING)
445 if (!BIT_TEST(proto->request->flags, REQ_HIERARCHICAL))
446 continue;
090089c4 447
9d1b6186 448 debug(15, 4, "neighborsUdpPing: pinging cache %s for '%s'\n",
090089c4 449 e->host, url);
450
2d25dc1a 451 if (BIT_TEST(entry->flag, KEY_PRIVATE))
1061b406 452 reqnum = atoi(entry->key);
9151555a 453 else
1061b406 454 reqnum = getKeyCounter();
0ffd22bc 455 debug(15, 3, "neighborsUdpPing: key = '%s'\n", entry->key);
1061b406 456 debug(15, 3, "neighborsUdpPing: reqnum = %d\n", reqnum);
090089c4 457
30a4f2a8 458 if (e->icp_port == echo_port) {
019dd986 459 debug(15, 4, "neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n");
1061b406 460 echo_hdr.reqnum = reqnum;
9dee2904 461 query = icpCreateMessage(ICP_OP_DECHO, 0, url, reqnum, 0);
af00901c 462 icpUdpSend(theOutIcpConnection,
95d659f0 463 &e->in_addr,
9dee2904 464 query,
a528eb87 465 LOG_TAG_NONE,
466 PROTO_NONE);
090089c4 467 } else {
6d2296d4 468 flags = 0;
469 /* check if we should set ICP_FLAG_HIT_OBJ */
470 if (opt_udp_hit_obj)
471 if (!BIT_TEST(proto->request->flags, REQ_NOCACHE))
472 if (e->icp_version == ICP_VERSION_2)
473 flags |= ICP_FLAG_HIT_OBJ;
9dee2904 474 query = icpCreateMessage(ICP_OP_QUERY, flags, url, reqnum, 0);
af00901c 475 icpUdpSend(theOutIcpConnection,
95d659f0 476 &e->in_addr,
9dee2904 477 query,
a528eb87 478 LOG_TAG_NONE,
479 PROTO_NONE);
090089c4 480 }
481
e90100aa 482 if (e->mcast_ttl > 0) {
483 /* XXX kill us off, so Squid won't expect a reply */
484 e->stats.ack_deficit = HIER_MAX_DEFICIT;
485 } else {
486 e->stats.ack_deficit++;
487 }
30a4f2a8 488 e->stats.pings_sent++;
090089c4 489
457bedbd 490 debug(15, 3, "neighborsUdpPing: %s: ack_deficit = %d\n",
491 e->host, e->stats.ack_deficit);
4aa84b41 492
30a4f2a8 493 if (e->stats.ack_deficit < HIER_MAX_DEFICIT) {
1061b406 494 /* its alive, expect a reply from it */
090089c4 495 e->neighbor_up = 1;
30a4f2a8 496 mem->e_pings_n_pings++;
090089c4 497 } else {
1061b406 498 /* Neighbor is dead; ping it anyway, but don't expect a reply */
090089c4 499 e->neighbor_up = 0;
090089c4 500 /* log it once at the threshold */
30a4f2a8 501 if ((e->stats.ack_deficit == HIER_MAX_DEFICIT)) {
3fc6cf44 502 debug(15, 0, "Detected DEAD %s: %s/%d/%d\n",
d2af9477 503 e->type == EDGE_SIBLING ? "SIBLING" : "PARENT",
3fc6cf44 504 e->host, e->http_port, e->icp_port);
090089c4 505 }
506 }
28070024 507 friends.first_ping = e->next;
090089c4 508 }
509
510 /* only do source_ping if we have neighbors */
28070024 511 if (friends.n) {
1061b406 512 if (!proto->source_ping) {
513 debug(15, 6, "neighborsUdpPing: Source Ping is disabled.\n");
e5f6c5c2 514 } else if ((ia = ipcache_gethostbyname(host, IP_BLOCKING_LOOKUP))) {
1061b406 515 debug(15, 6, "neighborsUdpPing: Source Ping: to %s for '%s'\n",
516 host, url);
1061b406 517 echo_hdr.reqnum = reqnum;
16b204c4 518 if (icmp_sock != -1) {
e5f6c5c2 519 icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url);
16b204c4 520 } else {
521 to_addr.sin_family = AF_INET;
e5f6c5c2 522 to_addr.sin_addr = ia->in_addrs[ia->cur];
16b204c4 523 to_addr.sin_port = htons(echo_port);
9dee2904 524 query = icpCreateMessage(ICP_OP_SECHO, 0, url, reqnum, 0);
16b204c4 525 icpUdpSend(theOutIcpConnection,
16b204c4 526 &to_addr,
9dee2904 527 query,
16b204c4 528 LOG_TAG_NONE,
529 PROTO_NONE);
530 }
090089c4 531 } else {
1061b406 532 debug(15, 6, "neighborsUdpPing: Source Ping: unknown host: %s\n",
533 host);
090089c4 534 }
535 }
1061b406 536 return mem->e_pings_n_pings;
090089c4 537}
538
d73844ca 539static void
fe4e214f 540neighborAlive(edge * e, const MemObject * mem, const icp_common_t * header)
4eda6afe 541{
542 int rtt;
543 int n;
544 /* Neighbor is alive, reset the ack deficit */
545 if (e->stats.ack_deficit >= HIER_MAX_DEFICIT) {
546 debug(15, 0, "Detected REVIVED %s: %s/%d/%d\n",
547 e->type == EDGE_PARENT ? "PARENT" : "SIBLING",
548 e->host, e->http_port, e->icp_port);
549 }
550 e->neighbor_up = 1;
551 e->stats.ack_deficit = 0;
552 n = ++e->stats.pings_acked;
553 if ((icp_opcode) header->opcode <= ICP_OP_END)
554 e->stats.counts[header->opcode]++;
555 if (n > RTT_AV_FACTOR)
556 n = RTT_AV_FACTOR;
557 if (mem) {
558 rtt = tvSubMsec(mem->start_ping, current_time);
559 e->stats.rtt = (e->stats.rtt * (n - 1) + rtt) / n;
560 e->icp_version = (int) header->version;
561 }
562}
090089c4 563
38792624 564static void
a7e59001 565neighborCountIgnored(edge * e, icp_opcode op_unused)
566{
567 if (e == NULL)
568 return;
569 e->stats.ignored_replies++;
570}
571
090089c4 572/* I should attach these records to the entry. We take the first
573 * hit we get our wait until everyone misses. The timeout handler
574 * call needs to nip this shopping list or call one of the misses.
575 *
576 * If a hit process is already started, then sobeit
577 */
b8d8561b 578void
fe4e214f 579neighborsUdpAck(int fd, const char *url, icp_common_t * header, const struct sockaddr_in *from, StoreEntry * entry, char *data, int data_sz)
090089c4 580{
581 edge *e = NULL;
30a4f2a8 582 MemObject *mem = entry->mem_obj;
fe113054 583 int w_rtt;
30a4f2a8 584 HttpStateData *httpState = NULL;
24a1003d 585 neighbor_t ntype = EDGE_NONE;
5e5126cc 586 char *opcode_d;
a7e59001 587 icp_opcode opcode = (icp_opcode) header->opcode;
090089c4 588
a7e59001 589 debug(15, 6, "neighborsUdpAck: opcode %d '%s'\n", (int) opcode, url);
4eda6afe 590 if ((e = whichEdge(from)))
591 neighborAlive(e, mem, header);
a7e59001 592 if (opcode > ICP_OP_END)
d2af9477 593 return;
a7e59001 594 opcode_d = IcpOpcodeStr[opcode];
2d1c6a4f 595 /* check if someone is already fetching it */
596 if (BIT_TEST(entry->flag, ENTRY_DISPATCHED)) {
597 debug(15, 3, "neighborsUdpAck: '%s' already being fetched.\n", url);
a7e59001 598 neighborCountIgnored(e, opcode);
d2af9477 599 return;
600 }
2d1c6a4f 601 if (mem == NULL) {
602 debug(15, 1, "Ignoring %s for missing mem_obj: %s\n", opcode_d, url);
a7e59001 603 neighborCountIgnored(e, opcode);
8de2f7ad 604 return;
605 }
606 if (entry->ping_status != PING_WAITING) {
2d1c6a4f 607 debug(15, 1, "neighborsUdpAck: Unexpected %s for %s\n", opcode_d, url);
a7e59001 608 neighborCountIgnored(e, opcode);
8de2f7ad 609 return;
610 }
4eda6afe 611 if (entry->lock_count == 0) {
2d1c6a4f 612 debug(12, 1, "neighborsUdpAck: '%s' has no locks\n", url);
a7e59001 613 neighborCountIgnored(e, opcode);
4eda6afe 614 return;
090089c4 615 }
d2af9477 616 debug(15, 3, "neighborsUdpAck: %s for '%s' from %s \n",
5e5126cc 617 opcode_d, url, e ? e->host : "source");
d2af9477 618 mem->e_pings_n_acks++;
5e5126cc 619 if (e)
ceb8994e 620 ntype = neighborType(e, mem->request);
a7e59001 621 if (opcode == ICP_OP_SECHO) {
d2af9477 622 /* Received source-ping reply */
090089c4 623 if (e) {
004b3381 624 debug(15, 1, "Ignoring SECHO from neighbor %s\n", e->host);
a7e59001 625 neighborCountIgnored(e, opcode);
090089c4 626 } else {
d2af9477 627 /* if we reach here, source-ping reply is the first 'parent',
628 * so fetch directly from the source */
019dd986 629 debug(15, 6, "Source is the first to respond.\n");
eaa77841 630 hierarchyNote(entry->mem_obj->request,
090089c4 631 HIER_SOURCE_FASTEST,
632 0,
1061b406 633 fqdnFromAddr(from->sin_addr));
30a4f2a8 634 entry->ping_status = PING_DONE;
457bedbd 635 protoStart(0, entry, NULL, entry->mem_obj->request);
d2af9477 636 return;
090089c4 637 }
a7e59001 638 } else if (opcode == ICP_OP_HIT_OBJ) {
a99f5332 639 if (e == NULL) {
640 debug(15, 0, "Ignoring ICP_OP_HIT_OBJ from non-neighbor %s\n",
641 inet_ntoa(from->sin_addr));
642 } else if (entry->object_len != 0) {
d2af9477 643 debug(15, 1, "Too late UDP_HIT_OBJ '%s'?\n", entry->url);
644 } else {
b8d8561b 645 if (e->options & NEIGHBOR_PROXY_ONLY)
646 storeReleaseRequest(entry);
d2af9477 647 protoCancelTimeout(0, entry);
648 entry->ping_status = PING_DONE;
649 httpState = xcalloc(1, sizeof(HttpStateData));
650 httpState->entry = entry;
651 httpProcessReplyHeader(httpState, data, data_sz);
652 storeAppend(entry, data, data_sz);
eaa77841 653 hierarchyNote(entry->mem_obj->request,
24a1003d 654 ntype == EDGE_PARENT ? HIER_PARENT_UDP_HIT_OBJ : HIER_SIBLING_UDP_HIT_OBJ,
d2af9477 655 0,
a99f5332 656 e->host);
37bcb332 657 storeComplete(entry); /* This might release entry! */
d2af9477 658 if (httpState->reply_hdr)
659 put_free_8k_page(httpState->reply_hdr);
660 safe_free(httpState);
30a4f2a8 661 return;
662 }
a7e59001 663 } else if (opcode == ICP_OP_HIT) {
d2af9477 664 if (e == NULL) {
004b3381 665 debug(15, 1, "Ignoring HIT from non-neighbor %s\n",
666 inet_ntoa(from->sin_addr));
d2af9477 667 } else {
eaa77841 668 hierarchyNote(entry->mem_obj->request,
24a1003d 669 ntype == EDGE_PARENT ? HIER_PARENT_HIT : HIER_SIBLING_HIT,
d2af9477 670 0,
671 e->host);
d2af9477 672 entry->ping_status = PING_DONE;
457bedbd 673 protoStart(0, entry, e, entry->mem_obj->request);
090089c4 674 return;
675 }
a7e59001 676 } else if (opcode == ICP_OP_DECHO) {
d2af9477 677 if (e == NULL) {
004b3381 678 debug(15, 1, "Ignoring DECHO from non-neighbor %s\n",
679 inet_ntoa(from->sin_addr));
24a1003d 680 } else if (ntype == EDGE_SIBLING) {
5e604a0e 681 debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n");
682 debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n");
090089c4 683 } else {
30a4f2a8 684 w_rtt = tvSubMsec(mem->start_ping, current_time) / e->weight;
685 if (mem->w_rtt == 0 || w_rtt < mem->w_rtt) {
686 mem->e_pings_first_miss = e;
687 mem->w_rtt = w_rtt;
090089c4 688 }
689 }
a7e59001 690 } else if (opcode == ICP_OP_MISS) {
d2af9477 691 if (e == NULL) {
004b3381 692 debug(15, 1, "Ignoring MISS from non-neighbor %s\n",
693 inet_ntoa(from->sin_addr));
24a1003d 694 } else if (ntype == EDGE_PARENT) {
d2af9477 695 w_rtt = tvSubMsec(mem->start_ping, current_time) / e->weight;
696 if (mem->w_rtt == 0 || w_rtt < mem->w_rtt) {
697 mem->e_pings_first_miss = e;
698 mem->w_rtt = w_rtt;
699 }
090089c4 700 }
a7e59001 701 } else if (opcode == ICP_OP_DENIED) {
d2af9477 702 if (e == NULL) {
004b3381 703 debug(15, 1, "Ignoring DENIED from non-neighbor %s\n",
704 inet_ntoa(from->sin_addr));
d2af9477 705 } else if (e->stats.pings_acked > 100) {
30a4f2a8 706 if (100 * e->stats.counts[ICP_OP_DENIED] / e->stats.pings_acked > 95) {
707 debug(15, 0, "95%% of replies from '%s' are UDP_DENIED\n", e->host);
708 debug(15, 0, "Disabling '%s', please check your configuration.\n", e->host);
709 neighborRemove(e);
e006d372 710 e = NULL;
711 } else {
c4e93e73 712 neighborCountIgnored(e, opcode);
30a4f2a8 713 }
714 }
a7e59001 715 } else if (opcode == ICP_OP_RELOADING) {
1061b406 716 if (e)
717 debug(15, 3, "neighborsUdpAck: %s is RELOADING\n", e->host);
090089c4 718 } else {
5e5126cc 719 debug(15, 0, "neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d);
d2af9477 720 }
721 if (mem->e_pings_n_acks == mem->e_pings_n_pings) {
d2af9477 722 entry->ping_status = PING_DONE;
723 debug(15, 6, "neighborsUdpAck: All replies received.\n");
457bedbd 724 /* pass in fd=0 here so protoStart() looks up the real FD
d2af9477 725 * and resets the timeout handler */
726 getFromDefaultSource(0, entry);
727 return;
090089c4 728 }
729}
730
b8d8561b 731void
b012353a 732neighborAdd(const char *host,
9dee2904 733 const char *type,
734 int http_port,
735 int icp_port,
736 int options,
737 int weight,
738 int mcast_ttl)
090089c4 739{
9dee2904 740 edge *e = NULL;
741 const char *me = getMyHostname();
9dee2904 742 if (!strcmp(host, me) && http_port == Config.Port.http) {
28070024 743 debug(15, 0, "neighborAdd: skipping cache_host %s %s/%d/%d\n",
9dee2904 744 type, host, http_port, icp_port);
745 return;
746 }
747 e = xcalloc(1, sizeof(edge));
748 e->http_port = http_port;
749 e->icp_port = icp_port;
750 e->mcast_ttl = mcast_ttl;
751 e->options = options;
752 e->weight = weight;
753 e->host = xstrdup(host);
754 e->pinglist = NULL;
755 e->typelist = NULL;
756 e->acls = NULL;
757 e->neighbor_up = 0;
758 e->icp_version = ICP_VERSION_CURRENT;
759 e->type = parseNeighborType(type);
760 if (e->type == EDGE_PARENT)
28070024 761 friends.n_parent++;
9dee2904 762 else if (e->type == EDGE_SIBLING)
28070024 763 friends.n_sibling++;
9dee2904 764
765 /* Append edge */
28070024 766 if (!friends.edges_head)
767 friends.edges_head = e;
768 if (friends.edges_tail)
769 friends.edges_tail->next = e;
770 friends.edges_tail = e;
771 friends.n++;
090089c4 772}
773
b8d8561b 774void
b012353a 775neighborAddDomainPing(const char *host, const char *domain)
090089c4 776{
b012353a 777 struct _domain_ping *l = NULL;
778 struct _domain_ping **L = NULL;
779 edge *e;
780 if ((e = neighborFindByName(host)) == NULL) {
30a4f2a8 781 debug(15, 0, "%s, line %d: No cache_host '%s'\n",
782 cfg_filename, config_lineno, host);
783 return;
784 }
b012353a 785 l = xmalloc(sizeof(struct _domain_ping));
090089c4 786 l->do_ping = 1;
787 if (*domain == '!') { /* check for !.edu */
788 l->do_ping = 0;
789 domain++;
790 }
791 l->domain = xstrdup(domain);
792 l->next = NULL;
b012353a 793 for (L = &(e->pinglist); *L; L = &((*L)->next));
794 *L = l;
795}
796
797void
798neighborAddDomainType(const char *host, const char *domain, const char *type)
799{
800 struct _domain_type *l = NULL;
801 struct _domain_type **L = NULL;
802 edge *e;
803 if ((e = neighborFindByName(host)) == NULL) {
804 debug(15, 0, "%s, line %d: No cache_host '%s'\n",
805 cfg_filename, config_lineno, host);
806 return;
807 }
808 l = xmalloc(sizeof(struct _domain_type));
809 l->type = parseNeighborType(type);
810 l->domain = xstrdup(domain);
811 l->next = NULL;
812 for (L = &(e->typelist); *L; L = &((*L)->next));
090089c4 813 *L = l;
30a4f2a8 814}
815
b8d8561b 816void
b012353a 817neighborAddAcl(const char *host, const char *aclname)
30a4f2a8 818{
b012353a 819 edge *e;
30a4f2a8 820 struct _acl_list *L = NULL;
821 struct _acl_list **Tail = NULL;
822 struct _acl *a = NULL;
090089c4 823
b012353a 824 if ((e = neighborFindByName(host)) == NULL) {
30a4f2a8 825 debug(15, 0, "%s, line %d: No cache_host '%s'\n",
826 cfg_filename, config_lineno, host);
827 return;
828 }
829 L = xcalloc(1, sizeof(struct _acl_list));
830 L->op = 1;
831 if (*aclname == '!') {
832 L->op = 0;
833 aclname++;
834 }
b012353a 835 debug(15, 3, "neighborAddAcl: looking for ACL name '%s'\n", aclname);
30a4f2a8 836 a = aclFindByName(aclname);
837 if (a == NULL) {
838 debug(15, 0, "%s line %d: %s\n",
839 cfg_filename, config_lineno, config_input_line);
b012353a 840 debug(15, 0, "neighborAddAcl: ACL name '%s' not found.\n", aclname);
30a4f2a8 841 xfree(L);
842 return;
843 }
844 if (a->type == ACL_SRC_IP) {
845 debug(15, 0, "%s line %d: %s\n",
846 cfg_filename, config_lineno, config_input_line);
b012353a 847 debug(15, 0, "neighborAddAcl: 'src' ALC's not supported for 'cache_host_acl'\n");
30a4f2a8 848 xfree(L);
849 return;
850 }
851 L->acl = a;
b012353a 852 for (Tail = &(e->acls); *Tail; Tail = &((*Tail)->next));
30a4f2a8 853 *Tail = L;
090089c4 854}
855
b8d8561b 856edge *
0ee4272b 857neighborFindByName(const char *name)
98ffb7e4 858{
859 edge *e = NULL;
28070024 860 for (e = friends.edges_head; e; e = e->next) {
98ffb7e4 861 if (!strcasecmp(name, e->host))
862 break;
863 }
864 return e;
865}
b012353a 866
867static neighbor_t
868parseNeighborType(const char *s)
869{
9dee2904 870 if (!strcasecmp(s, "parent"))
871 return EDGE_PARENT;
872 if (!strcasecmp(s, "neighbor"))
873 return EDGE_SIBLING;
874 if (!strcasecmp(s, "neighbour"))
875 return EDGE_SIBLING;
876 if (!strcasecmp(s, "sibling"))
b012353a 877 return EDGE_SIBLING;
9dee2904 878 debug(15, 0, "WARNING: Unknown neighbor type: %s\n", s);
879 return EDGE_SIBLING;
b012353a 880}
e6e3b09b 881
882void
883edgeDestroy(edge *e)
884{
885 if (e == NULL)
886 return;
887 safe_free(e->host);
888 safe_free(e);
889}