Squid Web Cache master
Loading...
Searching...
No Matches
net_db.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 38 Network Measurement Database */
10
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
19#include "squid.h"
20#include "CachePeer.h"
21#include "CachePeers.h"
22#include "cbdata.h"
23#include "event.h"
24#include "fde.h"
25#include "fs_io.h"
26#include "FwdState.h"
27#include "HttpReply.h"
28#include "icmp/net_db.h"
29#include "internal.h"
30#include "ip/Address.h"
31#include "log/File.h"
32#include "MemObject.h"
33#include "mgr/Registration.h"
34#include "mime_header.h"
35#include "neighbors.h"
36#include "PeerSelectState.h"
37#include "sbuf/SBuf.h"
38#include "SquidConfig.h"
39#include "Store.h"
40#include "StoreClient.h"
41#include "tools.h"
42#include "wordlist.h"
43
44#if HAVE_SYS_STAT_H
45#include <sys/stat.h>
46#endif
47
48#if USE_ICMP
49#include "icmp/IcmpSquid.h"
50#include "ipcache.h"
51#include "StoreClient.h"
52
58
60{
62
63public:
65 p(aPeer),
66 r(theReq)
67 {
68 assert(r);
69 // TODO: check if we actually need to do this. should be implicit
71 }
72
74 debugs(38, 3, e->url());
75 storeUnregister(sc, e, this);
76 e->unlock("netdbExchangeDone");
77 }
78
80 StoreEntry *e = nullptr;
81 store_client *sc = nullptr;
83
86
88};
89
91
92static hash_table *addr_table = nullptr;
93static hash_table *host_table = nullptr;
94
96static void netdbRelease(netdbEntry * n);
97
98static void netdbHashInsert(netdbEntry * n, Ip::Address &addr);
99static void netdbHashDelete(const char *key);
100static void netdbHostInsert(netdbEntry * n, const char *hostname);
101static void netdbHostDelete(const net_db_name * x);
102static void netdbPurgeLRU(void);
103static netdbEntry *netdbLookupHost(const char *key);
104static net_db_peer *netdbPeerByName(const netdbEntry * n, const char *);
106static const char *netdbPeerName(const char *name);
109
110/* We have to keep a local list of CachePeer names. The Peers structure
111 * gets freed during a reconfigure. We want this database to
112 * remain persistent, so _net_db_peer->peername points into this
113 * linked list */
114static wordlist *peer_names = nullptr;
115
116static void
118{
120 n->key = n->network;
121 assert(hash_lookup(addr_table, n->network) == nullptr);
123}
124
125static void
126netdbHashDelete(const char *key)
127{
128 hash_link *hptr = (hash_link *)hash_lookup(addr_table, key);
129
130 if (hptr == nullptr) {
131 debug_trap("netdbHashDelete: key not found");
132 return;
133 }
134
136}
137
138net_db_name::net_db_name(const char *hostname, netdbEntry *e) :
139 next(e ? e->hosts : nullptr),
140 net_db_entry(e)
141{
142 key = xstrdup(hostname);
143 if (e) {
144 e->hosts = this;
145 ++ e->link_count;
146 }
147}
148
149static void
150netdbHostInsert(netdbEntry * n, const char *hostname)
151{
152 net_db_name *x = new net_db_name(hostname, n);
153 assert(hash_lookup(host_table, hostname) == nullptr);
155}
156
157static void
159{
160 assert(x != nullptr);
161 assert(x->net_db_entry != nullptr);
162
163 netdbEntry *n = x->net_db_entry;
164 -- n->link_count;
165
166 for (auto **X = &n->hosts; *X; X = &(*X)->next) {
167 if (*X == x) {
168 *X = x->next;
169 break;
170 }
171 }
172
174 delete x;
175}
176
177static netdbEntry *
178netdbLookupHost(const char *key)
179{
181 return x ? x->net_db_entry : nullptr;
182}
183
184static void
186{
187 net_db_name *x;
188 net_db_name *next;
189
190 for (x = n->hosts; x; x = next) {
191 next = x->next;
193 }
194
195 n->hosts = nullptr;
196 safe_free(n->peers);
197 n->peers = nullptr;
198 n->n_peers = 0;
199 n->n_peers_alloc = 0;
200
201 if (n->link_count == 0) {
203 delete n;
204 }
205}
206
207static int
208netdbLRU(const void *A, const void *B)
209{
210 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
211 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
212
213 if ((*n1)->last_use_time > (*n2)->last_use_time)
214 return (1);
215
216 if ((*n1)->last_use_time < (*n2)->last_use_time)
217 return (-1);
218
219 return (0);
220}
221
222static void
224{
225 netdbEntry *n;
226 netdbEntry **list;
227 int k = 0;
228 int list_count = 0;
229 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
231
232 while ((n = (netdbEntry *) hash_next(addr_table))) {
233 assert(list_count < netdbEntry::UseCount());
234 *(list + list_count) = n;
235 ++list_count;
236 }
237
238 qsort((char *) list,
239 list_count,
240 sizeof(netdbEntry *),
241 netdbLRU);
242
243 for (k = 0; k < list_count; ++k) {
244 if (netdbEntry::UseCount() < Config.Netdb.low)
245 break;
246
247 netdbRelease(*(list + k));
248 }
249
250 xfree(list);
251}
252
253static netdbEntry *
255{
256 netdbEntry *n;
257 char *key = new char[MAX_IPSTRLEN];
259 n = (netdbEntry *) hash_lookup(addr_table, key);
260 delete[] key;
261 return n;
262}
263
264static netdbEntry *
266{
267 netdbEntry *n;
268
269 if (netdbEntry::UseCount() > Config.Netdb.high)
271
272 if ((n = netdbLookupAddr(addr)) == nullptr) {
273 n = new netdbEntry;
274 netdbHashInsert(n, addr);
275 }
276
277 return n;
278}
279
280static void
281netdbSendPing(const ipcache_addrs *ia, const Dns::LookupDetails &, void *data)
282{
283 Ip::Address addr;
284 char *hostname = nullptr;
285 static_cast<generic_cbdata *>(data)->unwrap(&hostname);
286 netdbEntry *n;
287 netdbEntry *na;
288 net_db_name *x;
289 net_db_name **X;
290
291 if (ia == nullptr) {
292 xfree(hostname);
293 return;
294 }
295
296 addr = ia->current();
297
298 if ((n = netdbLookupHost(hostname)) == nullptr) {
299 n = netdbAdd(addr);
300 netdbHostInsert(n, hostname);
301 } else if ((na = netdbLookupAddr(addr)) != n) {
302 /*
303 *hostname moved from 'network n' to 'network na'!
304 */
305
306 if (na == nullptr)
307 na = netdbAdd(addr);
308
309 debugs(38, 3, "netdbSendPing: " << hostname << " moved from " << n->network << " to " << na->network);
310
311 x = (net_db_name *) hash_lookup(host_table, hostname);
312
313 if (x == nullptr) {
314 debugs(38, DBG_IMPORTANT, "ERROR: Squid BUG: net_db_name list bug: " << hostname << " not found");
315 xfree(hostname);
316 return;
317 }
318
319 /* remove net_db_name from 'network n' linked list */
320 for (X = &n->hosts; *X; X = &(*X)->next) {
321 if (*X == x) {
322 *X = x->next;
323 break;
324 }
325 }
326
327 -- n->link_count;
328 /* point to 'network na' from host entry */
329 x->net_db_entry = na;
330 /* link net_db_name to 'network na' */
331 x->next = na->hosts;
332 na->hosts = x;
333 ++ na->link_count;
334 n = na;
335 }
336
337 if (n->next_ping_time <= squid_curtime) {
338 debugs(38, 3, "netdbSendPing: pinging " << hostname);
339 icmpEngine.DomainPing(addr, hostname);
340 ++ n->pings_sent;
343 }
344
345 xfree(hostname);
346}
347
348static Ip::Address
350{
351 Ip::Address out;
352
353 out = in;
354
355 /* in IPv6 the 'network' should be the routing section. */
356 if ( in.isIPv6() ) {
357 out.applyMask(64, AF_INET6);
358 debugs(14, 5, "networkFromInaddr : Masked IPv6 Address to " << in << "/64 routing part.");
359 return out;
360 }
361
362 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << out << "/24.");
363
364 /* use /24 for everything under IPv4 */
365 out.applyMask(24, AF_INET);
366 debugs(14, 5, "networkFromInaddr : Masked IPv4 Address to " << in << "/24.");
367
368 return out;
369}
370
371static int
372sortByRtt(const void *A, const void *B)
373{
374 const netdbEntry *const *n1 = (const netdbEntry *const *)A;
375 const netdbEntry *const *n2 = (const netdbEntry *const *)B;
376
377 if ((*n1)->rtt > (*n2)->rtt)
378 return 1;
379 else if ((*n1)->rtt < (*n2)->rtt)
380 return -1;
381 else
382 return 0;
383}
384
385static net_db_peer *
386netdbPeerByName(const netdbEntry * n, const char *peername)
387{
388 int i;
389 net_db_peer *p = n->peers;
390
391 for (i = 0; i < n->n_peers; ++i, ++p) {
392 if (!strcmp(p->peername, peername))
393 return p;
394 }
395
396 return nullptr;
397}
398
399static net_db_peer *
401{
402 net_db_peer *p;
403 net_db_peer *o;
404 int osize;
405 int i;
406
407 if (n->n_peers == n->n_peers_alloc) {
408 o = n->peers;
409 osize = n->n_peers_alloc;
410
411 if (n->n_peers_alloc == 0)
412 n->n_peers_alloc = 2;
413 else
414 n->n_peers_alloc <<= 1;
415
416 debugs(38, 3, "netdbPeerAdd: Growing peer list for '" << n->network << "' to " << n->n_peers_alloc);
417
419
420 for (i = 0; i < osize; ++i)
421 *(n->peers + i) = *(o + i);
422
423 if (osize) {
424 safe_free(o);
425 }
426 }
427
428 p = n->peers + n->n_peers;
429 p->peername = netdbPeerName(e->host);
430 ++ n->n_peers;
431 return p;
432}
433
434static int
435sortPeerByRtt(const void *A, const void *B)
436{
437 const net_db_peer *p1 = (net_db_peer *)A;
438 const net_db_peer *p2 = (net_db_peer *)B;
439
440 if (p1->rtt > p2->rtt)
441 return 1;
442 else if (p1->rtt < p2->rtt)
443 return -1;
444 else
445 return 0;
446}
447
448static void
450{
451 if (strcmp(Config.netdbFilename, "none") == 0)
452 return;
453
454 Logfile *lf;
455 netdbEntry *n;
456 net_db_name *x;
457
458 struct timeval start = current_time;
459 int count = 0;
460 /*
461 * This was nicer when we were using stdio, but thanks to
462 * Solaris bugs, its a bad idea. fopen can fail if more than
463 * 256 FDs are open.
464 */
465 /*
466 * unlink() is here because there is currently no way to make
467 * logfileOpen() use O_TRUNC.
468 */
469 unlink(Config.netdbFilename);
470 lf = logfileOpen(Config.netdbFilename, 4096, 0);
471
472 if (!lf) {
473 int xerrno = errno;
474 debugs(50, DBG_IMPORTANT, MYNAME << Config.netdbFilename << ": " << xstrerr(xerrno));
475 return;
476 }
477
479
480 while ((n = (netdbEntry *) hash_next(addr_table))) {
481 if (n->pings_recv == 0)
482 continue;
483
484 logfilePrintf(lf, "%s %d %d %10.5f %10.5f %d %d",
485 n->network,
486 n->pings_sent,
487 n->pings_recv,
488 n->hops,
489 n->rtt,
490 (int) n->next_ping_time,
491 (int) n->last_use_time);
492
493 for (x = n->hosts; x; x = x->next)
494 logfilePrintf(lf, " %s", hashKeyStr(x));
495
496 logfilePrintf(lf, "\n");
497
498 ++count;
499
500#undef RBUF_SZ
501
502 }
503
504 logfileClose(lf);
506 debugs(38, DBG_IMPORTANT, "NETDB state saved; " <<
507 count << " entries, " <<
508 tvSubMsec(start, current_time) << " msec" );
509 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
510}
511
512static void
514{
515 if (strcmp(Config.netdbFilename, "none") == 0)
516 return;
517
518 char *s;
519 int fd;
520 int l;
521
522 struct stat sb;
523 netdbEntry *n;
524 netdbEntry N;
525
526 Ip::Address addr;
527 int count = 0;
528
529 struct timeval start = current_time;
530 /*
531 * This was nicer when we were using stdio, but thanks to
532 * Solaris bugs, its a bad idea. fopen can fail if more than
533 * 256 FDs are open.
534 */
535 fd = file_open(Config.netdbFilename, O_RDONLY | O_BINARY);
536
537 if (fd < 0)
538 return;
539
540 if (fstat(fd, &sb) < 0) {
541 file_close(fd);
542 return;
543 }
544
545 char *t;
546 char *buf = (char *)xcalloc(1, sb.st_size + 1);
547 t = buf;
548 l = FD_READ_METHOD(fd, buf, sb.st_size);
549 file_close(fd);
550
551 if (l <= 0) {
552 safe_free (buf);
553 return;
554 };
555
556 while ((s = strchr(t, '\n'))) {
557 char *q;
558 assert(s - buf < l);
559 *s = '\0';
560 N = netdbEntry();
561 q = strtok(t, w_space);
562 t = s + 1;
563
564 if (nullptr == q)
565 continue;
566
567 if (! (addr = q) )
568 continue;
569
570 if (netdbLookupAddr(addr) != nullptr) /* no dups! */
571 continue;
572
573 if ((q = strtok(nullptr, w_space)) == nullptr)
574 continue;
575
576 N.pings_sent = atoi(q);
577
578 if ((q = strtok(nullptr, w_space)) == nullptr)
579 continue;
580
581 N.pings_recv = atoi(q);
582
583 if (N.pings_recv == 0)
584 continue;
585
586 /* give this measurement low weight */
587 N.pings_sent = 1;
588
589 N.pings_recv = 1;
590
591 if ((q = strtok(nullptr, w_space)) == nullptr)
592 continue;
593
594 N.hops = atof(q);
595
596 if ((q = strtok(nullptr, w_space)) == nullptr)
597 continue;
598
599 N.rtt = atof(q);
600
601 if ((q = strtok(nullptr, w_space)) == nullptr)
602 continue;
603
604 N.next_ping_time = (time_t) atoi(q);
605
606 if ((q = strtok(nullptr, w_space)) == nullptr)
607 continue;
608
609 N.last_use_time = (time_t) atoi(q);
610
611 n = new netdbEntry;
612
613 memcpy(n, &N, sizeof(netdbEntry));
614
615 netdbHashInsert(n, addr);
616
617 while ((q = strtok(nullptr, w_space)) != nullptr) {
618 if (netdbLookupHost(q) != nullptr) /* no dups! */
619 continue;
620
621 netdbHostInsert(n, q);
622 }
623
624 ++count;
625 }
626
627 xfree(buf);
629 debugs(38, DBG_IMPORTANT, "NETDB state reloaded; " <<
630 count << " entries, " <<
631 tvSubMsec(start, current_time) << " msec" );
632}
633
634static const char *
635netdbPeerName(const char *name)
636{
637 const wordlist *w;
638
639 for (w = peer_names; w; w = w->next) {
640 if (!strcmp(w->key, name))
641 return w->key;
642 }
643
644 return wordlistAdd(&peer_names, name);
645}
646
647static void
649{
650 Ip::Address addr;
651
653
654 struct in_addr line_addr;
655 double rtt;
656 double hops;
657 int j;
658 int nused = 0;
659
660 size_t rec_sz = 0; // received record size (TODO: make const)
661 rec_sz += 1 + sizeof(struct in_addr);
662 rec_sz += 1 + sizeof(int);
663 rec_sz += 1 + sizeof(int);
664 // to make progress without growing buffer space, we must parse at least one record per call
665 Assure(rec_sz <= ex->parsingBuffer.capacity());
666 debugs(38, 3, "netdbExchangeHandleReply: " << receivedData.length << " read bytes");
667
668 if (!ex->p.valid()) {
669 debugs(38, 3, "netdbExchangeHandleReply: Peer became invalid");
670 delete ex;
671 return;
672 }
673
674 debugs(38, 3, "for " << *ex->p);
675
676 if (receivedData.flags.error) {
677 delete ex;
678 return;
679 }
680
681 if (ex->connstate == STATE_HEADER) {
682 const auto scode = ex->e->mem().baseReply().sline.status();
683 assert(scode != Http::scNone);
684 debugs(38, 3, "reply status " << scode);
685 if (scode != Http::scOkay) {
686 delete ex;
687 return;
688 }
689 ex->connstate = STATE_BODY;
690 }
691
693
694 ex->parsingBuffer.appended(receivedData.data, receivedData.length);
695 auto p = ex->parsingBuffer.c_str(); // current parsing position
696 auto size = ex->parsingBuffer.contentSize(); // bytes we still need to parse
697
698 /* If we get here, we have some body to parse .. */
699 debugs(38, 5, "netdbExchangeHandleReply: start parsing loop, size = " << size);
700
701 while (size >= rec_sz) {
702 debugs(38, 5, "netdbExchangeHandleReply: in parsing loop, size = " << size);
703 addr.setAnyAddr();
704 hops = rtt = 0.0;
705
706 size_t o; // current record parsing offset
707 for (o = 0; o < rec_sz;) {
708 switch ((int) *(p + o)) {
709
710 case NETDB_EX_NETWORK:
711 ++o;
712 // XXX: NetDB can still only send IPv4
713 memcpy(&line_addr, p + o, sizeof(struct in_addr));
714 addr = line_addr;
715 o += sizeof(struct in_addr);
716 break;
717
718 case NETDB_EX_RTT:
719 ++o;
720 memcpy(&j, p + o, sizeof(int));
721 o += sizeof(int);
722 rtt = (double) ntohl(j) / 1000.0;
723 break;
724
725 case NETDB_EX_HOPS:
726 ++o;
727 memcpy(&j, p + o, sizeof(int));
728 o += sizeof(int);
729 hops = (double) ntohl(j) / 1000.0;
730 break;
731
732 default:
733 debugs(38, DBG_IMPORTANT, "ERROR: netdbExchangeHandleReply: corrupt data, aborting");
734 delete ex;
735 return;
736 }
737 }
738
739 if (!addr.isAnyAddr() && rtt > 0)
740 netdbExchangeUpdatePeer(addr, ex->p.get(), rtt, hops);
741
742 assert(o == rec_sz);
743
744 size -= rec_sz;
745
746 p += rec_sz;
747
748 ++nused;
749 }
750
751 const auto parsedSize = ex->parsingBuffer.contentSize() - size;
752 ex->parsingBuffer.consume(parsedSize);
753
754 debugs(38, 3, "netdbExchangeHandleReply: size left over in this buffer: " << size << " bytes");
755
756 debugs(38, 3, "netdbExchangeHandleReply: used " << nused <<
757 " entries, (x " << rec_sz << " bytes) == " << nused * rec_sz <<
758 " bytes total");
759
760 if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
761 debugs(38, 3, "netdbExchangeHandleReply: ENTRY_ABORTED");
762 delete ex;
763 return;
764 }
765
766 if (ex->sc->atEof()) {
767 if (const auto leftoverBytes = ex->parsingBuffer.contentSize())
768 debugs(38, 2, "discarding a partially received record due to Store EOF: " << leftoverBytes);
769 delete ex;
770 return;
771 }
772
773 // TODO: To protect us from a broken peer sending an "infinite" stream of
774 // new addresses, limit the cumulative number of received bytes or records?
775
776 const auto remainingSpace = ex->parsingBuffer.space().positionAt(receivedData.offset + receivedData.length);
777 // rec_sz is at most buffer capacity, and we consume all fully loaded records
778 Assure(remainingSpace.length);
779 storeClientCopy(ex->sc, ex->e, remainingSpace, netdbExchangeHandleReply, ex);
780}
781
782#endif /* USE_ICMP */
783
784/* PUBLIC FUNCTIONS */
785
786void
788{
789#if USE_ICMP
790 Mgr::RegisterAction("netdb", "Network Measurement Database", netdbDump, 0, 1);
791
792 if (addr_table)
793 return;
794
795 int n = hashPrime(Config.Netdb.high / 4);
796
797 addr_table = hash_create((HASHCMP *) strcmp, n, hash_string);
798
799 n = hashPrime(3 * Config.Netdb.high / 4);
800
801 host_table = hash_create((HASHCMP *) strcmp, n, hash_string);
802
803 eventAddIsh("netdbSaveState", netdbSaveState, nullptr, 3600.0, 1);
804
806
807#endif
808}
809
810void
811netdbPingSite(const char *hostname)
812{
813#if USE_ICMP
814 netdbEntry *n;
815
816 if ((n = netdbLookupHost(hostname)) != nullptr)
818 return;
819
821 new generic_cbdata(xstrdup(hostname)));
822#else
823 (void)hostname;
824#endif
825}
826
827void
828netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
829{
830#if USE_ICMP
831 netdbEntry *n;
832 int N;
833 debugs(38, 3, "netdbHandlePingReply: from " << from);
834
835 if ((n = netdbLookupAddr(from)) == nullptr)
836 return;
837
838 N = ++n->pings_recv;
839
840 if (N > 5)
841 N = 5;
842
843 if (rtt < 1)
844 rtt = 1;
845
846 n->hops = ((n->hops * (N - 1)) + hops) / N;
847
848 n->rtt = ((n->rtt * (N - 1)) + rtt) / N;
849
850 debugs(38, 3, "netdbHandlePingReply: " << n->network << "; rtt="<<
851 std::setw(5)<< std::setprecision(2) << n->rtt << " hops="<<
852 std::setw(4) << n->hops);
853#else
854 (void)from;
855 (void)hops;
856 (void)rtt;
857#endif
858}
859
860void
862{
863#if USE_ICMP
864 netdbEntry *n;
865 netdbEntry **list;
866 net_db_name *x;
867 int k;
868 int i;
869 int j;
870 net_db_peer *p;
871 storeAppendPrintf(sentry, "Network DB Statistics:\n");
872 storeAppendPrintf(sentry, "%-46.46s %9s %7s %5s %s\n", /* Max between 16 (IPv4) or 46 (IPv6) */
873 "Network",
874 "recv/sent",
875 "RTT",
876 "Hops",
877 "Hostnames");
878 list = (netdbEntry **)xcalloc(netdbEntry::UseCount(), sizeof(netdbEntry *));
879 i = 0;
881
882 while ((n = (netdbEntry *) hash_next(addr_table))) {
883 *(list + i) = n;
884 ++i;
885 }
886
887 if (i != netdbEntry::UseCount())
888 debugs(38, DBG_CRITICAL, "WARNING: netdb_addrs count off, found " << i <<
889 ", expected " << netdbEntry::UseCount());
890
891 qsort((char *) list,
892 i,
893 sizeof(netdbEntry *),
894 sortByRtt);
895
896 for (k = 0; k < i; ++k) {
897 n = *(list + k);
898 storeAppendPrintf(sentry, "%-46.46s %4d/%4d %7.1f %5.1f", /* Max between 16 (IPv4) or 46 (IPv6) */
899 n->network,
900 n->pings_recv,
901 n->pings_sent,
902 n->rtt,
903 n->hops);
904
905 for (x = n->hosts; x; x = x->next)
906 storeAppendPrintf(sentry, " %s", hashKeyStr(x));
907
908 storeAppendPrintf(sentry, "\n");
909
910 p = n->peers;
911
912 for (j = 0; j < n->n_peers; ++j, ++p) {
913 storeAppendPrintf(sentry, " %-22.22s %7.1f %5.1f\n",
914 p->peername,
915 p->rtt,
916 p->hops);
917 }
918 }
919
920 xfree(list);
921#else
922
923 storeAppendPrintf(sentry,"NETDB support not compiled into this Squid cache.\n");
924#endif
925}
926
927int
928netdbHostHops(const char *host)
929{
930#if USE_ICMP
931 netdbEntry *n = netdbLookupHost(host);
932
933 if (n) {
935 return (int) (n->hops + 0.5);
936 }
937#else
938 (void)host;
939#endif
940 return 0;
941}
942
943int
944netdbHostRtt(const char *host)
945{
946#if USE_ICMP
947 netdbEntry *n = netdbLookupHost(host);
948
949 if (n) {
951 return (int) (n->rtt + 0.5);
952 }
953
954#else
955 (void)host;
956#endif
957 return 0;
958}
959
960void
961netdbHostData(const char *host, int *samp, int *rtt, int *hops)
962{
963#if USE_ICMP
964 netdbEntry *n = netdbLookupHost(host);
965
966 if (n == nullptr)
967 return;
968
969 *samp = n->pings_recv;
970
971 *rtt = (int) (n->rtt + 0.5);
972
973 *hops = (int) (n->hops + 0.5);
974
976
977#else
978 (void)hops;
979 (void)rtt;
980 (void)samp;
981 (void)host;
982#endif
983}
984
985void
986netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
987{
988#if USE_ICMP
989 netdbEntry *n;
990 double rtt = (double) irtt;
991 double hops = (double) ihops;
992 net_db_peer *p;
993 debugs(38, 3, url.host() << ", " << ihops << " hops, " << irtt << " rtt");
994 n = netdbLookupHost(url.host());
995
996 if (n == nullptr) {
997 debugs(38, 3, "host " << url.host() << " not found");
998 return;
999 }
1000
1001 if ((p = netdbPeerByName(n, e->host)) == nullptr)
1002 p = netdbPeerAdd(n, e);
1003
1004 p->rtt = rtt;
1005
1006 p->hops = hops;
1007
1008 p->expires = squid_curtime + 3600;
1009
1010 if (n->n_peers < 2)
1011 return;
1012
1013 qsort((char *) n->peers,
1014 n->n_peers,
1015 sizeof(net_db_peer),
1017
1018#else
1019 (void)ihops;
1020 (void)irtt;
1021 (void)e;
1022 (void)url;
1023#endif
1024}
1025
1026void
1027netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer * e, double rtt, double hops)
1028{
1029#if USE_ICMP
1030 netdbEntry *n;
1031 net_db_peer *p;
1032 debugs(38, 5, "netdbExchangeUpdatePeer: '" << addr << "', "<<
1033 std::setfill('0')<< std::setprecision(2) << hops << " hops, " <<
1034 rtt << " rtt");
1035
1036 if ( !addr.isIPv4() ) {
1037 debugs(38, 5, "netdbExchangeUpdatePeer: Aborting peer update for '" << addr << "', NetDB cannot handle IPv6.");
1038 return;
1039 }
1040
1041 n = netdbLookupAddr(addr);
1042
1043 if (n == nullptr)
1044 n = netdbAdd(addr);
1045
1046 assert(nullptr != n);
1047
1048 if ((p = netdbPeerByName(n, e->host)) == nullptr)
1049 p = netdbPeerAdd(n, e);
1050
1051 p->rtt = rtt;
1052
1053 p->hops = hops;
1054
1055 p->expires = squid_curtime + 3600; /* XXX ? */
1056
1057 if (n->n_peers < 2)
1058 return;
1059
1060 qsort((char *) n->peers,
1061 n->n_peers,
1062 sizeof(net_db_peer),
1064
1065#else
1066 (void)hops;
1067 (void)rtt;
1068 (void)e;
1069 (void)addr;
1070#endif
1071}
1072
1073void
1075{
1076#if USE_ICMP
1077 netdbEntry *n = netdbLookupAddr(addr);
1078
1079 if (n == nullptr)
1080 return;
1081
1082 debugs(38, 3, "netdbDeleteAddrNetwork: " << n->network);
1083
1084 netdbRelease(n);
1085#else
1086 (void)addr;
1087#endif
1088}
1089
1090void
1092{
1093 HttpReply *reply = new HttpReply;
1094#if USE_ICMP
1095
1096 Ip::Address addr;
1097
1098 netdbEntry *n;
1099 int i;
1100 int j;
1101 int rec_sz;
1102 char *buf;
1103
1104 struct in_addr line_addr;
1105 s->buffer();
1106 reply->setHeaders(Http::scOkay, "OK", nullptr, -1, squid_curtime, -2);
1107 s->replaceHttpReply(reply);
1108 rec_sz = 0;
1109 rec_sz += 1 + sizeof(struct in_addr);
1110 rec_sz += 1 + sizeof(int);
1111 rec_sz += 1 + sizeof(int);
1112 buf = (char *)memAllocate(MEM_4K_BUF);
1113 i = 0;
1115
1116 while ((n = (netdbEntry *) hash_next(addr_table))) {
1117 if (0.0 == n->rtt)
1118 continue;
1119
1120 if (n->rtt > 60000) /* RTT > 1 MIN probably bogus */
1121 continue;
1122
1123 if (! (addr = n->network) )
1124 continue;
1125
1126 // XXX: NetDB cannot yet handle IPv6 addresses
1127 if ( !addr.isIPv4() )
1128 continue;
1129
1130 if (i + rec_sz > 4096) {
1131 s->append(buf, i);
1132 i = 0;
1133 }
1134
1135 buf[i] = (char) NETDB_EX_NETWORK;
1136 ++i;
1137
1138 addr.getInAddr(line_addr);
1139 memcpy(&buf[i], &line_addr, sizeof(struct in_addr));
1140
1141 i += sizeof(struct in_addr);
1142
1143 buf[i] = (char) NETDB_EX_RTT;
1144 ++i;
1145
1146 j = htonl((int) (n->rtt * 1000));
1147
1148 memcpy(&buf[i], &j, sizeof(int));
1149
1150 i += sizeof(int);
1151
1152 buf[i] = (char) NETDB_EX_HOPS;
1153 ++i;
1154
1155 j = htonl((int) (n->hops * 1000));
1156
1157 memcpy(&buf[i], &j, sizeof(int));
1158
1159 i += sizeof(int);
1160 }
1161
1162 if (i > 0) {
1163 s->append(buf, i);
1164 i = 0;
1165 }
1166
1167 assert(0 == i);
1168 s->flush();
1169 memFree(buf, MEM_4K_BUF);
1170#else
1171
1172 reply->setHeaders(Http::scBadRequest, "Bad Request", nullptr, -1, squid_curtime, -2);
1173 s->replaceHttpReply(reply);
1174 storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
1175#endif
1176
1177 s->complete();
1178}
1179
1180void
1182{
1183#if USE_ICMP
1184 CachePeer *p = (CachePeer *)data;
1185 static const SBuf netDB("netdb");
1186 char *uri = internalRemoteUri(p->secure.encryptTransport, p->host, p->http_port, "/squid-internal-dynamic/", netDB);
1187 debugs(38, 3, "Requesting '" << uri << "'");
1188 const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcmp>();
1190
1191 if (!req) {
1192 debugs(38, DBG_IMPORTANT, "ERROR: " << MYNAME << ": Bad URI " << uri);
1193 return;
1194 }
1195
1196 netdbExchangeState *ex = new netdbExchangeState(p, req);
1197 ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET);
1198 assert(nullptr != ex->e);
1199
1200 ex->sc = storeClientListAdd(ex->e, ex);
1202
1203 ex->r->flags.loopDetected = true; /* cheat! -- force direct */
1204
1205 // XXX: send as Proxy-Authenticate instead
1206 if (p->login)
1207 ex->r->url.userInfo(SBuf(p->login));
1208
1210#else
1211 (void)data;
1212#endif
1213}
1214
1215#if USE_ICMP
1218static CachePeer *
1219findUsableParentAtHostname(PeerSelector *ps, const char * const hostname, const HttpRequest &request)
1220{
1221 for (const auto &peer: CurrentCachePeers()) {
1222 const auto p = peer.get();
1223 // Both fields should be lowercase, but no code ensures that invariant.
1224 // TODO: net_db_peer should point to CachePeer instead of its hostname!
1225 if (strcasecmp(p->host, hostname) != 0)
1226 continue;
1227
1228 if (neighborType(p, request.url) != PEER_PARENT)
1229 continue;
1230
1231 if (!peerHTTPOkay(p, ps))
1232 continue;
1233
1234 return p;
1235 }
1236
1237 return nullptr;
1238}
1239#endif
1240
1241CachePeer *
1243{
1244#if USE_ICMP
1245 assert(ps);
1246 HttpRequest *request = ps->request;
1247
1248 netdbEntry *n;
1249 const ipcache_addrs *ia;
1250 net_db_peer *h;
1251 int i;
1252 n = netdbLookupHost(request->url.host());
1253
1254 if (nullptr == n) {
1255 /* try IP addr */
1256 ia = ipcache_gethostbyname(request->url.host(), 0);
1257
1258 if (nullptr != ia)
1259 n = netdbLookupAddr(ia->current());
1260 }
1261
1262 if (nullptr == n)
1263 return nullptr;
1264
1265 if (0 == n->n_peers)
1266 return nullptr;
1267
1269
1270 /*
1271 * Find the parent with the least RTT to the origin server.
1272 * Make sure we don't return a parent who is farther away than
1273 * we are. Note, the n->peers list is pre-sorted by RTT.
1274 */
1275 for (i = 0; i < n->n_peers; ++i) {
1276 h = &n->peers[i];
1277
1278 if (n->rtt > 0)
1279 if (n->rtt < h->rtt)
1280 break;
1281
1282 if (const auto p = findUsableParentAtHostname(ps, h->peername, *request))
1283 return p;
1284 }
1285
1286#else
1287 (void)ps;
1288#endif
1289 return nullptr;
1290}
1291
#define Assure(condition)
Definition Assure.h:35
const CachePeers & CurrentCachePeers()
Definition CachePeers.cc:43
IcmpSquid icmpEngine
Definition IcmpSquid.cc:28
int size
Definition ModDevPoll.cc:70
time_t squid_curtime
class SquidConfig Config
void(void *, StoreIOBuffer) STCB
Definition StoreClient.h:32
#define assert(EX)
Definition assert.h:17
#define CBDATA_CLASS_INIT(type)
Definition cbdata.h:325
void host(const char *src)
Definition Uri.cc:154
void userInfo(const SBuf &s)
Definition Uri.h:70
unsigned short http_port
Definition CachePeer.h:104
char * login
Definition CachePeer.h:202
char * host
Definition CachePeer.h:66
Security::PeerOptions secure
security settings for peer connection
Definition CachePeer.h:219
Cbc * valid() const
was set and is valid
Definition CbcPointer.h:41
Cbc * get() const
a temporary valid raw Cbc pointer or NULL
Definition CbcPointer.h:159
const Ip::Address & current() const
Definition ipcache.h:59
encapsulates DNS lookup results
static void fwdStart(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *)
Same as Start() but no master xaction info (AccessLogEntry) available.
Definition FwdState.cc:407
Http::StatusLine sline
Definition HttpReply.h:56
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition HttpReply.cc:170
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
RequestFlags flags
AnyP::Uri url
the request URI
AnyP::ProtocolVersion http_ver
Definition Message.h:72
Http::StatusCode status() const
retrieve the status code for this status line
Definition StatusLine.h:45
void DomainPing(Ip::Address &to, const char *domain)
Definition IcmpSquid.cc:206
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition Address.cc:804
bool isIPv4() const
Definition Address.cc:178
bool isAnyAddr() const
Definition Address.cc:190
bool isIPv6() const
Definition Address.cc:184
bool getInAddr(struct in_addr &) const
Definition Address.cc:1032
int applyMask(const Address &mask)
Definition Address.cc:97
void setAnyAddr()
NOTE: Does NOT clear the Port stored. Only the Address and Type.
Definition Address.cc:197
Definition File.h:39
const HttpReply & baseReply() const
Definition MemObject.h:60
HttpRequest * request
C * getRaw() const
Definition RefCount.h:89
Definition SBuf.h:94
bool encryptTransport
whether transport encryption (TLS/SSL) is to be used on connections to the peer
struct SquidConfig::@89 Netdb
char * netdbFilename
time_t period
uint16_t flags
Definition Store.h:231
MemObject & mem()
Definition Store.h:47
int unlock(const char *context)
Definition store.cc:469
const char * url() const
Definition store.cc:1566
void complete()
Definition store.cc:1031
void flush() override
Definition store.cc:1612
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition store.cc:1705
void append(char const *, int) override
Appends a c-string to existing packed data.
Definition store.cc:803
void buffer() override
Definition store.cc:1601
StoreIOBuffer & positionAt(const int64_t newOffset)
convenience method for changing the offset of a being-configured buffer
struct StoreIOBuffer::@123 flags
void consume(size_t)
get rid of previously appended() prefix of a given size
const char * c_str()
a NUL-terminated version of content(); same lifetime as content()
size_t contentSize() const
the total number of append()ed bytes that were not consume()d
StoreIOBuffer space()
StoreIOBuffer makeInitialSpace()
void appended(const char *, size_t)
remember the new bytes received into the previously provided space()
net_db_name(const char *name, netdbEntry *)
Definition net_db.cc:138
netdbEntry * net_db_entry
Definition net_db.h:33
net_db_name * next
Definition net_db.h:32
double hops
Definition net_db.h:43
time_t expires
Definition net_db.h:45
double rtt
Definition net_db.h:44
const char * peername
associated CachePeer::host (i.e. cache_peer hostname, not name=value!)
Definition net_db.h:41
int link_count
Definition net_db.h:63
char network[MAX_IPSTRLEN]
Definition net_db.h:56
double hops
Definition net_db.h:59
int pings_recv
Definition net_db.h:58
int n_peers_alloc
Definition net_db.h:66
net_db_peer * peers
Definition net_db.h:65
int pings_sent
Definition net_db.h:57
time_t next_ping_time
Definition net_db.h:61
time_t last_use_time
Definition net_db.h:62
net_db_name * hosts
Definition net_db.h:64
int n_peers
Definition net_db.h:67
double rtt
Definition net_db.h:60
CBDATA_CLASS(netdbExchangeState)
HttpRequestPointer r
Definition net_db.cc:82
CbcPointer< CachePeer > p
Definition net_db.cc:79
netdb_conn_state_t connstate
Definition net_db.cc:87
netdbExchangeState(CachePeer *aPeer, const HttpRequestPointer &theReq)
Definition net_db.cc:64
store_client * sc
Definition net_db.cc:81
StoreEntry * e
Definition net_db.cc:80
Store::ParsingBuffer parsingBuffer
for receiving a NetDB reply body from Store and interpreting it
Definition net_db.cc:85
bool atEof() const
char * key
Definition wordlist.h:59
wordlist * next
Definition wordlist.h:60
#define w_space
#define MYNAME
Definition Stream.h:219
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define DBG_CRITICAL
Definition Stream.h:37
#define O_BINARY
Definition defines.h:134
#define EBIT_TEST(flag, bit)
Definition defines.h:67
@ NETDB_EX_HOPS
Definition enums.h:181
@ NETDB_EX_RTT
Definition enums.h:180
@ NETDB_EX_NETWORK
Definition enums.h:179
@ ENTRY_ABORTED
Definition enums.h:110
@ PEER_PARENT
Definition enums.h:25
void eventAddIsh(const char *name, EVH *func, void *arg, double delta_ish, int weight)
Definition event.cc:114
int FD_READ_METHOD(int fd, char *buf, int len)
Definition fde.h:194
int file_open(const char *path, int mode)
Definition fs_io.cc:66
void file_close(int fd)
Definition fs_io.cc:92
const ipcache_addrs * ipcache_gethostbyname(const char *name, int flags)
Definition ipcache.cc:729
void ipcache_nbgethostbyname(const char *name, IPH *handler, void *handlerData)
Definition ipcache.cc:609
HASHHASH hash_string
Definition hash.h:45
hash_link * hash_lookup(hash_table *, const void *)
Definition hash.cc:146
hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition hash.cc:108
int HASHCMP(const void *, const void *)
Definition hash.h:13
hash_link * hash_next(hash_table *)
Definition hash.cc:188
void hash_first(hash_table *)
Definition hash.cc:172
void hash_join(hash_table *, hash_link *)
Definition hash.cc:131
void hash_remove_link(hash_table *, hash_link *)
Definition hash.cc:220
int hashPrime(int n)
Definition hash.cc:293
const char * hashKeyStr(const hash_link *)
Definition hash.cc:313
char * internalRemoteUri(bool encrypt, const char *host, unsigned short port, const char *dir, const SBuf &name)
Definition internal.cc:95
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition forward.h:25
void IPH(const ipcache_addrs *, const Dns::LookupDetails &details, void *)
Definition ipcache.h:227
void logfileClose(Logfile *lf)
Definition File.cc:92
void logfilePrintf(Logfile *lf, const char *fmt,...)
Definition File.cc:114
Logfile * logfileOpen(const char *path, size_t bufsz, int fatal_flag)
Definition File.cc:40
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition minimal.cc:61
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition old_api.cc:122
@ MEM_4K_BUF
Definition forward.h:49
@ scBadRequest
Definition StatusCode.h:45
@ scNone
Definition StatusCode.h:21
@ scOkay
Definition StatusCode.h:27
@ METHOD_GET
Definition MethodType.h:25
AnyP::ProtocolVersion ProtocolVersion()
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
#define xfree
#define xstrdup
int peerHTTPOkay(const CachePeer *p, PeerSelector *ps)
Definition neighbors.cc:253
peer_t neighborType(const CachePeer *p, const AnyP::Uri &url)
Definition neighbors.cc:117
static void netdbHashInsert(netdbEntry *n, Ip::Address &addr)
Definition net_db.cc:117
static void netdbRelease(netdbEntry *n)
Definition net_db.cc:185
void netdbPingSite(const char *hostname)
Definition net_db.cc:811
void netdbHostData(const char *host, int *samp, int *rtt, int *hops)
Definition net_db.cc:961
static void netdbHashDelete(const char *key)
Definition net_db.cc:126
void netdbDump(StoreEntry *sentry)
Definition net_db.cc:861
static void netdbSaveState(void *)
Definition net_db.cc:449
int netdbHostHops(const char *host)
Definition net_db.cc:928
int netdbHostRtt(const char *host)
Definition net_db.cc:944
static STCB netdbExchangeHandleReply
Definition net_db.cc:108
static netdbEntry * netdbLookupHost(const char *key)
Definition net_db.cc:178
static net_db_peer * netdbPeerByName(const netdbEntry *n, const char *)
Definition net_db.cc:386
void netdbExchangeUpdatePeer(Ip::Address &addr, CachePeer *e, double rtt, double hops)
Definition net_db.cc:1027
static IPH netdbSendPing
Definition net_db.cc:107
static netdbEntry * netdbAdd(Ip::Address &addr)
Definition net_db.cc:265
static void netdbPurgeLRU(void)
Definition net_db.cc:223
static void netdbHostInsert(netdbEntry *n, const char *hostname)
Definition net_db.cc:150
void netdbInit(void)
Definition net_db.cc:787
static netdbEntry * netdbLookupAddr(const Ip::Address &addr)
Definition net_db.cc:254
static void netdbHostDelete(const net_db_name *x)
Definition net_db.cc:158
static hash_table * host_table
Definition net_db.cc:93
static void netdbReloadState(void)
Definition net_db.cc:513
static hash_table * addr_table
Definition net_db.cc:92
void netdbUpdatePeer(const AnyP::Uri &url, CachePeer *e, int irtt, int ihops)
Definition net_db.cc:986
void netdbDeleteAddrNetwork(Ip::Address &addr)
Definition net_db.cc:1074
static wordlist * peer_names
Definition net_db.cc:114
static CachePeer * findUsableParentAtHostname(PeerSelector *ps, const char *const hostname, const HttpRequest &request)
Definition net_db.cc:1219
static const char * netdbPeerName(const char *name)
Definition net_db.cc:635
void netdbBinaryExchange(StoreEntry *s)
Definition net_db.cc:1091
netdb_conn_state_t
Definition net_db.cc:53
@ STATE_HEADER
Definition net_db.cc:55
@ STATE_BODY
Definition net_db.cc:56
@ STATE_NONE
Definition net_db.cc:54
static int sortPeerByRtt(const void *A, const void *B)
Definition net_db.cc:435
static net_db_peer * netdbPeerAdd(netdbEntry *n, CachePeer *e)
Definition net_db.cc:400
static int sortByRtt(const void *A, const void *B)
Definition net_db.cc:372
static Ip::Address networkFromInaddr(const Ip::Address &a)
Definition net_db.cc:349
CachePeer * netdbClosestParent(PeerSelector *ps)
Definition net_db.cc:1242
void netdbHandlePingReply(const Ip::Address &from, int hops, int rtt)
Definition net_db.cc:828
void netdbExchangeStart(void *data)
Definition net_db.cc:1181
static int netdbLRU(const void *A, const void *B)
Definition net_db.cc:208
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition store.cc:759
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
store_client * storeClientListAdd(StoreEntry *e, void *data)
void EVH void double
Definition stub_event.cc:16
int unsigned int
Definition stub_fd.cc:19
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
struct timeval current_time
the current UNIX time in timeval {seconds, microseconds} format
Definition gadgets.cc:18
int tvSubMsec(struct timeval t1, struct timeval t2)
Definition gadgets.cc:51
void debug_trap(const char *message)
Definition tools.cc:459
const char * wordlistAdd(wordlist **list, const char *key)
Definition wordlist.cc:25
void * xcalloc(size_t n, size_t sz)
Definition xalloc.cc:71
#define safe_free(x)
Definition xalloc.h:73
const char * xstrerr(int error)
Definition xstrerror.cc:83