]>
Commit | Line | Data |
---|---|---|
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 | 40 | typedef 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 | 50 | static hash_table *addr_table = NULL; |
365e5b34 | 51 | static hash_table *host_table = NULL; |
87801fcb | 52 | |
f5b8bbc4 | 53 | static struct in_addr networkFromInaddr(struct in_addr a); |
54 | static void netdbRelease(netdbEntry * n); | |
f5b8bbc4 | 55 | static void netdbHashInsert(netdbEntry * n, struct in_addr addr); |
56 | static void netdbHashDelete(const char *key); | |
587d8445 | 57 | static void netdbHostInsert(netdbEntry * n, const char *hostname); |
58 | static void netdbHostDelete(const net_db_name * x); | |
f5b8bbc4 | 59 | static void netdbPurgeLRU(void); |
23d92c64 | 60 | static netdbEntry *netdbLookupHost(const char *key); |
f5b8bbc4 | 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); | |
b69f7771 | 64 | static IPH netdbSendPing; |
79d39a72 | 65 | static QS sortPeerByRtt; |
66 | static QS sortByRtt; | |
67 | static QS netdbLRU; | |
ec878047 | 68 | static FREE netdbFreeNameEntry; |
69 | static FREE netdbFreeNetdbEntry; | |
9ad1cbca | 70 | static STCB netdbExchangeHandleReply; |
45c54bdc | 71 | static 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 */ | |
77 | static wordlist *peer_names = NULL; | |
67508012 | 78 | |
87801fcb | 79 | static void |
67508012 | 80 | netdbHashInsert(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 | ||
88 | static void | |
0ee4272b | 89 | netdbHashDelete(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 | ||
99 | static void | |
587d8445 | 100 | netdbHostInsert(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 | ||
112 | static void | |
587d8445 | 113 | netdbHostDelete(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 | ||
132 | static netdbEntry * | |
0ee4272b | 133 | netdbLookupHost(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 | 139 | static void |
140 | netdbRelease(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 | ||
159 | static int | |
79d39a72 | 160 | netdbLRU(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 | 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; | |
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 | 199 | static netdbEntry * |
200 | netdbLookupAddr(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 | 208 | static netdbEntry * |
587d8445 | 209 | netdbAdd(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 | ||
221 | static void | |
82691c78 | 222 | netdbSendPing(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 | 277 | static struct in_addr |
278 | networkFromInaddr(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 | ||
297 | static int | |
79d39a72 | 298 | sortByRtt(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 | 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; | |
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 | ||
351 | static int | |
79d39a72 | 352 | sortPeerByRtt(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 | ||
364 | static void | |
365 | netdbSaveState(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 | ||
403 | static void | |
404 | netdbReloadState(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 | ||
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 | } | |
c68e9c6b | 476 | w = wordlistAdd(&peer_names, name); |
429fdbec | 477 | return w->key; |
478 | } | |
479 | ||
45c54bdc | 480 | static void |
481 | netdbFreeNetdbEntry(void *data) | |
482 | { | |
483 | netdbEntry *n = data; | |
484 | safe_free(n->peers); | |
485 | memFree(MEM_NETDBENTRY, n); | |
486 | } | |
429fdbec | 487 | |
45c54bdc | 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; | |
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 | ||
605 | static void | |
606 | netdbExchangeDone(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 | 622 | void |
e97f40f4 | 623 | netdbInit(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 | ||
641 | void | |
0ee4272b | 642 | netdbPingSite(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 | 657 | void |
0ee4272b | 658 | netdbHandlePingReply(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 | |
678 | void | |
679 | netdbFreeMemory(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 | 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; | |
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 | |
762 | int | |
763 | netdbHostHops(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 | ||
775 | int | |
776 | netdbHostRtt(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 | 788 | void |
789 | netdbHostData(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 | 801 | int |
9ef28b60 | 802 | netdbHostPeerRtt(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 | 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; | |
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 | 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; | |
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 | 869 | void |
870 | netdbDeleteAddrNetwork(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 | |
881 | void | |
882 | netdbBinaryExchange(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 | ||
943 | void | |
944 | netdbExchangeStart(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 | |
976 | peer * | |
5999b776 | 977 | netdbClosestParent(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 | } |