--------------------- PatchSet 5316 Date: 2002/10/09 05:55:49 Author: rbcollins Branch: rbcollins_cxxtest Tag: (none) Log: tada, some more Members: src/ICP.h:1.1.2.3->1.1.2.4 src/Makefile.am:1.29.2.20->1.29.2.21 src/event.c:1.5.18.1->1.5.18.2(DEAD) src/event.cc:1.1->1.1.2.1 src/icp_v2.cc:1.1.2.16->1.1.2.17 src/main.cc:1.1.2.2->1.1.2.3 src/neighbors.c:1.20.6.2->1.20.6.3(DEAD) src/neighbors.cc:1.1->1.1.2.1 src/protos.h:1.63.2.20->1.63.2.21 src/store_digest.c:1.10.44.2->1.10.44.3(DEAD) src/store_digest.cc:1.1->1.1.2.1 src/wccp.c:1.11->1.11.6.1(DEAD) src/wccp.cc:1.1->1.1.2.1 Index: squid/src/ICP.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/ICP.h,v retrieving revision 1.1.2.3 retrieving revision 1.1.2.4 diff -u -r1.1.2.3 -r1.1.2.4 --- squid/src/ICP.h 8 Oct 2002 03:15:50 -0000 1.1.2.3 +++ squid/src/ICP.h 9 Oct 2002 05:55:49 -0000 1.1.2.4 @@ -1,6 +1,6 @@ /* - * $Id: ICP.h,v 1.1.2.3 2002/10/08 03:15:50 rbcollins Exp $ + * $Id: ICP.h,v 1.1.2.4 2002/10/09 05:55:49 rbcollins Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -52,12 +52,22 @@ u_int32_t pad; u_int32_t shostid; /* sender host id */ #ifdef __cplusplus + _icp_common_t (); _icp_common_t (char *buf, unsigned int len); void handleReply(char *buf, struct sockaddr_in *from); + static _icp_common_t *createMessage(icp_opcode opcode, int flags, const char *url, int reqnum, int pad); #endif }; #ifdef __cplusplus + +inline icp_opcode &operator++ (icp_opcode &aCode) +{ + aCode = (icp_opcode)(++(int)aCode); + return aCode; +} + + /* todo: mempool this */ class ICPState { public: @@ -74,11 +84,6 @@ request_t * icpGetRequest(char *url, int reqnum, int fd, struct sockaddr_in *from); int icpAccessAllowed(struct sockaddr_in *from, request_t * icp_request); -SQUIDCEXTERN void *icpCreateMessage(icp_opcode opcode, - int flags, - const char *url, - int reqnum, - int pad); SQUIDCEXTERN void icpCreateAndSend(icp_opcode, int flags, char const *url, int reqnum, int pad, int fd, const struct sockaddr_in *from); extern icp_opcode icpGetCommonOpcode(); @@ -87,6 +92,15 @@ void icpDenyAccess(struct sockaddr_in *from, char *url, int reqnum, int fd); SQUIDCEXTERN PF icpHandleUdp; SQUIDCEXTERN PF icpUdpSendQueue; +SQUIDCEXTERN void icpHandleIcpV3(int, struct sockaddr_in, char *, int); +SQUIDCEXTERN int icpCheckUdpHit(StoreEntry *, request_t * request); +SQUIDCEXTERN void icpConnectionsOpen(void); +SQUIDCEXTERN void icpConnectionShutdown(void); +SQUIDCEXTERN void icpConnectionClose(void); +SQUIDCEXTERN int icpSetCacheKey(const cache_key * key); +SQUIDCEXTERN const cache_key *icpGetCacheKey(const char *url, int reqnum); + + #endif /* SQUID_ICP_H */ Index: squid/src/Makefile.am =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Makefile.am,v retrieving revision 1.29.2.20 retrieving revision 1.29.2.21 diff -u -r1.29.2.20 -r1.29.2.21 --- squid/src/Makefile.am 9 Oct 2002 05:21:28 -0000 1.29.2.20 +++ squid/src/Makefile.am 9 Oct 2002 05:55:49 -0000 1.29.2.21 @@ -144,7 +144,7 @@ enums.h \ errorpage.cc \ ETag.cc \ - event.c \ + event.cc \ external_acl.cc \ fd.cc \ filemap.cc \ @@ -182,7 +182,7 @@ MemBuf.cc \ mime.cc \ multicast.cc \ - neighbors.c \ + neighbors.cc \ net_db.cc \ Packer.cc \ $(XPROF_STATS_SOURCE) \ @@ -207,7 +207,7 @@ StoreIOBuffer.h \ store_client.cc \ StoreClient.h \ - store_digest.c \ + store_digest.cc \ store_dir.cc \ store_key_md5.c \ store_log.c \ @@ -223,7 +223,7 @@ urn.cc \ useragent.c \ wais.cc \ - wccp.c \ + wccp.cc \ whois.cc \ $(WIN32SOURCE) --- squid/src/event.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,201 +0,0 @@ - -/* - * $Id: event.c,v 1.5.18.1 2002/10/04 07:07:25 rbcollins Exp $ - * - * DEBUG: section 41 Event Processing - * AUTHOR: Henrik Nordstrom - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" -#include "Store.h" - -/* The list of event processes */ -struct ev_entry { - EVH *func; - void *arg; - const char *name; - double when; - struct ev_entry *next; - int weight; - int id; -}; - -static struct ev_entry *tasks = NULL; -static OBJH eventDump; -static int run_id = 0; -static const char *last_event_ran = NULL; - -void -eventAdd(const char *name, EVH * func, void *arg, double when, int weight) -{ - struct ev_entry *event = memAllocate(MEM_EVENT); - struct ev_entry **E; - event->func = func; - event->arg = cbdataReference(arg); - event->name = name; - event->when = current_dtime + when; - event->weight = weight; - event->id = run_id; - debug(41, 7) ("eventAdd: Adding '%s', in %f seconds\n", name, when); - /* Insert after the last event with the same or earlier time */ - for (E = &tasks; *E; E = &(*E)->next) { - if ((*E)->when > event->when) - break; - } - event->next = *E; - *E = event; -} - -/* same as eventAdd but adds a random offset within +-1/3 of delta_ish */ -void -eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight) -{ - if (delta_ish >= 3.0) { - const double two_third = (2.0 * delta_ish) / 3.0; - delta_ish = two_third + (drand48() * two_third); - /* - * I'm sure drand48() isn't portable. Tell me what function - * you have that returns a random double value in the range 0,1. - */ - } - eventAdd(name, func, arg, delta_ish, weight); -} - -void -eventDelete(EVH * func, void *arg) -{ - struct ev_entry **E; - struct ev_entry *event; - for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) { - if (event->func != func) - continue; - if (event->arg != arg) - continue; - *E = event->next; - cbdataReferenceDone(event->arg); - memFree(event, MEM_EVENT); - return; - } - debug_trap("eventDelete: event not found"); -} - -void -eventRun(void) -{ - struct ev_entry *event = NULL; - int weight = 0; - if (NULL == tasks) - return; - if (tasks->when > current_dtime) - return; - run_id++; - debug(41, 5) ("eventRun: RUN ID %d\n", run_id); - while ((event = tasks)) { - EVH *callback; - void *cbdata; - if (event->when > current_dtime) - break; - if (event->id == run_id) /* was added during this run */ - break; - if (weight) - break; - tasks = event->next; - callback = event->func; - event->func = NULL; - if (cbdataReferenceValidDone(event->arg, &cbdata)) { - weight += event->weight; - /* XXX assumes ->name is static memory! */ - last_event_ran = event->name; - debug(41, 5) ("eventRun: Running '%s', id %d\n", - event->name, event->id); - callback(cbdata); - } - memFree(event, MEM_EVENT); - } -} - -time_t -eventNextTime(void) -{ - if (!tasks) - return (time_t) 10; - return (time_t) ((tasks->when - current_dtime) * 1000); -} - -void -eventInit(void) -{ - memDataInit(MEM_EVENT, "event", sizeof(struct ev_entry), 0); - cachemgrRegister("events", - "Event Queue", - eventDump, 0, 1); -} - -static void -eventDump(StoreEntry * sentry) -{ - struct ev_entry *e = tasks; - if (last_event_ran) - storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran); - storeAppendPrintf(sentry, "%s\t%s\t%s\t%s\n", - "Operation", - "Next Execution", - "Weight", - "Callback Valid?"); - while (e != NULL) { - storeAppendPrintf(sentry, "%s\t%f seconds\t%d\t%s\n", - e->name, e->when - current_dtime, e->weight, - e->arg ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A"); - e = e->next; - } -} - -void -eventFreeMemory(void) -{ - struct ev_entry *event; - while ((event = tasks)) { - tasks = event->next; - cbdataReferenceDone(event->arg); - memFree(event, MEM_EVENT); - } - tasks = NULL; -} - -int -eventFind(EVH * func, void *arg) -{ - struct ev_entry *event; - for (event = tasks; event != NULL; event = event->next) { - if (event->func == func && event->arg == arg) - return 1; - } - return 0; -} --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/event.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,201 @@ + +/* + * $Id: event.cc,v 1.1.2.1 2002/10/09 05:55:49 rbcollins Exp $ + * + * DEBUG: section 41 Event Processing + * AUTHOR: Henrik Nordstrom + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "Store.h" + +/* The list of event processes */ +struct ev_entry { + EVH *func; + void *arg; + const char *name; + double when; + struct ev_entry *next; + int weight; + int id; +}; + +static struct ev_entry *tasks = NULL; +static OBJH eventDump; +static int run_id = 0; +static const char *last_event_ran = NULL; + +void +eventAdd(const char *name, EVH * func, void *arg, double when, int weight) +{ + struct ev_entry *event = (ev_entry *)memAllocate(MEM_EVENT); + struct ev_entry **E; + event->func = func; + event->arg = cbdataReference(arg); + event->name = name; + event->when = current_dtime + when; + event->weight = weight; + event->id = run_id; + debug(41, 7) ("eventAdd: Adding '%s', in %f seconds\n", name, when); + /* Insert after the last event with the same or earlier time */ + for (E = &tasks; *E; E = &(*E)->next) { + if ((*E)->when > event->when) + break; + } + event->next = *E; + *E = event; +} + +/* same as eventAdd but adds a random offset within +-1/3 of delta_ish */ +void +eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight) +{ + if (delta_ish >= 3.0) { + const double two_third = (2.0 * delta_ish) / 3.0; + delta_ish = two_third + (drand48() * two_third); + /* + * I'm sure drand48() isn't portable. Tell me what function + * you have that returns a random double value in the range 0,1. + */ + } + eventAdd(name, func, arg, delta_ish, weight); +} + +void +eventDelete(EVH * func, void *arg) +{ + struct ev_entry **E; + struct ev_entry *event; + for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) { + if (event->func != func) + continue; + if (event->arg != arg) + continue; + *E = event->next; + cbdataReferenceDone(event->arg); + memFree(event, MEM_EVENT); + return; + } + debug_trap("eventDelete: event not found"); +} + +void +eventRun(void) +{ + struct ev_entry *event = NULL; + int weight = 0; + if (NULL == tasks) + return; + if (tasks->when > current_dtime) + return; + run_id++; + debug(41, 5) ("eventRun: RUN ID %d\n", run_id); + while ((event = tasks)) { + EVH *callback; + void *cbdata; + if (event->when > current_dtime) + break; + if (event->id == run_id) /* was added during this run */ + break; + if (weight) + break; + tasks = event->next; + callback = event->func; + event->func = NULL; + if (cbdataReferenceValidDone(event->arg, &cbdata)) { + weight += event->weight; + /* XXX assumes ->name is static memory! */ + last_event_ran = event->name; + debug(41, 5) ("eventRun: Running '%s', id %d\n", + event->name, event->id); + callback(cbdata); + } + memFree(event, MEM_EVENT); + } +} + +time_t +eventNextTime(void) +{ + if (!tasks) + return (time_t) 10; + return (time_t) ((tasks->when - current_dtime) * 1000); +} + +void +eventInit(void) +{ + memDataInit(MEM_EVENT, "event", sizeof(struct ev_entry), 0); + cachemgrRegister("events", + "Event Queue", + eventDump, 0, 1); +} + +static void +eventDump(StoreEntry * sentry) +{ + struct ev_entry *e = tasks; + if (last_event_ran) + storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran); + storeAppendPrintf(sentry, "%s\t%s\t%s\t%s\n", + "Operation", + "Next Execution", + "Weight", + "Callback Valid?"); + while (e != NULL) { + storeAppendPrintf(sentry, "%s\t%f seconds\t%d\t%s\n", + e->name, e->when - current_dtime, e->weight, + e->arg ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A"); + e = e->next; + } +} + +void +eventFreeMemory(void) +{ + struct ev_entry *event; + while ((event = tasks)) { + tasks = event->next; + cbdataReferenceDone(event->arg); + memFree(event, MEM_EVENT); + } + tasks = NULL; +} + +int +eventFind(EVH * func, void *arg) +{ + struct ev_entry *event; + for (event = tasks; event != NULL; event = event->next) { + if (event->func == func && event->arg == arg) + return 1; + } + return 0; +} Index: squid/src/icp_v2.cc =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/icp_v2.cc,v retrieving revision 1.1.2.16 retrieving revision 1.1.2.17 diff -u -r1.1.2.16 -r1.1.2.17 --- squid/src/icp_v2.cc 8 Oct 2002 03:28:47 -0000 1.1.2.16 +++ squid/src/icp_v2.cc 9 Oct 2002 05:55:49 -0000 1.1.2.17 @@ -1,6 +1,6 @@ /* - * $Id: icp_v2.cc,v 1.1.2.16 2002/10/08 03:28:47 rbcollins Exp $ + * $Id: icp_v2.cc,v 1.1.2.17 2002/10/09 05:55:49 rbcollins Exp $ * * DEBUG: section 12 Internet Cache Protocol * AUTHOR: Duane Wessels @@ -48,6 +48,10 @@ static icpUdpData *IcpQueueTail = NULL; /* icp_common_t */ +_icp_common_t::_icp_common_t() : opcode(ICP_INVALID), version(0), length(0), reqnum(0), flags(0), pad(0), shostid(0) +{ +} + _icp_common_t::_icp_common_t(char *buf, unsigned int len) { if (len < sizeof(_icp_common_t)) { @@ -161,8 +165,8 @@ } } -void * -icpCreateMessage( +_icp_common_t * +_icp_common_t::createMessage( icp_opcode opcode, int flags, const char *url, @@ -189,7 +193,7 @@ if (opcode == ICP_QUERY) urloffset += sizeof(u_int32_t); xmemcpy(urloffset, url, strlen(url)); - return buf; + return (icp_common_t *)buf; } int @@ -288,7 +292,7 @@ void icpCreateAndSend(icp_opcode opcode, int flags, char const *url, int reqnum, int pad, int fd, const struct sockaddr_in *from) { - icp_common_t *reply = (icp_common_t *) icpCreateMessage(opcode, flags, url, reqnum, pad); + icp_common_t *reply = _icp_common_t::createMessage(opcode, flags, url, reqnum, pad); icpUdpSend(fd, from, reply, icpLogFromICPCode(opcode), 0); } Index: squid/src/main.cc =================================================================== RCS file: /cvsroot/squid-sf//squid/src/Attic/main.cc,v retrieving revision 1.1.2.2 retrieving revision 1.1.2.3 diff -u -r1.1.2.2 -r1.1.2.3 --- squid/src/main.cc 4 Oct 2002 07:07:26 -0000 1.1.2.2 +++ squid/src/main.cc 9 Oct 2002 05:55:49 -0000 1.1.2.3 @@ -1,6 +1,6 @@ /* - * $Id: main.cc,v 1.1.2.2 2002/10/04 07:07:26 rbcollins Exp $ + * $Id: main.cc,v 1.1.2.3 2002/10/09 05:55:49 rbcollins Exp $ * * DEBUG: section 1 Startup and Main Loop * AUTHOR: Harvest Derived @@ -36,6 +36,7 @@ #include "squid.h" #include "authenticate.h" #include "Store.h" +#include "ICP.h" /* for error reporting from xmalloc and friends */ extern void (*failure_notify) (const char *); --- squid/src/neighbors.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,1442 +0,0 @@ - -/* - * $Id: neighbors.c,v 1.20.6.2 2002/10/08 02:25:37 rbcollins Exp $ - * - * DEBUG: section 15 Neighbor Routines - * AUTHOR: Harvest Derived - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid.h" -#include "Store.h" -#include "ICP.h" - -/* count mcast group peers every 15 minutes */ -#define MCAST_COUNT_RATE 900 - -static int peerAllowedToUse(const peer *, request_t *); -static int peerWouldBePinged(const peer *, request_t *); -static void neighborRemove(peer *); -static void neighborAlive(peer *, const MemObject *, const icp_common_t *); -#if USE_HTCP -static void neighborAliveHtcp(peer *, const MemObject *, const htcpReplyData *); -#endif -static void neighborCountIgnored(peer *); -static void peerRefreshDNS(void *); -static IPH peerDNSConfigure; -static void peerProbeConnect(peer *); -static IPH peerProbeConnect2; -static CNCB peerProbeConnectDone; -static void peerCountMcastPeersDone(void *data); -static void peerCountMcastPeersStart(void *data); -static void peerCountMcastPeersSchedule(peer * p, time_t when); -static IRCB peerCountHandleIcpReply; -static void neighborIgnoreNonPeer(const struct sockaddr_in *, icp_opcode); -static OBJH neighborDumpPeers; -static OBJH neighborDumpNonPeers; -static void dump_peers(StoreEntry * sentry, peer * peers); - -static icp_common_t echo_hdr; -static u_short echo_port; - -static int NLateReplies = 0; -static peer *first_ping = NULL; - -const char * -neighborTypeStr(const peer * p) -{ - if (p->type == PEER_NONE) - return "Non-Peer"; - if (p->type == PEER_SIBLING) - return "Sibling"; - if (p->type == PEER_MULTICAST) - return "Multicast Group"; - return "Parent"; -} - - -peer * -whichPeer(const struct sockaddr_in * from) -{ - int j; - u_short port = ntohs(from->sin_port); - struct in_addr ip = from->sin_addr; - peer *p = NULL; - debug(15, 3) ("whichPeer: from %s port %d\n", inet_ntoa(ip), port); - for (p = Config.peers; p; p = p->next) { - for (j = 0; j < p->n_addresses; j++) { - if (ip.s_addr == p->addresses[j].s_addr && port == p->icp.port) { - return p; - } - } - } - return NULL; -} - -peer_t -neighborType(const peer * p, const request_t * request) -{ - const struct _domain_type *d = NULL; - for (d = p->typelist; d; d = d->next) { - if (0 == matchDomainName(request->host, d->domain)) - if (d->type != PEER_NONE) - return d->type; - } - return p->type; -} - -/* - * peerAllowedToUse - * - * this function figures out if it is appropriate to fetch REQUEST - * from PEER. - */ -static int -peerAllowedToUse(const peer * p, request_t * request) -{ - const struct _domain_ping *d = NULL; - int do_ping = 1; - aclCheck_t checklist; - assert(request != NULL); - if (neighborType(p, request) == PEER_SIBLING) { - if (request->flags.nocache) - return 0; - if (request->flags.refresh) - return 0; - if (request->flags.loopdetect) - return 0; - if (request->flags.need_validation) - return 0; - } - if (p->peer_domain == NULL && p->access == NULL) - return do_ping; - do_ping = 0; - for (d = p->peer_domain; d; d = d->next) { - if (0 == matchDomainName(request->host, d->domain)) { - do_ping = d->do_ping; - break; - } - do_ping = !d->do_ping; - } - if (p->peer_domain && 0 == do_ping) - return do_ping; - if (p->access == NULL) - return do_ping; - memset(&checklist, '\0', sizeof(checklist)); - checklist.src_addr = request->client_addr; - checklist.my_addr = request->my_addr; - checklist.my_port = request->my_port; - checklist.request = request; -#if 0 && USE_IDENT - /* - * this is currently broken because 'request->user_ident' has been - * moved to conn->rfc931 and we don't have access to the parent - * ConnStateData here. - */ - if (request->user_ident[0]) - xstrncpy(checklist.rfc931, request->user_ident, USER_IDENT_SZ); -#endif - return aclCheckFast(p->access, &checklist); -} - -/* Return TRUE if it is okay to send an ICP request to this peer. */ -static int -peerWouldBePinged(const peer * p, request_t * request) -{ - if (!peerAllowedToUse(p, request)) - return 0; - if (p->options.no_query) - return 0; - if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate)) - return 0; - if (p->options.mcast_responder) - return 0; - if (p->n_addresses == 0) - return 0; - if (p->icp.port == 0) - return 0; - /* the case below seems strange, but can happen if the - * URL host is on the other side of a firewall */ - if (p->type == PEER_SIBLING) - if (!request->flags.hierarchical) - return 0; - /* Ping dead peers every timeout interval */ - if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer) - return 1; - if (p->icp.port == echo_port) - if (!neighborUp(p)) - return 0; - return 1; -} - -/* Return TRUE if it is okay to send an HTTP request to this peer. */ -int -peerHTTPOkay(const peer * p, request_t * request) -{ - if (!peerAllowedToUse(p, request)) - return 0; - if (!neighborUp(p)) - return 0; - if (p->max_conn) - if (p->stats.conn_open >= p->max_conn) - return 0; - return 1; -} - -int -neighborsCount(request_t * request) -{ - peer *p = NULL; - int count = 0; - for (p = Config.peers; p; p = p->next) - if (peerWouldBePinged(p, request)) - count++; - debug(15, 3) ("neighborsCount: %d\n", count); - return count; -} - -#if UNUSED_CODE -peer * -getSingleParent(request_t * request) -{ - peer *p = NULL; - peer *q = NULL; - for (q = Config.peers; q; q = q->next) { - if (!peerHTTPOkay(q, request)) - continue; - if (neighborType(q, request) != PEER_PARENT) - return NULL; /* oops, found SIBLING */ - if (p) - return NULL; /* oops, found second parent */ - p = q; - } - if (p != NULL && !p->options.no_query) - return NULL; - debug(15, 3) ("getSingleParent: returning %s\n", p ? p->host : "NULL"); - return p; -} -#endif - -peer * -getFirstUpParent(request_t * request) -{ - peer *p = NULL; - for (p = Config.peers; p; p = p->next) { - if (!neighborUp(p)) - continue; - if (neighborType(p, request) != PEER_PARENT) - continue; - if (!peerHTTPOkay(p, request)) - continue; - break; - } - debug(15, 3) ("getFirstUpParent: returning %s\n", p ? p->host : "NULL"); - return p; -} - -peer * -getRoundRobinParent(request_t * request) -{ - peer *p; - peer *q = NULL; - for (p = Config.peers; p; p = p->next) { - if (!p->options.roundrobin) - continue; - if (neighborType(p, request) != PEER_PARENT) - continue; - if (!peerHTTPOkay(p, request)) - continue; - if (q && q->rr_count < p->rr_count) - continue; - q = p; - } - if (q) - q->rr_count++; - debug(15, 3) ("getRoundRobinParent: returning %s\n", q ? q->host : "NULL"); - return q; -} - -peer * -getWeightedRoundRobinParent(request_t * request) -{ - peer *p; - peer *q = NULL; - int weighted_rtt; - for (p = Config.peers; p; p = p->next) { - if (!p->options.weighted_roundrobin) - continue; - if (neighborType(p, request) != PEER_PARENT) - continue; - if (!peerHTTPOkay(p, request)) - continue; - if (q && q->rr_count < p->rr_count) - continue; - q = p; - } - if (q && q->rr_count > 1000000) - for (p = Config.peers; p; p = p->next) { - if (!p->options.weighted_roundrobin) - continue; - if (neighborType(p, request) != PEER_PARENT) - continue; - p->rr_count = 0; - } - if (q) { - weighted_rtt = (q->stats.rtt - q->basetime) / q->weight; - - if (weighted_rtt < 1) - weighted_rtt = 1; - q->rr_count += weighted_rtt; - debug(15, 3) ("getWeightedRoundRobinParent: weighted_rtt %d\n", (int) weighted_rtt); - } - debug(15, 3) ("getWeightedRoundRobinParent: returning %s\n", q ? q->host : "NULL"); - return q; -} - -/* This gets called every 5 minutes to clear the round-robin counter. */ -void -peerClearRR(void *data) -{ - peer *p = data; - p->rr_count -= p->rr_lastcount; - if (p->rr_count < 0) - p->rr_count = 0; - p->rr_lastcount = p->rr_count; - eventAdd("peerClearRR", peerClearRR, p, 5 * 60.0, 0); -} - -peer * -getDefaultParent(request_t * request) -{ - peer *p = NULL; - for (p = Config.peers; p; p = p->next) { - if (neighborType(p, request) != PEER_PARENT) - continue; - if (!p->options.default_parent) - continue; - if (!peerHTTPOkay(p, request)) - continue; - debug(15, 3) ("getDefaultParent: returning %s\n", p->host); - return p; - } - debug(15, 3) ("getDefaultParent: returning NULL\n"); - return NULL; -} - -/* - * XXX DW thinks this function is equivalent to/redundant with - * getFirstUpParent(). peerHTTPOkay() only returns true if the - * peer is UP anyway, so this function would not return a - * DOWN parent. - */ -peer * -getAnyParent(request_t * request) -{ - peer *p = NULL; - for (p = Config.peers; p; p = p->next) { - if (neighborType(p, request) != PEER_PARENT) - continue; - if (!peerHTTPOkay(p, request)) - continue; - debug(15, 3) ("getAnyParent: returning %s\n", p->host); - return p; - } - debug(15, 3) ("getAnyParent: returning NULL\n"); - return NULL; -} - -peer * -getNextPeer(peer * p) -{ - return p->next; -} - -peer * -getFirstPeer(void) -{ - return Config.peers; -} - -static void -neighborRemove(peer * target) -{ - peer *p = NULL; - peer **P = NULL; - p = Config.peers; - P = &Config.peers; - while (p) { - if (target == p) - break; - P = &p->next; - p = p->next; - } - if (p) { - *P = p->next; - cbdataFree(p); - Config.npeers--; - } - first_ping = Config.peers; -} - -void -neighbors_open(int fd) -{ - struct sockaddr_in name; - socklen_t len = sizeof(struct sockaddr_in); - struct servent *sep = NULL; - const char *me = getMyHostname(); - peer *this; - peer *next; - memset(&name, '\0', sizeof(struct sockaddr_in)); - if (getsockname(fd, (struct sockaddr *) &name, &len) < 0) - debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len); - for (this = Config.peers; this; this = next) { - sockaddr_in_list *s; - next = this->next; - if (0 != strcmp(this->host, me)) - continue; - for (s = Config.Sockaddr.http; s; s = s->next) { - if (this->http_port != ntohs(s->s.sin_port)) - continue; - debug(15, 1) ("WARNING: Peer looks like this host\n"); - debug(15, 1) (" Ignoring %s %s/%d/%d\n", - neighborTypeStr(this), this->host, this->http_port, - this->icp.port); - neighborRemove(this); - } - } - - peerRefreshDNS((void *) 1); - if (0 == echo_hdr.opcode) { - echo_hdr.opcode = ICP_SECHO; - echo_hdr.version = ICP_VERSION_CURRENT; - echo_hdr.length = 0; - echo_hdr.reqnum = 0; - echo_hdr.flags = 0; - echo_hdr.pad = 0; - echo_hdr.shostid = name.sin_addr.s_addr; - sep = getservbyname("echo", "udp"); - echo_port = sep ? ntohs((u_short) sep->s_port) : 7; - } - first_ping = Config.peers; - cachemgrRegister("server_list", - "Peer Cache Statistics", - neighborDumpPeers, 0, 1); - cachemgrRegister("non_peers", - "List of Unknown sites sending ICP messages", - neighborDumpNonPeers, 0, 1); -} - -int -neighborsUdpPing(request_t * request, - StoreEntry * entry, - IRCB * callback, - void *callback_data, - int *exprep, - int *timeout) -{ - const char *url = storeUrl(entry); - MemObject *mem = entry->mem_obj; - peer *p = NULL; - int i; - int reqnum = 0; - int flags; - icp_common_t *query; - int queries_sent = 0; - int peers_pinged = 0; - int parent_timeout = 0, parent_exprep = 0; - int sibling_timeout = 0, sibling_exprep = 0; - - if (Config.peers == NULL) - return 0; - if (theOutIcpConnection < 0) - fatal("neighborsUdpPing: There is no ICP socket!"); - assert(entry->swap_status == SWAPOUT_NONE); - mem->start_ping = current_time; - mem->ping_reply_callback = callback; - mem->ircb_data = callback_data; - reqnum = icpSetCacheKey(entry->hash.key); - for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) { - if (p == NULL) - p = Config.peers; - debug(15, 5) ("neighborsUdpPing: Peer %s\n", p->host); - if (!peerWouldBePinged(p, request)) - continue; /* next peer */ - peers_pinged++; - debug(15, 4) ("neighborsUdpPing: pinging peer %s for '%s'\n", - p->host, url); - if (p->type == PEER_MULTICAST) - mcastSetTtl(theOutIcpConnection, p->mcast.ttl); - debug(15, 3) ("neighborsUdpPing: key = '%s'\n", storeKeyText(entry->hash.key)); - debug(15, 3) ("neighborsUdpPing: reqnum = %d\n", reqnum); - -#if USE_HTCP - if (p->options.htcp) { - debug(15, 3) ("neighborsUdpPing: sending HTCP query\n"); - htcpQuery(entry, request, p); - } else -#endif - if (p->icp.port == echo_port) { - debug(15, 4) ("neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n"); - echo_hdr.reqnum = reqnum; - query = icpCreateMessage(ICP_DECHO, 0, url, reqnum, 0); - icpUdpSend(theOutIcpConnection, - &p->in_addr, - query, - LOG_ICP_QUERY, - 0); - } else { - flags = 0; - if (Config.onoff.query_icmp) - if (p->icp.version == ICP_VERSION_2) - flags |= ICP_FLAG_SRC_RTT; - query = icpCreateMessage(ICP_QUERY, flags, url, reqnum, 0); - icpUdpSend(theOutIcpConnection, - &p->in_addr, - query, - LOG_ICP_QUERY, - 0); - } - queries_sent++; - - p->stats.pings_sent++; - if (p->type == PEER_MULTICAST) { - /* - * set a bogus last_reply time so neighborUp() never - * says a multicast peer is dead. - */ - p->stats.last_reply = squid_curtime; - (*exprep) += p->mcast.n_replies_expected; - } else if (neighborUp(p)) { - /* its alive, expect a reply from it */ - if (neighborType(p, request) == PEER_PARENT) { - parent_exprep++; - parent_timeout += p->stats.rtt; - } else { - sibling_exprep++; - sibling_timeout += p->stats.rtt; - } - } else { - /* Neighbor is dead; ping it anyway, but don't expect a reply */ - /* log it once at the threshold */ - if (p->stats.logged_state == PEER_ALIVE) { - debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n", - neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - p->stats.logged_state = PEER_DEAD; - } - } - p->stats.last_query = squid_curtime; - if (p->stats.probe_start == 0) - p->stats.probe_start = squid_curtime; - } - if ((first_ping = first_ping->next) == NULL) - first_ping = Config.peers; - -#if ALLOW_SOURCE_PING - /* only do source_ping if we have neighbors */ - if (Config.npeers) { - const ipcache_addrs *ia = NULL; - struct sockaddr_in to_addr; - char *host = request->host; - if (!Config.onoff.source_ping) { - debug(15, 6) ("neighborsUdpPing: Source Ping is disabled.\n"); - } else if ((ia = ipcache_gethostbyname(host, 0))) { - debug(15, 6) ("neighborsUdpPing: Source Ping: to %s for '%s'\n", - host, url); - echo_hdr.reqnum = reqnum; - if (icmp_sock != -1) { - icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url); - } else { - to_addr.sin_family = AF_INET; - to_addr.sin_addr = ia->in_addrs[ia->cur]; - to_addr.sin_port = htons(echo_port); - query = icpCreateMessage(ICP_SECHO, 0, url, reqnum, 0); - icpUdpSend(theOutIcpConnection, - &to_addr, - query, - LOG_ICP_QUERY, - 0); - } - } else { - debug(15, 6) ("neighborsUdpPing: Source Ping: unknown host: %s\n", - host); - } - } -#endif - /* - * How many replies to expect? - */ - *exprep = parent_exprep + sibling_exprep; - - /* - * If there is a configured timeout, use it - */ - if (Config.Timeout.icp_query) - *timeout = Config.Timeout.icp_query; - else { - if (*exprep > 0) { - if (parent_exprep) - *timeout = 2 * parent_timeout / parent_exprep; - else - *timeout = 2 * sibling_timeout / sibling_exprep; - } else - *timeout = 2000; /* 2 seconds */ - if (Config.Timeout.icp_query_max) - if (*timeout > Config.Timeout.icp_query_max) - *timeout = Config.Timeout.icp_query_max; - if (*timeout < Config.Timeout.icp_query_min) - *timeout = Config.Timeout.icp_query_min; - } - return peers_pinged; -} - -/* lookup the digest of a given peer */ -lookup_t -peerDigestLookup(peer * p, request_t * request) -{ -#if USE_CACHE_DIGESTS - const cache_key *key = request ? storeKeyPublicByRequest(request) : NULL; - assert(p); - assert(request); - debug(15, 5) ("peerDigestLookup: peer %s\n", p->host); - /* does the peeer have a valid digest? */ - if (!p->digest) { - debug(15, 5) ("peerDigestLookup: gone!\n"); - return LOOKUP_NONE; - } else if (!peerHTTPOkay(p, request)) { - debug(15, 5) ("peerDigestLookup: !peerHTTPOkay\n"); - return LOOKUP_NONE; - } else if (p->digest->flags.usable) { - debug(15, 5) ("peerDigestLookup: usable\n"); - /* fall through; put here to have common case on top */ ; - } else if (!p->digest->flags.needed) { - debug(15, 5) ("peerDigestLookup: note need\n"); - peerDigestNeeded(p->digest); - return LOOKUP_NONE; - } else { - debug(15, 5) ("peerDigestLookup: !ready && %srequested\n", - p->digest->flags.requested ? "" : "!"); - return LOOKUP_NONE; - } - debug(15, 5) ("peerDigestLookup: OK to lookup peer %s\n", p->host); - assert(p->digest->cd); - /* does digest predict a hit? */ - if (!cacheDigestTest(p->digest->cd, key)) - return LOOKUP_MISS; - debug(15, 5) ("peerDigestLookup: peer %s says HIT!\n", p->host); - return LOOKUP_HIT; -#endif - return LOOKUP_NONE; -} - -/* select best peer based on cache digests */ -peer * -neighborsDigestSelect(request_t * request) -{ - peer *best_p = NULL; -#if USE_CACHE_DIGESTS - const cache_key *key; - int best_rtt = 0; - int choice_count = 0; - int ichoice_count = 0; - peer *p; - int p_rtt; - int i; - if (!request->flags.hierarchical) - return NULL; - key = storeKeyPublicByRequest(request); - for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) { - lookup_t lookup; - if (!p) - p = Config.peers; - if (i == 1) - first_ping = p; - lookup = peerDigestLookup(p, request); - if (lookup == LOOKUP_NONE) - continue; - choice_count++; - if (lookup == LOOKUP_MISS) - continue; - p_rtt = netdbHostRtt(p->host); - debug(15, 5) ("neighborsDigestSelect: peer %s rtt: %d\n", - p->host, p_rtt); - /* is this peer better than others in terms of rtt ? */ - if (!best_p || (p_rtt && p_rtt < best_rtt)) { - best_p = p; - best_rtt = p_rtt; - if (p_rtt) /* informative choice (aka educated guess) */ - ichoice_count++; - debug(15, 4) ("neighborsDigestSelect: peer %s leads with rtt %d\n", - p->host, best_rtt); - } - } - debug(15, 4) ("neighborsDigestSelect: choices: %d (%d)\n", - choice_count, ichoice_count); - peerNoteDigestLookup(request, best_p, - best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE)); - request->hier.n_choices = choice_count; - request->hier.n_ichoices = ichoice_count; -#endif - return best_p; -} - -void -peerNoteDigestLookup(request_t * request, peer * p, lookup_t lookup) -{ -#if USE_CACHE_DIGESTS - if (p) - strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host)); - else - *request->hier.cd_host = '\0'; - request->hier.cd_lookup = lookup; - debug(15, 4) ("peerNoteDigestLookup: peer %s, lookup: %s\n", - p ? p->host : "", lookup_t_str[lookup]); -#endif -} - -static void -neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header) -{ - if (p->stats.logged_state == PEER_DEAD && p->tcp_up) { - debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", - neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - p->stats.logged_state = PEER_ALIVE; - } - p->stats.last_reply = squid_curtime; - p->stats.probe_start = 0; - p->stats.pings_acked++; - if ((icp_opcode) header->opcode <= ICP_END) - p->icp.counts[header->opcode]++; - p->icp.version = (int) header->version; -} - -static void -neighborUpdateRtt(peer * p, MemObject * mem) -{ - int rtt, rtt_av_factor; - if (!mem) - return; - if (!mem->start_ping.tv_sec) - return; - rtt = tvSubMsec(mem->start_ping, current_time); - if (rtt < 1 || rtt > 10000) - return; - rtt_av_factor = RTT_AV_FACTOR; - if (p->options.weighted_roundrobin) - rtt_av_factor = RTT_BACKGROUND_AV_FACTOR; - p->stats.rtt = intAverage(p->stats.rtt, rtt, - p->stats.pings_acked, rtt_av_factor); -} - -#if USE_HTCP -static void -neighborAliveHtcp(peer * p, const MemObject * mem, const htcpReplyData * htcp) -{ - if (p->stats.logged_state == PEER_DEAD && p->tcp_up) { - debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", - neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - p->stats.logged_state = PEER_ALIVE; - } - p->stats.last_reply = squid_curtime; - p->stats.probe_start = 0; - p->stats.pings_acked++; - p->htcp.counts[htcp->hit ? 1 : 0]++; - p->htcp.version = htcp->version; -} -#endif - -static void -neighborCountIgnored(peer * p) -{ - if (p == NULL) - return; - p->stats.ignored_replies++; - NLateReplies++; -} - -static peer *non_peers = NULL; - -static void -neighborIgnoreNonPeer(const struct sockaddr_in *from, icp_opcode opcode) -{ - peer *np; - for (np = non_peers; np; np = np->next) { - if (np->in_addr.sin_addr.s_addr != from->sin_addr.s_addr) - continue; - if (np->in_addr.sin_port != from->sin_port) - continue; - break; - } - if (np == NULL) { - np = xcalloc(1, sizeof(peer)); - np->in_addr.sin_addr = from->sin_addr; - np->in_addr.sin_port = from->sin_port; - np->icp.port = ntohl(from->sin_port); - np->type = PEER_NONE; - np->host = xstrdup(inet_ntoa(from->sin_addr)); - np->next = non_peers; - non_peers = np; - } - np->icp.counts[opcode]++; - if (isPowTen(++np->stats.ignored_replies)) - debug(15, 1) ("WARNING: Ignored %d replies from non-peer %s\n", - np->stats.ignored_replies, np->host); -} - -/* ignoreMulticastReply - * - * * We want to ignore replies from multicast peers if the - * * cache_host_domain rules would normally prevent the peer - * * from being used - */ -static int -ignoreMulticastReply(peer * p, MemObject * mem) -{ - if (p == NULL) - return 0; - if (!p->options.mcast_responder) - return 0; - if (peerHTTPOkay(p, mem->request)) - return 0; - return 1; -} - -/* I should attach these records to the entry. We take the first - * hit we get our wait until everyone misses. The timeout handler - * call needs to nip this shopping list or call one of the misses. - * - * If a hit process is already started, then sobeit - */ -void -neighborsUdpAck(const cache_key * key, icp_common_t * header, const struct sockaddr_in *from) -{ - peer *p = NULL; - StoreEntry *entry; - MemObject *mem = NULL; - peer_t ntype = PEER_NONE; - char *opcode_d; - icp_opcode opcode = (icp_opcode) header->opcode; - - debug(15, 6) ("neighborsUdpAck: opcode %d '%s'\n", - (int) opcode, storeKeyText(key)); - if (NULL != (entry = storeGet(key))) - mem = entry->mem_obj; - if ((p = whichPeer(from))) - neighborAlive(p, mem, header); - if (opcode > ICP_END) - return; - opcode_d = icp_opcode_str[opcode]; - if (p) - neighborUpdateRtt(p, mem); - /* Does the entry exist? */ - if (NULL == entry) { - debug(12, 3) ("neighborsUdpAck: Cache key '%s' not found\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - /* check if someone is already fetching it */ - if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) { - debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (mem == NULL) { - debug(15, 2) ("Ignoring %s for missing mem_obj: %s\n", - opcode_d, storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (entry->ping_status != PING_WAITING) { - debug(15, 2) ("neighborsUdpAck: Late %s for %s\n", - opcode_d, storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (entry->lock_count == 0) { - debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - debug(15, 3) ("neighborsUdpAck: %s for '%s' from %s \n", - opcode_d, storeKeyText(key), p ? p->host : "source"); - if (p) { - ntype = neighborType(p, mem->request); - } - if (ignoreMulticastReply(p, mem)) { - neighborCountIgnored(p); - } else if (opcode == ICP_MISS) { - if (p == NULL) { - neighborIgnoreNonPeer(from, opcode); - } else { - mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); - } - } else if (opcode == ICP_HIT) { - if (p == NULL) { - neighborIgnoreNonPeer(from, opcode); - } else { - header->opcode = ICP_HIT; - mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); - } - } else if (opcode == ICP_DECHO) { - if (p == NULL) { - neighborIgnoreNonPeer(from, opcode); - } else if (ntype == PEER_SIBLING) { - debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n"); - debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n"); - } else { - mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); - } - } else if (opcode == ICP_SECHO) { - if (p) { - debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host); - neighborCountIgnored(p); -#if ALLOW_SOURCE_PING - } else if (Config.onoff.source_ping) { - mem->ping_reply_callback(NULL, ntype, PROTO_ICP, header, mem->ircb_data); -#endif - } else { - debug(15, 1) ("Unsolicited SECHO from %s\n", inet_ntoa(from->sin_addr)); - } - } else if (opcode == ICP_DENIED) { - if (p == NULL) { - neighborIgnoreNonPeer(from, opcode); - } else if (p->stats.pings_acked > 100) { - if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) { - debug(15, 0) ("95%% of replies from '%s' are UDP_DENIED\n", p->host); - debug(15, 0) ("Disabling '%s', please check your configuration.\n", p->host); - neighborRemove(p); - p = NULL; - } else { - neighborCountIgnored(p); - } - } - } else if (opcode == ICP_MISS_NOFETCH) { - mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); - } else { - debug(15, 0) ("neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d); - } -} - -peer * -peerFindByName(const char *name) -{ - peer *p = NULL; - for (p = Config.peers; p; p = p->next) { - if (!strcasecmp(name, p->host)) - break; - } - return p; -} - -peer * -peerFindByNameAndPort(const char *name, unsigned short port) -{ - peer *p = NULL; - for (p = Config.peers; p; p = p->next) { - if (strcasecmp(name, p->host)) - continue; - if (port != p->http_port) - continue; - break; - } - return p; -} - -int -neighborUp(const peer * p) -{ - if (!p->tcp_up) { - peerProbeConnect((peer *) p); - return 0; - } - if (p->options.no_query) - return 1; - if (p->stats.probe_start != 0 && - squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer) - return 0; - /* - * The peer can not be UP if we don't have any IP addresses - * for it. - */ - if (0 == p->n_addresses) - return 0; - return 1; -} - -void -peerDestroy(void *data) -{ - peer *p = data; - struct _domain_ping *l = NULL; - struct _domain_ping *nl = NULL; - if (p == NULL) - return; - for (l = p->peer_domain; l; l = nl) { - nl = l->next; - safe_free(l->domain); - safe_free(l); - } - safe_free(p->host); -#if USE_CACHE_DIGESTS - cbdataReferenceDone(p->digest); -#endif -} - -void -peerNoteDigestGone(peer * p) -{ -#if USE_CACHE_DIGESTS - cbdataReferenceDone(p->digest); -#endif -} - -static void -peerDNSConfigure(const ipcache_addrs * ia, void *data) -{ - peer *p = data; - struct sockaddr_in *ap; - int j; - if (p->n_addresses == 0) { - debug(15, 1) ("Configuring %s %s/%d/%d\n", neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - if (p->type == PEER_MULTICAST) - debug(15, 1) (" Multicast TTL = %d\n", p->mcast.ttl); - } - p->n_addresses = 0; - if (ia == NULL) { - debug(0, 0) ("WARNING: DNS lookup for '%s' failed!\n", p->host); - return; - } - if ((int) ia->count < 1) { - debug(0, 0) ("WARNING: No IP address found for '%s'!\n", p->host); - return; - } - for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) { - p->addresses[j] = ia->in_addrs[j]; - debug(15, 2) ("--> IP address #%d: %s\n", j, inet_ntoa(p->addresses[j])); - p->n_addresses++; - } - ap = &p->in_addr; - memset(ap, '\0', sizeof(struct sockaddr_in)); - ap->sin_family = AF_INET; - ap->sin_addr = p->addresses[0]; - ap->sin_port = htons(p->icp.port); - if (p->type == PEER_MULTICAST) - peerCountMcastPeersSchedule(p, 10); - if (p->type != PEER_MULTICAST) - if (!p->options.no_netdb_exchange) - eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1); -} - -static void -peerRefreshDNS(void *data) -{ - peer *p = NULL; - if (eventFind(peerRefreshDNS, NULL)) - eventDelete(peerRefreshDNS, NULL); - if (!data && 0 == stat5minClientRequests()) { - /* no recent client traffic, wait a bit */ - eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1); - return; - } - for (p = Config.peers; p; p = p->next) - ipcache_nbgethostbyname(p->host, peerDNSConfigure, p); - /* Reconfigure the peers every hour */ - eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1); -} - -void -peerConnectFailed(peer * p) -{ - p->stats.last_connect_failure = squid_curtime; - if (!p->tcp_up) { - debug(15, 2) ("TCP connection to %s/%d dead\n", p->host, p->http_port); - return; - } - debug(15, 1) ("TCP connection to %s/%d failed\n", p->host, p->http_port); - p->tcp_up--; - if (!p->tcp_up) { - debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n", - neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - p->stats.logged_state = PEER_DEAD; - } -} - -void -peerConnectSucceded(peer * p) -{ - if (!p->tcp_up) { - debug(15, 2) ("TCP connection to %s/%d succeded\n", p->host, p->http_port); - debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", - neighborTypeStr(p), - p->host, p->http_port, p->icp.port); - p->stats.logged_state = PEER_ALIVE; - } - p->tcp_up = PEER_TCP_MAGIC_COUNT; -} - -/* - * peerProbeConnect will be called on dead peers by neighborUp - */ -static void -peerProbeConnect(peer * p) -{ - int fd; - if (p->test_fd != -1) - return; /* probe already running */ - if (squid_curtime - p->stats.last_connect_probe < Config.Timeout.connect) - return; /* don't probe to often */ - fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), - 0, COMM_NONBLOCKING, p->host); - if (fd < 0) - return; - p->test_fd = fd; - p->stats.last_connect_probe = squid_curtime; - ipcache_nbgethostbyname(p->host, peerProbeConnect2, p); -} - -static void -peerProbeConnect2(const ipcache_addrs * ianotused, void *data) -{ - peer *p = data; - commConnectStart(p->test_fd, - p->host, - p->http_port, - peerProbeConnectDone, - p); -} - -static void -peerProbeConnectDone(int fd, comm_err_t status, void *data) -{ - peer *p = data; - if (status == COMM_OK) { - peerConnectSucceded(p); - } else { - peerConnectFailed(p); - } - comm_close(fd); - p->test_fd = -1; - return; -} - -static void -peerCountMcastPeersSchedule(peer * p, time_t when) -{ - if (p->mcast.flags.count_event_pending) - return; - eventAdd("peerCountMcastPeersStart", - peerCountMcastPeersStart, - p, - (double) when, 1); - p->mcast.flags.count_event_pending = 1; -} - -static void -peerCountMcastPeersStart(void *data) -{ - peer *p = data; - ps_state *psstate; - StoreEntry *fake; - MemObject *mem; - icp_common_t *query; - int reqnum; - LOCAL_ARRAY(char, url, MAX_URL); - assert(p->type == PEER_MULTICAST); - p->mcast.flags.count_event_pending = 0; - snprintf(url, MAX_URL, "http://%s/", inet_ntoa(p->in_addr.sin_addr)); - fake = storeCreateEntry(url, url, null_request_flags, METHOD_GET); - psstate = cbdataAlloc(ps_state); - psstate->request = requestLink(urlParse(METHOD_GET, url)); - psstate->entry = fake; - psstate->callback = NULL; - psstate->callback_data = p; - psstate->ping.start = current_time; - mem = fake->mem_obj; - mem->request = requestLink(psstate->request); - mem->start_ping = current_time; - mem->ping_reply_callback = peerCountHandleIcpReply; - mem->ircb_data = psstate; - mcastSetTtl(theOutIcpConnection, p->mcast.ttl); - p->mcast.id = mem->id; - reqnum = icpSetCacheKey(fake->hash.key); - query = icpCreateMessage(ICP_QUERY, 0, url, reqnum, 0); - icpUdpSend(theOutIcpConnection, - &p->in_addr, - query, - LOG_ICP_QUERY, - 0); - fake->ping_status = PING_WAITING; - eventAdd("peerCountMcastPeersDone", - peerCountMcastPeersDone, - psstate, - (double) Config.Timeout.mcast_icp_query, 1); - p->mcast.flags.counting = 1; - peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE); -} - -static void -peerCountMcastPeersDone(void *data) -{ - ps_state *psstate = data; - peer *p = psstate->callback_data; - StoreEntry *fake = psstate->entry; - p->mcast.flags.counting = 0; - p->mcast.avg_n_members = doubleAverage(p->mcast.avg_n_members, - (double) psstate->ping.n_recv, - ++p->mcast.n_times_counted, - 10); - debug(15, 1) ("Group %s: %d replies, %4.1f average, RTT %d\n", - p->host, - psstate->ping.n_recv, - p->mcast.avg_n_members, - p->stats.rtt); - p->mcast.n_replies_expected = (int) p->mcast.avg_n_members; - EBIT_SET(fake->flags, ENTRY_ABORTED); - requestUnlink(fake->mem_obj->request); - fake->mem_obj->request = NULL; - storeReleaseRequest(fake); - storeUnlockObject(fake); - requestUnlink(psstate->request); - cbdataFree(psstate); -} - -static void -peerCountHandleIcpReply(peer * p, peer_t type, protocol_t proto, void *hdrnotused, void *data) -{ - int rtt_av_factor; - - ps_state *psstate = data; - StoreEntry *fake = psstate->entry; - MemObject *mem = fake->mem_obj; - int rtt = tvSubMsec(mem->start_ping, current_time); - assert(proto == PROTO_ICP); - assert(fake); - assert(mem); - psstate->ping.n_recv++; - rtt_av_factor = RTT_AV_FACTOR; - if (p->options.weighted_roundrobin) - rtt_av_factor = RTT_BACKGROUND_AV_FACTOR; - p->stats.rtt = intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, rtt_av_factor); -} - -static void -neighborDumpPeers(StoreEntry * sentry) -{ - dump_peers(sentry, Config.peers); -} - -static void -neighborDumpNonPeers(StoreEntry * sentry) -{ - dump_peers(sentry, non_peers); -} - -void -dump_peer_options(StoreEntry * sentry, peer * p) -{ - if (p->options.proxy_only) - storeAppendPrintf(sentry, " proxy-only"); - if (p->options.no_query) - storeAppendPrintf(sentry, " no-query"); - if (p->options.background_ping) - storeAppendPrintf(sentry, " background-ping"); - if (p->options.no_digest) - storeAppendPrintf(sentry, " no-digest"); - if (p->options.default_parent) - storeAppendPrintf(sentry, " default"); - if (p->options.roundrobin) - storeAppendPrintf(sentry, " round-robin"); - if (p->options.weighted_roundrobin) - storeAppendPrintf(sentry, " weighted-round-robin"); - if (p->options.mcast_responder) - storeAppendPrintf(sentry, " multicast-responder"); - if (p->options.closest_only) - storeAppendPrintf(sentry, " closest-only"); -#if USE_HTCP - if (p->options.htcp) - storeAppendPrintf(sentry, " htcp"); -#endif - if (p->options.no_netdb_exchange) - storeAppendPrintf(sentry, " no-netdb-exchange"); -#if DELAY_POOLS - if (p->options.no_delay) - storeAppendPrintf(sentry, " no-delay"); -#endif - if (p->login) - storeAppendPrintf(sentry, " login=%s", p->login); - if (p->mcast.ttl > 0) - storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl); - storeAppendPrintf(sentry, "\n"); -} - -static void -dump_peers(StoreEntry * sentry, peer * peers) -{ - peer *e = NULL; - struct _domain_ping *d = NULL; - icp_opcode op; - int i; - if (peers == NULL) - storeAppendPrintf(sentry, "There are no neighbors installed.\n"); - for (e = peers; e; e = e->next) { - assert(e->host != NULL); - storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n", - neighborTypeStr(e), - e->host, - e->http_port, - e->icp.port); - storeAppendPrintf(sentry, "Flags :"); - dump_peer_options(sentry, e); - for (i = 0; i < e->n_addresses; i++) { - storeAppendPrintf(sentry, "Address[%d] : %s\n", i, - inet_ntoa(e->addresses[i])); - } - storeAppendPrintf(sentry, "Status : %s\n", - neighborUp(e) ? "Up" : "Down"); - storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt); - storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open); - storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n", - (int) (squid_curtime - e->stats.last_query)); - storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n", - (int) (squid_curtime - e->stats.last_reply)); - storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent); - storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n", - e->stats.pings_acked, - percent(e->stats.pings_acked, e->stats.pings_sent)); - storeAppendPrintf(sentry, "FETCHES : %8d %3d%%\n", - e->stats.fetches, - percent(e->stats.fetches, e->stats.pings_acked)); - storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n", - e->stats.ignored_replies, - percent(e->stats.ignored_replies, e->stats.pings_acked)); - storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n"); -#if USE_HTCP - if (e->options.htcp) { - storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n", - e->htcp.counts[0], - percent(e->htcp.counts[0], e->stats.pings_acked)); - storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n", - e->htcp.counts[1], - percent(e->htcp.counts[1], e->stats.pings_acked)); - } else { -#endif - for (op = ICP_INVALID; op < ICP_END; op++) { - if (e->icp.counts[op] == 0) - continue; - storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n", - icp_opcode_str[op], - e->icp.counts[op], - percent(e->icp.counts[op], e->stats.pings_acked)); - } -#if USE_HTCP - } -#endif - if (e->stats.last_connect_failure) { - storeAppendPrintf(sentry, "Last failed connect() at: %s\n", - mkhttpdlogtime(&(e->stats.last_connect_failure))); - } - if (e->peer_domain != NULL) { - storeAppendPrintf(sentry, "DOMAIN LIST: "); - for (d = e->peer_domain; d; d = d->next) { - storeAppendPrintf(sentry, "%s%s ", - d->do_ping ? null_string : "!", d->domain); - } - storeAppendPrintf(sentry, "\n"); - } - storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n", - percent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent)); - } -} - -#if USE_HTCP -void -neighborsHtcpReply(const cache_key * key, htcpReplyData * htcp, const struct sockaddr_in *from) -{ - StoreEntry *e = storeGet(key); - MemObject *mem = NULL; - peer *p; - peer_t ntype = PEER_NONE; - debug(15, 6) ("neighborsHtcpReply: %s %s\n", - htcp->hit ? "HIT" : "MISS", storeKeyText(key)); - if (NULL != (e = storeGet(key))) - mem = e->mem_obj; - if ((p = whichPeer(from))) - neighborAliveHtcp(p, mem, htcp); - /* Does the entry exist? */ - if (NULL == e) { - debug(12, 3) ("neighyborsHtcpReply: Cache key '%s' not found\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - /* check if someone is already fetching it */ - if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) { - debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (mem == NULL) { - debug(15, 2) ("Ignoring reply for missing mem_obj: %s\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (e->ping_status != PING_WAITING) { - debug(15, 2) ("neighborsUdpAck: Entry %s is not PING_WAITING\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (e->lock_count == 0) { - debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n", - storeKeyText(key)); - neighborCountIgnored(p); - return; - } - if (p) { - ntype = neighborType(p, mem->request); - neighborUpdateRtt(p, mem); - } - if (ignoreMulticastReply(p, mem)) { - neighborCountIgnored(p); - return; - } - debug(15, 3) ("neighborsHtcpReply: e = %p\n", e); - mem->ping_reply_callback(p, ntype, PROTO_HTCP, htcp, mem->ircb_data); -} -#endif --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/neighbors.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,1442 @@ + +/* + * $Id: neighbors.cc,v 1.1.2.1 2002/10/09 05:55:49 rbcollins Exp $ + * + * DEBUG: section 15 Neighbor Routines + * AUTHOR: Harvest Derived + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "Store.h" +#include "ICP.h" + +/* count mcast group peers every 15 minutes */ +#define MCAST_COUNT_RATE 900 + +static int peerAllowedToUse(const peer *, request_t *); +static int peerWouldBePinged(const peer *, request_t *); +static void neighborRemove(peer *); +static void neighborAlive(peer *, const MemObject *, const icp_common_t *); +#if USE_HTCP +static void neighborAliveHtcp(peer *, const MemObject *, const htcpReplyData *); +#endif +static void neighborCountIgnored(peer *); +static void peerRefreshDNS(void *); +static IPH peerDNSConfigure; +static void peerProbeConnect(peer *); +static IPH peerProbeConnect2; +static CNCB peerProbeConnectDone; +static void peerCountMcastPeersDone(void *data); +static void peerCountMcastPeersStart(void *data); +static void peerCountMcastPeersSchedule(peer * p, time_t when); +static IRCB peerCountHandleIcpReply; +static void neighborIgnoreNonPeer(const struct sockaddr_in *, icp_opcode); +static OBJH neighborDumpPeers; +static OBJH neighborDumpNonPeers; +static void dump_peers(StoreEntry * sentry, peer * peers); + +static icp_common_t echo_hdr; +static u_short echo_port; + +static int NLateReplies = 0; +static peer *first_ping = NULL; + +const char * +neighborTypeStr(const peer * p) +{ + if (p->type == PEER_NONE) + return "Non-Peer"; + if (p->type == PEER_SIBLING) + return "Sibling"; + if (p->type == PEER_MULTICAST) + return "Multicast Group"; + return "Parent"; +} + + +peer * +whichPeer(const struct sockaddr_in * from) +{ + int j; + u_short port = ntohs(from->sin_port); + struct in_addr ip = from->sin_addr; + peer *p = NULL; + debug(15, 3) ("whichPeer: from %s port %d\n", inet_ntoa(ip), port); + for (p = Config.peers; p; p = p->next) { + for (j = 0; j < p->n_addresses; j++) { + if (ip.s_addr == p->addresses[j].s_addr && port == p->icp.port) { + return p; + } + } + } + return NULL; +} + +peer_t +neighborType(const peer * p, const request_t * request) +{ + const struct _domain_type *d = NULL; + for (d = p->typelist; d; d = d->next) { + if (0 == matchDomainName(request->host, d->domain)) + if (d->type != PEER_NONE) + return d->type; + } + return p->type; +} + +/* + * peerAllowedToUse + * + * this function figures out if it is appropriate to fetch REQUEST + * from PEER. + */ +static int +peerAllowedToUse(const peer * p, request_t * request) +{ + const struct _domain_ping *d = NULL; + int do_ping = 1; + aclCheck_t checklist; + assert(request != NULL); + if (neighborType(p, request) == PEER_SIBLING) { + if (request->flags.nocache) + return 0; + if (request->flags.refresh) + return 0; + if (request->flags.loopdetect) + return 0; + if (request->flags.need_validation) + return 0; + } + if (p->peer_domain == NULL && p->access == NULL) + return do_ping; + do_ping = 0; + for (d = p->peer_domain; d; d = d->next) { + if (0 == matchDomainName(request->host, d->domain)) { + do_ping = d->do_ping; + break; + } + do_ping = !d->do_ping; + } + if (p->peer_domain && 0 == do_ping) + return do_ping; + if (p->access == NULL) + return do_ping; + memset(&checklist, '\0', sizeof(checklist)); + checklist.src_addr = request->client_addr; + checklist.my_addr = request->my_addr; + checklist.my_port = request->my_port; + checklist.request = request; +#if 0 && USE_IDENT + /* + * this is currently broken because 'request->user_ident' has been + * moved to conn->rfc931 and we don't have access to the parent + * ConnStateData here. + */ + if (request->user_ident[0]) + xstrncpy(checklist.rfc931, request->user_ident, USER_IDENT_SZ); +#endif + return aclCheckFast(p->access, &checklist); +} + +/* Return TRUE if it is okay to send an ICP request to this peer. */ +static int +peerWouldBePinged(const peer * p, request_t * request) +{ + if (!peerAllowedToUse(p, request)) + return 0; + if (p->options.no_query) + return 0; + if (p->options.background_ping && (squid_curtime - p->stats.last_query < Config.backgroundPingRate)) + return 0; + if (p->options.mcast_responder) + return 0; + if (p->n_addresses == 0) + return 0; + if (p->icp.port == 0) + return 0; + /* the case below seems strange, but can happen if the + * URL host is on the other side of a firewall */ + if (p->type == PEER_SIBLING) + if (!request->flags.hierarchical) + return 0; + /* Ping dead peers every timeout interval */ + if (squid_curtime - p->stats.last_query > Config.Timeout.deadPeer) + return 1; + if (p->icp.port == echo_port) + if (!neighborUp(p)) + return 0; + return 1; +} + +/* Return TRUE if it is okay to send an HTTP request to this peer. */ +int +peerHTTPOkay(const peer * p, request_t * request) +{ + if (!peerAllowedToUse(p, request)) + return 0; + if (!neighborUp(p)) + return 0; + if (p->max_conn) + if (p->stats.conn_open >= p->max_conn) + return 0; + return 1; +} + +int +neighborsCount(request_t * request) +{ + peer *p = NULL; + int count = 0; + for (p = Config.peers; p; p = p->next) + if (peerWouldBePinged(p, request)) + count++; + debug(15, 3) ("neighborsCount: %d\n", count); + return count; +} + +#if UNUSED_CODE +peer * +getSingleParent(request_t * request) +{ + peer *p = NULL; + peer *q = NULL; + for (q = Config.peers; q; q = q->next) { + if (!peerHTTPOkay(q, request)) + continue; + if (neighborType(q, request) != PEER_PARENT) + return NULL; /* oops, found SIBLING */ + if (p) + return NULL; /* oops, found second parent */ + p = q; + } + if (p != NULL && !p->options.no_query) + return NULL; + debug(15, 3) ("getSingleParent: returning %s\n", p ? p->host : "NULL"); + return p; +} +#endif + +peer * +getFirstUpParent(request_t * request) +{ + peer *p = NULL; + for (p = Config.peers; p; p = p->next) { + if (!neighborUp(p)) + continue; + if (neighborType(p, request) != PEER_PARENT) + continue; + if (!peerHTTPOkay(p, request)) + continue; + break; + } + debug(15, 3) ("getFirstUpParent: returning %s\n", p ? p->host : "NULL"); + return p; +} + +peer * +getRoundRobinParent(request_t * request) +{ + peer *p; + peer *q = NULL; + for (p = Config.peers; p; p = p->next) { + if (!p->options.roundrobin) + continue; + if (neighborType(p, request) != PEER_PARENT) + continue; + if (!peerHTTPOkay(p, request)) + continue; + if (q && q->rr_count < p->rr_count) + continue; + q = p; + } + if (q) + q->rr_count++; + debug(15, 3) ("getRoundRobinParent: returning %s\n", q ? q->host : "NULL"); + return q; +} + +peer * +getWeightedRoundRobinParent(request_t * request) +{ + peer *p; + peer *q = NULL; + int weighted_rtt; + for (p = Config.peers; p; p = p->next) { + if (!p->options.weighted_roundrobin) + continue; + if (neighborType(p, request) != PEER_PARENT) + continue; + if (!peerHTTPOkay(p, request)) + continue; + if (q && q->rr_count < p->rr_count) + continue; + q = p; + } + if (q && q->rr_count > 1000000) + for (p = Config.peers; p; p = p->next) { + if (!p->options.weighted_roundrobin) + continue; + if (neighborType(p, request) != PEER_PARENT) + continue; + p->rr_count = 0; + } + if (q) { + weighted_rtt = (q->stats.rtt - q->basetime) / q->weight; + + if (weighted_rtt < 1) + weighted_rtt = 1; + q->rr_count += weighted_rtt; + debug(15, 3) ("getWeightedRoundRobinParent: weighted_rtt %d\n", (int) weighted_rtt); + } + debug(15, 3) ("getWeightedRoundRobinParent: returning %s\n", q ? q->host : "NULL"); + return q; +} + +/* This gets called every 5 minutes to clear the round-robin counter. */ +void +peerClearRR(void *data) +{ + peer *p = (peer *)data; + p->rr_count -= p->rr_lastcount; + if (p->rr_count < 0) + p->rr_count = 0; + p->rr_lastcount = p->rr_count; + eventAdd("peerClearRR", peerClearRR, p, 5 * 60.0, 0); +} + +peer * +getDefaultParent(request_t * request) +{ + peer *p = NULL; + for (p = Config.peers; p; p = p->next) { + if (neighborType(p, request) != PEER_PARENT) + continue; + if (!p->options.default_parent) + continue; + if (!peerHTTPOkay(p, request)) + continue; + debug(15, 3) ("getDefaultParent: returning %s\n", p->host); + return p; + } + debug(15, 3) ("getDefaultParent: returning NULL\n"); + return NULL; +} + +/* + * XXX DW thinks this function is equivalent to/redundant with + * getFirstUpParent(). peerHTTPOkay() only returns true if the + * peer is UP anyway, so this function would not return a + * DOWN parent. + */ +peer * +getAnyParent(request_t * request) +{ + peer *p = NULL; + for (p = Config.peers; p; p = p->next) { + if (neighborType(p, request) != PEER_PARENT) + continue; + if (!peerHTTPOkay(p, request)) + continue; + debug(15, 3) ("getAnyParent: returning %s\n", p->host); + return p; + } + debug(15, 3) ("getAnyParent: returning NULL\n"); + return NULL; +} + +peer * +getNextPeer(peer * p) +{ + return p->next; +} + +peer * +getFirstPeer(void) +{ + return Config.peers; +} + +static void +neighborRemove(peer * target) +{ + peer *p = NULL; + peer **P = NULL; + p = Config.peers; + P = &Config.peers; + while (p) { + if (target == p) + break; + P = &p->next; + p = p->next; + } + if (p) { + *P = p->next; + cbdataFree(p); + Config.npeers--; + } + first_ping = Config.peers; +} + +void +neighbors_open(int fd) +{ + struct sockaddr_in name; + socklen_t len = sizeof(struct sockaddr_in); + struct servent *sep = NULL; + const char *me = getMyHostname(); + peer *thisPeer; + peer *next; + memset(&name, '\0', sizeof(struct sockaddr_in)); + if (getsockname(fd, (struct sockaddr *) &name, &len) < 0) + debug(15, 1) ("getsockname(%d,%p,%p) failed.\n", fd, &name, &len); + for (thisPeer = Config.peers; thisPeer; thisPeer = next) { + sockaddr_in_list *s; + next = thisPeer->next; + if (0 != strcmp(thisPeer->host, me)) + continue; + for (s = Config.Sockaddr.http; s; s = s->next) { + if (thisPeer->http_port != ntohs(s->s.sin_port)) + continue; + debug(15, 1) ("WARNING: Peer looks like this host\n"); + debug(15, 1) (" Ignoring %s %s/%d/%d\n", + neighborTypeStr(thisPeer), thisPeer->host, thisPeer->http_port, + thisPeer->icp.port); + neighborRemove(thisPeer); + } + } + + peerRefreshDNS((void *) 1); + if (ICP_INVALID == echo_hdr.opcode) { + echo_hdr.opcode = ICP_SECHO; + echo_hdr.version = ICP_VERSION_CURRENT; + echo_hdr.length = 0; + echo_hdr.reqnum = 0; + echo_hdr.flags = 0; + echo_hdr.pad = 0; + echo_hdr.shostid = name.sin_addr.s_addr; + sep = getservbyname("echo", "udp"); + echo_port = sep ? ntohs((u_short) sep->s_port) : 7; + } + first_ping = Config.peers; + cachemgrRegister("server_list", + "Peer Cache Statistics", + neighborDumpPeers, 0, 1); + cachemgrRegister("non_peers", + "List of Unknown sites sending ICP messages", + neighborDumpNonPeers, 0, 1); +} + +int +neighborsUdpPing(request_t * request, + StoreEntry * entry, + IRCB * callback, + void *callback_data, + int *exprep, + int *timeout) +{ + const char *url = storeUrl(entry); + MemObject *mem = entry->mem_obj; + peer *p = NULL; + int i; + int reqnum = 0; + int flags; + icp_common_t *query; + int queries_sent = 0; + int peers_pinged = 0; + int parent_timeout = 0, parent_exprep = 0; + int sibling_timeout = 0, sibling_exprep = 0; + + if (Config.peers == NULL) + return 0; + if (theOutIcpConnection < 0) + fatal("neighborsUdpPing: There is no ICP socket!"); + assert(entry->swap_status == SWAPOUT_NONE); + mem->start_ping = current_time; + mem->ping_reply_callback = callback; + mem->ircb_data = callback_data; + reqnum = icpSetCacheKey((const cache_key *)entry->hash.key); + for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) { + if (p == NULL) + p = Config.peers; + debug(15, 5) ("neighborsUdpPing: Peer %s\n", p->host); + if (!peerWouldBePinged(p, request)) + continue; /* next peer */ + peers_pinged++; + debug(15, 4) ("neighborsUdpPing: pinging peer %s for '%s'\n", + p->host, url); + if (p->type == PEER_MULTICAST) + mcastSetTtl(theOutIcpConnection, p->mcast.ttl); + debug(15, 3) ("neighborsUdpPing: key = '%s'\n", storeKeyText((const cache_key *)entry->hash.key)); + debug(15, 3) ("neighborsUdpPing: reqnum = %d\n", reqnum); + +#if USE_HTCP + if (p->options.htcp) { + debug(15, 3) ("neighborsUdpPing: sending HTCP query\n"); + htcpQuery(entry, request, p); + } else +#endif + if (p->icp.port == echo_port) { + debug(15, 4) ("neighborsUdpPing: Looks like a dumb cache, send DECHO ping\n"); + echo_hdr.reqnum = reqnum; + query = _icp_common_t::createMessage(ICP_DECHO, 0, url, reqnum, 0); + icpUdpSend(theOutIcpConnection, + &p->in_addr, + query, + LOG_ICP_QUERY, + 0); + } else { + flags = 0; + if (Config.onoff.query_icmp) + if (p->icp.version == ICP_VERSION_2) + flags |= ICP_FLAG_SRC_RTT; + query = _icp_common_t::createMessage(ICP_QUERY, flags, url, reqnum, 0); + icpUdpSend(theOutIcpConnection, + &p->in_addr, + query, + LOG_ICP_QUERY, + 0); + } + queries_sent++; + + p->stats.pings_sent++; + if (p->type == PEER_MULTICAST) { + /* + * set a bogus last_reply time so neighborUp() never + * says a multicast peer is dead. + */ + p->stats.last_reply = squid_curtime; + (*exprep) += p->mcast.n_replies_expected; + } else if (neighborUp(p)) { + /* its alive, expect a reply from it */ + if (neighborType(p, request) == PEER_PARENT) { + parent_exprep++; + parent_timeout += p->stats.rtt; + } else { + sibling_exprep++; + sibling_timeout += p->stats.rtt; + } + } else { + /* Neighbor is dead; ping it anyway, but don't expect a reply */ + /* log it once at the threshold */ + if (p->stats.logged_state == PEER_ALIVE) { + debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n", + neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + p->stats.logged_state = PEER_DEAD; + } + } + p->stats.last_query = squid_curtime; + if (p->stats.probe_start == 0) + p->stats.probe_start = squid_curtime; + } + if ((first_ping = first_ping->next) == NULL) + first_ping = Config.peers; + +#if ALLOW_SOURCE_PING + /* only do source_ping if we have neighbors */ + if (Config.npeers) { + const ipcache_addrs *ia = NULL; + struct sockaddr_in to_addr; + char *host = request->host; + if (!Config.onoff.source_ping) { + debug(15, 6) ("neighborsUdpPing: Source Ping is disabled.\n"); + } else if ((ia = ipcache_gethostbyname(host, 0))) { + debug(15, 6) ("neighborsUdpPing: Source Ping: to %s for '%s'\n", + host, url); + echo_hdr.reqnum = reqnum; + if (icmp_sock != -1) { + icmpSourcePing(ia->in_addrs[ia->cur], &echo_hdr, url); + } else { + to_addr.sin_family = AF_INET; + to_addr.sin_addr = ia->in_addrs[ia->cur]; + to_addr.sin_port = htons(echo_port); + query = _icp_common_t::createMessage(ICP_SECHO, 0, url, reqnum, 0); + icpUdpSend(theOutIcpConnection, + &to_addr, + query, + LOG_ICP_QUERY, + 0); + } + } else { + debug(15, 6) ("neighborsUdpPing: Source Ping: unknown host: %s\n", + host); + } + } +#endif + /* + * How many replies to expect? + */ + *exprep = parent_exprep + sibling_exprep; + + /* + * If there is a configured timeout, use it + */ + if (Config.Timeout.icp_query) + *timeout = Config.Timeout.icp_query; + else { + if (*exprep > 0) { + if (parent_exprep) + *timeout = 2 * parent_timeout / parent_exprep; + else + *timeout = 2 * sibling_timeout / sibling_exprep; + } else + *timeout = 2000; /* 2 seconds */ + if (Config.Timeout.icp_query_max) + if (*timeout > Config.Timeout.icp_query_max) + *timeout = Config.Timeout.icp_query_max; + if (*timeout < Config.Timeout.icp_query_min) + *timeout = Config.Timeout.icp_query_min; + } + return peers_pinged; +} + +/* lookup the digest of a given peer */ +lookup_t +peerDigestLookup(peer * p, request_t * request) +{ +#if USE_CACHE_DIGESTS + const cache_key *key = request ? storeKeyPublicByRequest(request) : NULL; + assert(p); + assert(request); + debug(15, 5) ("peerDigestLookup: peer %s\n", p->host); + /* does the peeer have a valid digest? */ + if (!p->digest) { + debug(15, 5) ("peerDigestLookup: gone!\n"); + return LOOKUP_NONE; + } else if (!peerHTTPOkay(p, request)) { + debug(15, 5) ("peerDigestLookup: !peerHTTPOkay\n"); + return LOOKUP_NONE; + } else if (p->digest->flags.usable) { + debug(15, 5) ("peerDigestLookup: usable\n"); + /* fall through; put here to have common case on top */ ; + } else if (!p->digest->flags.needed) { + debug(15, 5) ("peerDigestLookup: note need\n"); + peerDigestNeeded(p->digest); + return LOOKUP_NONE; + } else { + debug(15, 5) ("peerDigestLookup: !ready && %srequested\n", + p->digest->flags.requested ? "" : "!"); + return LOOKUP_NONE; + } + debug(15, 5) ("peerDigestLookup: OK to lookup peer %s\n", p->host); + assert(p->digest->cd); + /* does digest predict a hit? */ + if (!cacheDigestTest(p->digest->cd, key)) + return LOOKUP_MISS; + debug(15, 5) ("peerDigestLookup: peer %s says HIT!\n", p->host); + return LOOKUP_HIT; +#endif + return LOOKUP_NONE; +} + +/* select best peer based on cache digests */ +peer * +neighborsDigestSelect(request_t * request) +{ + peer *best_p = NULL; +#if USE_CACHE_DIGESTS + const cache_key *key; + int best_rtt = 0; + int choice_count = 0; + int ichoice_count = 0; + peer *p; + int p_rtt; + int i; + if (!request->flags.hierarchical) + return NULL; + key = storeKeyPublicByRequest(request); + for (i = 0, p = first_ping; i++ < Config.npeers; p = p->next) { + lookup_t lookup; + if (!p) + p = Config.peers; + if (i == 1) + first_ping = p; + lookup = peerDigestLookup(p, request); + if (lookup == LOOKUP_NONE) + continue; + choice_count++; + if (lookup == LOOKUP_MISS) + continue; + p_rtt = netdbHostRtt(p->host); + debug(15, 5) ("neighborsDigestSelect: peer %s rtt: %d\n", + p->host, p_rtt); + /* is this peer better than others in terms of rtt ? */ + if (!best_p || (p_rtt && p_rtt < best_rtt)) { + best_p = p; + best_rtt = p_rtt; + if (p_rtt) /* informative choice (aka educated guess) */ + ichoice_count++; + debug(15, 4) ("neighborsDigestSelect: peer %s leads with rtt %d\n", + p->host, best_rtt); + } + } + debug(15, 4) ("neighborsDigestSelect: choices: %d (%d)\n", + choice_count, ichoice_count); + peerNoteDigestLookup(request, best_p, + best_p ? LOOKUP_HIT : (choice_count ? LOOKUP_MISS : LOOKUP_NONE)); + request->hier.n_choices = choice_count; + request->hier.n_ichoices = ichoice_count; +#endif + return best_p; +} + +void +peerNoteDigestLookup(request_t * request, peer * p, lookup_t lookup) +{ +#if USE_CACHE_DIGESTS + if (p) + strncpy(request->hier.cd_host, p->host, sizeof(request->hier.cd_host)); + else + *request->hier.cd_host = '\0'; + request->hier.cd_lookup = lookup; + debug(15, 4) ("peerNoteDigestLookup: peer %s, lookup: %s\n", + p ? p->host : "", lookup_t_str[lookup]); +#endif +} + +static void +neighborAlive(peer * p, const MemObject * mem, const icp_common_t * header) +{ + if (p->stats.logged_state == PEER_DEAD && p->tcp_up) { + debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", + neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + p->stats.logged_state = PEER_ALIVE; + } + p->stats.last_reply = squid_curtime; + p->stats.probe_start = 0; + p->stats.pings_acked++; + if ((icp_opcode) header->opcode <= ICP_END) + p->icp.counts[header->opcode]++; + p->icp.version = (int) header->version; +} + +static void +neighborUpdateRtt(peer * p, MemObject * mem) +{ + int rtt, rtt_av_factor; + if (!mem) + return; + if (!mem->start_ping.tv_sec) + return; + rtt = tvSubMsec(mem->start_ping, current_time); + if (rtt < 1 || rtt > 10000) + return; + rtt_av_factor = RTT_AV_FACTOR; + if (p->options.weighted_roundrobin) + rtt_av_factor = RTT_BACKGROUND_AV_FACTOR; + p->stats.rtt = intAverage(p->stats.rtt, rtt, + p->stats.pings_acked, rtt_av_factor); +} + +#if USE_HTCP +static void +neighborAliveHtcp(peer * p, const MemObject * mem, const htcpReplyData * htcp) +{ + if (p->stats.logged_state == PEER_DEAD && p->tcp_up) { + debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", + neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + p->stats.logged_state = PEER_ALIVE; + } + p->stats.last_reply = squid_curtime; + p->stats.probe_start = 0; + p->stats.pings_acked++; + p->htcp.counts[htcp->hit ? 1 : 0]++; + p->htcp.version = htcp->version; +} +#endif + +static void +neighborCountIgnored(peer * p) +{ + if (p == NULL) + return; + p->stats.ignored_replies++; + NLateReplies++; +} + +static peer *non_peers = NULL; + +static void +neighborIgnoreNonPeer(const struct sockaddr_in *from, icp_opcode opcode) +{ + peer *np; + for (np = non_peers; np; np = np->next) { + if (np->in_addr.sin_addr.s_addr != from->sin_addr.s_addr) + continue; + if (np->in_addr.sin_port != from->sin_port) + continue; + break; + } + if (np == NULL) { + np = (peer *)xcalloc(1, sizeof(peer)); + np->in_addr.sin_addr = from->sin_addr; + np->in_addr.sin_port = from->sin_port; + np->icp.port = ntohl(from->sin_port); + np->type = PEER_NONE; + np->host = xstrdup(inet_ntoa(from->sin_addr)); + np->next = non_peers; + non_peers = np; + } + np->icp.counts[opcode]++; + if (isPowTen(++np->stats.ignored_replies)) + debug(15, 1) ("WARNING: Ignored %d replies from non-peer %s\n", + np->stats.ignored_replies, np->host); +} + +/* ignoreMulticastReply + * + * * We want to ignore replies from multicast peers if the + * * cache_host_domain rules would normally prevent the peer + * * from being used + */ +static int +ignoreMulticastReply(peer * p, MemObject * mem) +{ + if (p == NULL) + return 0; + if (!p->options.mcast_responder) + return 0; + if (peerHTTPOkay(p, mem->request)) + return 0; + return 1; +} + +/* I should attach these records to the entry. We take the first + * hit we get our wait until everyone misses. The timeout handler + * call needs to nip this shopping list or call one of the misses. + * + * If a hit process is already started, then sobeit + */ +void +neighborsUdpAck(const cache_key * key, icp_common_t * header, const struct sockaddr_in *from) +{ + peer *p = NULL; + StoreEntry *entry; + MemObject *mem = NULL; + peer_t ntype = PEER_NONE; + char *opcode_d; + icp_opcode opcode = (icp_opcode) header->opcode; + + debug(15, 6) ("neighborsUdpAck: opcode %d '%s'\n", + (int) opcode, storeKeyText(key)); + if (NULL != (entry = storeGet(key))) + mem = entry->mem_obj; + if ((p = whichPeer(from))) + neighborAlive(p, mem, header); + if (opcode > ICP_END) + return; + opcode_d = icp_opcode_str[opcode]; + if (p) + neighborUpdateRtt(p, mem); + /* Does the entry exist? */ + if (NULL == entry) { + debug(12, 3) ("neighborsUdpAck: Cache key '%s' not found\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + /* check if someone is already fetching it */ + if (EBIT_TEST(entry->flags, ENTRY_DISPATCHED)) { + debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (mem == NULL) { + debug(15, 2) ("Ignoring %s for missing mem_obj: %s\n", + opcode_d, storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (entry->ping_status != PING_WAITING) { + debug(15, 2) ("neighborsUdpAck: Late %s for %s\n", + opcode_d, storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (entry->lock_count == 0) { + debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + debug(15, 3) ("neighborsUdpAck: %s for '%s' from %s \n", + opcode_d, storeKeyText(key), p ? p->host : "source"); + if (p) { + ntype = neighborType(p, mem->request); + } + if (ignoreMulticastReply(p, mem)) { + neighborCountIgnored(p); + } else if (opcode == ICP_MISS) { + if (p == NULL) { + neighborIgnoreNonPeer(from, opcode); + } else { + mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); + } + } else if (opcode == ICP_HIT) { + if (p == NULL) { + neighborIgnoreNonPeer(from, opcode); + } else { + header->opcode = ICP_HIT; + mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); + } + } else if (opcode == ICP_DECHO) { + if (p == NULL) { + neighborIgnoreNonPeer(from, opcode); + } else if (ntype == PEER_SIBLING) { + debug_trap("neighborsUdpAck: Found non-ICP cache as SIBLING\n"); + debug_trap("neighborsUdpAck: non-ICP neighbors must be a PARENT\n"); + } else { + mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); + } + } else if (opcode == ICP_SECHO) { + if (p) { + debug(15, 1) ("Ignoring SECHO from neighbor %s\n", p->host); + neighborCountIgnored(p); +#if ALLOW_SOURCE_PING + } else if (Config.onoff.source_ping) { + mem->ping_reply_callback(NULL, ntype, PROTO_ICP, header, mem->ircb_data); +#endif + } else { + debug(15, 1) ("Unsolicited SECHO from %s\n", inet_ntoa(from->sin_addr)); + } + } else if (opcode == ICP_DENIED) { + if (p == NULL) { + neighborIgnoreNonPeer(from, opcode); + } else if (p->stats.pings_acked > 100) { + if (100 * p->icp.counts[ICP_DENIED] / p->stats.pings_acked > 95) { + debug(15, 0) ("95%% of replies from '%s' are UDP_DENIED\n", p->host); + debug(15, 0) ("Disabling '%s', please check your configuration.\n", p->host); + neighborRemove(p); + p = NULL; + } else { + neighborCountIgnored(p); + } + } + } else if (opcode == ICP_MISS_NOFETCH) { + mem->ping_reply_callback(p, ntype, PROTO_ICP, header, mem->ircb_data); + } else { + debug(15, 0) ("neighborsUdpAck: Unexpected ICP reply: %s\n", opcode_d); + } +} + +peer * +peerFindByName(const char *name) +{ + peer *p = NULL; + for (p = Config.peers; p; p = p->next) { + if (!strcasecmp(name, p->host)) + break; + } + return p; +} + +peer * +peerFindByNameAndPort(const char *name, unsigned short port) +{ + peer *p = NULL; + for (p = Config.peers; p; p = p->next) { + if (strcasecmp(name, p->host)) + continue; + if (port != p->http_port) + continue; + break; + } + return p; +} + +int +neighborUp(const peer * p) +{ + if (!p->tcp_up) { + peerProbeConnect((peer *) p); + return 0; + } + if (p->options.no_query) + return 1; + if (p->stats.probe_start != 0 && + squid_curtime - p->stats.probe_start > Config.Timeout.deadPeer) + return 0; + /* + * The peer can not be UP if we don't have any IP addresses + * for it. + */ + if (0 == p->n_addresses) + return 0; + return 1; +} + +void +peerDestroy(void *data) +{ + peer *p = (peer *)data; + struct _domain_ping *l = NULL; + struct _domain_ping *nl = NULL; + if (p == NULL) + return; + for (l = p->peer_domain; l; l = nl) { + nl = l->next; + safe_free(l->domain); + safe_free(l); + } + safe_free(p->host); +#if USE_CACHE_DIGESTS + cbdataReferenceDone(p->digest); +#endif +} + +void +peerNoteDigestGone(peer * p) +{ +#if USE_CACHE_DIGESTS + cbdataReferenceDone(p->digest); +#endif +} + +static void +peerDNSConfigure(const ipcache_addrs * ia, void *data) +{ + peer *p = (peer *)data; + struct sockaddr_in *ap; + int j; + if (p->n_addresses == 0) { + debug(15, 1) ("Configuring %s %s/%d/%d\n", neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + if (p->type == PEER_MULTICAST) + debug(15, 1) (" Multicast TTL = %d\n", p->mcast.ttl); + } + p->n_addresses = 0; + if (ia == NULL) { + debug(0, 0) ("WARNING: DNS lookup for '%s' failed!\n", p->host); + return; + } + if ((int) ia->count < 1) { + debug(0, 0) ("WARNING: No IP address found for '%s'!\n", p->host); + return; + } + for (j = 0; j < (int) ia->count && j < PEER_MAX_ADDRESSES; j++) { + p->addresses[j] = ia->in_addrs[j]; + debug(15, 2) ("--> IP address #%d: %s\n", j, inet_ntoa(p->addresses[j])); + p->n_addresses++; + } + ap = &p->in_addr; + memset(ap, '\0', sizeof(struct sockaddr_in)); + ap->sin_family = AF_INET; + ap->sin_addr = p->addresses[0]; + ap->sin_port = htons(p->icp.port); + if (p->type == PEER_MULTICAST) + peerCountMcastPeersSchedule(p, 10); + if (p->type != PEER_MULTICAST) + if (!p->options.no_netdb_exchange) + eventAddIsh("netdbExchangeStart", netdbExchangeStart, p, 30.0, 1); +} + +static void +peerRefreshDNS(void *data) +{ + peer *p = NULL; + if (eventFind(peerRefreshDNS, NULL)) + eventDelete(peerRefreshDNS, NULL); + if (!data && 0 == stat5minClientRequests()) { + /* no recent client traffic, wait a bit */ + eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 180.0, 1); + return; + } + for (p = Config.peers; p; p = p->next) + ipcache_nbgethostbyname(p->host, peerDNSConfigure, p); + /* Reconfigure the peers every hour */ + eventAddIsh("peerRefreshDNS", peerRefreshDNS, NULL, 3600.0, 1); +} + +void +peerConnectFailed(peer * p) +{ + p->stats.last_connect_failure = squid_curtime; + if (!p->tcp_up) { + debug(15, 2) ("TCP connection to %s/%d dead\n", p->host, p->http_port); + return; + } + debug(15, 1) ("TCP connection to %s/%d failed\n", p->host, p->http_port); + p->tcp_up--; + if (!p->tcp_up) { + debug(15, 1) ("Detected DEAD %s: %s/%d/%d\n", + neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + p->stats.logged_state = PEER_DEAD; + } +} + +void +peerConnectSucceded(peer * p) +{ + if (!p->tcp_up) { + debug(15, 2) ("TCP connection to %s/%d succeded\n", p->host, p->http_port); + debug(15, 1) ("Detected REVIVED %s: %s/%d/%d\n", + neighborTypeStr(p), + p->host, p->http_port, p->icp.port); + p->stats.logged_state = PEER_ALIVE; + } + p->tcp_up = PEER_TCP_MAGIC_COUNT; +} + +/* + * peerProbeConnect will be called on dead peers by neighborUp + */ +static void +peerProbeConnect(peer * p) +{ + int fd; + if (p->test_fd != -1) + return; /* probe already running */ + if (squid_curtime - p->stats.last_connect_probe < Config.Timeout.connect) + return; /* don't probe to often */ + fd = comm_open(SOCK_STREAM, 0, getOutgoingAddr(NULL), + 0, COMM_NONBLOCKING, p->host); + if (fd < 0) + return; + p->test_fd = fd; + p->stats.last_connect_probe = squid_curtime; + ipcache_nbgethostbyname(p->host, peerProbeConnect2, p); +} + +static void +peerProbeConnect2(const ipcache_addrs * ianotused, void *data) +{ + peer *p = (peer *)data; + commConnectStart(p->test_fd, + p->host, + p->http_port, + peerProbeConnectDone, + p); +} + +static void +peerProbeConnectDone(int fd, comm_err_t status, void *data) +{ + peer *p = (peer*)data; + if (status == COMM_OK) { + peerConnectSucceded(p); + } else { + peerConnectFailed(p); + } + comm_close(fd); + p->test_fd = -1; + return; +} + +static void +peerCountMcastPeersSchedule(peer * p, time_t when) +{ + if (p->mcast.flags.count_event_pending) + return; + eventAdd("peerCountMcastPeersStart", + peerCountMcastPeersStart, + p, + (double) when, 1); + p->mcast.flags.count_event_pending = 1; +} + +static void +peerCountMcastPeersStart(void *data) +{ + peer *p = (peer *)data; + ps_state *psstate; + StoreEntry *fake; + MemObject *mem; + icp_common_t *query; + int reqnum; + LOCAL_ARRAY(char, url, MAX_URL); + assert(p->type == PEER_MULTICAST); + p->mcast.flags.count_event_pending = 0; + snprintf(url, MAX_URL, "http://%s/", inet_ntoa(p->in_addr.sin_addr)); + fake = storeCreateEntry(url, url, null_request_flags, METHOD_GET); + psstate = cbdataAlloc(ps_state); + psstate->request = requestLink(urlParse(METHOD_GET, url)); + psstate->entry = fake; + psstate->callback = NULL; + psstate->callback_data = p; + psstate->ping.start = current_time; + mem = fake->mem_obj; + mem->request = requestLink(psstate->request); + mem->start_ping = current_time; + mem->ping_reply_callback = peerCountHandleIcpReply; + mem->ircb_data = psstate; + mcastSetTtl(theOutIcpConnection, p->mcast.ttl); + p->mcast.id = mem->id; + reqnum = icpSetCacheKey((const cache_key *)fake->hash.key); + query = _icp_common_t::createMessage(ICP_QUERY, 0, url, reqnum, 0); + icpUdpSend(theOutIcpConnection, + &p->in_addr, + query, + LOG_ICP_QUERY, + 0); + fake->ping_status = PING_WAITING; + eventAdd("peerCountMcastPeersDone", + peerCountMcastPeersDone, + psstate, + (double) Config.Timeout.mcast_icp_query, 1); + p->mcast.flags.counting = 1; + peerCountMcastPeersSchedule(p, MCAST_COUNT_RATE); +} + +static void +peerCountMcastPeersDone(void *data) +{ + ps_state *psstate = (ps_state *)data; + peer *p = (peer *)psstate->callback_data; + StoreEntry *fake = psstate->entry; + p->mcast.flags.counting = 0; + p->mcast.avg_n_members = doubleAverage(p->mcast.avg_n_members, + (double) psstate->ping.n_recv, + ++p->mcast.n_times_counted, + 10); + debug(15, 1) ("Group %s: %d replies, %4.1f average, RTT %d\n", + p->host, + psstate->ping.n_recv, + p->mcast.avg_n_members, + p->stats.rtt); + p->mcast.n_replies_expected = (int) p->mcast.avg_n_members; + EBIT_SET(fake->flags, ENTRY_ABORTED); + requestUnlink(fake->mem_obj->request); + fake->mem_obj->request = NULL; + storeReleaseRequest(fake); + storeUnlockObject(fake); + requestUnlink(psstate->request); + cbdataFree(psstate); +} + +static void +peerCountHandleIcpReply(peer * p, peer_t type, protocol_t proto, void *hdrnotused, void *data) +{ + int rtt_av_factor; + + ps_state *psstate = (ps_state *)data; + StoreEntry *fake = psstate->entry; + MemObject *mem = fake->mem_obj; + int rtt = tvSubMsec(mem->start_ping, current_time); + assert(proto == PROTO_ICP); + assert(fake); + assert(mem); + psstate->ping.n_recv++; + rtt_av_factor = RTT_AV_FACTOR; + if (p->options.weighted_roundrobin) + rtt_av_factor = RTT_BACKGROUND_AV_FACTOR; + p->stats.rtt = intAverage(p->stats.rtt, rtt, psstate->ping.n_recv, rtt_av_factor); +} + +static void +neighborDumpPeers(StoreEntry * sentry) +{ + dump_peers(sentry, Config.peers); +} + +static void +neighborDumpNonPeers(StoreEntry * sentry) +{ + dump_peers(sentry, non_peers); +} + +void +dump_peer_options(StoreEntry * sentry, peer * p) +{ + if (p->options.proxy_only) + storeAppendPrintf(sentry, " proxy-only"); + if (p->options.no_query) + storeAppendPrintf(sentry, " no-query"); + if (p->options.background_ping) + storeAppendPrintf(sentry, " background-ping"); + if (p->options.no_digest) + storeAppendPrintf(sentry, " no-digest"); + if (p->options.default_parent) + storeAppendPrintf(sentry, " default"); + if (p->options.roundrobin) + storeAppendPrintf(sentry, " round-robin"); + if (p->options.weighted_roundrobin) + storeAppendPrintf(sentry, " weighted-round-robin"); + if (p->options.mcast_responder) + storeAppendPrintf(sentry, " multicast-responder"); + if (p->options.closest_only) + storeAppendPrintf(sentry, " closest-only"); +#if USE_HTCP + if (p->options.htcp) + storeAppendPrintf(sentry, " htcp"); +#endif + if (p->options.no_netdb_exchange) + storeAppendPrintf(sentry, " no-netdb-exchange"); +#if DELAY_POOLS + if (p->options.no_delay) + storeAppendPrintf(sentry, " no-delay"); +#endif + if (p->login) + storeAppendPrintf(sentry, " login=%s", p->login); + if (p->mcast.ttl > 0) + storeAppendPrintf(sentry, " ttl=%d", p->mcast.ttl); + storeAppendPrintf(sentry, "\n"); +} + +static void +dump_peers(StoreEntry * sentry, peer * peers) +{ + peer *e = NULL; + struct _domain_ping *d = NULL; + icp_opcode op; + int i; + if (peers == NULL) + storeAppendPrintf(sentry, "There are no neighbors installed.\n"); + for (e = peers; e; e = e->next) { + assert(e->host != NULL); + storeAppendPrintf(sentry, "\n%-11.11s: %s/%d/%d\n", + neighborTypeStr(e), + e->host, + e->http_port, + e->icp.port); + storeAppendPrintf(sentry, "Flags :"); + dump_peer_options(sentry, e); + for (i = 0; i < e->n_addresses; i++) { + storeAppendPrintf(sentry, "Address[%d] : %s\n", i, + inet_ntoa(e->addresses[i])); + } + storeAppendPrintf(sentry, "Status : %s\n", + neighborUp(e) ? "Up" : "Down"); + storeAppendPrintf(sentry, "AVG RTT : %d msec\n", e->stats.rtt); + storeAppendPrintf(sentry, "OPEN CONNS : %d\n", e->stats.conn_open); + storeAppendPrintf(sentry, "LAST QUERY : %8d seconds ago\n", + (int) (squid_curtime - e->stats.last_query)); + storeAppendPrintf(sentry, "LAST REPLY : %8d seconds ago\n", + (int) (squid_curtime - e->stats.last_reply)); + storeAppendPrintf(sentry, "PINGS SENT : %8d\n", e->stats.pings_sent); + storeAppendPrintf(sentry, "PINGS ACKED: %8d %3d%%\n", + e->stats.pings_acked, + percent(e->stats.pings_acked, e->stats.pings_sent)); + storeAppendPrintf(sentry, "FETCHES : %8d %3d%%\n", + e->stats.fetches, + percent(e->stats.fetches, e->stats.pings_acked)); + storeAppendPrintf(sentry, "IGNORED : %8d %3d%%\n", + e->stats.ignored_replies, + percent(e->stats.ignored_replies, e->stats.pings_acked)); + storeAppendPrintf(sentry, "Histogram of PINGS ACKED:\n"); +#if USE_HTCP + if (e->options.htcp) { + storeAppendPrintf(sentry, "\tMisses\t%8d %3d%%\n", + e->htcp.counts[0], + percent(e->htcp.counts[0], e->stats.pings_acked)); + storeAppendPrintf(sentry, "\tHits\t%8d %3d%%\n", + e->htcp.counts[1], + percent(e->htcp.counts[1], e->stats.pings_acked)); + } else { +#endif + for (op = ICP_INVALID; op < ICP_END; ++op) { + if (e->icp.counts[op] == 0) + continue; + storeAppendPrintf(sentry, " %12.12s : %8d %3d%%\n", + icp_opcode_str[op], + e->icp.counts[op], + percent(e->icp.counts[op], e->stats.pings_acked)); + } +#if USE_HTCP + } +#endif + if (e->stats.last_connect_failure) { + storeAppendPrintf(sentry, "Last failed connect() at: %s\n", + mkhttpdlogtime(&(e->stats.last_connect_failure))); + } + if (e->peer_domain != NULL) { + storeAppendPrintf(sentry, "DOMAIN LIST: "); + for (d = e->peer_domain; d; d = d->next) { + storeAppendPrintf(sentry, "%s%s ", + d->do_ping ? null_string : "!", d->domain); + } + storeAppendPrintf(sentry, "\n"); + } + storeAppendPrintf(sentry, "keep-alive ratio: %d%%\n", + percent(e->stats.n_keepalives_recv, e->stats.n_keepalives_sent)); + } +} + +#if USE_HTCP +void +neighborsHtcpReply(const cache_key * key, htcpReplyData * htcp, const struct sockaddr_in *from) +{ + StoreEntry *e = storeGet(key); + MemObject *mem = NULL; + peer *p; + peer_t ntype = PEER_NONE; + debug(15, 6) ("neighborsHtcpReply: %s %s\n", + htcp->hit ? "HIT" : "MISS", storeKeyText(key)); + if (NULL != (e = storeGet(key))) + mem = e->mem_obj; + if ((p = whichPeer(from))) + neighborAliveHtcp(p, mem, htcp); + /* Does the entry exist? */ + if (NULL == e) { + debug(12, 3) ("neighyborsHtcpReply: Cache key '%s' not found\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + /* check if someone is already fetching it */ + if (EBIT_TEST(e->flags, ENTRY_DISPATCHED)) { + debug(15, 3) ("neighborsUdpAck: '%s' already being fetched.\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (mem == NULL) { + debug(15, 2) ("Ignoring reply for missing mem_obj: %s\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (e->ping_status != PING_WAITING) { + debug(15, 2) ("neighborsUdpAck: Entry %s is not PING_WAITING\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (e->lock_count == 0) { + debug(12, 1) ("neighborsUdpAck: '%s' has no locks\n", + storeKeyText(key)); + neighborCountIgnored(p); + return; + } + if (p) { + ntype = neighborType(p, mem->request); + neighborUpdateRtt(p, mem); + } + if (ignoreMulticastReply(p, mem)) { + neighborCountIgnored(p); + return; + } + debug(15, 3) ("neighborsHtcpReply: e = %p\n", e); + mem->ping_reply_callback(p, ntype, PROTO_HTCP, htcp, mem->ircb_data); +} +#endif Index: squid/src/protos.h =================================================================== RCS file: /cvsroot/squid-sf//squid/src/protos.h,v retrieving revision 1.63.2.20 retrieving revision 1.63.2.21 diff -u -r1.63.2.20 -r1.63.2.21 --- squid/src/protos.h 9 Oct 2002 03:42:37 -0000 1.63.2.20 +++ squid/src/protos.h 9 Oct 2002 05:55:49 -0000 1.63.2.21 @@ -1,6 +1,6 @@ /* - * $Id: protos.h,v 1.63.2.20 2002/10/09 03:42:37 rbcollins Exp $ + * $Id: protos.h,v 1.63.2.21 2002/10/09 05:55:49 rbcollins Exp $ * * * SQUID Web Proxy Cache http://www.squid-cache.org/ @@ -567,14 +567,6 @@ SQUIDCEXTERN void wccpConnectionClose(void); #endif /* USE_WCCP */ -SQUIDCEXTERN void icpHandleIcpV3(int, struct sockaddr_in, char *, int); -SQUIDCEXTERN int icpCheckUdpHit(StoreEntry *, request_t * request); -SQUIDCEXTERN void icpConnectionsOpen(void); -SQUIDCEXTERN void icpConnectionShutdown(void); -SQUIDCEXTERN void icpConnectionClose(void); -SQUIDCEXTERN int icpSetCacheKey(const cache_key * key); -SQUIDCEXTERN const cache_key *icpGetCacheKey(const char *url, int reqnum); - SQUIDCEXTERN void ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData); @@ -725,7 +717,7 @@ SQUIDCEXTERN void fwdLogRotate(void); SQUIDCEXTERN void fwdStatus(FwdState *, http_status); #endif -struct in_addr getOutgoingAddr(request_t * request); +SQUIDCEXTERN struct in_addr getOutgoingAddr(request_t * request); unsigned long getOutgoingTOS(request_t * request); SQUIDCEXTERN void urnStart(request_t *, StoreEntry *); --- squid/src/store_digest.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,506 +0,0 @@ - -/* - * $Id: store_digest.c,v 1.10.44.2 2002/10/04 11:09:08 rbcollins Exp $ - * - * DEBUG: section 71 Store Digest Manager - * AUTHOR: Alex Rousskov - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - - -/* - * TODO: We probably do not track all the cases when - * storeDigestNoteStoreReady() must be called; this may prevent - * storeDigestRebuild/write schedule to be activated - */ - -#include "squid.h" -#include "Store.h" - - -#if USE_CACHE_DIGESTS - -/* - * local types - */ - -typedef struct { - StoreDigestCBlock cblock; - int rebuild_lock; /* bucket number */ - generic_cbdata *rewrite_lock; /* points to store entry with the digest */ - int rebuild_offset; - int rewrite_offset; - int rebuild_count; - int rewrite_count; -} StoreDigestState; - -typedef struct { - int del_count; /* #store entries deleted from store_digest */ - int del_lost_count; /* #store entries not found in store_digest on delete */ - int add_count; /* #store entries accepted to store_digest */ - int add_coll_count; /* #accepted entries that collided with existing ones */ - int rej_count; /* #store entries not accepted to store_digest */ - int rej_coll_count; /* #not accepted entries that collided with existing ones */ -} StoreDigestStats; - -/* local vars */ -static StoreDigestState sd_state; -static StoreDigestStats sd_stats; - -/* local prototypes */ -static void storeDigestRebuildStart(void *datanotused); -static void storeDigestRebuildResume(void); -static void storeDigestRebuildFinish(void); -static void storeDigestRebuildStep(void *datanotused); -static void storeDigestRewriteStart(void *); -static void storeDigestRewriteResume(void); -static void storeDigestRewriteFinish(StoreEntry * e); -static EVH storeDigestSwapOutStep; -static void storeDigestCBlockSwapOut(StoreEntry * e); -static int storeDigestCalcCap(void); -static int storeDigestResize(void); -static void storeDigestAdd(const StoreEntry *); - -#endif /* USE_CACHE_DIGESTS */ - -/* - * PUBLIC FUNCTIONS - */ - -void -storeDigestInit(void) -{ -#if USE_CACHE_DIGESTS - const int cap = storeDigestCalcCap(); - - if (!Config.onoff.digest_generation) { - store_digest = NULL; - debug(71, 3) ("Local cache digest generation disabled\n"); - return; - } - store_digest = cacheDigestCreate(cap, Config.digest.bits_per_entry); - debug(71, 1) ("Local cache digest enabled; rebuild/rewrite every %d/%d sec\n", - (int) Config.digest.rebuild_period, (int) Config.digest.rewrite_period); - memset(&sd_state, 0, sizeof(sd_state)); - cachemgrRegister("store_digest", "Store Digest", - storeDigestReport, 0, 1); -#else - store_digest = NULL; - debug(71, 3) ("Local cache digest is 'off'\n"); -#endif -} - -/* called when store_rebuild completes */ -void -storeDigestNoteStoreReady(void) -{ -#if USE_CACHE_DIGESTS - if (Config.onoff.digest_generation) { - storeDigestRebuildStart(NULL); - storeDigestRewriteStart(NULL); - } -#endif -} - -void -storeDigestDel(const StoreEntry * entry) -{ -#if USE_CACHE_DIGESTS - if (!Config.onoff.digest_generation) { - return; - } - assert(entry && store_digest); - debug(71, 6) ("storeDigestDel: checking entry, key: %s\n", - storeKeyText(entry->hash.key)); - if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) { - if (!cacheDigestTest(store_digest, entry->hash.key)) { - sd_stats.del_lost_count++; - debug(71, 6) ("storeDigestDel: lost entry, key: %s url: %s\n", - storeKeyText(entry->hash.key), storeUrl(entry)); - } else { - sd_stats.del_count++; - cacheDigestDel(store_digest, entry->hash.key); - debug(71, 6) ("storeDigestDel: deled entry, key: %s\n", - storeKeyText(entry->hash.key)); - } - } -#endif -} - -void -storeDigestReport(StoreEntry * e) -{ -#if USE_CACHE_DIGESTS - if (!Config.onoff.digest_generation) { - return; - } - if (store_digest) { - cacheDigestReport(store_digest, "store", e); - storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n", - sd_stats.add_count, - sd_stats.rej_count, - xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count), - sd_stats.del_count); - storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n", - xpercent(sd_stats.add_coll_count, sd_stats.add_count), - xpercent(sd_stats.rej_coll_count, sd_stats.rej_count)); - } else { - storeAppendPrintf(e, "store digest: disabled.\n"); - } -#endif -} - -/* - * LOCAL FUNCTIONS - */ - -#if USE_CACHE_DIGESTS - -/* should we digest this entry? used by storeDigestAdd() */ -static int -storeDigestAddable(const StoreEntry * e) -{ - /* add some stats! XXX */ - - debug(71, 6) ("storeDigestAddable: checking entry, key: %s\n", - storeKeyText(e->hash.key)); - - /* check various entry flags (mimics storeCheckCachable XXX) */ - if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) { - debug(71, 6) ("storeDigestAddable: NO: not cachable\n"); - return 0; - } - if (EBIT_TEST(e->flags, KEY_PRIVATE)) { - debug(71, 6) ("storeDigestAddable: NO: private key\n"); - return 0; - } - if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) { - debug(71, 6) ("storeDigestAddable: NO: negative cached\n"); - return 0; - } - if (EBIT_TEST(e->flags, RELEASE_REQUEST)) { - debug(71, 6) ("storeDigestAddable: NO: release requested\n"); - return 0; - } - if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) { - debug(71, 6) ("storeDigestAddable: NO: wrong content-length\n"); - return 0; - } - /* do not digest huge objects */ - if (e->swap_file_sz > Config.Store.maxObjectSize) { - debug(71, 6) ("storeDigestAddable: NO: too big\n"); - return 0; - } - /* still here? check staleness */ - /* Note: We should use the time of the next rebuild, not (cur_time+period) */ - if (refreshCheckDigest(e, Config.digest.rebuild_period)) { - debug(71, 6) ("storeDigestAdd: entry expires within %d secs, ignoring\n", - (int) Config.digest.rebuild_period); - return 0; - } - /* - * idea: how about also skipping very fresh (thus, potentially - * unstable) entries? Should be configurable through - * cd_refresh_pattern, of course. - */ - /* - * idea: skip objects that are going to be purged before the next - * update. - */ -#if OLD_UNUSED_CODE /* This code isn't applicable anymore, we can't fix it atm either :( */ - if ((squid_curtime + Config.digest.rebuild_period) - e->lastref > storeExpiredReferenceAge()) - return 0; -#endif - return 1; -} - -static void -storeDigestAdd(const StoreEntry * entry) -{ - assert(entry && store_digest); - - if (storeDigestAddable(entry)) { - sd_stats.add_count++; - if (cacheDigestTest(store_digest, entry->hash.key)) - sd_stats.add_coll_count++; - cacheDigestAdd(store_digest, entry->hash.key); - debug(71, 6) ("storeDigestAdd: added entry, key: %s\n", - storeKeyText(entry->hash.key)); - } else { - sd_stats.rej_count++; - if (cacheDigestTest(store_digest, entry->hash.key)) - sd_stats.rej_coll_count++; - } -} - -/* rebuilds digest from scratch */ -static void -storeDigestRebuildStart(void *datanotused) -{ - assert(store_digest); - /* prevent overlapping if rebuild schedule is too tight */ - if (sd_state.rebuild_lock) { - debug(71, 1) ("storeDigestRebuildStart: overlap detected, consider increasing rebuild period\n"); - return; - } - sd_state.rebuild_lock = 1; - debug(71, 2) ("storeDigestRebuildStart: rebuild #%d\n", sd_state.rebuild_count + 1); - if (sd_state.rewrite_lock) { - debug(71, 2) ("storeDigestRebuildStart: waiting for Rewrite to finish.\n"); - return; - } - storeDigestRebuildResume(); -} - -/* called be Rewrite to push Rebuild forward */ -static void -storeDigestRebuildResume(void) -{ - assert(sd_state.rebuild_lock); - assert(!sd_state.rewrite_lock); - sd_state.rebuild_offset = 0; - /* resize or clear */ - if (!storeDigestResize()) - cacheDigestClear(store_digest); /* not clean()! */ - memset(&sd_stats, 0, sizeof(sd_stats)); - eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1); -} - -/* finishes swap out sequence for the digest; schedules next rebuild */ -static void -storeDigestRebuildFinish(void) -{ - assert(sd_state.rebuild_lock); - sd_state.rebuild_lock = 0; - sd_state.rebuild_count++; - debug(71, 2) ("storeDigestRebuildFinish: done.\n"); - eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, (double) - Config.digest.rebuild_period, 1); - /* resume pending Rewrite if any */ - if (sd_state.rewrite_lock) - storeDigestRewriteResume(); -} - -/* recalculate a few hash buckets per invocation; schedules next step */ -static void -storeDigestRebuildStep(void *datanotused) -{ - int bcount = (int) ceil((double) store_hash_buckets * - (double) Config.digest.rebuild_chunk_percentage / 100.0); - assert(sd_state.rebuild_lock); - if (sd_state.rebuild_offset + bcount > store_hash_buckets) - bcount = store_hash_buckets - sd_state.rebuild_offset; - debug(71, 3) ("storeDigestRebuildStep: buckets: %d offset: %d chunk: %d buckets\n", - store_hash_buckets, sd_state.rebuild_offset, bcount); - while (bcount--) { - hash_link *link_ptr = hash_get_bucket(store_table, sd_state.rebuild_offset); - for (; link_ptr; link_ptr = link_ptr->next) { - storeDigestAdd((StoreEntry *) link_ptr); - } - sd_state.rebuild_offset++; - } - /* are we done ? */ - if (sd_state.rebuild_offset >= store_hash_buckets) - storeDigestRebuildFinish(); - else - eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1); -} - - -/* starts swap out sequence for the digest */ -static void -storeDigestRewriteStart(void *datanotused) -{ - request_flags flags; - char *url; - StoreEntry *e; - - assert(store_digest); - /* prevent overlapping if rewrite schedule is too tight */ - if (sd_state.rewrite_lock) { - debug(71, 1) ("storeDigestRewrite: overlap detected, consider increasing rewrite period\n"); - return; - } - debug(71, 2) ("storeDigestRewrite: start rewrite #%d\n", sd_state.rewrite_count + 1); - /* make new store entry */ - url = internalLocalUri("/squid-internal-periodic/", StoreDigestFileName); - flags = null_request_flags; - flags.cachable = 1; - e = storeCreateEntry(url, url, flags, METHOD_GET); - assert(e); - sd_state.rewrite_lock = cbdataAlloc(generic_cbdata); - sd_state.rewrite_lock->data = e; - debug(71, 3) ("storeDigestRewrite: url: %s key: %s\n", url, storeKeyText(e->hash.key)); - e->mem_obj->request = requestLink(urlParse(METHOD_GET, url)); - /* wait for rebuild (if any) to finish */ - if (sd_state.rebuild_lock) { - debug(71, 2) ("storeDigestRewriteStart: waiting for rebuild to finish.\n"); - return; - } - storeDigestRewriteResume(); -} - -static void -storeDigestRewriteResume(void) -{ - StoreEntry *e; - http_version_t version; - - assert(sd_state.rewrite_lock); - assert(!sd_state.rebuild_lock); - e = sd_state.rewrite_lock->data; - sd_state.rewrite_offset = 0; - EBIT_SET(e->flags, ENTRY_SPECIAL); - /* setting public key will purge old digest entry if any */ - storeSetPublicKey(e); - /* fake reply */ - httpReplyReset(e->mem_obj->reply); - httpBuildVersion(&version, 1, 0); - httpReplySetHeaders(e->mem_obj->reply, version, 200, "Cache Digest OK", - "application/cache-digest", store_digest->mask_size + sizeof(sd_state.cblock), - squid_curtime, squid_curtime + Config.digest.rewrite_period); - debug(71, 3) ("storeDigestRewrite: entry expires on %ld (%+d)\n", - (long int) e->mem_obj->reply->expires, (int) (e->mem_obj->reply->expires - squid_curtime)); - storeBuffer(e); - httpReplySwapOut(e->mem_obj->reply, e); - storeDigestCBlockSwapOut(e); - storeBufferFlush(e); - eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1); -} - -/* finishes swap out sequence for the digest; schedules next rewrite */ -static void -storeDigestRewriteFinish(StoreEntry * e) -{ - assert(sd_state.rewrite_lock && e == sd_state.rewrite_lock->data); - storeComplete(e); - storeTimestampsSet(e); - debug(71, 2) ("storeDigestRewriteFinish: digest expires at %ld (%+d)\n", - (long int) e->expires, (int) (e->expires - squid_curtime)); - /* is this the write order? @?@ */ - requestUnlink(e->mem_obj->request); - e->mem_obj->request = NULL; - storeUnlockObject(e); - cbdataFree(sd_state.rewrite_lock); - e = NULL; - sd_state.rewrite_lock = NULL; - sd_state.rewrite_count++; - eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, (double) - Config.digest.rewrite_period, 1); - /* resume pending Rebuild if any */ - if (sd_state.rebuild_lock) - storeDigestRebuildResume(); -} - -/* swaps out one digest "chunk" per invocation; schedules next swap out */ -static void -storeDigestSwapOutStep(void *data) -{ - StoreEntry *e; - int chunk_size = Config.digest.swapout_chunk_size; - assert(data == sd_state.rewrite_lock); - e = (StoreEntry *) ((generic_cbdata *) data)->data; - assert(e); - /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */ - if (sd_state.rewrite_offset + chunk_size > store_digest->mask_size) - chunk_size = store_digest->mask_size - sd_state.rewrite_offset; - storeAppend(e, store_digest->mask + sd_state.rewrite_offset, chunk_size); - debug(71, 3) ("storeDigestSwapOutStep: size: %d offset: %d chunk: %d bytes\n", - store_digest->mask_size, sd_state.rewrite_offset, chunk_size); - sd_state.rewrite_offset += chunk_size; - /* are we done ? */ - if (sd_state.rewrite_offset >= store_digest->mask_size) - storeDigestRewriteFinish(e); - else - eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, data, 0.0, 1); -} - -static void -storeDigestCBlockSwapOut(StoreEntry * e) -{ - memset(&sd_state.cblock, 0, sizeof(sd_state.cblock)); - sd_state.cblock.ver.current = htons(CacheDigestVer.current); - sd_state.cblock.ver.required = htons(CacheDigestVer.required); - sd_state.cblock.capacity = htonl(store_digest->capacity); - sd_state.cblock.count = htonl(store_digest->count); - sd_state.cblock.del_count = htonl(store_digest->del_count); - sd_state.cblock.mask_size = htonl(store_digest->mask_size); - sd_state.cblock.bits_per_entry = (unsigned char) - Config.digest.bits_per_entry; - sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount; - storeAppend(e, (char *) &sd_state.cblock, sizeof(sd_state.cblock)); -} - -/* calculates digest capacity */ -static int -storeDigestCalcCap(void) -{ - /* - * To-Do: Bloom proved that the optimal filter utilization is 50% (half of - * the bits are off). However, we do not have a formula to calculate the - * number of _entries_ we want to pre-allocate for. - */ - const int hi_cap = Config.Swap.maxSize / Config.Store.avgObjectSize; - const int lo_cap = 1 + store_swap_size / Config.Store.avgObjectSize; - const int e_count = storeEntryInUse(); - int cap = e_count ? e_count : hi_cap; - debug(71, 2) ("storeDigestCalcCap: have: %d, want %d entries; limits: [%d, %d]\n", - e_count, cap, lo_cap, hi_cap); - if (cap < lo_cap) - cap = lo_cap; - /* do not enforce hi_cap limit, average-based estimation may be wrong - *if (cap > hi_cap) - * cap = hi_cap; - */ - return cap; -} - -/* returns true if we actually resized the digest */ -static int -storeDigestResize(void) -{ - const int cap = storeDigestCalcCap(); - int diff; - assert(store_digest); - diff = abs(cap - store_digest->capacity); - debug(71, 2) ("storeDigestResize: %d -> %d; change: %d (%d%%)\n", - store_digest->capacity, cap, diff, - xpercentInt(diff, store_digest->capacity)); - /* avoid minor adjustments */ - if (diff <= store_digest->capacity / 10) { - debug(71, 2) ("storeDigestResize: small change, will not resize.\n"); - return 0; - } else { - debug(71, 2) ("storeDigestResize: big change, resizing.\n"); - cacheDigestChangeCap(store_digest, cap); - return 1; - } -} - -#endif /* USE_CACHE_DIGESTS */ --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/store_digest.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,506 @@ + +/* + * $Id: store_digest.cc,v 1.1.2.1 2002/10/09 05:55:50 rbcollins Exp $ + * + * DEBUG: section 71 Store Digest Manager + * AUTHOR: Alex Rousskov + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + + +/* + * TODO: We probably do not track all the cases when + * storeDigestNoteStoreReady() must be called; this may prevent + * storeDigestRebuild/write schedule to be activated + */ + +#include "squid.h" +#include "Store.h" + + +#if USE_CACHE_DIGESTS + +/* + * local types + */ + +typedef struct { + StoreDigestCBlock cblock; + int rebuild_lock; /* bucket number */ + generic_cbdata *rewrite_lock; /* points to store entry with the digest */ + int rebuild_offset; + int rewrite_offset; + int rebuild_count; + int rewrite_count; +} StoreDigestState; + +typedef struct { + int del_count; /* #store entries deleted from store_digest */ + int del_lost_count; /* #store entries not found in store_digest on delete */ + int add_count; /* #store entries accepted to store_digest */ + int add_coll_count; /* #accepted entries that collided with existing ones */ + int rej_count; /* #store entries not accepted to store_digest */ + int rej_coll_count; /* #not accepted entries that collided with existing ones */ +} StoreDigestStats; + +/* local vars */ +static StoreDigestState sd_state; +static StoreDigestStats sd_stats; + +/* local prototypes */ +static void storeDigestRebuildStart(void *datanotused); +static void storeDigestRebuildResume(void); +static void storeDigestRebuildFinish(void); +static void storeDigestRebuildStep(void *datanotused); +static void storeDigestRewriteStart(void *); +static void storeDigestRewriteResume(void); +static void storeDigestRewriteFinish(StoreEntry * e); +static EVH storeDigestSwapOutStep; +static void storeDigestCBlockSwapOut(StoreEntry * e); +static int storeDigestCalcCap(void); +static int storeDigestResize(void); +static void storeDigestAdd(const StoreEntry *); + +#endif /* USE_CACHE_DIGESTS */ + +/* + * PUBLIC FUNCTIONS + */ + +void +storeDigestInit(void) +{ +#if USE_CACHE_DIGESTS + const int cap = storeDigestCalcCap(); + + if (!Config.onoff.digest_generation) { + store_digest = NULL; + debug(71, 3) ("Local cache digest generation disabled\n"); + return; + } + store_digest = cacheDigestCreate(cap, Config.digest.bits_per_entry); + debug(71, 1) ("Local cache digest enabled; rebuild/rewrite every %d/%d sec\n", + (int) Config.digest.rebuild_period, (int) Config.digest.rewrite_period); + memset(&sd_state, 0, sizeof(sd_state)); + cachemgrRegister("store_digest", "Store Digest", + storeDigestReport, 0, 1); +#else + store_digest = NULL; + debug(71, 3) ("Local cache digest is 'off'\n"); +#endif +} + +/* called when store_rebuild completes */ +void +storeDigestNoteStoreReady(void) +{ +#if USE_CACHE_DIGESTS + if (Config.onoff.digest_generation) { + storeDigestRebuildStart(NULL); + storeDigestRewriteStart(NULL); + } +#endif +} + +void +storeDigestDel(const StoreEntry * entry) +{ +#if USE_CACHE_DIGESTS + if (!Config.onoff.digest_generation) { + return; + } + assert(entry && store_digest); + debug(71, 6) ("storeDigestDel: checking entry, key: %s\n", + storeKeyText( (const cache_key *)entry->hash.key)); + if (!EBIT_TEST(entry->flags, KEY_PRIVATE)) { + if (!cacheDigestTest(store_digest, (const cache_key *)entry->hash.key)) { + sd_stats.del_lost_count++; + debug(71, 6) ("storeDigestDel: lost entry, key: %s url: %s\n", + storeKeyText( (const cache_key *)entry->hash.key), storeUrl(entry)); + } else { + sd_stats.del_count++; + cacheDigestDel(store_digest, (const cache_key *)entry->hash.key); + debug(71, 6) ("storeDigestDel: deled entry, key: %s\n", + storeKeyText((const cache_key *)entry->hash.key)); + } + } +#endif +} + +void +storeDigestReport(StoreEntry * e) +{ +#if USE_CACHE_DIGESTS + if (!Config.onoff.digest_generation) { + return; + } + if (store_digest) { + cacheDigestReport(store_digest, "store", e); + storeAppendPrintf(e, "\t added: %d rejected: %d ( %.2f %%) del-ed: %d\n", + sd_stats.add_count, + sd_stats.rej_count, + xpercent(sd_stats.rej_count, sd_stats.rej_count + sd_stats.add_count), + sd_stats.del_count); + storeAppendPrintf(e, "\t collisions: on add: %.2f %% on rej: %.2f %%\n", + xpercent(sd_stats.add_coll_count, sd_stats.add_count), + xpercent(sd_stats.rej_coll_count, sd_stats.rej_count)); + } else { + storeAppendPrintf(e, "store digest: disabled.\n"); + } +#endif +} + +/* + * LOCAL FUNCTIONS + */ + +#if USE_CACHE_DIGESTS + +/* should we digest this entry? used by storeDigestAdd() */ +static int +storeDigestAddable(const StoreEntry * e) +{ + /* add some stats! XXX */ + + debug(71, 6) ("storeDigestAddable: checking entry, key: %s\n", + storeKeyText((const cache_key *)e->hash.key)); + + /* check various entry flags (mimics storeCheckCachable XXX) */ + if (!EBIT_TEST(e->flags, ENTRY_CACHABLE)) { + debug(71, 6) ("storeDigestAddable: NO: not cachable\n"); + return 0; + } + if (EBIT_TEST(e->flags, KEY_PRIVATE)) { + debug(71, 6) ("storeDigestAddable: NO: private key\n"); + return 0; + } + if (EBIT_TEST(e->flags, ENTRY_NEGCACHED)) { + debug(71, 6) ("storeDigestAddable: NO: negative cached\n"); + return 0; + } + if (EBIT_TEST(e->flags, RELEASE_REQUEST)) { + debug(71, 6) ("storeDigestAddable: NO: release requested\n"); + return 0; + } + if (e->store_status == STORE_OK && EBIT_TEST(e->flags, ENTRY_BAD_LENGTH)) { + debug(71, 6) ("storeDigestAddable: NO: wrong content-length\n"); + return 0; + } + /* do not digest huge objects */ + if (e->swap_file_sz > Config.Store.maxObjectSize) { + debug(71, 6) ("storeDigestAddable: NO: too big\n"); + return 0; + } + /* still here? check staleness */ + /* Note: We should use the time of the next rebuild, not (cur_time+period) */ + if (refreshCheckDigest(e, Config.digest.rebuild_period)) { + debug(71, 6) ("storeDigestAdd: entry expires within %d secs, ignoring\n", + (int) Config.digest.rebuild_period); + return 0; + } + /* + * idea: how about also skipping very fresh (thus, potentially + * unstable) entries? Should be configurable through + * cd_refresh_pattern, of course. + */ + /* + * idea: skip objects that are going to be purged before the next + * update. + */ +#if OLD_UNUSED_CODE /* This code isn't applicable anymore, we can't fix it atm either :( */ + if ((squid_curtime + Config.digest.rebuild_period) - e->lastref > storeExpiredReferenceAge()) + return 0; +#endif + return 1; +} + +static void +storeDigestAdd(const StoreEntry * entry) +{ + assert(entry && store_digest); + + if (storeDigestAddable(entry)) { + sd_stats.add_count++; + if (cacheDigestTest(store_digest, (const cache_key *)entry->hash.key)) + sd_stats.add_coll_count++; + cacheDigestAdd(store_digest, (const cache_key *)entry->hash.key); + debug(71, 6) ("storeDigestAdd: added entry, key: %s\n", + storeKeyText( (const cache_key *)entry->hash.key)); + } else { + sd_stats.rej_count++; + if (cacheDigestTest(store_digest, (const cache_key *)entry->hash.key)) + sd_stats.rej_coll_count++; + } +} + +/* rebuilds digest from scratch */ +static void +storeDigestRebuildStart(void *datanotused) +{ + assert(store_digest); + /* prevent overlapping if rebuild schedule is too tight */ + if (sd_state.rebuild_lock) { + debug(71, 1) ("storeDigestRebuildStart: overlap detected, consider increasing rebuild period\n"); + return; + } + sd_state.rebuild_lock = 1; + debug(71, 2) ("storeDigestRebuildStart: rebuild #%d\n", sd_state.rebuild_count + 1); + if (sd_state.rewrite_lock) { + debug(71, 2) ("storeDigestRebuildStart: waiting for Rewrite to finish.\n"); + return; + } + storeDigestRebuildResume(); +} + +/* called be Rewrite to push Rebuild forward */ +static void +storeDigestRebuildResume(void) +{ + assert(sd_state.rebuild_lock); + assert(!sd_state.rewrite_lock); + sd_state.rebuild_offset = 0; + /* resize or clear */ + if (!storeDigestResize()) + cacheDigestClear(store_digest); /* not clean()! */ + memset(&sd_stats, 0, sizeof(sd_stats)); + eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1); +} + +/* finishes swap out sequence for the digest; schedules next rebuild */ +static void +storeDigestRebuildFinish(void) +{ + assert(sd_state.rebuild_lock); + sd_state.rebuild_lock = 0; + sd_state.rebuild_count++; + debug(71, 2) ("storeDigestRebuildFinish: done.\n"); + eventAdd("storeDigestRebuildStart", storeDigestRebuildStart, NULL, (double) + Config.digest.rebuild_period, 1); + /* resume pending Rewrite if any */ + if (sd_state.rewrite_lock) + storeDigestRewriteResume(); +} + +/* recalculate a few hash buckets per invocation; schedules next step */ +static void +storeDigestRebuildStep(void *datanotused) +{ + int bcount = (int) ceil((double) store_hash_buckets * + (double) Config.digest.rebuild_chunk_percentage / 100.0); + assert(sd_state.rebuild_lock); + if (sd_state.rebuild_offset + bcount > store_hash_buckets) + bcount = store_hash_buckets - sd_state.rebuild_offset; + debug(71, 3) ("storeDigestRebuildStep: buckets: %d offset: %d chunk: %d buckets\n", + store_hash_buckets, sd_state.rebuild_offset, bcount); + while (bcount--) { + hash_link *link_ptr = hash_get_bucket(store_table, sd_state.rebuild_offset); + for (; link_ptr; link_ptr = link_ptr->next) { + storeDigestAdd((StoreEntry *) link_ptr); + } + sd_state.rebuild_offset++; + } + /* are we done ? */ + if (sd_state.rebuild_offset >= store_hash_buckets) + storeDigestRebuildFinish(); + else + eventAdd("storeDigestRebuildStep", storeDigestRebuildStep, NULL, 0.0, 1); +} + + +/* starts swap out sequence for the digest */ +static void +storeDigestRewriteStart(void *datanotused) +{ + request_flags flags; + char *url; + StoreEntry *e; + + assert(store_digest); + /* prevent overlapping if rewrite schedule is too tight */ + if (sd_state.rewrite_lock) { + debug(71, 1) ("storeDigestRewrite: overlap detected, consider increasing rewrite period\n"); + return; + } + debug(71, 2) ("storeDigestRewrite: start rewrite #%d\n", sd_state.rewrite_count + 1); + /* make new store entry */ + url = internalLocalUri("/squid-internal-periodic/", StoreDigestFileName); + flags = null_request_flags; + flags.cachable = 1; + e = storeCreateEntry(url, url, flags, METHOD_GET); + assert(e); + sd_state.rewrite_lock = cbdataAlloc(generic_cbdata); + sd_state.rewrite_lock->data = e; + debug(71, 3) ("storeDigestRewrite: url: %s key: %s\n", url, storeKeyText( (const cache_key *)e->hash.key)); + e->mem_obj->request = requestLink(urlParse(METHOD_GET, url)); + /* wait for rebuild (if any) to finish */ + if (sd_state.rebuild_lock) { + debug(71, 2) ("storeDigestRewriteStart: waiting for rebuild to finish.\n"); + return; + } + storeDigestRewriteResume(); +} + +static void +storeDigestRewriteResume(void) +{ + StoreEntry *e; + http_version_t version; + + assert(sd_state.rewrite_lock); + assert(!sd_state.rebuild_lock); + e = (StoreEntry *)sd_state.rewrite_lock->data; + sd_state.rewrite_offset = 0; + EBIT_SET(e->flags, ENTRY_SPECIAL); + /* setting public key will purge old digest entry if any */ + storeSetPublicKey(e); + /* fake reply */ + httpReplyReset(e->mem_obj->reply); + httpBuildVersion(&version, 1, 0); + httpReplySetHeaders(e->mem_obj->reply, version, HTTP_OK, "Cache Digest OK", + "application/cache-digest", store_digest->mask_size + sizeof(sd_state.cblock), + squid_curtime, squid_curtime + Config.digest.rewrite_period); + debug(71, 3) ("storeDigestRewrite: entry expires on %ld (%+d)\n", + (long int) e->mem_obj->reply->expires, (int) (e->mem_obj->reply->expires - squid_curtime)); + storeBuffer(e); + httpReplySwapOut(e->mem_obj->reply, e); + storeDigestCBlockSwapOut(e); + storeBufferFlush(e); + eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, sd_state.rewrite_lock, 0.0, 1); +} + +/* finishes swap out sequence for the digest; schedules next rewrite */ +static void +storeDigestRewriteFinish(StoreEntry * e) +{ + assert(sd_state.rewrite_lock && e == sd_state.rewrite_lock->data); + storeComplete(e); + storeTimestampsSet(e); + debug(71, 2) ("storeDigestRewriteFinish: digest expires at %ld (%+d)\n", + (long int) e->expires, (int) (e->expires - squid_curtime)); + /* is this the write order? @?@ */ + requestUnlink(e->mem_obj->request); + e->mem_obj->request = NULL; + storeUnlockObject(e); + cbdataFree(sd_state.rewrite_lock); + e = NULL; + sd_state.rewrite_lock = NULL; + sd_state.rewrite_count++; + eventAdd("storeDigestRewriteStart", storeDigestRewriteStart, NULL, (double) + Config.digest.rewrite_period, 1); + /* resume pending Rebuild if any */ + if (sd_state.rebuild_lock) + storeDigestRebuildResume(); +} + +/* swaps out one digest "chunk" per invocation; schedules next swap out */ +static void +storeDigestSwapOutStep(void *data) +{ + StoreEntry *e; + int chunk_size = Config.digest.swapout_chunk_size; + assert(data == sd_state.rewrite_lock); + e = (StoreEntry *) ((generic_cbdata *) data)->data; + assert(e); + /* _add_ check that nothing bad happened while we were waiting @?@ @?@ */ + if ((size_t)(sd_state.rewrite_offset + chunk_size) > store_digest->mask_size) + chunk_size = store_digest->mask_size - sd_state.rewrite_offset; + storeAppend(e, store_digest->mask + sd_state.rewrite_offset, chunk_size); + debug(71, 3) ("storeDigestSwapOutStep: size: %d offset: %d chunk: %d bytes\n", + store_digest->mask_size, sd_state.rewrite_offset, chunk_size); + sd_state.rewrite_offset += chunk_size; + /* are we done ? */ + if ((size_t)sd_state.rewrite_offset >= store_digest->mask_size) + storeDigestRewriteFinish(e); + else + eventAdd("storeDigestSwapOutStep", storeDigestSwapOutStep, data, 0.0, 1); +} + +static void +storeDigestCBlockSwapOut(StoreEntry * e) +{ + memset(&sd_state.cblock, 0, sizeof(sd_state.cblock)); + sd_state.cblock.ver.current = htons(CacheDigestVer.current); + sd_state.cblock.ver.required = htons(CacheDigestVer.required); + sd_state.cblock.capacity = htonl(store_digest->capacity); + sd_state.cblock.count = htonl(store_digest->count); + sd_state.cblock.del_count = htonl(store_digest->del_count); + sd_state.cblock.mask_size = htonl(store_digest->mask_size); + sd_state.cblock.bits_per_entry = (unsigned char) + Config.digest.bits_per_entry; + sd_state.cblock.hash_func_count = (unsigned char) CacheDigestHashFuncCount; + storeAppend(e, (char *) &sd_state.cblock, sizeof(sd_state.cblock)); +} + +/* calculates digest capacity */ +static int +storeDigestCalcCap(void) +{ + /* + * To-Do: Bloom proved that the optimal filter utilization is 50% (half of + * the bits are off). However, we do not have a formula to calculate the + * number of _entries_ we want to pre-allocate for. + */ + const int hi_cap = Config.Swap.maxSize / Config.Store.avgObjectSize; + const int lo_cap = 1 + store_swap_size / Config.Store.avgObjectSize; + const int e_count = storeEntryInUse(); + int cap = e_count ? e_count : hi_cap; + debug(71, 2) ("storeDigestCalcCap: have: %d, want %d entries; limits: [%d, %d]\n", + e_count, cap, lo_cap, hi_cap); + if (cap < lo_cap) + cap = lo_cap; + /* do not enforce hi_cap limit, average-based estimation may be wrong + *if (cap > hi_cap) + * cap = hi_cap; + */ + return cap; +} + +/* returns true if we actually resized the digest */ +static int +storeDigestResize(void) +{ + const int cap = storeDigestCalcCap(); + int diff; + assert(store_digest); + diff = abs(cap - store_digest->capacity); + debug(71, 2) ("storeDigestResize: %d -> %d; change: %d (%d%%)\n", + store_digest->capacity, cap, diff, + xpercentInt(diff, store_digest->capacity)); + /* avoid minor adjustments */ + if (diff <= store_digest->capacity / 10) { + debug(71, 2) ("storeDigestResize: small change, will not resize.\n"); + return 0; + } else { + debug(71, 2) ("storeDigestResize: big change, resizing.\n"); + cacheDigestChangeCap(store_digest, cap); + return 1; + } +} + +#endif /* USE_CACHE_DIGESTS */ --- squid/src/wccp.c Wed Feb 14 01:07:38 2007 +++ /dev/null Wed Feb 14 01:07:22 2007 @@ -1,339 +0,0 @@ - -/* - * $Id: wccp.c,v 1.11 2002/09/01 16:30:43 squidadm Exp $ - * - * DEBUG: section 80 WCCP Support - * AUTHOR: Glenn Chisholm - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ -#include "squid.h" - -#if USE_WCCP - -#define WCCP_PORT 2048 -#define WCCP_REVISION 0 -#define WCCP_RESPONSE_SIZE 12448 -#define WCCP_ACTIVE_CACHES 32 -#define WCCP_HASH_SIZE 32 -#define WCCP_BUCKETS 256 -#define WCCP_CACHE_LEN 4 - -#define WCCP_HERE_I_AM 7 -#define WCCP_I_SEE_YOU 8 -#define WCCP_ASSIGN_BUCKET 9 - -struct wccp_here_i_am_t { - int type; - int version; - int revision; - char hash[WCCP_HASH_SIZE]; - int reserved; - int id; -}; - -struct wccp_cache_entry_t { - struct in_addr ip_addr; - int revision; - char hash[WCCP_HASH_SIZE]; - int reserved; -}; - -struct wccp_i_see_you_t { - int type; - int version; - int change; - int id; - int number; - struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES]; -}; - -struct wccp_assign_bucket_t { - int type; - int id; - int number; -}; - -static int theInWccpConnection = -1; -static int theOutWccpConnection = -1; -static struct wccp_here_i_am_t wccp_here_i_am; -static struct wccp_i_see_you_t wccp_i_see_you; -static int change; -static int number_caches; -static struct in_addr local_ip; - -static PF wccpHandleUdp; -static int wccpLowestIP(void); -static EVH wccpHereIam; -static void wccpAssignBuckets(void); - -/* - * The functions used during startup: - * wccpInit - * wccpConnectionOpen - * wccpConnectionShutdown - * wccpConnectionClose - */ - -void -wccpInit(void) -{ - debug(80, 5) ("wccpInit: Called\n"); - memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am)); - wccp_here_i_am.type = htonl(WCCP_HERE_I_AM); - wccp_here_i_am.version = htonl(Config.Wccp.version); - wccp_here_i_am.revision = htonl(WCCP_REVISION); - change = 0; - if (Config.Wccp.router.s_addr != any_addr.s_addr) - if (!eventFind(wccpHereIam, NULL)) - eventAdd("wccpHereIam", wccpHereIam, NULL, 5.0, 1); -} - -void -wccpConnectionOpen(void) -{ - u_short port = WCCP_PORT; - struct sockaddr_in router, local; - int local_len, router_len; - debug(80, 5) ("wccpConnectionOpen: Called\n"); - if (Config.Wccp.router.s_addr == any_addr.s_addr) { - debug(1, 1) ("WCCP Disabled.\n"); - return; - } - theInWccpConnection = comm_open(SOCK_DGRAM, - 0, - Config.Wccp.incoming, - port, - COMM_NONBLOCKING, - "WCCP Socket"); - if (theInWccpConnection < 0) - fatal("Cannot open WCCP Port"); - commSetSelect(theInWccpConnection, - COMM_SELECT_READ, - wccpHandleUdp, - NULL, - 0); - debug(1, 1) ("Accepting WCCP messages on port %d, FD %d.\n", - (int) port, theInWccpConnection); - if (Config.Wccp.outgoing.s_addr != no_addr.s_addr) { - theOutWccpConnection = comm_open(SOCK_DGRAM, - 0, - Config.Wccp.outgoing, - port, - COMM_NONBLOCKING, - "WCCP Socket"); - if (theOutWccpConnection < 0) - fatal("Cannot open Outgoing WCCP Port"); - commSetSelect(theOutWccpConnection, - COMM_SELECT_READ, - wccpHandleUdp, - NULL, 0); - debug(1, 1) ("Outgoing WCCP messages on port %d, FD %d.\n", - (int) port, theOutWccpConnection); - fd_note(theOutWccpConnection, "Outgoing WCCP socket"); - fd_note(theInWccpConnection, "Incoming WCCP socket"); - } else { - theOutWccpConnection = theInWccpConnection; - } - router_len = sizeof(router); - memset(&router, '\0', router_len); - router.sin_family = AF_INET; - router.sin_port = htons(port); - router.sin_addr = Config.Wccp.router; - if (connect(theOutWccpConnection, (struct sockaddr *) &router, router_len)) - fatal("Unable to connect WCCP out socket"); - local_len = sizeof(local); - memset(&local, '\0', local_len); - if (getsockname(theOutWccpConnection, (struct sockaddr *) &local, &local_len)) - fatal("Unable to getsockname on WCCP out socket"); - local_ip.s_addr = local.sin_addr.s_addr; -} - -void -wccpConnectionShutdown(void) -{ - if (theInWccpConnection < 0) - return; - if (theInWccpConnection != theOutWccpConnection) { - debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccpConnection); - comm_close(theInWccpConnection); - theInWccpConnection = -1; - } - assert(theOutWccpConnection > -1); - commSetSelect(theOutWccpConnection, COMM_SELECT_READ, NULL, NULL, 0); -} - -void -wccpConnectionClose(void) -{ - wccpConnectionShutdown(); - if (theOutWccpConnection > -1) { - debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccpConnection); - comm_close(theOutWccpConnection); - } -} - -/* - * Functions for handling the requests. - */ - -/* - * Accept the UDP packet - */ -static void -wccpHandleUdp(int sock, void *not_used) -{ - struct sockaddr_in from; - socklen_t from_len; - int len; - - debug(80, 6) ("wccpHandleUdp: Called.\n"); - - commSetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, NULL, 0); - from_len = sizeof(struct sockaddr_in); - memset(&from, '\0', from_len); - memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you)); - - statCounter.syscalls.sock.recvfroms++; - - len = recvfrom(sock, - (void *) &wccp_i_see_you, - WCCP_RESPONSE_SIZE, - 0, - (struct sockaddr *) &from, - &from_len); - debug(80, 3) ("wccpHandleUdp: %d bytes WCCP pkt from %s: type=%u, version=%u, change=%u, id=%u, number=%u\n", - len, - inet_ntoa(from.sin_addr), - (unsigned) ntohl(wccp_i_see_you.type), - (unsigned) ntohl(wccp_i_see_you.version), - (unsigned) ntohl(wccp_i_see_you.change), - (unsigned) ntohl(wccp_i_see_you.id), - (unsigned) ntohl(wccp_i_see_you.number)); - if (len < 0) - return; - if (Config.Wccp.router.s_addr != from.sin_addr.s_addr) - return; - if (ntohl(wccp_i_see_you.version) != Config.Wccp.version) - return; - if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU) - return; - if ((!change) && (number_caches == ntohl(wccp_i_see_you.number))) { - change = wccp_i_see_you.change; - return; - } - if (change != wccp_i_see_you.change) { - change = wccp_i_see_you.change; - if (wccpLowestIP() && wccp_i_see_you.number) - wccpAssignBuckets(); - } -} - -static int -wccpLowestIP(void) -{ - int loop; - for (loop = 0; loop < ntohl(wccp_i_see_you.number); loop++) { - if (wccp_i_see_you.wccp_cache_entry[loop].ip_addr.s_addr < local_ip.s_addr) - return 0; - } - return 1; -} - -static void -wccpHereIam(void *voidnotused) -{ - debug(80, 6) ("wccpHereIam: Called\n"); - - wccp_here_i_am.id = wccp_i_see_you.id; - send(theOutWccpConnection, - &wccp_here_i_am, - sizeof(wccp_here_i_am), - 0); - - if (!eventFind(wccpHereIam, NULL)) - eventAdd("wccpHereIam", wccpHereIam, NULL, 10.0, 1); -} - -static void -wccpAssignBuckets(void) -{ - struct wccp_assign_bucket_t *wccp_assign_bucket; - int wab_len; - char *buckets; - int buckets_per_cache; - int loop; - int bucket = 0; - int *caches; - int cache_len; - char *buf; - - debug(80, 6) ("wccpAssignBuckets: Called\n"); - number_caches = ntohl(wccp_i_see_you.number); - if (number_caches > WCCP_ACTIVE_CACHES) - number_caches = WCCP_ACTIVE_CACHES; - wab_len = sizeof(struct wccp_assign_bucket_t); - cache_len = WCCP_CACHE_LEN * number_caches; - - buf = xmalloc(wab_len + - WCCP_BUCKETS + - cache_len); - wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf; - caches = (int *) (buf + wab_len); - buckets = buf + wab_len + cache_len; - - memset(wccp_assign_bucket, '\0', sizeof(wccp_assign_bucket)); - memset(buckets, 0xFF, WCCP_BUCKETS); - - buckets_per_cache = WCCP_BUCKETS / number_caches; - for (loop = 0; loop < number_caches; loop++) { - int i; - xmemcpy(&caches[loop], - &wccp_i_see_you.wccp_cache_entry[loop].ip_addr.s_addr, - sizeof(*caches)); - for (i = 0; i < buckets_per_cache; i++) { - assert(bucket < WCCP_BUCKETS); - buckets[bucket++] = loop; - } - } - while (bucket < WCCP_BUCKETS) { - buckets[bucket++] = number_caches - 1; - } - wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET); - wccp_assign_bucket->id = wccp_i_see_you.id; - wccp_assign_bucket->number = wccp_i_see_you.number; - - send(theOutWccpConnection, - buf, - wab_len + WCCP_BUCKETS + cache_len, - 0); - change = 0; - xfree(buf); -} - -#endif /* USE_WCCP */ --- /dev/null Wed Feb 14 01:07:22 2007 +++ squid/src/wccp.cc Wed Feb 14 01:07:38 2007 @@ -0,0 +1,339 @@ + +/* + * $Id: wccp.cc,v 1.1.2.1 2002/10/09 05:55:50 rbcollins Exp $ + * + * DEBUG: section 80 WCCP Support + * AUTHOR: Glenn Chisholm + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ +#include "squid.h" + +#if USE_WCCP + +#define WCCP_PORT 2048 +#define WCCP_REVISION 0 +#define WCCP_RESPONSE_SIZE 12448 +#define WCCP_ACTIVE_CACHES 32 +#define WCCP_HASH_SIZE 32 +#define WCCP_BUCKETS 256 +#define WCCP_CACHE_LEN 4 + +#define WCCP_HERE_I_AM 7 +#define WCCP_I_SEE_YOU 8 +#define WCCP_ASSIGN_BUCKET 9 + +struct wccp_here_i_am_t { + int type; + int version; + int revision; + char hash[WCCP_HASH_SIZE]; + int reserved; + int id; +}; + +struct wccp_cache_entry_t { + struct in_addr ip_addr; + int revision; + char hash[WCCP_HASH_SIZE]; + int reserved; +}; + +struct wccp_i_see_you_t { + int type; + int version; + int change; + int id; + int number; + struct wccp_cache_entry_t wccp_cache_entry[WCCP_ACTIVE_CACHES]; +}; + +struct wccp_assign_bucket_t { + int type; + int id; + int number; +}; + +static int theInWccpConnection = -1; +static int theOutWccpConnection = -1; +static struct wccp_here_i_am_t wccp_here_i_am; +static struct wccp_i_see_you_t wccp_i_see_you; +static int change; +static int number_caches; +static struct in_addr local_ip; + +static PF wccpHandleUdp; +static int wccpLowestIP(void); +static EVH wccpHereIam; +static void wccpAssignBuckets(void); + +/* + * The functions used during startup: + * wccpInit + * wccpConnectionOpen + * wccpConnectionShutdown + * wccpConnectionClose + */ + +void +wccpInit(void) +{ + debug(80, 5) ("wccpInit: Called\n"); + memset(&wccp_here_i_am, '\0', sizeof(wccp_here_i_am)); + wccp_here_i_am.type = htonl(WCCP_HERE_I_AM); + wccp_here_i_am.version = htonl(Config.Wccp.version); + wccp_here_i_am.revision = htonl(WCCP_REVISION); + change = 0; + if (Config.Wccp.router.s_addr != any_addr.s_addr) + if (!eventFind(wccpHereIam, NULL)) + eventAdd("wccpHereIam", wccpHereIam, NULL, 5.0, 1); +} + +void +wccpConnectionOpen(void) +{ + u_short port = WCCP_PORT; + struct sockaddr_in router, local; + int local_len, router_len; + debug(80, 5) ("wccpConnectionOpen: Called\n"); + if (Config.Wccp.router.s_addr == any_addr.s_addr) { + debug(1, 1) ("WCCP Disabled.\n"); + return; + } + theInWccpConnection = comm_open(SOCK_DGRAM, + 0, + Config.Wccp.incoming, + port, + COMM_NONBLOCKING, + "WCCP Socket"); + if (theInWccpConnection < 0) + fatal("Cannot open WCCP Port"); + commSetSelect(theInWccpConnection, + COMM_SELECT_READ, + wccpHandleUdp, + NULL, + 0); + debug(1, 1) ("Accepting WCCP messages on port %d, FD %d.\n", + (int) port, theInWccpConnection); + if (Config.Wccp.outgoing.s_addr != no_addr.s_addr) { + theOutWccpConnection = comm_open(SOCK_DGRAM, + 0, + Config.Wccp.outgoing, + port, + COMM_NONBLOCKING, + "WCCP Socket"); + if (theOutWccpConnection < 0) + fatal("Cannot open Outgoing WCCP Port"); + commSetSelect(theOutWccpConnection, + COMM_SELECT_READ, + wccpHandleUdp, + NULL, 0); + debug(1, 1) ("Outgoing WCCP messages on port %d, FD %d.\n", + (int) port, theOutWccpConnection); + fd_note(theOutWccpConnection, "Outgoing WCCP socket"); + fd_note(theInWccpConnection, "Incoming WCCP socket"); + } else { + theOutWccpConnection = theInWccpConnection; + } + router_len = sizeof(router); + memset(&router, '\0', router_len); + router.sin_family = AF_INET; + router.sin_port = htons(port); + router.sin_addr = Config.Wccp.router; + if (connect(theOutWccpConnection, (struct sockaddr *) &router, router_len)) + fatal("Unable to connect WCCP out socket"); + local_len = sizeof(local); + memset(&local, '\0', local_len); + if (getsockname(theOutWccpConnection, (struct sockaddr *) &local, &local_len)) + fatal("Unable to getsockname on WCCP out socket"); + local_ip.s_addr = local.sin_addr.s_addr; +} + +void +wccpConnectionShutdown(void) +{ + if (theInWccpConnection < 0) + return; + if (theInWccpConnection != theOutWccpConnection) { + debug(80, 1) ("FD %d Closing WCCP socket\n", theInWccpConnection); + comm_close(theInWccpConnection); + theInWccpConnection = -1; + } + assert(theOutWccpConnection > -1); + commSetSelect(theOutWccpConnection, COMM_SELECT_READ, NULL, NULL, 0); +} + +void +wccpConnectionClose(void) +{ + wccpConnectionShutdown(); + if (theOutWccpConnection > -1) { + debug(80, 1) ("FD %d Closing WCCP socket\n", theOutWccpConnection); + comm_close(theOutWccpConnection); + } +} + +/* + * Functions for handling the requests. + */ + +/* + * Accept the UDP packet + */ +static void +wccpHandleUdp(int sock, void *not_used) +{ + struct sockaddr_in from; + socklen_t from_len; + int len; + + debug(80, 6) ("wccpHandleUdp: Called.\n"); + + commSetSelect(sock, COMM_SELECT_READ, wccpHandleUdp, NULL, 0); + from_len = sizeof(struct sockaddr_in); + memset(&from, '\0', from_len); + memset(&wccp_i_see_you, '\0', sizeof(wccp_i_see_you)); + + statCounter.syscalls.sock.recvfroms++; + + len = recvfrom(sock, + (void *) &wccp_i_see_you, + WCCP_RESPONSE_SIZE, + 0, + (struct sockaddr *) &from, + &from_len); + debug(80, 3) ("wccpHandleUdp: %d bytes WCCP pkt from %s: type=%u, version=%u, change=%u, id=%u, number=%u\n", + len, + inet_ntoa(from.sin_addr), + (unsigned) ntohl(wccp_i_see_you.type), + (unsigned) ntohl(wccp_i_see_you.version), + (unsigned) ntohl(wccp_i_see_you.change), + (unsigned) ntohl(wccp_i_see_you.id), + (unsigned) ntohl(wccp_i_see_you.number)); + if (len < 0) + return; + if (Config.Wccp.router.s_addr != from.sin_addr.s_addr) + return; + if (ntohl(wccp_i_see_you.version) != Config.Wccp.version) + return; + if (ntohl(wccp_i_see_you.type) != WCCP_I_SEE_YOU) + return; + if ((!change) && (number_caches == ntohl(wccp_i_see_you.number))) { + change = wccp_i_see_you.change; + return; + } + if (change != wccp_i_see_you.change) { + change = wccp_i_see_you.change; + if (wccpLowestIP() && wccp_i_see_you.number) + wccpAssignBuckets(); + } +} + +static int +wccpLowestIP(void) +{ + int loop; + for (loop = 0; loop < ntohl(wccp_i_see_you.number); loop++) { + if (wccp_i_see_you.wccp_cache_entry[loop].ip_addr.s_addr < local_ip.s_addr) + return 0; + } + return 1; +} + +static void +wccpHereIam(void *voidnotused) +{ + debug(80, 6) ("wccpHereIam: Called\n"); + + wccp_here_i_am.id = wccp_i_see_you.id; + send(theOutWccpConnection, + &wccp_here_i_am, + sizeof(wccp_here_i_am), + 0); + + if (!eventFind(wccpHereIam, NULL)) + eventAdd("wccpHereIam", wccpHereIam, NULL, 10.0, 1); +} + +static void +wccpAssignBuckets(void) +{ + struct wccp_assign_bucket_t *wccp_assign_bucket; + int wab_len; + char *buckets; + int buckets_per_cache; + int loop; + int bucket = 0; + int *caches; + int cache_len; + char *buf; + + debug(80, 6) ("wccpAssignBuckets: Called\n"); + number_caches = ntohl(wccp_i_see_you.number); + if (number_caches > WCCP_ACTIVE_CACHES) + number_caches = WCCP_ACTIVE_CACHES; + wab_len = sizeof(struct wccp_assign_bucket_t); + cache_len = WCCP_CACHE_LEN * number_caches; + + buf = xmalloc(wab_len + + WCCP_BUCKETS + + cache_len); + wccp_assign_bucket = (struct wccp_assign_bucket_t *) buf; + caches = (int *) (buf + wab_len); + buckets = buf + wab_len + cache_len; + + memset(wccp_assign_bucket, '\0', sizeof(wccp_assign_bucket)); + memset(buckets, 0xFF, WCCP_BUCKETS); + + buckets_per_cache = WCCP_BUCKETS / number_caches; + for (loop = 0; loop < number_caches; loop++) { + int i; + xmemcpy(&caches[loop], + &wccp_i_see_you.wccp_cache_entry[loop].ip_addr.s_addr, + sizeof(*caches)); + for (i = 0; i < buckets_per_cache; i++) { + assert(bucket < WCCP_BUCKETS); + buckets[bucket++] = loop; + } + } + while (bucket < WCCP_BUCKETS) { + buckets[bucket++] = number_caches - 1; + } + wccp_assign_bucket->type = htonl(WCCP_ASSIGN_BUCKET); + wccp_assign_bucket->id = wccp_i_see_you.id; + wccp_assign_bucket->number = wccp_i_see_you.number; + + send(theOutWccpConnection, + buf, + wab_len + WCCP_BUCKETS + cache_len, + 0); + change = 0; + xfree(buf); +} + +#endif /* USE_WCCP */