]> git.ipfire.org Git - thirdparty/squid.git/blob - src/net_db.cc
2.1 branch merge
[thirdparty/squid.git] / src / net_db.cc
1
2 /*
3 * $Id: net_db.cc,v 1.132 1998/11/12 06:28:17 wessels Exp $
4 *
5 * DEBUG: section 38 Network Measurement Database
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 #if USE_ICMP
39
40 typedef struct {
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;
48 } netdbExchangeState;
49
50 static hash_table *addr_table = NULL;
51 static hash_table *host_table = NULL;
52
53 static struct in_addr networkFromInaddr(struct in_addr a);
54 static void netdbRelease(netdbEntry * n);
55 static void netdbHashInsert(netdbEntry * n, struct in_addr addr);
56 static void netdbHashDelete(const char *key);
57 static void netdbHostInsert(netdbEntry * n, const char *hostname);
58 static void netdbHostDelete(const net_db_name * x);
59 static void netdbPurgeLRU(void);
60 static netdbEntry *netdbLookupHost(const char *key);
61 static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
62 static net_db_peer *netdbPeerAdd(netdbEntry * n, peer * e);
63 static char *netdbPeerName(const char *name);
64 static IPH netdbSendPing;
65 static QS sortPeerByRtt;
66 static QS sortByRtt;
67 static QS netdbLRU;
68 static FREE netdbFreeNameEntry;
69 static FREE netdbFreeNetdbEntry;
70 static STCB netdbExchangeHandleReply;
71 static void netdbExchangeDone(void *);
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 */
77 static wordlist *peer_names = NULL;
78
79 static void
80 netdbHashInsert(netdbEntry * n, struct in_addr addr)
81 {
82 xstrncpy(n->network, inet_ntoa(networkFromInaddr(addr)), 16);
83 n->key = n->network;
84 assert(hash_lookup(addr_table, n->network) == NULL);
85 hash_join(addr_table, (hash_link *) n);
86 }
87
88 static void
89 netdbHashDelete(const char *key)
90 {
91 hash_link *hptr = hash_lookup(addr_table, key);
92 if (hptr == NULL) {
93 debug_trap("netdbHashDelete: key not found");
94 return;
95 }
96 hash_remove_link(addr_table, hptr);
97 }
98
99 static void
100 netdbHostInsert(netdbEntry * n, const char *hostname)
101 {
102 net_db_name *x = memAllocate(MEM_NET_DB_NAME);
103 x->name = xstrdup(hostname);
104 x->next = n->hosts;
105 n->hosts = x;
106 x->net_db_entry = n;
107 assert(hash_lookup(host_table, hostname) == NULL);
108 hash_join(host_table, (hash_link *) x);
109 n->link_count++;
110 }
111
112 static void
113 netdbHostDelete(const net_db_name * x)
114 {
115 netdbEntry *n;
116 net_db_name **X;
117 assert(x != NULL);
118 assert(x->net_db_entry != NULL);
119 n = x->net_db_entry;
120 n->link_count--;
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);
130 }
131
132 static netdbEntry *
133 netdbLookupHost(const char *key)
134 {
135 net_db_name *x = (net_db_name *) hash_lookup(host_table, key);
136 return x ? x->net_db_entry : NULL;
137 }
138
139 static void
140 netdbRelease(netdbEntry * n)
141 {
142 net_db_name *x;
143 net_db_name *next;
144 for (x = n->hosts; x; x = next) {
145 next = x->next;
146 netdbHostDelete(x);
147 }
148 n->hosts = NULL;
149 safe_free(n->peers);
150 n->peers = NULL;
151 n->n_peers = 0;
152 n->n_peers_alloc = 0;
153 if (n->link_count == 0) {
154 netdbHashDelete(n->network);
155 memFree(MEM_NETDBENTRY, n);
156 }
157 }
158
159 static int
160 netdbLRU(const void *A, const void *B)
161 {
162 const netdbEntry *const *n1 = A;
163 const netdbEntry *const *n2 = B;
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
171 static void
172 netdbPurgeLRU(void)
173 {
174 netdbEntry *n;
175 netdbEntry **list;
176 int k = 0;
177 int list_count = 0;
178 int removed = 0;
179 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
180 hash_first(addr_table);
181 while ((n = (netdbEntry *) hash_next(addr_table))) {
182 assert(list_count < memInUse(MEM_NETDBENTRY));
183 *(list + list_count) = n;
184 list_count++;
185 }
186 qsort((char *) list,
187 list_count,
188 sizeof(netdbEntry *),
189 netdbLRU);
190 for (k = 0; k < list_count; k++) {
191 if (memInUse(MEM_NETDBENTRY) < Config.Netdb.low)
192 break;
193 netdbRelease(*(list + k));
194 removed++;
195 }
196 xfree(list);
197 }
198
199 static netdbEntry *
200 netdbLookupAddr(struct in_addr addr)
201 {
202 netdbEntry *n;
203 char *key = inet_ntoa(networkFromInaddr(addr));
204 n = (netdbEntry *) hash_lookup(addr_table, key);
205 return n;
206 }
207
208 static netdbEntry *
209 netdbAdd(struct in_addr addr)
210 {
211 netdbEntry *n;
212 if (memInUse(MEM_NETDBENTRY) > Config.Netdb.high)
213 netdbPurgeLRU();
214 if ((n = netdbLookupAddr(addr)) == NULL) {
215 n = memAllocate(MEM_NETDBENTRY);
216 netdbHashInsert(n, addr);
217 }
218 return n;
219 }
220
221 static void
222 netdbSendPing(const ipcache_addrs * ia, void *data)
223 {
224 struct in_addr addr;
225 char *hostname = data;
226 netdbEntry *n;
227 netdbEntry *na;
228 net_db_name *x;
229 net_db_name **X;
230 cbdataUnlock(hostname);
231 if (ia == NULL) {
232 cbdataFree(hostname);
233 return;
234 }
235 addr = ia->in_addrs[ia->cur];
236 if ((n = netdbLookupHost(hostname)) == NULL) {
237 n = netdbAdd(addr);
238 netdbHostInsert(n, hostname);
239 } else if ((na = netdbLookupAddr(addr)) != n) {
240 /*
241 *hostname moved from 'network n' to 'network na'!
242 */
243 if (na == NULL)
244 na = netdbAdd(addr);
245 debug(38, 3) ("netdbSendPing: %s moved from %s to %s\n",
246 hostname, n->network, na->network);
247 x = (net_db_name *) hash_lookup(host_table, hostname);
248 if (x == NULL) {
249 debug(38, 1) ("netdbSendPing: net_db_name list bug: %s not found", hostname);
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++;
267 n = na;
268 }
269 debug(38, 3) ("netdbSendPing: pinging %s\n", hostname);
270 icmpDomainPing(addr, hostname);
271 n->pings_sent++;
272 n->next_ping_time = squid_curtime + Config.Netdb.period;
273 n->last_use_time = squid_curtime;
274 cbdataFree(hostname);
275 }
276
277 static struct in_addr
278 networkFromInaddr(struct in_addr a)
279 {
280 struct in_addr b;
281 b.s_addr = ntohl(a.s_addr);
282 #if USE_CLASSFUL
283 if (IN_CLASSC(b.s_addr))
284 b.s_addr &= IN_CLASSC_NET;
285 else if (IN_CLASSB(b.s_addr))
286 b.s_addr &= IN_CLASSB_NET;
287 else if (IN_CLASSA(b.s_addr))
288 b.s_addr &= IN_CLASSA_NET;
289 #else
290 /* use /24 for everything */
291 b.s_addr &= IN_CLASSC_NET;
292 #endif
293 b.s_addr = htonl(b.s_addr);
294 return b;
295 }
296
297 static int
298 sortByRtt(const void *A, const void *B)
299 {
300 const netdbEntry *const *n1 = A;
301 const netdbEntry *const *n2 = B;
302 if ((*n1)->rtt > (*n2)->rtt)
303 return 1;
304 else if ((*n1)->rtt < (*n2)->rtt)
305 return -1;
306 else
307 return 0;
308 }
309
310 static net_db_peer *
311 netdbPeerByName(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
322 static net_db_peer *
323 netdbPeerAdd(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;
336 debug(38, 3) ("netdbPeerAdd: Growing peer list for '%s' to %d\n",
337 n->network, n->n_peers_alloc);
338 n->peers = xcalloc(n->n_peers_alloc, sizeof(net_db_peer));
339 for (i = 0; i < osize; i++)
340 *(n->peers + i) = *(o + i);
341 if (osize) {
342 safe_free(o);
343 }
344 }
345 p = n->peers + n->n_peers;
346 p->peername = netdbPeerName(e->host);
347 n->n_peers++;
348 return p;
349 }
350
351 static int
352 sortPeerByRtt(const void *A, const void *B)
353 {
354 const net_db_peer *p1 = A;
355 const net_db_peer *p2 = B;
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
364 static void
365 netdbSaveState(void *foo)
366 {
367 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
368 FILE *fp;
369 netdbEntry *n;
370 net_db_name *x;
371 struct timeval start = current_time;
372 int count = 0;
373 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
374 fp = fopen(path, "w");
375 if (fp == NULL) {
376 debug(50, 1) ("netdbSaveState: %s: %s\n", path, xstrerror());
377 return;
378 }
379 hash_first(addr_table);
380 while ((n = (netdbEntry *) hash_next(addr_table))) {
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);
397 getCurrentTime();
398 debug(38, 1) ("NETDB state saved; %d entries, %d msec\n",
399 count, tvSubMsec(start, current_time));
400 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
401 }
402
403 static void
404 netdbReloadState(void)
405 {
406 LOCAL_ARRAY(char, path, SQUID_MAXPATHLEN);
407 char *buf;
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;
415 snprintf(path, SQUID_MAXPATHLEN, "%s/netdb_state", storeSwapDir(0));
416 fp = fopen(path, "r");
417 if (fp == NULL)
418 return;
419 buf = memAllocate(MEM_4K_BUF);
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;
426 if (netdbLookupAddr(addr) != NULL) /* no dups! */
427 continue;
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);
451 n = memAllocate(MEM_NETDBENTRY);
452 xmemcpy(n, &N, sizeof(netdbEntry));
453 netdbHashInsert(n, addr);
454 while ((t = strtok(NULL, w_space)) != NULL) {
455 if (netdbLookupHost(t) != NULL) /* no dups! */
456 continue;
457 netdbHostInsert(n, t);
458 }
459 count++;
460 }
461 memFree(MEM_4K_BUF, buf);
462 fclose(fp);
463 getCurrentTime();
464 debug(38, 1) ("NETDB state reloaded; %d entries, %d msec\n",
465 count, tvSubMsec(start, current_time));
466 }
467
468 static char *
469 netdbPeerName(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 }
476 w = wordlistAdd(&peer_names, name);
477 return w->key;
478 }
479
480 static void
481 netdbFreeNetdbEntry(void *data)
482 {
483 netdbEntry *n = data;
484 safe_free(n->peers);
485 memFree(MEM_NETDBENTRY, n);
486 }
487
488 static void
489 netdbFreeNameEntry(void *data)
490 {
491 net_db_name *x = data;
492 xfree(x->name);
493 memFree(MEM_NET_DB_NAME, x);
494 }
495
496 static void
497 netdbExchangeHandleReply(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;
509 int nused = 0;
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;
515 debug(38, 3) ("netdbExchangeHandleReply: %d bytes\n", (int) size);
516 if (!cbdataValid(ex->p)) {
517 debug(38, 3) ("netdbExchangeHandleReply: Peer became invalid\n");
518 netdbExchangeDone(ex);
519 return;
520 }
521 debug(38, 3) ("netdbExchangeHandleReply: for '%s:%d'\n", ex->p->host, ex->p->http_port);
522 p = buf;
523 if (0 == ex->used) {
524 /* skip reply headers */
525 if ((hdr_sz = headersEnd(p, size))) {
526 debug(38, 5) ("netdbExchangeHandleReply: hdr_sz = %d\n", hdr_sz);
527 rep = ex->e->mem_obj->reply;
528 if (0 == rep->sline.status)
529 httpReplyParse(rep, buf);
530 debug(38, 3) ("netdbExchangeHandleReply: reply status %d\n",
531 rep->sline.status);
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 }
544 debug(38, 5) ("netdbExchangeHandleReply: start parsing loop, size = %d\n",
545 size);
546 while (size >= rec_sz) {
547 debug(38, 5) ("netdbExchangeHandleReply: in parsing loop, size = %d\n",
548 size);
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;
578 /*
579 * This is a fairly cpu-intensive loop, break after adding
580 * just a few
581 */
582 if (++nused == 20)
583 break;
584 }
585 debug(38, 3) ("netdbExchangeHandleReply: used %d entries, (x %d bytes) == %d bytes total\n",
586 nused, rec_sz, nused * rec_sz);
587 debug(38, 3) ("netdbExchangeHandleReply: seen %d, used %d\n", ex->seen, ex->used);
588 if (ex->e->store_status == STORE_ABORTED) {
589 debug(38, 3) ("netdbExchangeHandleReply: STORE_ABORTED\n");
590 netdbExchangeDone(ex);
591 } else if (ex->e->store_status == STORE_PENDING) {
592 debug(38, 3) ("netdbExchangeHandleReply: STORE_PENDING\n");
593 storeClientCopy(ex->e, ex->seen, ex->used, ex->buf_sz,
594 ex->buf, netdbExchangeHandleReply, ex);
595 } else if (ex->seen < ex->e->mem_obj->inmem_hi) {
596 debug(38, 3) ("netdbExchangeHandleReply: ex->e->mem_obj->inmem_hi\n");
597 storeClientCopy(ex->e, ex->seen, ex->used, ex->buf_sz,
598 ex->buf, netdbExchangeHandleReply, ex);
599 } else {
600 debug(38, 3) ("netdbExchangeHandleReply: Done\n");
601 netdbExchangeDone(ex);
602 }
603 }
604
605 static void
606 netdbExchangeDone(void *data)
607 {
608 netdbExchangeState *ex = data;
609 debug(38, 3) ("netdbExchangeDone: %s\n", storeUrl(ex->e));
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 }
617
618 #endif /* USE_ICMP */
619
620 /* PUBLIC FUNCTIONS */
621
622 void
623 netdbInit(void)
624 {
625 #if USE_ICMP
626 int n;
627 if (addr_table)
628 return;
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);
633 eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1);
634 netdbReloadState();
635 cachemgrRegister("netdb",
636 "Network Measurement Database",
637 netdbDump, 0, 1);
638 #endif
639 }
640
641 void
642 netdbPingSite(const char *hostname)
643 {
644 #if USE_ICMP
645 netdbEntry *n;
646 char *h;
647 if ((n = netdbLookupHost(hostname)) != NULL)
648 if (n->next_ping_time > squid_curtime)
649 return;
650 h = xstrdup(hostname);
651 cbdataAdd(h, MEM_NONE);
652 cbdataLock(h);
653 ipcache_nbgethostbyname(hostname, netdbSendPing, h);
654 #endif
655 }
656
657 void
658 netdbHandlePingReply(const struct sockaddr_in *from, int hops, int rtt)
659 {
660 #if USE_ICMP
661 netdbEntry *n;
662 int N;
663 debug(38, 3) ("netdbHandlePingReply: from %s\n", inet_ntoa(from->sin_addr));
664 if ((n = netdbLookupAddr(from->sin_addr)) == NULL)
665 return;
666 N = ++n->pings_recv;
667 if (N > 5)
668 N = 5;
669 n->hops = ((n->hops * (N - 1)) + hops) / N;
670 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
671 debug(38, 3) ("netdbHandlePingReply: %s; rtt=%5.1f hops=%4.1f\n",
672 n->network,
673 n->rtt,
674 n->hops);
675 #endif
676 }
677
678 void
679 netdbFreeMemory(void)
680 {
681 #if USE_ICMP
682 hashFreeItems(addr_table, netdbFreeNetdbEntry);
683 hashFreeMemory(addr_table);
684 addr_table = NULL;
685 hashFreeItems(host_table, netdbFreeNameEntry);
686 hashFreeMemory(host_table);
687 host_table = NULL;
688 wordlistDestroy(&peer_names);
689 peer_names = NULL;
690 #endif
691 }
692
693 int
694 netdbHops(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
706 void
707 netdbDump(StoreEntry * sentry)
708 {
709 #if USE_ICMP
710 netdbEntry *n;
711 netdbEntry **list;
712 net_db_name *x;
713 int k;
714 int i;
715 int j;
716 net_db_peer *p;
717 storeAppendPrintf(sentry, "Network DB Statistics:\n");
718 storeAppendPrintf(sentry, "%-16.16s %9s %7s %5s %s\n",
719 "Network",
720 "recv/sent",
721 "RTT",
722 "Hops",
723 "Hostnames");
724 list = xcalloc(memInUse(MEM_NETDBENTRY), sizeof(netdbEntry *));
725 i = 0;
726 hash_first(addr_table);
727 while ((n = (netdbEntry *) hash_next(addr_table)))
728 *(list + i++) = n;
729 if (i != memInUse(MEM_NETDBENTRY))
730 debug(38, 0) ("WARNING: netdb_addrs count off, found %d, expected %d\n",
731 i, memInUse(MEM_NETDBENTRY));
732 qsort((char *) list,
733 i,
734 sizeof(netdbEntry *),
735 sortByRtt);
736 for (k = 0; k < i; k++) {
737 n = *(list + k);
738 storeAppendPrintf(sentry, "%-16.16s %4d/%4d %7.1f %5.1f",
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);
746 storeAppendPrintf(sentry, "\n");
747 p = n->peers;
748 for (j = 0; j < n->n_peers; j++, p++) {
749 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
750 p->peername,
751 p->rtt,
752 p->hops);
753 }
754 }
755 xfree(list);
756 #else
757 storeAppendPrintf(sentry,
758 "NETDB support not compiled into this Squid cache.\n");
759 #endif
760 }
761
762 int
763 netdbHostHops(const char *host)
764 {
765 #if USE_ICMP
766 netdbEntry *n = netdbLookupHost(host);
767 if (n) {
768 n->last_use_time = squid_curtime;
769 return (int) (n->hops + 0.5);
770 }
771 #endif
772 return 0;
773 }
774
775 int
776 netdbHostRtt(const char *host)
777 {
778 #if USE_ICMP
779 netdbEntry *n = netdbLookupHost(host);
780 if (n) {
781 n->last_use_time = squid_curtime;
782 return (int) (n->rtt + 0.5);
783 }
784 #endif
785 return 0;
786 }
787
788 void
789 netdbHostData(const char *host, int *samp, int *rtt, int *hops)
790 {
791 #if USE_ICMP
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);
798 #endif
799 }
800
801 int
802 netdbHostPeerRtt(const char *host, peer * p)
803 {
804 #if USE_ICMP
805 const netdbEntry *n = netdbLookupHost(host);
806 if (n) {
807 const net_db_peer *np = netdbPeerByName(n, p->host);
808 if (np && np->expires >= squid_curtime)
809 return (int) (np->rtt + 0.5);
810 }
811 #endif
812 return 0;
813 }
814
815 void
816 netdbUpdatePeer(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;
823 debug(38, 3) ("netdbUpdatePeer: '%s', %d hops, %d rtt\n", r->host, ihops, irtt);
824 n = netdbLookupHost(r->host);
825 if (n == NULL) {
826 debug(38, 3) ("netdbUpdatePeer: host '%s' not found\n", r->host);
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),
839 sortPeerByRtt);
840 #endif
841 }
842
843 void
844 netdbExchangeUpdatePeer(struct in_addr addr, peer * e, double rtt, double hops)
845 {
846 #if USE_ICMP
847 netdbEntry *n;
848 net_db_peer *p;
849 debug(38, 5) ("netdbExchangeUpdatePeer: '%s', %0.1f hops, %0.1f rtt\n",
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
869 void
870 netdbDeleteAddrNetwork(struct in_addr addr)
871 {
872 #if USE_ICMP
873 netdbEntry *n = netdbLookupAddr(addr);
874 if (n == NULL)
875 return;
876 debug(38, 3) ("netdbDeleteAddrNetwork: %s\n", n->network);
877 netdbRelease(n);
878 #endif
879 }
880
881 void
882 netdbBinaryExchange(StoreEntry * s)
883 {
884 http_reply *reply = s->mem_obj->reply;
885 #if USE_ICMP
886 netdbEntry *n;
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;
903 hash_first(addr_table);
904 while ((n = (netdbEntry *) hash_next(addr_table))) {
905 if (0.0 == n->rtt)
906 continue;
907 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
908 continue;
909 if (!safe_inet_addr(n->network, &addr))
910 continue;
911 buf[i++] = (char) NETDB_EX_NETWORK;
912 xmemcpy(&buf[i], &addr.s_addr, sizeof(addr.s_addr));
913 i += sizeof(addr.s_addr);
914 buf[i++] = (char) NETDB_EX_RTT;
915 j = htonl((int) (n->rtt * 1000));
916 xmemcpy(&buf[i], &j, sizeof(int));
917 i += sizeof(int);
918 buf[i++] = (char) NETDB_EX_HOPS;
919 j = htonl((int) (n->hops * 1000));
920 xmemcpy(&buf[i], &j, sizeof(int));
921 i += sizeof(int);
922 if (i + rec_sz > 4096) {
923 storeAppend(s, buf, i);
924 i = 0;
925 }
926 }
927 if (i > 0) {
928 storeAppend(s, buf, i);
929 i = 0;
930 }
931 assert(0 == i);
932 storeBufferFlush(s);
933 memFree(MEM_4K_BUF, buf);
934 #else
935 httpReplyReset(reply);
936 httpReplySetHeaders(reply, 1.0, HTTP_BAD_REQUEST, "Bad Request",
937 NULL, -1, squid_curtime, -2);
938 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
939 #endif
940 storeComplete(s);
941 }
942
943 void
944 netdbExchangeStart(void *data)
945 {
946 #if USE_ICMP
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");
954 debug(38, 3) ("netdbExchangeStart: Requesting '%s'\n", uri);
955 assert(NULL != uri);
956 ex->r = urlParse(METHOD_GET, uri);
957 if (NULL == ex->r) {
958 debug(38, 1) ("netdbExchangeStart: Bad URI %s\n", uri);
959 return;
960 }
961 requestLink(ex->r);
962 assert(NULL != ex->r);
963 ex->r->http_ver = 1.0;
964 ex->e = storeCreateEntry(uri, uri, null_request_flags, METHOD_GET);
965 ex->buf_sz = 4096;;
966 ex->buf = memAllocate(MEM_4K_BUF);
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);
971 ex->r->flags.loopdetect = 1; /* cheat! -- force direct */
972 fwdStart(-1, ex->e, ex->r, no_addr);
973 #endif
974 }
975
976 peer *
977 netdbClosestParent(request_t * request)
978 {
979 #if USE_ICMP
980 peer *p = NULL;
981 netdbEntry *n;
982 const ipcache_addrs *ia;
983 net_db_peer *h;
984 int i;
985 n = netdbLookupHost(request->host);
986 if (NULL == n) {
987 /* try IP addr */
988 ia = ipcache_gethostbyname(request->host, 0);
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;
1006 p = peerFindByName(h->peername);
1007 if (NULL == p) /* not found */
1008 continue;
1009 if (neighborType(p, request) != PEER_PARENT)
1010 continue;
1011 if (!peerHTTPOkay(p, request)) /* not allowed */
1012 continue;
1013 return p;
1014 }
1015 #endif
1016 return NULL;
1017 }