]>
Commit | Line | Data |
---|---|---|
516350ca | 1 | /* |
2cd0bda2 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
516350ca | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 38 Network Measurement Database */ |
10 | ||
add2192d | 11 | /* |
12 | * XXX XXX XXX | |
13 | * | |
14 | * This code may be slightly broken now. If you're getting consistent | |
15 | * (sometimes working) corrupt data exchanges, please contact adrian | |
16 | * (adrian@squid-cache.org) to sort them out. | |
17 | */ | |
18 | ||
582c2af2 | 19 | #include "squid.h" |
a011edee | 20 | #include "CachePeer.h" |
aa839030 | 21 | #include "cbdata.h" |
a553a5a3 | 22 | #include "event.h" |
528b2c61 | 23 | #include "fde.h" |
b3f7fd88 | 24 | #include "fs_io.h" |
eb13c21e | 25 | #include "FwdState.h" |
5bed43d6 FC |
26 | #include "HttpReply.h" |
27 | #include "HttpRequest.h" | |
28 | #include "icmp/net_db.h" | |
fc54b8d2 | 29 | #include "internal.h" |
5bed43d6 FC |
30 | #include "ip/Address.h" |
31 | #include "log/File.h" | |
5bed43d6 FC |
32 | #include "MemObject.h" |
33 | #include "mgr/Registration.h" | |
b6149797 | 34 | #include "mime_header.h" |
5bed43d6 | 35 | #include "neighbors.h" |
2a0ab044 | 36 | #include "SquidConfig.h" |
985c86bc | 37 | #include "SquidTime.h" |
5bed43d6 | 38 | #include "Store.h" |
e87137f1 | 39 | #include "StoreClient.h" |
5bed43d6 FC |
40 | #include "tools.h" |
41 | #include "URL.h" | |
42 | #include "wordlist.h" | |
fc54b8d2 | 43 | |
582c2af2 FC |
44 | #if HAVE_SYS_STAT_H |
45 | #include <sys/stat.h> | |
46 | #endif | |
47 | ||
a97cfa48 | 48 | #if USE_ICMP |
9b5c4a9a | 49 | #include "icmp/IcmpSquid.h" |
714e68b7 | 50 | #include "ipcache.h" |
4b725156 | 51 | #include "StoreClient.h" |
52 | ||
f53969cc | 53 | #define NETDB_REQBUF_SZ 4096 |
add2192d | 54 | |
55 | typedef enum { | |
fa80a8ef | 56 | STATE_NONE, |
57 | STATE_HEADER, | |
58 | STATE_BODY | |
add2192d | 59 | } netdb_conn_state_t; |
8b833697 | 60 | |
fa91d030 AJ |
61 | class netdbExchangeState |
62 | { | |
63 | CBDATA_CLASS(netdbExchangeState); | |
64 | ||
65 | public: | |
66 | netdbExchangeState(CachePeer *aPeer, HttpRequest *theReq) : | |
67 | p(cbdataReference(aPeer)), | |
68 | e(NULL), | |
69 | sc(NULL), | |
70 | r(theReq), | |
71 | used(0), | |
72 | buf_sz(NETDB_REQBUF_SZ), | |
73 | buf_ofs(0), | |
74 | connstate(STATE_HEADER) | |
75 | { | |
76 | *buf = 0; | |
77 | ||
78 | assert(NULL != r); | |
79 | HTTPMSGLOCK(r); | |
80 | // TODO: check if we actually need to do this. should be implicit | |
81 | r->http_ver = Http::ProtocolVersion(); | |
82 | } | |
83 | ||
84 | ~netdbExchangeState() { | |
85 | debugs(38, 3, e->url()); | |
86 | storeUnregister(sc, e, this); | |
87 | e->unlock("netdbExchangeDone"); | |
88 | HTTPMSGUNLOCK(r); | |
89 | cbdataReferenceDone(p); | |
90 | } | |
91 | ||
a3c6762c | 92 | CachePeer *p; |
a74c5601 | 93 | StoreEntry *e; |
06d2839d | 94 | store_client *sc; |
190154cf | 95 | HttpRequest *r; |
57d55dfa | 96 | int64_t used; |
a74c5601 | 97 | size_t buf_sz; |
add2192d | 98 | char buf[NETDB_REQBUF_SZ]; |
99 | int buf_ofs; | |
100 | netdb_conn_state_t connstate; | |
fa91d030 AJ |
101 | }; |
102 | ||
103 | CBDATA_CLASS_INIT(netdbExchangeState); | |
9ad1cbca | 104 | |
ce75f381 | 105 | static hash_table *addr_table = NULL; |
365e5b34 | 106 | static hash_table *host_table = NULL; |
87801fcb | 107 | |
b7ac5457 | 108 | Ip::Address networkFromInaddr(const Ip::Address &a); |
f5b8bbc4 | 109 | static void netdbRelease(netdbEntry * n); |
62e76326 | 110 | |
b7ac5457 | 111 | static void netdbHashInsert(netdbEntry * n, Ip::Address &addr); |
f5b8bbc4 | 112 | static void netdbHashDelete(const char *key); |
587d8445 | 113 | static void netdbHostInsert(netdbEntry * n, const char *hostname); |
114 | static void netdbHostDelete(const net_db_name * x); | |
f5b8bbc4 | 115 | static void netdbPurgeLRU(void); |
23d92c64 | 116 | static netdbEntry *netdbLookupHost(const char *key); |
f5b8bbc4 | 117 | static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *); |
a3c6762c | 118 | static net_db_peer *netdbPeerAdd(netdbEntry * n, CachePeer * e); |
858783c9 | 119 | static const char *netdbPeerName(const char *name); |
b69f7771 | 120 | static IPH netdbSendPing; |
ec878047 | 121 | static FREE netdbFreeNameEntry; |
122 | static FREE netdbFreeNetdbEntry; | |
9ad1cbca | 123 | static STCB netdbExchangeHandleReply; |
429fdbec | 124 | |
a3c6762c | 125 | /* We have to keep a local list of CachePeer names. The Peers structure |
429fdbec | 126 | * gets freed during a reconfigure. We want this database to |
127 | * remain persisitent, so _net_db_peer->peername points into this | |
128 | * linked list */ | |
129 | static wordlist *peer_names = NULL; | |
67508012 | 130 | |
87801fcb | 131 | static void |
b7ac5457 | 132 | netdbHashInsert(netdbEntry * n, Ip::Address &addr) |
87801fcb | 133 | { |
4dd643d5 | 134 | networkFromInaddr(addr).toStr(n->network, MAX_IPSTRLEN); |
186477c1 | 135 | n->hash.key = n->network; |
587d8445 | 136 | assert(hash_lookup(addr_table, n->network) == NULL); |
186477c1 | 137 | hash_join(addr_table, &n->hash); |
87801fcb | 138 | } |
139 | ||
140 | static void | |
0ee4272b | 141 | netdbHashDelete(const char *key) |
87801fcb | 142 | { |
e6ccf245 | 143 | hash_link *hptr = (hash_link *)hash_lookup(addr_table, key); |
62e76326 | 144 | |
87801fcb | 145 | if (hptr == NULL) { |
62e76326 | 146 | debug_trap("netdbHashDelete: key not found"); |
147 | return; | |
87801fcb | 148 | } |
62e76326 | 149 | |
e0b1c6aa | 150 | hash_remove_link(addr_table, hptr); |
67508012 | 151 | } |
152 | ||
3c670b50 AJ |
153 | net_db_name::net_db_name(const char *hostname, netdbEntry *e) : |
154 | next(e ? e->hosts : nullptr), | |
155 | net_db_entry(e) | |
156 | { | |
e6a66371 | 157 | hash.key = xstrdup(hostname); |
3c670b50 AJ |
158 | if (e) { |
159 | e->hosts = this; | |
160 | ++ e->link_count; | |
161 | } | |
162 | } | |
163 | ||
67508012 | 164 | static void |
587d8445 | 165 | netdbHostInsert(netdbEntry * n, const char *hostname) |
67508012 | 166 | { |
3c670b50 | 167 | net_db_name *x = new net_db_name(hostname, n); |
587d8445 | 168 | assert(hash_lookup(host_table, hostname) == NULL); |
186477c1 | 169 | hash_join(host_table, &x->hash); |
67508012 | 170 | } |
171 | ||
172 | static void | |
587d8445 | 173 | netdbHostDelete(const net_db_name * x) |
67508012 | 174 | { |
b19361fd | 175 | assert(x != NULL); |
587d8445 | 176 | assert(x->net_db_entry != NULL); |
3c670b50 AJ |
177 | |
178 | netdbEntry *n = x->net_db_entry; | |
7c64cac7 | 179 | -- n->link_count; |
62e76326 | 180 | |
3c670b50 | 181 | for (auto **X = &n->hosts; *X; X = &(*X)->next) { |
62e76326 | 182 | if (*X == x) { |
183 | *X = x->next; | |
184 | break; | |
185 | } | |
587d8445 | 186 | } |
62e76326 | 187 | |
587d8445 | 188 | hash_remove_link(host_table, (hash_link *) x); |
3c670b50 | 189 | delete x; |
87801fcb | 190 | } |
191 | ||
192 | static netdbEntry * | |
0ee4272b | 193 | netdbLookupHost(const char *key) |
87801fcb | 194 | { |
587d8445 | 195 | net_db_name *x = (net_db_name *) hash_lookup(host_table, key); |
196 | return x ? x->net_db_entry : NULL; | |
87801fcb | 197 | } |
198 | ||
c907000b | 199 | static void |
200 | netdbRelease(netdbEntry * n) | |
201 | { | |
429fdbec | 202 | net_db_name *x; |
587d8445 | 203 | net_db_name *next; |
62e76326 | 204 | |
c907000b | 205 | for (x = n->hosts; x; x = next) { |
62e76326 | 206 | next = x->next; |
207 | netdbHostDelete(x); | |
c907000b | 208 | } |
62e76326 | 209 | |
c907000b | 210 | n->hosts = NULL; |
429fdbec | 211 | safe_free(n->peers); |
429fdbec | 212 | n->peers = NULL; |
213 | n->n_peers = 0; | |
214 | n->n_peers_alloc = 0; | |
62e76326 | 215 | |
c907000b | 216 | if (n->link_count == 0) { |
62e76326 | 217 | netdbHashDelete(n->network); |
1a7cfe02 | 218 | delete n; |
c907000b | 219 | } |
220 | } | |
221 | ||
222 | static int | |
79d39a72 | 223 | netdbLRU(const void *A, const void *B) |
c907000b | 224 | { |
e6ccf245 | 225 | const netdbEntry *const *n1 = (const netdbEntry *const *)A; |
226 | const netdbEntry *const *n2 = (const netdbEntry *const *)B; | |
62e76326 | 227 | |
c907000b | 228 | if ((*n1)->last_use_time > (*n2)->last_use_time) |
62e76326 | 229 | return (1); |
230 | ||
c907000b | 231 | if ((*n1)->last_use_time < (*n2)->last_use_time) |
62e76326 | 232 | return (-1); |
233 | ||
c907000b | 234 | return (0); |
235 | } | |
236 | ||
c907000b | 237 | static void |
238 | netdbPurgeLRU(void) | |
239 | { | |
240 | netdbEntry *n; | |
241 | netdbEntry **list; | |
242 | int k = 0; | |
243 | int list_count = 0; | |
244 | int removed = 0; | |
1a7cfe02 | 245 | list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *)); |
0f6bebac | 246 | hash_first(addr_table); |
62e76326 | 247 | |
0f6bebac | 248 | while ((n = (netdbEntry *) hash_next(addr_table))) { |
1a7cfe02 | 249 | assert(list_count < netdbEntry::UseCount()); |
62e76326 | 250 | *(list + list_count) = n; |
7c64cac7 | 251 | ++list_count; |
c907000b | 252 | } |
62e76326 | 253 | |
c907000b | 254 | qsort((char *) list, |
62e76326 | 255 | list_count, |
256 | sizeof(netdbEntry *), | |
257 | netdbLRU); | |
258 | ||
7c64cac7 | 259 | for (k = 0; k < list_count; ++k) { |
1a7cfe02 | 260 | if (netdbEntry::UseCount() < Config.Netdb.low) |
62e76326 | 261 | break; |
262 | ||
263 | netdbRelease(*(list + k)); | |
264 | ||
7c64cac7 | 265 | ++removed; |
c907000b | 266 | } |
62e76326 | 267 | |
c907000b | 268 | xfree(list); |
269 | } | |
270 | ||
67508012 | 271 | static netdbEntry * |
b7ac5457 | 272 | netdbLookupAddr(const Ip::Address &addr) |
87801fcb | 273 | { |
587d8445 | 274 | netdbEntry *n; |
cc192b50 | 275 | char *key = new char[MAX_IPSTRLEN]; |
4dd643d5 | 276 | networkFromInaddr(addr).toStr(key,MAX_IPSTRLEN); |
587d8445 | 277 | n = (netdbEntry *) hash_lookup(addr_table, key); |
4a7799f9 | 278 | delete[] key; |
587d8445 | 279 | return n; |
87801fcb | 280 | } |
281 | ||
67508012 | 282 | static netdbEntry * |
b7ac5457 | 283 | netdbAdd(Ip::Address &addr) |
87801fcb | 284 | { |
285 | netdbEntry *n; | |
62e76326 | 286 | |
1a7cfe02 | 287 | if (netdbEntry::UseCount() > Config.Netdb.high) |
62e76326 | 288 | netdbPurgeLRU(); |
289 | ||
26ac0430 | 290 | if ((n = netdbLookupAddr(addr)) == NULL) { |
1a7cfe02 | 291 | n = new netdbEntry; |
62e76326 | 292 | netdbHashInsert(n, addr); |
87801fcb | 293 | } |
62e76326 | 294 | |
67508012 | 295 | return n; |
87801fcb | 296 | } |
297 | ||
298 | static void | |
4a3b98d7 | 299 | netdbSendPing(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data) |
87801fcb | 300 | { |
b7ac5457 | 301 | Ip::Address addr; |
2eaf5ca0 | 302 | char *hostname = NULL; |
aa839030 | 303 | static_cast<generic_cbdata *>(data)->unwrap(&hostname); |
87801fcb | 304 | netdbEntry *n; |
b19361fd | 305 | netdbEntry *na; |
587d8445 | 306 | net_db_name *x; |
307 | net_db_name **X; | |
62e76326 | 308 | |
e5f6c5c2 | 309 | if (ia == NULL) { |
62e76326 | 310 | xfree(hostname); |
311 | return; | |
cb190ed7 | 312 | } |
62e76326 | 313 | |
e5f6c5c2 | 314 | addr = ia->in_addrs[ia->cur]; |
62e76326 | 315 | |
b19361fd | 316 | if ((n = netdbLookupHost(hostname)) == NULL) { |
62e76326 | 317 | n = netdbAdd(addr); |
318 | netdbHostInsert(n, hostname); | |
b19361fd | 319 | } else if ((na = netdbLookupAddr(addr)) != n) { |
62e76326 | 320 | /* |
321 | *hostname moved from 'network n' to 'network na'! | |
322 | */ | |
323 | ||
324 | if (na == NULL) | |
325 | na = netdbAdd(addr); | |
326 | ||
bf8fe701 | 327 | debugs(38, 3, "netdbSendPing: " << hostname << " moved from " << n->network << " to " << na->network); |
62e76326 | 328 | |
329 | x = (net_db_name *) hash_lookup(host_table, hostname); | |
330 | ||
331 | if (x == NULL) { | |
e0236918 | 332 | debugs(38, DBG_IMPORTANT, "netdbSendPing: net_db_name list bug: " << hostname << " not found"); |
62e76326 | 333 | xfree(hostname); |
334 | return; | |
335 | } | |
336 | ||
337 | /* remove net_db_name from 'network n' linked list */ | |
338 | for (X = &n->hosts; *X; X = &(*X)->next) { | |
339 | if (*X == x) { | |
340 | *X = x->next; | |
341 | break; | |
342 | } | |
343 | } | |
344 | ||
7c64cac7 | 345 | -- n->link_count; |
62e76326 | 346 | /* point to 'network na' from host entry */ |
347 | x->net_db_entry = na; | |
348 | /* link net_db_name to 'network na' */ | |
349 | x->next = na->hosts; | |
350 | na->hosts = x; | |
7c64cac7 | 351 | ++ na->link_count; |
62e76326 | 352 | n = na; |
b19361fd | 353 | } |
62e76326 | 354 | |
674ac814 | 355 | if (n->next_ping_time <= squid_curtime) { |
bf8fe701 | 356 | debugs(38, 3, "netdbSendPing: pinging " << hostname); |
cc192b50 | 357 | icmpEngine.DomainPing(addr, hostname); |
7c64cac7 | 358 | ++ n->pings_sent; |
62e76326 | 359 | n->next_ping_time = squid_curtime + Config.Netdb.period; |
360 | n->last_use_time = squid_curtime; | |
674ac814 | 361 | } |
62e76326 | 362 | |
28c60158 | 363 | xfree(hostname); |
67508012 | 364 | } |
365 | ||
b7ac5457 AJ |
366 | Ip::Address |
367 | networkFromInaddr(const Ip::Address &in) | |
67508012 | 368 | { |
b7ac5457 | 369 | Ip::Address out; |
cc192b50 | 370 | |
371 | out = in; | |
cc192b50 | 372 | |
373 | /* in IPv6 the 'network' should be the routing section. */ | |
4dd643d5 AJ |
374 | if ( in.isIPv6() ) { |
375 | out.applyMask(64, AF_INET6); | |
cc192b50 | 376 | debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in << "/64 routing part."); |
377 | return out; | |
378 | } | |
62e76326 | 379 | |
429fdbec | 380 | #if USE_CLASSFUL |
cc192b50 | 381 | struct in_addr b; |
382 | ||
4dd643d5 | 383 | in.getInAddr(b); |
62e76326 | 384 | |
67508012 | 385 | if (IN_CLASSC(b.s_addr)) |
62e76326 | 386 | b.s_addr &= IN_CLASSC_NET; |
67508012 | 387 | else if (IN_CLASSB(b.s_addr)) |
62e76326 | 388 | b.s_addr &= IN_CLASSB_NET; |
67508012 | 389 | else if (IN_CLASSA(b.s_addr)) |
62e76326 | 390 | b.s_addr &= IN_CLASSA_NET; |
391 | ||
cc192b50 | 392 | out = b; |
62e76326 | 393 | |
429fdbec | 394 | #endif |
62e76326 | 395 | |
cc192b50 | 396 | debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out << "/24."); |
62e76326 | 397 | |
cc192b50 | 398 | /* use /24 for everything under IPv4 */ |
4dd643d5 | 399 | out.applyMask(24, AF_INET); |
cc192b50 | 400 | debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in << "/24."); |
401 | ||
402 | return out; | |
67508012 | 403 | } |
404 | ||
405 | static int | |
79d39a72 | 406 | sortByRtt(const void *A, const void *B) |
67508012 | 407 | { |
e6ccf245 | 408 | const netdbEntry *const *n1 = (const netdbEntry *const *)A; |
409 | const netdbEntry *const *n2 = (const netdbEntry *const *)B; | |
62e76326 | 410 | |
429fdbec | 411 | if ((*n1)->rtt > (*n2)->rtt) |
62e76326 | 412 | return 1; |
429fdbec | 413 | else if ((*n1)->rtt < (*n2)->rtt) |
62e76326 | 414 | return -1; |
67508012 | 415 | else |
62e76326 | 416 | return 0; |
67508012 | 417 | } |
418 | ||
429fdbec | 419 | static net_db_peer * |
420 | netdbPeerByName(const netdbEntry * n, const char *peername) | |
421 | { | |
422 | int i; | |
423 | net_db_peer *p = n->peers; | |
62e76326 | 424 | |
7c64cac7 | 425 | for (i = 0; i < n->n_peers; ++i, ++p) { |
62e76326 | 426 | if (!strcmp(p->peername, peername)) |
427 | return p; | |
429fdbec | 428 | } |
62e76326 | 429 | |
429fdbec | 430 | return NULL; |
431 | } | |
432 | ||
433 | static net_db_peer * | |
a3c6762c | 434 | netdbPeerAdd(netdbEntry * n, CachePeer * e) |
429fdbec | 435 | { |
436 | net_db_peer *p; | |
437 | net_db_peer *o; | |
438 | int osize; | |
439 | int i; | |
62e76326 | 440 | |
429fdbec | 441 | if (n->n_peers == n->n_peers_alloc) { |
62e76326 | 442 | o = n->peers; |
443 | osize = n->n_peers_alloc; | |
444 | ||
445 | if (n->n_peers_alloc == 0) | |
446 | n->n_peers_alloc = 2; | |
447 | else | |
448 | n->n_peers_alloc <<= 1; | |
449 | ||
bf8fe701 | 450 | debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n->network << "' to " << n->n_peers_alloc); |
62e76326 | 451 | |
452 | n->peers = (net_db_peer *)xcalloc(n->n_peers_alloc, sizeof(net_db_peer)); | |
453 | ||
7c64cac7 | 454 | for (i = 0; i < osize; ++i) |
62e76326 | 455 | *(n->peers + i) = *(o + i); |
456 | ||
457 | if (osize) { | |
458 | safe_free(o); | |
459 | } | |
429fdbec | 460 | } |
62e76326 | 461 | |
429fdbec | 462 | p = n->peers + n->n_peers; |
463 | p->peername = netdbPeerName(e->host); | |
7c64cac7 | 464 | ++ n->n_peers; |
429fdbec | 465 | return p; |
466 | } | |
467 | ||
468 | static int | |
79d39a72 | 469 | sortPeerByRtt(const void *A, const void *B) |
429fdbec | 470 | { |
e6ccf245 | 471 | const net_db_peer *p1 = (net_db_peer *)A; |
472 | const net_db_peer *p2 = (net_db_peer *)B; | |
62e76326 | 473 | |
429fdbec | 474 | if (p1->rtt > p2->rtt) |
62e76326 | 475 | return 1; |
429fdbec | 476 | else if (p1->rtt < p2->rtt) |
62e76326 | 477 | return -1; |
429fdbec | 478 | else |
62e76326 | 479 | return 0; |
429fdbec | 480 | } |
481 | ||
482 | static void | |
483 | netdbSaveState(void *foo) | |
484 | { | |
b7ed6dbb | 485 | if (strcmp(Config.netdbFilename, "none") == 0) |
26ac0430 | 486 | return; |
2b753521 | 487 | |
08e8e020 | 488 | Logfile *lf; |
429fdbec | 489 | netdbEntry *n; |
429fdbec | 490 | net_db_name *x; |
62e76326 | 491 | |
429fdbec | 492 | struct timeval start = current_time; |
493 | int count = 0; | |
111b16cf | 494 | /* |
495 | * This was nicer when we were using stdio, but thanks to | |
496 | * Solaris bugs, its a bad idea. fopen can fail if more than | |
497 | * 256 FDs are open. | |
498 | */ | |
e3732f4f | 499 | /* |
500 | * unlink() is here because there is currently no way to make | |
501 | * logfileOpen() use O_TRUNC. | |
502 | */ | |
2b753521 | 503 | unlink(Config.netdbFilename); |
504 | lf = logfileOpen(Config.netdbFilename, 4096, 0); | |
62e76326 | 505 | |
b69e9ffa AJ |
506 | if (lf) { |
507 | int xerrno = errno; | |
508 | debugs(50, DBG_IMPORTANT, MYNAME << Config.netdbFilename << ": " << xstrerr(xerrno)); | |
62e76326 | 509 | return; |
429fdbec | 510 | } |
62e76326 | 511 | |
0f6bebac | 512 | hash_first(addr_table); |
62e76326 | 513 | |
0f6bebac | 514 | while ((n = (netdbEntry *) hash_next(addr_table))) { |
62e76326 | 515 | if (n->pings_recv == 0) |
516 | continue; | |
517 | ||
518 | logfilePrintf(lf, "%s %d %d %10.5f %10.5f %d %d", | |
519 | n->network, | |
520 | n->pings_sent, | |
521 | n->pings_recv, | |
522 | n->hops, | |
523 | n->rtt, | |
524 | (int) n->next_ping_time, | |
525 | (int) n->last_use_time); | |
526 | ||
527 | for (x = n->hosts; x; x = x->next) | |
528 | logfilePrintf(lf, " %s", hashKeyStr(&x->hash)); | |
529 | ||
530 | logfilePrintf(lf, "\n"); | |
531 | ||
7c64cac7 | 532 | ++count; |
62e76326 | 533 | |
111b16cf | 534 | #undef RBUF_SZ |
62e76326 | 535 | |
429fdbec | 536 | } |
62e76326 | 537 | |
08e8e020 | 538 | logfileClose(lf); |
587d8445 | 539 | getCurrentTime(); |
e0236918 | 540 | debugs(38, DBG_IMPORTANT, "NETDB state saved; " << |
26ac0430 AJ |
541 | count << " entries, " << |
542 | tvSubMsec(start, current_time) << " msec" ); | |
52040193 | 543 | eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1); |
429fdbec | 544 | } |
545 | ||
546 | static void | |
547 | netdbReloadState(void) | |
548 | { | |
b7ed6dbb | 549 | if (strcmp(Config.netdbFilename, "none") == 0) |
26ac0430 | 550 | return; |
2b753521 | 551 | |
111b16cf | 552 | char *s; |
553 | int fd; | |
554 | int l; | |
62e76326 | 555 | |
111b16cf | 556 | struct stat sb; |
429fdbec | 557 | netdbEntry *n; |
558 | netdbEntry N; | |
62e76326 | 559 | |
b7ac5457 | 560 | Ip::Address addr; |
429fdbec | 561 | int count = 0; |
62e76326 | 562 | |
429fdbec | 563 | struct timeval start = current_time; |
111b16cf | 564 | /* |
565 | * This was nicer when we were using stdio, but thanks to | |
566 | * Solaris bugs, its a bad idea. fopen can fail if more than | |
567 | * 256 FDs are open. | |
568 | */ | |
2b753521 | 569 | fd = file_open(Config.netdbFilename, O_RDONLY | O_BINARY); |
62e76326 | 570 | |
111b16cf | 571 | if (fd < 0) |
62e76326 | 572 | return; |
573 | ||
053ea9f4 | 574 | if (fstat(fd, &sb) < 0) { |
62e76326 | 575 | file_close(fd); |
576 | return; | |
053ea9f4 | 577 | } |
62e76326 | 578 | |
e0bbe966 | 579 | char *t; |
580 | char *buf = (char *)xcalloc(1, sb.st_size + 1); | |
581 | t = buf; | |
d0ef8ea8 | 582 | l = FD_READ_METHOD(fd, buf, sb.st_size); |
111b16cf | 583 | file_close(fd); |
62e76326 | 584 | |
e0bbe966 | 585 | if (l <= 0) { |
62e76326 | 586 | safe_free (buf); |
587 | return; | |
e0bbe966 | 588 | }; |
62e76326 | 589 | |
111b16cf | 590 | while ((s = strchr(t, '\n'))) { |
62e76326 | 591 | char *q; |
592 | assert(s - buf < l); | |
593 | *s = '\0'; | |
594 | memset(&N, '\0', sizeof(netdbEntry)); | |
595 | q = strtok(t, w_space); | |
596 | t = s + 1; | |
597 | ||
598 | if (NULL == q) | |
599 | continue; | |
600 | ||
cc192b50 | 601 | if (! (addr = q) ) |
62e76326 | 602 | continue; |
603 | ||
f53969cc | 604 | if (netdbLookupAddr(addr) != NULL) /* no dups! */ |
62e76326 | 605 | continue; |
606 | ||
607 | if ((q = strtok(NULL, w_space)) == NULL) | |
608 | continue; | |
609 | ||
610 | N.pings_sent = atoi(q); | |
611 | ||
612 | if ((q = strtok(NULL, w_space)) == NULL) | |
613 | continue; | |
614 | ||
615 | N.pings_recv = atoi(q); | |
616 | ||
617 | if (N.pings_recv == 0) | |
618 | continue; | |
619 | ||
620 | /* give this measurement low weight */ | |
621 | N.pings_sent = 1; | |
622 | ||
623 | N.pings_recv = 1; | |
624 | ||
625 | if ((q = strtok(NULL, w_space)) == NULL) | |
626 | continue; | |
627 | ||
628 | N.hops = atof(q); | |
629 | ||
630 | if ((q = strtok(NULL, w_space)) == NULL) | |
631 | continue; | |
632 | ||
633 | N.rtt = atof(q); | |
634 | ||
635 | if ((q = strtok(NULL, w_space)) == NULL) | |
636 | continue; | |
637 | ||
638 | N.next_ping_time = (time_t) atoi(q); | |
639 | ||
640 | if ((q = strtok(NULL, w_space)) == NULL) | |
641 | continue; | |
642 | ||
643 | N.last_use_time = (time_t) atoi(q); | |
644 | ||
1a7cfe02 | 645 | n = new netdbEntry; |
62e76326 | 646 | |
41d00cd3 | 647 | memcpy(n, &N, sizeof(netdbEntry)); |
62e76326 | 648 | |
649 | netdbHashInsert(n, addr); | |
650 | ||
651 | while ((q = strtok(NULL, w_space)) != NULL) { | |
f53969cc | 652 | if (netdbLookupHost(q) != NULL) /* no dups! */ |
62e76326 | 653 | continue; |
654 | ||
655 | netdbHostInsert(n, q); | |
656 | } | |
657 | ||
7c64cac7 | 658 | ++count; |
429fdbec | 659 | } |
62e76326 | 660 | |
111b16cf | 661 | xfree(buf); |
587d8445 | 662 | getCurrentTime(); |
e0236918 | 663 | debugs(38, DBG_IMPORTANT, "NETDB state reloaded; " << |
26ac0430 AJ |
664 | count << " entries, " << |
665 | tvSubMsec(start, current_time) << " msec" ); | |
429fdbec | 666 | } |
667 | ||
858783c9 | 668 | static const char * |
429fdbec | 669 | netdbPeerName(const char *name) |
670 | { | |
858783c9 | 671 | const wordlist *w; |
62e76326 | 672 | |
429fdbec | 673 | for (w = peer_names; w; w = w->next) { |
62e76326 | 674 | if (!strcmp(w->key, name)) |
675 | return w->key; | |
429fdbec | 676 | } |
62e76326 | 677 | |
858783c9 | 678 | return wordlistAdd(&peer_names, name); |
429fdbec | 679 | } |
680 | ||
45c54bdc | 681 | static void |
682 | netdbFreeNetdbEntry(void *data) | |
683 | { | |
e6ccf245 | 684 | netdbEntry *n = (netdbEntry *)data; |
45c54bdc | 685 | safe_free(n->peers); |
1a7cfe02 | 686 | delete n; |
45c54bdc | 687 | } |
429fdbec | 688 | |
45c54bdc | 689 | static void |
690 | netdbFreeNameEntry(void *data) | |
691 | { | |
e6ccf245 | 692 | net_db_name *x = (net_db_name *)data; |
3c670b50 | 693 | delete x; |
45c54bdc | 694 | } |
695 | ||
696 | static void | |
2324cda2 | 697 | netdbExchangeHandleReply(void *data, StoreIOBuffer receivedData) |
45c54bdc | 698 | { |
b7ac5457 | 699 | Ip::Address addr; |
cc192b50 | 700 | |
e6ccf245 | 701 | netdbExchangeState *ex = (netdbExchangeState *)data; |
45c54bdc | 702 | int rec_sz = 0; |
57d55dfa | 703 | int o; |
62e76326 | 704 | |
cc192b50 | 705 | struct in_addr line_addr; |
45c54bdc | 706 | double rtt; |
707 | double hops; | |
708 | char *p; | |
709 | int j; | |
528b2c61 | 710 | HttpReply const *rep; |
45c54bdc | 711 | size_t hdr_sz; |
70305391 | 712 | int nused = 0; |
add2192d | 713 | int size; |
714 | int oldbufofs = ex->buf_ofs; | |
715 | ||
45c54bdc | 716 | rec_sz = 0; |
cc192b50 | 717 | rec_sz += 1 + sizeof(struct in_addr); |
45c54bdc | 718 | rec_sz += 1 + sizeof(int); |
719 | rec_sz += 1 + sizeof(int); | |
2324cda2 | 720 | debugs(38, 3, "netdbExchangeHandleReply: " << receivedData.length << " read bytes"); |
62e76326 | 721 | |
fa80a8ef | 722 | if (!cbdataReferenceValid(ex->p)) { |
bf8fe701 | 723 | debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid"); |
fa91d030 | 724 | delete ex; |
62e76326 | 725 | return; |
45c54bdc | 726 | } |
62e76326 | 727 | |
bf8fe701 | 728 | debugs(38, 3, "netdbExchangeHandleReply: for '" << ex->p->host << ":" << ex->p->http_port << "'"); |
528b2c61 | 729 | |
fa91d030 | 730 | if (receivedData.length == 0 && !receivedData.flags.error) { |
bf8fe701 | 731 | debugs(38, 3, "netdbExchangeHandleReply: Done"); |
fa91d030 | 732 | delete ex; |
62e76326 | 733 | return; |
528b2c61 | 734 | } |
62e76326 | 735 | |
add2192d | 736 | p = ex->buf; |
737 | ||
738 | /* Get the size of the buffer now */ | |
2324cda2 | 739 | size = ex->buf_ofs + receivedData.length; |
137a13ea | 740 | debugs(38, 3, "netdbExchangeHandleReply: " << size << " bytes buf"); |
add2192d | 741 | |
742 | /* Check if we're still doing headers */ | |
62e76326 | 743 | |
add2192d | 744 | if (ex->connstate == STATE_HEADER) { |
745 | ||
2324cda2 | 746 | ex->buf_ofs += receivedData.length; |
62e76326 | 747 | |
748 | /* skip reply headers */ | |
749 | ||
750 | if ((hdr_sz = headersEnd(p, ex->buf_ofs))) { | |
137a13ea | 751 | debugs(38, 5, "netdbExchangeHandleReply: hdr_sz = " << hdr_sz); |
62e76326 | 752 | rep = ex->e->getReply(); |
9b769c67 AJ |
753 | assert(rep->sline.status() != Http::scNone); |
754 | debugs(38, 3, "netdbExchangeHandleReply: reply status " << rep->sline.status()); | |
62e76326 | 755 | |
9b769c67 | 756 | if (rep->sline.status() != Http::scOkay) { |
fa91d030 | 757 | delete ex; |
62e76326 | 758 | return; |
759 | } | |
760 | ||
761 | assert((size_t)ex->buf_ofs >= hdr_sz); | |
762 | ||
763 | /* | |
764 | * Now, point p to the part of the buffer where the data | |
765 | * starts, and update the size accordingly | |
766 | */ | |
767 | assert(ex->used == 0); | |
768 | ex->used = hdr_sz; | |
769 | size = ex->buf_ofs - hdr_sz; | |
770 | p += hdr_sz; | |
771 | ||
772 | /* Finally, set the conn state mode to STATE_BODY */ | |
773 | ex->connstate = STATE_BODY; | |
774 | } else { | |
775 | StoreIOBuffer tempBuffer; | |
776 | tempBuffer.offset = ex->buf_ofs; | |
777 | tempBuffer.length = ex->buf_sz - ex->buf_ofs; | |
778 | tempBuffer.data = ex->buf + ex->buf_ofs; | |
779 | /* Have more headers .. */ | |
780 | storeClientCopy(ex->sc, ex->e, tempBuffer, | |
781 | netdbExchangeHandleReply, ex); | |
782 | return; | |
783 | } | |
45c54bdc | 784 | } |
62e76326 | 785 | |
add2192d | 786 | assert(ex->connstate == STATE_BODY); |
787 | ||
788 | /* If we get here, we have some body to parse .. */ | |
bf8fe701 | 789 | debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size); |
62e76326 | 790 | |
45c54bdc | 791 | while (size >= rec_sz) { |
bf8fe701 | 792 | debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size); |
4dd643d5 | 793 | addr.setAnyAddr(); |
62e76326 | 794 | hops = rtt = 0.0; |
795 | ||
796 | for (o = 0; o < rec_sz;) { | |
797 | switch ((int) *(p + o)) { | |
798 | ||
799 | case NETDB_EX_NETWORK: | |
7c64cac7 | 800 | ++o; |
cc192b50 | 801 | /* FIXME INET6 : NetDB can still ony send IPv4 */ |
41d00cd3 | 802 | memcpy(&line_addr, p + o, sizeof(struct in_addr)); |
cc192b50 | 803 | addr = line_addr; |
804 | o += sizeof(struct in_addr); | |
62e76326 | 805 | break; |
806 | ||
807 | case NETDB_EX_RTT: | |
7c64cac7 | 808 | ++o; |
41d00cd3 | 809 | memcpy(&j, p + o, sizeof(int)); |
62e76326 | 810 | o += sizeof(int); |
811 | rtt = (double) ntohl(j) / 1000.0; | |
812 | break; | |
813 | ||
814 | case NETDB_EX_HOPS: | |
7c64cac7 | 815 | ++o; |
41d00cd3 | 816 | memcpy(&j, p + o, sizeof(int)); |
62e76326 | 817 | o += sizeof(int); |
818 | hops = (double) ntohl(j) / 1000.0; | |
819 | break; | |
820 | ||
821 | default: | |
e0236918 | 822 | debugs(38, DBG_IMPORTANT, "netdbExchangeHandleReply: corrupt data, aborting"); |
fa91d030 | 823 | delete ex; |
62e76326 | 824 | return; |
825 | } | |
826 | } | |
827 | ||
4dd643d5 | 828 | if (!addr.isAnyAddr() && rtt > 0) |
62e76326 | 829 | netdbExchangeUpdatePeer(addr, ex->p, rtt, hops); |
830 | ||
831 | assert(o == rec_sz); | |
832 | ||
833 | ex->used += rec_sz; | |
834 | ||
835 | size -= rec_sz; | |
836 | ||
837 | p += rec_sz; | |
838 | ||
7c64cac7 | 839 | ++nused; |
45c54bdc | 840 | } |
add2192d | 841 | |
842 | /* | |
843 | * Copy anything that is left over to the beginning of the buffer, | |
844 | * and adjust buf_ofs accordingly | |
845 | */ | |
846 | ||
847 | /* | |
848 | * Evilly, size refers to the buf size left now, | |
849 | * ex->buf_ofs is the original buffer size, so just copy that | |
850 | * much data over | |
851 | */ | |
852 | memmove(ex->buf, ex->buf + (ex->buf_ofs - size), size); | |
62e76326 | 853 | |
add2192d | 854 | ex->buf_ofs = size; |
855 | ||
856 | /* | |
857 | * And don't re-copy the remaining data .. | |
858 | */ | |
859 | ex->used += size; | |
860 | ||
861 | /* | |
862 | * Now the tricky bit - size _included_ the leftover bit from the _last_ | |
863 | * storeClientCopy. We don't want to include that, or our offset will be wrong. | |
864 | * So, don't count the size of the leftover buffer we began with. | |
865 | * This can _disappear_ when we're not tracking offsets .. | |
866 | */ | |
867 | ex->used -= oldbufofs; | |
868 | ||
bf8fe701 | 869 | debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size << " bytes"); |
add2192d | 870 | |
bf8fe701 | 871 | debugs(38, 3, "netdbExchangeHandleReply: used " << nused << |
872 | " entries, (x " << rec_sz << " bytes) == " << nused * rec_sz << | |
873 | " bytes total"); | |
62e76326 | 874 | |
4a7a3d56 | 875 | debugs(38, 3, "netdbExchangeHandleReply: used " << ex->used); |
62e76326 | 876 | |
b7fe0ab0 | 877 | if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) { |
bf8fe701 | 878 | debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED"); |
fa91d030 | 879 | delete ex; |
ce9b54e8 | 880 | } else if (ex->e->store_status == STORE_PENDING) { |
62e76326 | 881 | StoreIOBuffer tempBuffer; |
882 | tempBuffer.offset = ex->used; | |
883 | tempBuffer.length = ex->buf_sz - ex->buf_ofs; | |
884 | tempBuffer.data = ex->buf + ex->buf_ofs; | |
2324cda2 | 885 | debugs(38, 3, "netdbExchangeHandleReply: EOF not received"); |
62e76326 | 886 | storeClientCopy(ex->sc, ex->e, tempBuffer, |
887 | netdbExchangeHandleReply, ex); | |
45c54bdc | 888 | } |
889 | } | |
890 | ||
f69d9265 AJ |
891 | #endif /* USE_ICMP */ |
892 | ||
e97f40f4 | 893 | /* PUBLIC FUNCTIONS */ |
894 | ||
67508012 | 895 | void |
e97f40f4 | 896 | netdbInit(void) |
897 | { | |
898 | #if USE_ICMP | |
1a7cfe02 | 899 | Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump, 0, 1); |
d120ed12 | 900 | |
19054954 | 901 | if (addr_table) |
62e76326 | 902 | return; |
903 | ||
1a7cfe02 | 904 | int n = hashPrime(Config.Netdb.high / 4); |
62e76326 | 905 | |
30abd221 | 906 | addr_table = hash_create((HASHCMP *) strcmp, n, hash_string); |
62e76326 | 907 | |
aa9e2cab | 908 | n = hashPrime(3 * Config.Netdb.high / 4); |
62e76326 | 909 | |
30abd221 | 910 | host_table = hash_create((HASHCMP *) strcmp, n, hash_string); |
62e76326 | 911 | |
52040193 | 912 | eventAddIsh("netdbSaveState", netdbSaveState, NULL, 3600.0, 1); |
62e76326 | 913 | |
429fdbec | 914 | netdbReloadState(); |
62e76326 | 915 | |
62ee09ca | 916 | #endif |
917 | } | |
918 | ||
e97f40f4 | 919 | void |
0ee4272b | 920 | netdbPingSite(const char *hostname) |
67508012 | 921 | { |
e97f40f4 | 922 | #if USE_ICMP |
67508012 | 923 | netdbEntry *n; |
62e76326 | 924 | |
e97f40f4 | 925 | if ((n = netdbLookupHost(hostname)) != NULL) |
62e76326 | 926 | if (n->next_ping_time > squid_curtime) |
927 | return; | |
928 | ||
aa839030 | 929 | ipcache_nbgethostbyname(hostname, netdbSendPing, |
26ac0430 | 930 | new generic_cbdata(xstrdup(hostname))); |
62e76326 | 931 | |
e97f40f4 | 932 | #endif |
67508012 | 933 | } |
934 | ||
e97f40f4 | 935 | void |
b7ac5457 | 936 | netdbHandlePingReply(const Ip::Address &from, int hops, int rtt) |
4d311579 | 937 | { |
e97f40f4 | 938 | #if USE_ICMP |
939 | netdbEntry *n; | |
940 | int N; | |
cc192b50 | 941 | debugs(38, 3, "netdbHandlePingReply: from " << from); |
62e76326 | 942 | |
cc192b50 | 943 | if ((n = netdbLookupAddr(from)) == NULL) |
62e76326 | 944 | return; |
945 | ||
429fdbec | 946 | N = ++n->pings_recv; |
62e76326 | 947 | |
429fdbec | 948 | if (N > 5) |
62e76326 | 949 | N = 5; |
950 | ||
e6ccf245 | 951 | if (rtt < 1) |
62e76326 | 952 | rtt = 1; |
953 | ||
e97f40f4 | 954 | n->hops = ((n->hops * (N - 1)) + hops) / N; |
62e76326 | 955 | |
e97f40f4 | 956 | n->rtt = ((n->rtt * (N - 1)) + rtt) / N; |
62e76326 | 957 | |
bf8fe701 | 958 | debugs(38, 3, "netdbHandlePingReply: " << n->network << "; rtt="<< |
959 | std::setw(5)<< std::setprecision(2) << n->rtt << " hops="<< | |
960 | std::setw(4) << n->hops); | |
62e76326 | 961 | |
e97f40f4 | 962 | #endif |
4d311579 | 963 | } |
e5f6c5c2 | 964 | |
965 | void | |
966 | netdbFreeMemory(void) | |
967 | { | |
e97f40f4 | 968 | #if USE_ICMP |
ec878047 | 969 | hashFreeItems(addr_table, netdbFreeNetdbEntry); |
e5f6c5c2 | 970 | hashFreeMemory(addr_table); |
afe95a7e | 971 | addr_table = NULL; |
ec878047 | 972 | hashFreeItems(host_table, netdbFreeNameEntry); |
973 | hashFreeMemory(host_table); | |
afe95a7e | 974 | host_table = NULL; |
429fdbec | 975 | wordlistDestroy(&peer_names); |
976 | peer_names = NULL; | |
e97f40f4 | 977 | #endif |
e5f6c5c2 | 978 | } |
979 | ||
e97f40f4 | 980 | void |
981 | netdbDump(StoreEntry * sentry) | |
982 | { | |
983 | #if USE_ICMP | |
984 | netdbEntry *n; | |
985 | netdbEntry **list; | |
429fdbec | 986 | net_db_name *x; |
e97f40f4 | 987 | int k; |
988 | int i; | |
429fdbec | 989 | int j; |
990 | net_db_peer *p; | |
15576b6a | 991 | storeAppendPrintf(sentry, "Network DB Statistics:\n"); |
cc192b50 | 992 | storeAppendPrintf(sentry, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */ |
62e76326 | 993 | "Network", |
994 | "recv/sent", | |
995 | "RTT", | |
996 | "Hops", | |
997 | "Hostnames"); | |
1a7cfe02 | 998 | list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *)); |
0ee4272b | 999 | i = 0; |
0f6bebac | 1000 | hash_first(addr_table); |
62e76326 | 1001 | |
a38ec4b1 FC |
1002 | while ((n = (netdbEntry *) hash_next(addr_table))) { |
1003 | *(list + i) = n; | |
1004 | ++i; | |
1005 | } | |
62e76326 | 1006 | |
1a7cfe02 | 1007 | if (i != netdbEntry::UseCount()) |
fa84c01d | 1008 | debugs(38, DBG_CRITICAL, "WARNING: netdb_addrs count off, found " << i << |
1a7cfe02 | 1009 | ", expected " << netdbEntry::UseCount()); |
62e76326 | 1010 | |
0ee4272b | 1011 | qsort((char *) list, |
62e76326 | 1012 | i, |
1013 | sizeof(netdbEntry *), | |
1014 | sortByRtt); | |
1015 | ||
7c64cac7 | 1016 | for (k = 0; k < i; ++k) { |
62e76326 | 1017 | n = *(list + k); |
cc192b50 | 1018 | storeAppendPrintf(sentry, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */ |
62e76326 | 1019 | n->network, |
1020 | n->pings_recv, | |
1021 | n->pings_sent, | |
1022 | n->rtt, | |
1023 | n->hops); | |
1024 | ||
1025 | for (x = n->hosts; x; x = x->next) | |
1026 | storeAppendPrintf(sentry, " %s", hashKeyStr(&x->hash)); | |
1027 | ||
1028 | storeAppendPrintf(sentry, "\n"); | |
1029 | ||
1030 | p = n->peers; | |
1031 | ||
7c64cac7 | 1032 | for (j = 0; j < n->n_peers; ++j, ++p) { |
62e76326 | 1033 | storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n", |
1034 | p->peername, | |
1035 | p->rtt, | |
1036 | p->hops); | |
1037 | } | |
0ee4272b | 1038 | } |
62e76326 | 1039 | |
0ee4272b | 1040 | xfree(list); |
c8391077 | 1041 | #else |
62e76326 | 1042 | |
9b5c4a9a | 1043 | storeAppendPrintf(sentry,"NETDB support not compiled into this Squid cache.\n"); |
e97f40f4 | 1044 | #endif |
1045 | } | |
48f44632 | 1046 | |
1047 | int | |
1048 | netdbHostHops(const char *host) | |
1049 | { | |
1050 | #if USE_ICMP | |
1051 | netdbEntry *n = netdbLookupHost(host); | |
62e76326 | 1052 | |
429fdbec | 1053 | if (n) { |
62e76326 | 1054 | n->last_use_time = squid_curtime; |
1055 | return (int) (n->hops + 0.5); | |
429fdbec | 1056 | } |
62e76326 | 1057 | |
48f44632 | 1058 | #endif |
429fdbec | 1059 | return 0; |
48f44632 | 1060 | } |
1061 | ||
1062 | int | |
1063 | netdbHostRtt(const char *host) | |
1064 | { | |
1065 | #if USE_ICMP | |
1066 | netdbEntry *n = netdbLookupHost(host); | |
62e76326 | 1067 | |
429fdbec | 1068 | if (n) { |
62e76326 | 1069 | n->last_use_time = squid_curtime; |
1070 | return (int) (n->rtt + 0.5); | |
429fdbec | 1071 | } |
62e76326 | 1072 | |
429fdbec | 1073 | #endif |
1074 | return 0; | |
1075 | } | |
1076 | ||
1d6ae62d | 1077 | void |
1078 | netdbHostData(const char *host, int *samp, int *rtt, int *hops) | |
1079 | { | |
1080 | #if USE_ICMP | |
5942e8d4 | 1081 | netdbEntry *n = netdbLookupHost(host); |
62e76326 | 1082 | |
5942e8d4 | 1083 | if (n == NULL) |
62e76326 | 1084 | return; |
1085 | ||
5942e8d4 | 1086 | *samp = n->pings_recv; |
62e76326 | 1087 | |
5942e8d4 | 1088 | *rtt = (int) (n->rtt + 0.5); |
62e76326 | 1089 | |
5942e8d4 | 1090 | *hops = (int) (n->hops + 0.5); |
62e76326 | 1091 | |
4ed6ef62 | 1092 | n->last_use_time = squid_curtime; |
62e76326 | 1093 | |
1d6ae62d | 1094 | #endif |
1095 | } | |
1096 | ||
429fdbec | 1097 | void |
5c51bffb | 1098 | netdbUpdatePeer(const URL &url, CachePeer * e, int irtt, int ihops) |
429fdbec | 1099 | { |
1100 | #if USE_ICMP | |
1101 | netdbEntry *n; | |
1102 | double rtt = (double) irtt; | |
1103 | double hops = (double) ihops; | |
1104 | net_db_peer *p; | |
5c51bffb AJ |
1105 | debugs(38, 3, url.host() << ", " << ihops << " hops, " << irtt << " rtt"); |
1106 | n = netdbLookupHost(url.host()); | |
62e76326 | 1107 | |
429fdbec | 1108 | if (n == NULL) { |
5c51bffb | 1109 | debugs(38, 3, "host " << url.host() << " not found"); |
62e76326 | 1110 | return; |
429fdbec | 1111 | } |
62e76326 | 1112 | |
429fdbec | 1113 | if ((p = netdbPeerByName(n, e->host)) == NULL) |
62e76326 | 1114 | p = netdbPeerAdd(n, e); |
1115 | ||
429fdbec | 1116 | p->rtt = rtt; |
62e76326 | 1117 | |
429fdbec | 1118 | p->hops = hops; |
62e76326 | 1119 | |
429fdbec | 1120 | p->expires = squid_curtime + 3600; |
62e76326 | 1121 | |
429fdbec | 1122 | if (n->n_peers < 2) |
62e76326 | 1123 | return; |
1124 | ||
429fdbec | 1125 | qsort((char *) n->peers, |
62e76326 | 1126 | n->n_peers, |
1127 | sizeof(net_db_peer), | |
1128 | sortPeerByRtt); | |
1129 | ||
48f44632 | 1130 | #endif |
48f44632 | 1131 | } |
ce75f381 | 1132 | |
27efd484 | 1133 | void |
a3c6762c | 1134 | netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer * e, double rtt, double hops) |
27efd484 | 1135 | { |
1136 | #if USE_ICMP | |
1137 | netdbEntry *n; | |
1138 | net_db_peer *p; | |
cc192b50 | 1139 | debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr << "', "<< |
bf8fe701 | 1140 | std::setfill('0')<< std::setprecision(2) << hops << " hops, " << |
1141 | rtt << " rtt"); | |
1142 | ||
4dd643d5 | 1143 | if ( !addr.isIPv4() ) { |
cc192b50 | 1144 | debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr << "', NetDB cannot handle IPv6."); |
1145 | return; | |
1146 | } | |
1147 | ||
27efd484 | 1148 | n = netdbLookupAddr(addr); |
62e76326 | 1149 | |
27efd484 | 1150 | if (n == NULL) |
62e76326 | 1151 | n = netdbAdd(addr); |
1152 | ||
27efd484 | 1153 | assert(NULL != n); |
62e76326 | 1154 | |
27efd484 | 1155 | if ((p = netdbPeerByName(n, e->host)) == NULL) |
62e76326 | 1156 | p = netdbPeerAdd(n, e); |
1157 | ||
27efd484 | 1158 | p->rtt = rtt; |
62e76326 | 1159 | |
27efd484 | 1160 | p->hops = hops; |
62e76326 | 1161 | |
f53969cc | 1162 | p->expires = squid_curtime + 3600; /* XXX ? */ |
62e76326 | 1163 | |
27efd484 | 1164 | if (n->n_peers < 2) |
62e76326 | 1165 | return; |
1166 | ||
27efd484 | 1167 | qsort((char *) n->peers, |
62e76326 | 1168 | n->n_peers, |
1169 | sizeof(net_db_peer), | |
1170 | sortPeerByRtt); | |
1171 | ||
27efd484 | 1172 | #endif |
1173 | } | |
1174 | ||
587d8445 | 1175 | void |
b7ac5457 | 1176 | netdbDeleteAddrNetwork(Ip::Address &addr) |
587d8445 | 1177 | { |
1178 | #if USE_ICMP | |
1179 | netdbEntry *n = netdbLookupAddr(addr); | |
62e76326 | 1180 | |
587d8445 | 1181 | if (n == NULL) |
62e76326 | 1182 | return; |
1183 | ||
bf8fe701 | 1184 | debugs(38, 3, "netdbDeleteAddrNetwork: " << n->network); |
62e76326 | 1185 | |
587d8445 | 1186 | netdbRelease(n); |
1187 | #endif | |
1188 | } | |
de2a0782 | 1189 | |
1190 | void | |
1191 | netdbBinaryExchange(StoreEntry * s) | |
1192 | { | |
06a5ae20 | 1193 | HttpReply *reply = new HttpReply; |
9ad1cbca | 1194 | #if USE_ICMP |
62e76326 | 1195 | |
b7ac5457 | 1196 | Ip::Address addr; |
cc192b50 | 1197 | |
de2a0782 | 1198 | netdbEntry *n; |
de2a0782 | 1199 | int i; |
1200 | int j; | |
1201 | int rec_sz; | |
1202 | char *buf; | |
62e76326 | 1203 | |
cc192b50 | 1204 | struct in_addr line_addr; |
3900307b | 1205 | s->buffer(); |
955394ce | 1206 | reply->setHeaders(Http::scOkay, "OK", NULL, -1, squid_curtime, -2); |
db237875 | 1207 | s->replaceHttpReply(reply); |
de2a0782 | 1208 | rec_sz = 0; |
cc192b50 | 1209 | rec_sz += 1 + sizeof(struct in_addr); |
de2a0782 | 1210 | rec_sz += 1 + sizeof(int); |
1211 | rec_sz += 1 + sizeof(int); | |
e6ccf245 | 1212 | buf = (char *)memAllocate(MEM_4K_BUF); |
de2a0782 | 1213 | i = 0; |
0f6bebac | 1214 | hash_first(addr_table); |
62e76326 | 1215 | |
0f6bebac | 1216 | while ((n = (netdbEntry *) hash_next(addr_table))) { |
62e76326 | 1217 | if (0.0 == n->rtt) |
1218 | continue; | |
1219 | ||
f53969cc | 1220 | if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */ |
62e76326 | 1221 | continue; |
1222 | ||
cc192b50 | 1223 | if (! (addr = n->network) ) |
1224 | continue; | |
1225 | ||
1226 | /* FIXME INET6 : NetDB cannot yet handle IPv6 addresses. Ensure only IPv4 get sent. */ | |
4dd643d5 | 1227 | if ( !addr.isIPv4() ) |
62e76326 | 1228 | continue; |
1229 | ||
a38ec4b1 FC |
1230 | buf[i] = (char) NETDB_EX_NETWORK; |
1231 | ++i; | |
62e76326 | 1232 | |
4dd643d5 | 1233 | addr.getInAddr(line_addr); |
41d00cd3 | 1234 | memcpy(&buf[i], &line_addr, sizeof(struct in_addr)); |
62e76326 | 1235 | |
cc192b50 | 1236 | i += sizeof(struct in_addr); |
62e76326 | 1237 | |
a38ec4b1 FC |
1238 | buf[i] = (char) NETDB_EX_RTT; |
1239 | ++i; | |
62e76326 | 1240 | |
1241 | j = htonl((int) (n->rtt * 1000)); | |
1242 | ||
41d00cd3 | 1243 | memcpy(&buf[i], &j, sizeof(int)); |
62e76326 | 1244 | |
1245 | i += sizeof(int); | |
1246 | ||
a38ec4b1 FC |
1247 | buf[i] = (char) NETDB_EX_HOPS; |
1248 | ++i; | |
62e76326 | 1249 | |
1250 | j = htonl((int) (n->hops * 1000)); | |
1251 | ||
41d00cd3 | 1252 | memcpy(&buf[i], &j, sizeof(int)); |
62e76326 | 1253 | |
1254 | i += sizeof(int); | |
1255 | ||
1256 | if (i + rec_sz > 4096) { | |
3900307b | 1257 | s->append(buf, i); |
62e76326 | 1258 | i = 0; |
1259 | } | |
de2a0782 | 1260 | } |
62e76326 | 1261 | |
84075e8f | 1262 | if (i > 0) { |
3900307b | 1263 | s->append(buf, i); |
62e76326 | 1264 | i = 0; |
84075e8f | 1265 | } |
62e76326 | 1266 | |
de2a0782 | 1267 | assert(0 == i); |
3900307b | 1268 | s->flush(); |
db1cd23c | 1269 | memFree(buf, MEM_4K_BUF); |
de2a0782 | 1270 | #else |
62e76326 | 1271 | |
955394ce | 1272 | reply->setHeaders(Http::scBadRequest, "Bad Request", NULL, -1, squid_curtime, -2); |
db237875 | 1273 | s->replaceHttpReply(reply); |
de2a0782 | 1274 | storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n"); |
1275 | #endif | |
62e76326 | 1276 | |
528b2c61 | 1277 | s->complete(); |
9ad1cbca | 1278 | } |
1279 | ||
1280 | void | |
1281 | netdbExchangeStart(void *data) | |
1282 | { | |
45c54bdc | 1283 | #if USE_ICMP |
a3c6762c | 1284 | CachePeer *p = (CachePeer *)data; |
51b5dcf5 AJ |
1285 | static const SBuf netDB("netdb"); |
1286 | char *uri = internalRemoteUri(p->host, p->http_port, "/squid-internal-dynamic/", netDB); | |
bf8fe701 | 1287 | debugs(38, 3, "netdbExchangeStart: Requesting '" << uri << "'"); |
a74c5601 | 1288 | assert(NULL != uri); |
fa91d030 | 1289 | HttpRequest *req = HttpRequest::CreateFromUrl(uri); |
62e76326 | 1290 | |
fa91d030 | 1291 | if (req == NULL) { |
e0236918 | 1292 | debugs(38, DBG_IMPORTANT, "netdbExchangeStart: Bad URI " << uri); |
62e76326 | 1293 | return; |
a74c5601 | 1294 | } |
62e76326 | 1295 | |
fa91d030 | 1296 | netdbExchangeState *ex = new netdbExchangeState(p, req); |
c2a7cefd | 1297 | ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET); |
a74c5601 | 1298 | assert(NULL != ex->e); |
fa91d030 AJ |
1299 | |
1300 | StoreIOBuffer tempBuffer; | |
4b725156 | 1301 | tempBuffer.length = ex->buf_sz; |
1302 | tempBuffer.data = ex->buf; | |
fa91d030 AJ |
1303 | |
1304 | ex->sc = storeClientListAdd(ex->e, ex); | |
1305 | ||
4b725156 | 1306 | storeClientCopy(ex->sc, ex->e, tempBuffer, |
62e76326 | 1307 | netdbExchangeHandleReply, ex); |
f53969cc | 1308 | ex->r->flags.loopDetected = true; /* cheat! -- force direct */ |
62e76326 | 1309 | |
92d6986d | 1310 | // XXX: send as Proxy-Authenticate instead |
9bc73deb | 1311 | if (p->login) |
92d6986d | 1312 | ex->r->url.userInfo(SBuf(p->login)); |
62e76326 | 1313 | |
e83cc785 | 1314 | FwdState::fwdStart(Comm::ConnectionPointer(), ex->e, ex->r); |
62e76326 | 1315 | |
45c54bdc | 1316 | #endif |
de2a0782 | 1317 | } |
69c95dd3 | 1318 | |
a3c6762c | 1319 | CachePeer * |
190154cf | 1320 | netdbClosestParent(HttpRequest * request) |
69c95dd3 | 1321 | { |
69c95dd3 | 1322 | #if USE_ICMP |
a3c6762c | 1323 | CachePeer *p = NULL; |
69c95dd3 | 1324 | netdbEntry *n; |
1325 | const ipcache_addrs *ia; | |
1326 | net_db_peer *h; | |
1327 | int i; | |
5c51bffb | 1328 | n = netdbLookupHost(request->url.host()); |
62e76326 | 1329 | |
69c95dd3 | 1330 | if (NULL == n) { |
62e76326 | 1331 | /* try IP addr */ |
5c51bffb | 1332 | ia = ipcache_gethostbyname(request->url.host(), 0); |
62e76326 | 1333 | |
1334 | if (NULL != ia) | |
1335 | n = netdbLookupAddr(ia->in_addrs[ia->cur]); | |
69c95dd3 | 1336 | } |
62e76326 | 1337 | |
69c95dd3 | 1338 | if (NULL == n) |
62e76326 | 1339 | return NULL; |
1340 | ||
69c95dd3 | 1341 | if (0 == n->n_peers) |
62e76326 | 1342 | return NULL; |
1343 | ||
4ed6ef62 | 1344 | n->last_use_time = squid_curtime; |
62e76326 | 1345 | |
1346 | /* | |
69c95dd3 | 1347 | * Find the parent with the least RTT to the origin server. |
1348 | * Make sure we don't return a parent who is farther away than | |
1349 | * we are. Note, the n->peers list is pre-sorted by RTT. | |
1350 | */ | |
7c64cac7 | 1351 | for (i = 0; i < n->n_peers; ++i) { |
62e76326 | 1352 | h = &n->peers[i]; |
1353 | ||
1354 | if (n->rtt > 0) | |
1355 | if (n->rtt < h->rtt) | |
1356 | break; | |
1357 | ||
1358 | p = peerFindByName(h->peername); | |
1359 | ||
f53969cc | 1360 | if (NULL == p) /* not found */ |
62e76326 | 1361 | continue; |
1362 | ||
5c51bffb | 1363 | if (neighborType(p, request->url) != PEER_PARENT) |
62e76326 | 1364 | continue; |
1365 | ||
f53969cc | 1366 | if (!peerHTTPOkay(p, request)) /* not allowed */ |
62e76326 | 1367 | continue; |
1368 | ||
1369 | return p; | |
69c95dd3 | 1370 | } |
62e76326 | 1371 | |
69c95dd3 | 1372 | #endif |
1373 | return NULL; | |
1374 | } | |
f53969cc | 1375 |