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