]> git.ipfire.org Git - thirdparty/squid.git/blame - src/net_db.cc
2.1 branch merge
[thirdparty/squid.git] / src / net_db.cc
CommitLineData
87801fcb 1
516350ca 2/*
c68e9c6b 3 * $Id: net_db.cc,v 1.132 1998/11/12 06:28:17 wessels Exp $
516350ca 4 *
67f46679 5 * DEBUG: section 38 Network Measurement Database
516350ca 6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 9 * ----------------------------------------------------------
516350ca 10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
e25c139f 13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
516350ca 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
516350ca 34 */
35
87801fcb 36#include "squid.h"
37
a97cfa48 38#if USE_ICMP
8b833697 39
9ad1cbca 40typedef struct {
a74c5601 41 peer *p;
42 StoreEntry *e;
43 request_t *r;
44 off_t seen;
45 off_t used;
46 size_t buf_sz;
47 char *buf;
9ad1cbca 48} netdbExchangeState;
49
ce75f381 50static hash_table *addr_table = NULL;
365e5b34 51static hash_table *host_table = NULL;
87801fcb 52
f5b8bbc4 53static struct in_addr networkFromInaddr(struct in_addr a);
54static void netdbRelease(netdbEntry * n);
f5b8bbc4 55static void netdbHashInsert(netdbEntry * n, struct in_addr addr);
56static void netdbHashDelete(const char *key);
587d8445 57static void netdbHostInsert(netdbEntry * n, const char *hostname);
58static void netdbHostDelete(const net_db_name * x);
f5b8bbc4 59static void netdbPurgeLRU(void);
23d92c64 60static netdbEntry *netdbLookupHost(const char *key);
f5b8bbc4 61static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
62static net_db_peer *netdbPeerAdd(netdbEntry * n, peer * e);
63static char *netdbPeerName(const char *name);
b69f7771 64static IPH netdbSendPing;
79d39a72 65static QS sortPeerByRtt;
66static QS sortByRtt;
67static QS netdbLRU;
ec878047 68static FREE netdbFreeNameEntry;
69static FREE netdbFreeNetdbEntry;
9ad1cbca 70static STCB netdbExchangeHandleReply;
45c54bdc 71static void netdbExchangeDone(void *);
429fdbec 72
73/* We have to keep a local list of peer names. The Peers structure
74 * gets freed during a reconfigure. We want this database to
75 * remain persisitent, so _net_db_peer->peername points into this
76 * linked list */
77static wordlist *peer_names = NULL;
67508012 78
87801fcb 79static void
67508012 80netdbHashInsert(netdbEntry * n, struct in_addr addr)
87801fcb 81{
f2052513 82 xstrncpy(n->network, inet_ntoa(networkFromInaddr(addr)), 16);
67508012 83 n->key = n->network;
587d8445 84 assert(hash_lookup(addr_table, n->network) == NULL);
67508012 85 hash_join(addr_table, (hash_link *) n);
87801fcb 86}
87
88static void
0ee4272b 89netdbHashDelete(const char *key)
87801fcb 90{
67508012 91 hash_link *hptr = hash_lookup(addr_table, key);
87801fcb 92 if (hptr == NULL) {
93 debug_trap("netdbHashDelete: key not found");
94 return;
95 }
e0b1c6aa 96 hash_remove_link(addr_table, hptr);
67508012 97}
98
99static void
587d8445 100netdbHostInsert(netdbEntry * n, const char *hostname)
67508012 101{
7021844c 102 net_db_name *x = memAllocate(MEM_NET_DB_NAME);
67508012 103 x->name = xstrdup(hostname);
104 x->next = n->hosts;
105 n->hosts = x;
587d8445 106 x->net_db_entry = n;
107 assert(hash_lookup(host_table, hostname) == NULL);
108 hash_join(host_table, (hash_link *) x);
67508012 109 n->link_count++;
110}
111
112static void
587d8445 113netdbHostDelete(const net_db_name * x)
67508012 114{
115 netdbEntry *n;
b19361fd 116 net_db_name **X;
b19361fd 117 assert(x != NULL);
587d8445 118 assert(x->net_db_entry != NULL);
119 n = x->net_db_entry;
b19361fd 120 n->link_count--;
587d8445 121 for (X = &n->hosts; *X; X = &(*X)->next) {
122 if (*X == x) {
123 *X = x->next;
124 break;
125 }
126 }
127 hash_remove_link(host_table, (hash_link *) x);
128 xfree(x->name);
129 memFree(MEM_NET_DB_NAME, (void *) x);
87801fcb 130}
131
132static netdbEntry *
0ee4272b 133netdbLookupHost(const char *key)
87801fcb 134{
587d8445 135 net_db_name *x = (net_db_name *) hash_lookup(host_table, key);
136 return x ? x->net_db_entry : NULL;
87801fcb 137}
138
c907000b 139static void
140netdbRelease(netdbEntry * n)
141{
429fdbec 142 net_db_name *x;
587d8445 143 net_db_name *next;
c907000b 144 for (x = n->hosts; x; x = next) {
145 next = x->next;
587d8445 146 netdbHostDelete(x);
c907000b 147 }
148 n->hosts = NULL;
429fdbec 149 safe_free(n->peers);
429fdbec 150 n->peers = NULL;
151 n->n_peers = 0;
152 n->n_peers_alloc = 0;
c907000b 153 if (n->link_count == 0) {
154 netdbHashDelete(n->network);
587d8445 155 memFree(MEM_NETDBENTRY, n);
c907000b 156 }
157}
158
159static int
79d39a72 160netdbLRU(const void *A, const void *B)
c907000b 161{
79d39a72 162 const netdbEntry *const *n1 = A;
163 const netdbEntry *const *n2 = B;
c907000b 164 if ((*n1)->last_use_time > (*n2)->last_use_time)
165 return (1);
166 if ((*n1)->last_use_time < (*n2)->last_use_time)
167 return (-1);
168 return (0);
169}
170
c907000b 171static void
172netdbPurgeLRU(void)
173{
174 netdbEntry *n;
175 netdbEntry **list;
176 int k = 0;
177 int list_count = 0;
178 int removed = 0;
587d8445 179 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
0f6bebac 180 hash_first(addr_table);
181 while ((n = (netdbEntry *) hash_next(addr_table))) {
587d8445 182 assert(list_count < memInUse(MEM_NETDBENTRY));
c907000b 183 *(list + list_count) = n;
184 list_count++;
c907000b 185 }
186 qsort((char *) list,
187 list_count,
188 sizeof(netdbEntry *),
79d39a72 189 netdbLRU);
c907000b 190 for (k = 0; k < list_count; k++) {
587d8445 191 if (memInUse(MEM_NETDBENTRY) < Config.Netdb.low)
c907000b 192 break;
193 netdbRelease(*(list + k));
194 removed++;
195 }
196 xfree(list);
197}
198
67508012 199static netdbEntry *
200netdbLookupAddr(struct in_addr addr)
87801fcb 201{
587d8445 202 netdbEntry *n;
67508012 203 char *key = inet_ntoa(networkFromInaddr(addr));
587d8445 204 n = (netdbEntry *) hash_lookup(addr_table, key);
205 return n;
87801fcb 206}
207
67508012 208static netdbEntry *
587d8445 209netdbAdd(struct in_addr addr)
87801fcb 210{
211 netdbEntry *n;
587d8445 212 if (memInUse(MEM_NETDBENTRY) > Config.Netdb.high)
c907000b 213 netdbPurgeLRU();
67508012 214 if ((n = netdbLookupAddr(addr)) == NULL) {
7021844c 215 n = memAllocate(MEM_NETDBENTRY);
67508012 216 netdbHashInsert(n, addr);
87801fcb 217 }
67508012 218 return n;
87801fcb 219}
220
221static void
82691c78 222netdbSendPing(const ipcache_addrs * ia, void *data)
87801fcb 223{
67508012 224 struct in_addr addr;
225 char *hostname = data;
87801fcb 226 netdbEntry *n;
b19361fd 227 netdbEntry *na;
587d8445 228 net_db_name *x;
229 net_db_name **X;
0383c140 230 cbdataUnlock(hostname);
e5f6c5c2 231 if (ia == NULL) {
0383c140 232 cbdataFree(hostname);
87801fcb 233 return;
cb190ed7 234 }
e5f6c5c2 235 addr = ia->in_addrs[ia->cur];
b19361fd 236 if ((n = netdbLookupHost(hostname)) == NULL) {
587d8445 237 n = netdbAdd(addr);
238 netdbHostInsert(n, hostname);
b19361fd 239 } else if ((na = netdbLookupAddr(addr)) != n) {
240 /*
241 *hostname moved from 'network n' to 'network na'!
242 */
243 if (na == NULL)
587d8445 244 na = netdbAdd(addr);
67f46679 245 debug(38, 3) ("netdbSendPing: %s moved from %s to %s\n",
b19361fd 246 hostname, n->network, na->network);
587d8445 247 x = (net_db_name *) hash_lookup(host_table, hostname);
248 if (x == NULL) {
67f46679 249 debug(38, 1) ("netdbSendPing: net_db_name list bug: %s not found", hostname);
587d8445 250 cbdataFree(hostname);
251 return;
252 }
253 /* remove net_db_name from 'network n' linked list */
254 for (X = &n->hosts; *X; X = &(*X)->next) {
255 if (*X == x) {
256 *X = x->next;
257 break;
258 }
259 }
260 n->link_count--;
261 /* point to 'network na' from host entry */
262 x->net_db_entry = na;
263 /* link net_db_name to 'network na' */
264 x->next = na->hosts;
265 na->hosts = x;
266 na->link_count++;
b19361fd 267 n = na;
268 }
67f46679 269 debug(38, 3) ("netdbSendPing: pinging %s\n", hostname);
67508012 270 icmpDomainPing(addr, hostname);
4d311579 271 n->pings_sent++;
429fdbec 272 n->next_ping_time = squid_curtime + Config.Netdb.period;
c907000b 273 n->last_use_time = squid_curtime;
0383c140 274 cbdataFree(hostname);
67508012 275}
276
67508012 277static struct in_addr
278networkFromInaddr(struct in_addr a)
279{
a9778d11 280 struct in_addr b;
281 b.s_addr = ntohl(a.s_addr);
429fdbec 282#if USE_CLASSFUL
67508012 283 if (IN_CLASSC(b.s_addr))
a9778d11 284 b.s_addr &= IN_CLASSC_NET;
67508012 285 else if (IN_CLASSB(b.s_addr))
a9778d11 286 b.s_addr &= IN_CLASSB_NET;
67508012 287 else if (IN_CLASSA(b.s_addr))
a9778d11 288 b.s_addr &= IN_CLASSA_NET;
429fdbec 289#else
290 /* use /24 for everything */
291 b.s_addr &= IN_CLASSC_NET;
292#endif
a9778d11 293 b.s_addr = htonl(b.s_addr);
67508012 294 return b;
295}
296
297static int
79d39a72 298sortByRtt(const void *A, const void *B)
67508012 299{
79d39a72 300 const netdbEntry *const *n1 = A;
301 const netdbEntry *const *n2 = B;
429fdbec 302 if ((*n1)->rtt > (*n2)->rtt)
67508012 303 return 1;
429fdbec 304 else if ((*n1)->rtt < (*n2)->rtt)
67508012 305 return -1;
306 else
307 return 0;
308}
309
429fdbec 310static net_db_peer *
311netdbPeerByName(const netdbEntry * n, const char *peername)
312{
313 int i;
314 net_db_peer *p = n->peers;
315 for (i = 0; i < n->n_peers; i++, p++) {
316 if (!strcmp(p->peername, peername))
317 return p;
318 }
319 return NULL;
320}
321
322static net_db_peer *
323netdbPeerAdd(netdbEntry * n, peer * e)
324{
325 net_db_peer *p;
326 net_db_peer *o;
327 int osize;
328 int i;
329 if (n->n_peers == n->n_peers_alloc) {
330 o = n->peers;
331 osize = n->n_peers_alloc;
332 if (n->n_peers_alloc == 0)
333 n->n_peers_alloc = 2;
334 else
335 n->n_peers_alloc <<= 1;
67f46679 336 debug(38, 3) ("netdbPeerAdd: Growing peer list for '%s' to %d\n",
429fdbec 337 n->network, n->n_peers_alloc);
338 n->peers = xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
429fdbec 339 for (i = 0; i < osize; i++)
340 *(n->peers + i) = *(o + i);
341 if (osize) {
342 safe_free(o);
429fdbec 343 }
344 }
345 p = n->peers + n->n_peers;
346 p->peername = netdbPeerName(e->host);
347 n->n_peers++;
348 return p;
349}
350
351static int
79d39a72 352sortPeerByRtt(const void *A, const void *B)
429fdbec 353{
79d39a72 354 const net_db_peer *p1 = A;
355 const net_db_peer *p2 = B;
429fdbec 356 if (p1->rtt > p2->rtt)
357 return 1;
358 else if (p1->rtt < p2->rtt)
359 return -1;
360 else
361 return 0;
362}
363
364static void
365netdbSaveState(void *foo)
366{
367 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
368 FILE *fp;
369 netdbEntry *n;
429fdbec 370 net_db_name *x;
371 struct timeval start = current_time;
372 int count = 0;
042461c3 373 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
429fdbec 374 fp = fopen(path, "w");
375 if (fp == NULL) {
a3d5953d 376 debug(50, 1) ("netdbSaveState: %s: %s\n", path, xstrerror());
429fdbec 377 return;
378 }
0f6bebac 379 hash_first(addr_table);
380 while ((n = (netdbEntry *) hash_next(addr_table))) {
429fdbec 381 if (n->pings_recv == 0)
382 continue;
383 fprintf(fp, "%s %d %d %10.5f %10.5f %d %d",
384 n->network,
385 n->pings_sent,
386 n->pings_recv,
387 n->hops,
388 n->rtt,
389 (int) n->next_ping_time,
390 (int) n->last_use_time);
391 for (x = n->hosts; x; x = x->next)
392 fprintf(fp, " %s", x->name);
393 fprintf(fp, "\n");
394 count++;
395 }
396 fclose(fp);
587d8445 397 getCurrentTime();
67f46679 398 debug(38, 1) ("NETDB state saved; %d entries, %d msec\n",
429fdbec 399 count, tvSubMsec(start, current_time));
52040193 400 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
429fdbec 401}
402
403static void
404netdbReloadState(void)
405{
406 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
1b4056ed 407 char *buf;
429fdbec 408 char *t;
409 FILE *fp;
410 netdbEntry *n;
411 netdbEntry N;
412 struct in_addr addr;
413 int count = 0;
414 struct timeval start = current_time;
042461c3 415 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
429fdbec 416 fp = fopen(path, "r");
417 if (fp == NULL)
418 return;
1b4056ed 419 buf = memAllocate(MEM_4K_BUF);
429fdbec 420 while (fgets(buf, 4095, fp)) {
421 memset(&N, '\0', sizeof(netdbEntry));
422 if ((t = strtok(buf, w_space)) == NULL)
423 continue;
424 if (!safe_inet_addr(t, &addr))
425 continue;
587d8445 426 if (netdbLookupAddr(addr) != NULL) /* no dups! */
427 continue;
429fdbec 428 if ((t = strtok(NULL, w_space)) == NULL)
429 continue;
430 N.pings_sent = atoi(t);
431 if ((t = strtok(NULL, w_space)) == NULL)
432 continue;
433 N.pings_recv = atoi(t);
434 if (N.pings_recv == 0)
435 continue;
436 /* give this measurement low weight */
437 N.pings_sent = 1;
438 N.pings_recv = 1;
439 if ((t = strtok(NULL, w_space)) == NULL)
440 continue;
441 N.hops = atof(t);
442 if ((t = strtok(NULL, w_space)) == NULL)
443 continue;
444 N.rtt = atof(t);
445 if ((t = strtok(NULL, w_space)) == NULL)
446 continue;
447 N.next_ping_time = (time_t) atoi(t);
448 if ((t = strtok(NULL, w_space)) == NULL)
449 continue;
450 N.last_use_time = (time_t) atoi(t);
7021844c 451 n = memAllocate(MEM_NETDBENTRY);
d6d7104a 452 xmemcpy(n, &N, sizeof(netdbEntry));
429fdbec 453 netdbHashInsert(n, addr);
587d8445 454 while ((t = strtok(NULL, w_space)) != NULL) {
455 if (netdbLookupHost(t) != NULL) /* no dups! */
456 continue;
457 netdbHostInsert(n, t);
458 }
429fdbec 459 count++;
460 }
3f6c0fb2 461 memFree(MEM_4K_BUF, buf);
429fdbec 462 fclose(fp);
587d8445 463 getCurrentTime();
67f46679 464 debug(38, 1) ("NETDB state reloaded; %d entries, %d msec\n",
429fdbec 465 count, tvSubMsec(start, current_time));
466}
467
468static char *
469netdbPeerName(const char *name)
470{
471 wordlist *w;
472 for (w = peer_names; w; w = w->next) {
473 if (!strcmp(w->key, name))
474 return w->key;
475 }
c68e9c6b 476 w = wordlistAdd(&peer_names, name);
429fdbec 477 return w->key;
478}
479
45c54bdc 480static void
481netdbFreeNetdbEntry(void *data)
482{
483 netdbEntry *n = data;
484 safe_free(n->peers);
485 memFree(MEM_NETDBENTRY, n);
486}
429fdbec 487
45c54bdc 488static void
489netdbFreeNameEntry(void *data)
490{
491 net_db_name *x = data;
492 xfree(x->name);
493 memFree(MEM_NET_DB_NAME, x);
494}
495
496static void
497netdbExchangeHandleReply(void *data, char *buf, ssize_t size)
498{
499 netdbExchangeState *ex = data;
500 int rec_sz = 0;
501 off_t o;
502 struct in_addr addr;
503 double rtt;
504 double hops;
505 char *p;
506 int j;
507 HttpReply *rep;
508 size_t hdr_sz;
70305391 509 int nused = 0;
45c54bdc 510 rec_sz = 0;
511 rec_sz += 1 + sizeof(addr.s_addr);
512 rec_sz += 1 + sizeof(int);
513 rec_sz += 1 + sizeof(int);
514 ex->seen = ex->used + size;
67f46679 515 debug(38, 3) ("netdbExchangeHandleReply: %d bytes\n", (int) size);
45c54bdc 516 if (!cbdataValid(ex->p)) {
67f46679 517 debug(38, 3) ("netdbExchangeHandleReply: Peer became invalid\n");
45c54bdc 518 netdbExchangeDone(ex);
519 return;
520 }
67f46679 521 debug(38, 3) ("netdbExchangeHandleReply: for '%s:%d'\n", ex->p->host, ex->p->http_port);
45c54bdc 522 p = buf;
523 if (0 == ex->used) {
524 /* skip reply headers */
525 if ((hdr_sz = headersEnd(p, size))) {
67f46679 526 debug(38, 5) ("netdbExchangeHandleReply: hdr_sz = %d\n", hdr_sz);
45c54bdc 527 rep = ex->e->mem_obj->reply;
528 if (0 == rep->sline.status)
529 httpReplyParse(rep, buf);
67f46679 530 debug(38, 3) ("netdbExchangeHandleReply: reply status %d\n",
70305391 531 rep->sline.status);
45c54bdc 532 if (HTTP_OK != rep->sline.status) {
533 netdbExchangeDone(ex);
534 return;
535 }
536 assert(size >= hdr_sz);
537 ex->used += hdr_sz;
538 size -= hdr_sz;
539 p += hdr_sz;
540 } else {
541 size = 0;
542 }
543 }
67f46679 544 debug(38, 5) ("netdbExchangeHandleReply: start parsing loop, size = %d\n",
70305391 545 size);
45c54bdc 546 while (size >= rec_sz) {
67f46679 547 debug(38, 5) ("netdbExchangeHandleReply: in parsing loop, size = %d\n",
70305391 548 size);
45c54bdc 549 addr.s_addr = any_addr.s_addr;
550 hops = rtt = 0.0;
551 for (o = 0; o < rec_sz;) {
552 switch ((int) *(p + o)) {
553 case NETDB_EX_NETWORK:
554 o++;
555 xmemcpy(&addr.s_addr, p + o, sizeof(addr.s_addr));
556 o += sizeof(addr.s_addr);
557 break;
558 case NETDB_EX_RTT:
559 o++;
560 xmemcpy(&j, p + o, sizeof(int));
561 o += sizeof(int);
562 rtt = (double) ntohl(j) / 1000.0;
563 break;
564 case NETDB_EX_HOPS:
565 o++;
566 xmemcpy(&j, p + o, sizeof(int));
567 o += sizeof(int);
568 hops = (double) ntohl(j) / 1000.0;
569 break;
570 }
571 }
572 if (addr.s_addr != any_addr.s_addr && rtt > 0)
573 netdbExchangeUpdatePeer(addr, ex->p, rtt, hops);
574 assert(o == rec_sz);
575 ex->used += rec_sz;
576 size -= rec_sz;
577 p += rec_sz;
009730dc 578 /*
579 * This is a fairly cpu-intensive loop, break after adding
580 * just a few
581 */
582 if (++nused == 20)
c43f5247 583 break;
45c54bdc 584 }
67f46679 585 debug(38, 3) ("netdbExchangeHandleReply: used %d entries, (x %d bytes) == %d bytes total\n",
70305391 586 nused, rec_sz, nused * rec_sz);
67f46679 587 debug(38, 3) ("netdbExchangeHandleReply: seen %d, used %d\n", ex->seen, ex->used);
ce9b54e8 588 if (ex->e->store_status == STORE_ABORTED) {
67f46679 589 debug(38, 3) ("netdbExchangeHandleReply: STORE_ABORTED\n");
ce9b54e8 590 netdbExchangeDone(ex);
591 } else if (ex->e->store_status == STORE_PENDING) {
67f46679 592 debug(38, 3) ("netdbExchangeHandleReply: STORE_PENDING\n");
45c54bdc 593 storeClientCopy(ex->e, ex->seen, ex->used, ex->buf_sz,
594 ex->buf, netdbExchangeHandleReply, ex);
70305391 595 } else if (ex->seen < ex->e->mem_obj->inmem_hi) {
67f46679 596 debug(38, 3) ("netdbExchangeHandleReply: ex->e->mem_obj->inmem_hi\n");
70305391 597 storeClientCopy(ex->e, ex->seen, ex->used, ex->buf_sz,
598 ex->buf, netdbExchangeHandleReply, ex);
599 } else {
67f46679 600 debug(38, 3) ("netdbExchangeHandleReply: Done\n");
70305391 601 netdbExchangeDone(ex);
45c54bdc 602 }
603}
604
605static void
606netdbExchangeDone(void *data)
607{
70305391 608 netdbExchangeState *ex = data;
67f46679 609 debug(38, 3) ("netdbExchangeDone: %s\n", storeUrl(ex->e));
45c54bdc 610 memFree(MEM_4K_BUF, ex->buf);
611 requestUnlink(ex->r);
612 storeUnregister(ex->e, ex);
613 storeUnlockObject(ex->e);
614 cbdataUnlock(ex->p);
615 cbdataFree(ex);
616}
429fdbec 617
e97f40f4 618#endif /* USE_ICMP */
619
620/* PUBLIC FUNCTIONS */
621
67508012 622void
e97f40f4 623netdbInit(void)
624{
625#if USE_ICMP
aa9e2cab 626 int n;
19054954 627 if (addr_table)
628 return;
aa9e2cab 629 n = hashPrime(Config.Netdb.high / 4);
630 addr_table = hash_create((HASHCMP *) strcmp, n, hash_string);
631 n = hashPrime(3 * Config.Netdb.high / 4);
632 host_table = hash_create((HASHCMP *) strcmp, n, hash_string);
52040193 633 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
429fdbec 634 netdbReloadState();
22f3fd98 635 cachemgrRegister("netdb",
636 "Network Measurement Database",
1da3b90b 637 netdbDump, 0, 1);
e97f40f4 638#endif
639}
640
641void
0ee4272b 642netdbPingSite(const char *hostname)
67508012 643{
e97f40f4 644#if USE_ICMP
67508012 645 netdbEntry *n;
0383c140 646 char *h;
e97f40f4 647 if ((n = netdbLookupHost(hostname)) != NULL)
648 if (n->next_ping_time > squid_curtime)
649 return;
0383c140 650 h = xstrdup(hostname);
3f6c0fb2 651 cbdataAdd(h, MEM_NONE);
0383c140 652 cbdataLock(h);
653 ipcache_nbgethostbyname(hostname, netdbSendPing, h);
e97f40f4 654#endif
67508012 655}
656
e97f40f4 657void
0ee4272b 658netdbHandlePingReply(const struct sockaddr_in *from, int hops, int rtt)
4d311579 659{
e97f40f4 660#if USE_ICMP
661 netdbEntry *n;
662 int N;
67f46679 663 debug(38, 3) ("netdbHandlePingReply: from %s\n", inet_ntoa(from->sin_addr));
e97f40f4 664 if ((n = netdbLookupAddr(from->sin_addr)) == NULL)
665 return;
429fdbec 666 N = ++n->pings_recv;
667 if (N > 5)
668 N = 5;
e97f40f4 669 n->hops = ((n->hops * (N - 1)) + hops) / N;
670 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
67f46679 671 debug(38, 3) ("netdbHandlePingReply: %s; rtt=%5.1f hops=%4.1f\n",
e97f40f4 672 n->network,
673 n->rtt,
674 n->hops);
675#endif
4d311579 676}
e5f6c5c2 677
678void
679netdbFreeMemory(void)
680{
e97f40f4 681#if USE_ICMP
ec878047 682 hashFreeItems(addr_table, netdbFreeNetdbEntry);
e5f6c5c2 683 hashFreeMemory(addr_table);
afe95a7e 684 addr_table = NULL;
ec878047 685 hashFreeItems(host_table, netdbFreeNameEntry);
686 hashFreeMemory(host_table);
afe95a7e 687 host_table = NULL;
429fdbec 688 wordlistDestroy(&peer_names);
689 peer_names = NULL;
e97f40f4 690#endif
e5f6c5c2 691}
692
e97f40f4 693int
694netdbHops(struct in_addr addr)
695{
696#if USE_ICMP
697 netdbEntry *n = netdbLookupAddr(addr);
698 if (n && n->pings_recv) {
699 n->last_use_time = squid_curtime;
700 return (int) (n->hops + 0.5);
701 }
702#endif
703 return 256;
704}
705
706void
707netdbDump(StoreEntry * sentry)
708{
709#if USE_ICMP
710 netdbEntry *n;
711 netdbEntry **list;
429fdbec 712 net_db_name *x;
e97f40f4 713 int k;
714 int i;
429fdbec 715 int j;
716 net_db_peer *p;
15576b6a 717 storeAppendPrintf(sentry, "Network DB Statistics:\n");
718 storeAppendPrintf(sentry, "%-16.16s %9s %7s %5s %s\n",
0ee4272b 719 "Network",
720 "recv/sent",
721 "RTT",
722 "Hops",
723 "Hostnames");
587d8445 724 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
0ee4272b 725 i = 0;
0f6bebac 726 hash_first(addr_table);
727 while ((n = (netdbEntry *) hash_next(addr_table)))
0ee4272b 728 *(list + i++) = n;
587d8445 729 if (i != memInUse(MEM_NETDBENTRY))
67f46679 730 debug(38, 0) ("WARNING: netdb_addrs count off, found %d, expected %d\n",
587d8445 731 i, memInUse(MEM_NETDBENTRY));
0ee4272b 732 qsort((char *) list,
733 i,
734 sizeof(netdbEntry *),
79d39a72 735 sortByRtt);
0ee4272b 736 for (k = 0; k < i; k++) {
737 n = *(list + k);
15576b6a 738 storeAppendPrintf(sentry, "%-16.16s %4d/%4d %7.1f %5.1f",
0ee4272b 739 n->network,
740 n->pings_recv,
741 n->pings_sent,
742 n->rtt,
743 n->hops);
744 for (x = n->hosts; x; x = x->next)
745 storeAppendPrintf(sentry, " %s", x->name);
1519582e 746 storeAppendPrintf(sentry, "\n");
429fdbec 747 p = n->peers;
748 for (j = 0; j < n->n_peers; j++, p++) {
15576b6a 749 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
429fdbec 750 p->peername,
751 p->rtt,
752 p->hops);
753 }
0ee4272b 754 }
0ee4272b 755 xfree(list);
c8391077 756#else
757 storeAppendPrintf(sentry,
758 "NETDB support not compiled into this Squid cache.\n");
e97f40f4 759#endif
760}
48f44632 761
762int
763netdbHostHops(const char *host)
764{
765#if USE_ICMP
766 netdbEntry *n = netdbLookupHost(host);
429fdbec 767 if (n) {
768 n->last_use_time = squid_curtime;
48f44632 769 return (int) (n->hops + 0.5);
429fdbec 770 }
48f44632 771#endif
429fdbec 772 return 0;
48f44632 773}
774
775int
776netdbHostRtt(const char *host)
777{
778#if USE_ICMP
779 netdbEntry *n = netdbLookupHost(host);
429fdbec 780 if (n) {
781 n->last_use_time = squid_curtime;
48f44632 782 return (int) (n->rtt + 0.5);
429fdbec 783 }
784#endif
785 return 0;
786}
787
1d6ae62d 788void
789netdbHostData(const char *host, int *samp, int *rtt, int *hops)
790{
791#if USE_ICMP
5942e8d4 792 netdbEntry *n = netdbLookupHost(host);
793 if (n == NULL)
794 return;
795 *samp = n->pings_recv;
796 *rtt = (int) (n->rtt + 0.5);
797 *hops = (int) (n->hops + 0.5);
1d6ae62d 798#endif
799}
800
ee48bff0 801int
9ef28b60 802netdbHostPeerRtt(const char *host, peer * p)
ee48bff0 803{
804#if USE_ICMP
e6acd170 805 const netdbEntry *n = netdbLookupHost(host);
806 if (n) {
9ef28b60 807 const net_db_peer *np = netdbPeerByName(n, p->host);
808 if (np && np->expires >= squid_curtime)
809 return (int) (np->rtt + 0.5);
ee48bff0 810 }
811#endif
812 return 0;
813}
814
429fdbec 815void
816netdbUpdatePeer(request_t * r, peer * e, int irtt, int ihops)
817{
818#if USE_ICMP
819 netdbEntry *n;
820 double rtt = (double) irtt;
821 double hops = (double) ihops;
822 net_db_peer *p;
67f46679 823 debug(38, 3) ("netdbUpdatePeer: '%s', %d hops, %d rtt\n", r->host, ihops, irtt);
429fdbec 824 n = netdbLookupHost(r->host);
825 if (n == NULL) {
67f46679 826 debug(38, 3) ("netdbUpdatePeer: host '%s' not found\n", r->host);
429fdbec 827 return;
828 }
829 if ((p = netdbPeerByName(n, e->host)) == NULL)
830 p = netdbPeerAdd(n, e);
831 p->rtt = rtt;
832 p->hops = hops;
833 p->expires = squid_curtime + 3600;
834 if (n->n_peers < 2)
835 return;
836 qsort((char *) n->peers,
837 n->n_peers,
838 sizeof(net_db_peer),
79d39a72 839 sortPeerByRtt);
48f44632 840#endif
48f44632 841}
ce75f381 842
27efd484 843void
844netdbExchangeUpdatePeer(struct in_addr addr, peer * e, double rtt, double hops)
845{
846#if USE_ICMP
847 netdbEntry *n;
848 net_db_peer *p;
67f46679 849 debug(38, 5) ("netdbExchangeUpdatePeer: '%s', %0.1f hops, %0.1f rtt\n",
27efd484 850 inet_ntoa(addr), hops, rtt);
851 n = netdbLookupAddr(addr);
852 if (n == NULL)
853 n = netdbAdd(addr);
854 assert(NULL != n);
855 if ((p = netdbPeerByName(n, e->host)) == NULL)
856 p = netdbPeerAdd(n, e);
857 p->rtt = rtt;
858 p->hops = hops;
859 p->expires = squid_curtime + 3600; /* XXX ? */
860 if (n->n_peers < 2)
861 return;
862 qsort((char *) n->peers,
863 n->n_peers,
864 sizeof(net_db_peer),
865 sortPeerByRtt);
866#endif
867}
868
587d8445 869void
870netdbDeleteAddrNetwork(struct in_addr addr)
871{
872#if USE_ICMP
873 netdbEntry *n = netdbLookupAddr(addr);
874 if (n == NULL)
875 return;
fd9f2966 876 debug(38, 3) ("netdbDeleteAddrNetwork: %s\n", n->network);
587d8445 877 netdbRelease(n);
878#endif
879}
de2a0782 880
881void
882netdbBinaryExchange(StoreEntry * s)
883{
de2a0782 884 http_reply *reply = s->mem_obj->reply;
9ad1cbca 885#if USE_ICMP
de2a0782 886 netdbEntry *n;
de2a0782 887 int i;
888 int j;
889 int rec_sz;
890 char *buf;
891 struct in_addr addr;
892 storeBuffer(s);
893 httpReplyReset(reply);
894 httpReplySetHeaders(reply, 1.0, HTTP_OK, "OK",
895 NULL, -1, squid_curtime, -2);
896 httpReplySwapOut(reply, s);
897 rec_sz = 0;
898 rec_sz += 1 + sizeof(addr.s_addr);
899 rec_sz += 1 + sizeof(int);
900 rec_sz += 1 + sizeof(int);
901 buf = memAllocate(MEM_4K_BUF);
902 i = 0;
0f6bebac 903 hash_first(addr_table);
904 while ((n = (netdbEntry *) hash_next(addr_table))) {
de2a0782 905 if (0.0 == n->rtt)
906 continue;
67a863b6 907 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
908 continue;
de2a0782 909 if (!safe_inet_addr(n->network, &addr))
910 continue;
9ad1cbca 911 buf[i++] = (char) NETDB_EX_NETWORK;
de2a0782 912 xmemcpy(&buf[i], &addr.s_addr, sizeof(addr.s_addr));
913 i += sizeof(addr.s_addr);
9ad1cbca 914 buf[i++] = (char) NETDB_EX_RTT;
915 j = htonl((int) (n->rtt * 1000));
de2a0782 916 xmemcpy(&buf[i], &j, sizeof(int));
917 i += sizeof(int);
9ad1cbca 918 buf[i++] = (char) NETDB_EX_HOPS;
919 j = htonl((int) (n->hops * 1000));
de2a0782 920 xmemcpy(&buf[i], &j, sizeof(int));
921 i += sizeof(int);
84075e8f 922 if (i + rec_sz > 4096) {
de2a0782 923 storeAppend(s, buf, i);
924 i = 0;
925 }
926 }
84075e8f 927 if (i > 0) {
928 storeAppend(s, buf, i);
929 i = 0;
930 }
de2a0782 931 assert(0 == i);
932 storeBufferFlush(s);
3a328ec8 933 memFree(MEM_4K_BUF, buf);
de2a0782 934#else
9ad1cbca 935 httpReplyReset(reply);
936 httpReplySetHeaders(reply, 1.0, HTTP_BAD_REQUEST, "Bad Request",
937 NULL, -1, squid_curtime, -2);
de2a0782 938 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
939#endif
9ad1cbca 940 storeComplete(s);
941}
942
943void
944netdbExchangeStart(void *data)
945{
45c54bdc 946#if USE_ICMP
a74c5601 947 peer *p = data;
948 char *uri;
949 netdbExchangeState *ex = xcalloc(1, sizeof(*ex));
950 cbdataAdd(ex, MEM_NONE);
951 cbdataLock(p);
952 ex->p = p;
953 uri = internalRemoteUri(p->host, p->http_port, "/squid-internal-dynamic/", "netdb");
fd9f2966 954 debug(38, 3) ("netdbExchangeStart: Requesting '%s'\n", uri);
a74c5601 955 assert(NULL != uri);
956 ex->r = urlParse(METHOD_GET, uri);
957 if (NULL == ex->r) {
67f46679 958 debug(38, 1) ("netdbExchangeStart: Bad URI %s\n", uri);
a74c5601 959 return;
960 }
961 requestLink(ex->r);
962 assert(NULL != ex->r);
56e82256 963 ex->r->http_ver = 1.0;
92695e5e 964 ex->e = storeCreateEntry(uri, uri, null_request_flags, METHOD_GET);
bf32c208 965 ex->buf_sz = 4096;;
966 ex->buf = memAllocate(MEM_4K_BUF);
a74c5601 967 assert(NULL != ex->e);
968 storeClientListAdd(ex->e, ex);
969 storeClientCopy(ex->e, ex->seen, ex->used, ex->buf_sz,
970 ex->buf, netdbExchangeHandleReply, ex);
92695e5e 971 ex->r->flags.loopdetect = 1; /* cheat! -- force direct */
5843eb62 972 fwdStart(-1, ex->e, ex->r, no_addr);
45c54bdc 973#endif
de2a0782 974}
69c95dd3 975
976peer *
5999b776 977netdbClosestParent(request_t * request)
69c95dd3 978{
69c95dd3 979#if USE_ICMP
f720985e 980 peer *p = NULL;
69c95dd3 981 netdbEntry *n;
982 const ipcache_addrs *ia;
983 net_db_peer *h;
984 int i;
8ff4505b 985 n = netdbLookupHost(request->host);
69c95dd3 986 if (NULL == n) {
987 /* try IP addr */
8ff4505b 988 ia = ipcache_gethostbyname(request->host, 0);
69c95dd3 989 if (NULL != ia)
990 n = netdbLookupAddr(ia->in_addrs[ia->cur]);
991 }
992 if (NULL == n)
993 return NULL;
994 if (0 == n->n_peers)
995 return NULL;
996 /*
997 * Find the parent with the least RTT to the origin server.
998 * Make sure we don't return a parent who is farther away than
999 * we are. Note, the n->peers list is pre-sorted by RTT.
1000 */
1001 for (i = 0; i < n->n_peers; i++) {
1002 h = &n->peers[i];
1003 if (n->rtt > 0)
1004 if (n->rtt < h->rtt)
1005 break;
8ff4505b 1006 p = peerFindByName(h->peername);
5999b776 1007 if (NULL == p) /* not found */
8ff4505b 1008 continue;
39b80cd0 1009 if (neighborType(p, request) != PEER_PARENT)
1010 continue;
8ff4505b 1011 if (!peerHTTPOkay(p, request)) /* not allowed */
1012 continue;
1013 return p;
69c95dd3 1014 }
1015#endif
1016 return NULL;
1017}